diff --git a/.eslintrc.js b/.eslintrc.js index 6ec70e9243..d7574f3ea6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -19,19 +19,20 @@ module.exports = { "plugin:security/recommended", ], rules: { - // comma-dangle used for browser compatibility for browsers that don't support trailing commas - "comma-dangle": ["error", "always-multiline"], "eol-last": "error", // security/detect-object-injection just gives a lot of false positives // see https://github.com/nodesecurity/eslint-plugin-security/issues/21 "security/detect-object-injection": "off", "@typescript-eslint/no-var-requires": "error", + // Use typescript-eslint’s version of the no-redeclare rule, which isn’t triggered by overload signatures. + // TODO remove this once we start using the full @typescript-eslint/recommended ruleset in #958 + 'no-redeclare': 'off', + '@typescript-eslint/no-redeclare': 'error', }, overrides: [ { files: ["**/*.{ts,tsx}"], rules: { - "comma-dangle": ["error", "only-multiline"], "@typescript-eslint/no-unused-vars": ["error", { "varsIgnorePattern": "^_" }], // TypeScript already enforces these rules better than any eslint setup can "no-undef": "off", @@ -40,7 +41,7 @@ module.exports = { }, }, { - files: 'ably.d.ts', + files: ['ably.d.ts', 'modular.d.ts'], extends: [ 'plugin:jsdoc/recommended', ], @@ -54,9 +55,10 @@ module.exports = { "test", "tools", "scripts", - "docs", + "typedoc/generated", "react", "Gruntfile.js", + "grunt", ], settings: { jsdoc: { diff --git a/.github/workflows/bundle-report.yml b/.github/workflows/bundle-report.yml index 7866bb9dc3..67a81e6eee 100644 --- a/.github/workflows/bundle-report.yml +++ b/.github/workflows/bundle-report.yml @@ -17,16 +17,15 @@ jobs: run: > git config --global url."https://github.com/".insteadOf ssh://git@github.com/ - - name: Use Node.js 14.x + - name: Use Node.js 20.x uses: actions/setup-node@v1 with: - node-version: 14.x + node-version: 20.x - run: npm ci - name: Build bundle reports run: | mkdir bundle-reports npm run sourcemap -- --html bundle-reports/index.html - npm run sourcemap:noencryption -- --html bundle-reports/noencryption.html - uses: aws-actions/configure-aws-credentials@v1 with: aws-region: eu-west-2 @@ -37,3 +36,5 @@ jobs: sourcePath: bundle-reports githubToken: ${{ secrets.GITHUB_TOKEN }} artifactName: bundle-report + # This step performs some validation and may fail, so it should come after the steps that we want to always execute. + - run: npm run modulereport diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 97d2d52717..90d1e81ed6 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -14,16 +14,18 @@ jobs: run: > git config --global url."https://github.com/".insteadOf ssh://git@github.com/ - - name: Use Node.js 14.x + - name: Use Node.js 20.x uses: actions/setup-node@v1 with: - node-version: 14.x + node-version: 20.x - run: npm ci - run: npm run lint - run: npm run format:check - - run: npm run check-closure-compiler - - run: npx tsc --noEmit ably.d.ts build/ably-webworker.min.d.ts + - run: npx tsc --noEmit ably.d.ts modular.d.ts # for some reason, this doesn't work in CI using `npx attw --pack .` - run: npm pack - - run: npx attw ably-$(node -e "console.log(require('./package.json').version)").tgz --summary + - run: npx attw ably-$(node -e "console.log(require('./package.json').version)").tgz --summary --exclude-entrypoints 'ably/modular' + # see https://github.com/ably/ably-js/issues/1546 for why we ignore 'false-cjs' currently. + # should remove when switched to auto-generated type declaration files for modular variant of the library. + - run: npx attw ably-$(node -e "console.log(require('./package.json').version)").tgz --summary --entrypoints 'ably/modular' --ignore-rules false-cjs - run: npm audit --production diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 96023da288..f9e7184297 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -16,10 +16,10 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Use Node.js 16.x + - name: Use Node.js 20.x uses: actions/setup-node@v3 with: - node-version: 16.x + node-version: 20.x - name: Install Package Dependencies run: npm ci @@ -37,6 +37,6 @@ jobs: - name: Upload Documentation uses: ably/sdk-upload-action@v1 with: - sourcePath: docs/generated + sourcePath: typedoc/generated githubToken: ${{ secrets.GITHUB_TOKEN }} artifactName: typedoc diff --git a/.github/workflows/publish-cdn.yml b/.github/workflows/publish-cdn.yml index a4fe17b64f..13e21c85fa 100644 --- a/.github/workflows/publish-cdn.yml +++ b/.github/workflows/publish-cdn.yml @@ -23,9 +23,9 @@ jobs: aws-region: us-east-1 role-to-assume: arn:aws:iam::${{ secrets.ABLY_AWS_ACCOUNT_ID_SDK }}:role/prod-ably-sdk-cdn role-session-name: "${{ github.run_id }}-${{ github.run_number }}" - - name: Use Node.js 14.x + - name: Use Node.js 20.x uses: actions/setup-node@v1 with: - node-version: 14.x + node-version: 20.x - run: npm ci - run: node scripts/cdn_deploy.js --skipCheckout --tag=${{ github.event.inputs.version }} diff --git a/.github/workflows/react.yml b/.github/workflows/react.yml index 2c48513e7d..7802ab0f5c 100644 --- a/.github/workflows/react.yml +++ b/.github/workflows/react.yml @@ -14,7 +14,6 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: - node-version: 16 + node-version: 20 - run: npm ci - - run: npm run format:check - run: npm run test:react diff --git a/.github/workflows/test-browser.yml b/.github/workflows/test-browser.yml index eaa2f3be41..c434c2e582 100644 --- a/.github/workflows/test-browser.yml +++ b/.github/workflows/test-browser.yml @@ -20,10 +20,10 @@ jobs: run: > git config --global url."https://github.com/".insteadOf ssh://git@github.com/ - - name: Use Node.js 16.x + - name: Use Node.js 20.x uses: actions/setup-node@v1 with: - node-version: 16.x + node-version: 20.x - run: npm ci - name: Install Playwright browsers and dependencies run: npx playwright install --with-deps diff --git a/.github/workflows/test-node.yml b/.github/workflows/test-node.yml index 7efb047890..39ccf4d0e6 100644 --- a/.github/workflows/test-node.yml +++ b/.github/workflows/test-node.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [12.x, 14.x, 16.x] + node-version: [16.x, 18.x, 20.x] steps: - uses: actions/checkout@v2 with: diff --git a/.github/workflows/test-package.yml b/.github/workflows/test-package.yml new file mode 100644 index 0000000000..8e622413a9 --- /dev/null +++ b/.github/workflows/test-package.yml @@ -0,0 +1,20 @@ +name: Test NPM package +on: + pull_request: + push: + branches: + - main + +jobs: + test-npm-package: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - name: Use Node.js 20.x + uses: actions/setup-node@v1 + with: + node-version: 20.x + - run: npm ci + - run: npm run test:package diff --git a/.gitignore b/.gitignore index b424979fdc..072dde41f2 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,6 @@ npm-debug.log .tool-versions build/ react/ -docs/generated/ +typedoc/generated/ junit/ test/support/mocha_junit_reporter/build/ diff --git a/.mocharc.js b/.mocharc.js new file mode 100644 index 0000000000..fb2c98119c --- /dev/null +++ b/.mocharc.js @@ -0,0 +1,19 @@ +const config = { + require: ['source-map-support/register', 'test/support/modules_helper.js', 'test/support/test_helper.js'], + file: ['test/support/root_hooks.js'], + reporter: 'test/support/mocha_reporter.js', +}; + +// mocha has a ridiculous issue (https://github.com/mochajs/mocha/issues/4100) that command line +// specs don't override config specs; they are merged instead, so you can't run a single test file +// if you've defined specs in your config. therefore we work around it by only adding specs to the +// config if none are passed as arguments +if (!process.argv.slice(2).some(isTestFile)) { + config.spec = ['test/realtime/*.test.js', 'test/rest/*.test.js']; +} + +function isTestFile(arg) { + return arg.match(/\.test.js$/); +} + +module.exports = config; diff --git a/.prettierrc.json b/.prettierrc.json index fc56e31458..489d21d22f 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,5 +1,5 @@ { - "trailingComma": "es5", + "trailingComma": "all", "tabWidth": 2, "semi": true, "singleQuote": true, diff --git a/CHANGELOG.md b/CHANGELOG.md index ec8b215a5f..5430363b98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,92 @@ This contains only the most important and/or user-facing changes; for a full changelog, see the commit history. +## [2.0.0](https://github.com/ably/ably-js/tree/2.0.0) (2024-03-22) + +The 2.0.0 release introduces a number of new features and QoL improvements, including a new way to remove bloat and reduce the bundle size of your ably-js client, first-class support for Promises, a more idiomatic approach to using ably-js' React Hooks, enhancements to TypeScript typings, and more. + +Below is an overview of the major changes in this release. + +Please refer to the ably-js v2 [lib migration guide](./docs/migration-guides/v2/lib.md) and [React Hooks migration guide](./docs/migration-guides/v2/react-hooks.md) for the full details, including a list of all breaking changes and instructions on how to address them. + +### Bundle Size Reduction + +The default bundle size for the web has been reduced by ~32% compared to v1 - from 234.11 KiB to 159.32 KiB. When calculated with gzip compression, the reduction is ~30%, from 82.54 KiB to 57.9 KiB. + +Additionally, by utilizing the new modular variant of the library (see below) and JavaScript tree shaking, you can create your own minimal useful `Realtime` client and achieve a bundle size reduction of ~60.5% compared to v1 - from 234.11 KiB to 92.38 KiB (or ~66% for gzip: from 82.54 KiB to 28.18 KiB). + +### Modular variant of the library + +An ESM variant of the library is now available for browsers (but not for Node.js) via import from `ably/modular`. This modular variant of the library supports tree shaking, allowing for a reduction in the Ably bundle size within your application and improving the user experience. It can also be used by Web Workers. + +### React Hooks changes + +React Hooks, exported at `ably/react`, now require the new `ChannelProvider` component to define the channels you wish to use and the options for them. This addresses the complexities previously encountered with `useChannel` and `usePresence` hooks when attempting to dynamically change options for a channel and provides a more straightforward approach to set and manage these options. + +The functionality of the `usePresence` hook has been split into two separate hooks: `usePresence`, which is now used only to enter presence, and `usePresenceListener`, which is used to listen for presence updates. These new hooks offer better control over the desired presence behavior in your React components. + +### First-class support for Promises + +The callbacks API has been entirely removed, and the library now supports promises for all its asynchronous operations by default. + +### TypeScript typings + +The Types namespace has been removed. All types it contained are now exported at the top level. + +### Browser and Web Worker bundles + +- The browser bundle now relies on the native Web Crypto API instead of CryptoJS. The `ably/build/ably.noencryption` bundle has been removed, as it is no longer necessary. +- The browser bundle can now be directly used by Web Workers. The `ably/build/ably-webworker` bundle has been removed, as it is no longer necessary. + +### Supported platforms changes + +- Support for Internet Explorer has been dropped. +- Support for Node.js versions lower than 16 has been dropped. The supported Node.js versions are now 16, 18, and 20. +- The minimum supported versions for major browsers are: Chrome 58, Firefox 52, Edge 79, Safari 11, and Opera 45. + +
+View merged Pull Requests + +### Breaking Changes + +- Add `ChannelProvider` component to React Hooks [\#1620](https://github.com/ably/ably-js/pull/1620), [\#1654](https://github.com/ably/ably-js/pull/1654) +- Add untyped stats API [\#1522](https://github.com/ably/ably-js/pull/1522) +- Add type definitions for key returned by `generateRandomKey` [\#1320](https://github.com/ably/ably-js/pull/1320) +- Add mandatory `version` param to `Rest.request` [\#1231](https://github.com/ably/ably-js/pull/1231) +- Change `id` field to be named `ablyId` in React Hooks [\#1676](https://github.com/ably/ably-js/pull/1676) +- Change `usePresence` hook to two different hooks: for entering presence and subscribing to presence updates [\#1674](https://github.com/ably/ably-js/pull/1674) +- Change naming for enum-like namespaces in type declarations and change meaning for public `ChannelModes` type [\#1601](https://github.com/ably/ably-js/pull/1601) +- Change publishing methods to accept a `Message`-shaped object [\#1515](https://github.com/ably/ably-js/pull/1515) +- Change `Crypto.generateRandomKey` API to use Promises [\#1351](https://github.com/ably/ably-js/pull/1351) +- Change `fromEncoded()` and `fromEncodedArray()` methods on `Message` and `PresenceMessage` to be async [\#1311](https://github.com/ably/ably-js/pull/1311) +- Remove `XHRStreaming` transport support [\#1645](https://github.com/ably/ably-js/pull/1645) +- Remove code that's supporting older platforms [\#1629](https://github.com/ably/ably-js/pull/1629), [\#1633](https://github.com/ably/ably-js/pull/1633), [\#1641](https://github.com/ably/ably-js/pull/1641) +- Remove `recoveryKey` in favour of `createRecoveryKey()` on `Connection` [\#1613](https://github.com/ably/ably-js/pull/1613) +- Remove `any` from `stats()` param type [\#1561](https://github.com/ably/ably-js/pull/1561) +- Remove the dedicated Web Worker bundle `ably/build/ably-webworker` and add support for using `ably` and `ably/modular` in Web Workers [\#1550](https://github.com/ably/ably-js/pull/1550) +- Remove false class exports in type declarations [\#1524](https://github.com/ably/ably-js/pull/1524) +- Remove the `Types` namespace [\#1518](https://github.com/ably/ably-js/pull/1518) +- Remove `noencryption` variant of the library [\#1500](https://github.com/ably/ably-js/pull/1500) +- Remove public callbacks API [\#1358](https://github.com/ably/ably-js/pull/1358) +- Remove CryptoJS library and replace it with the Web Crypto API in web bundle [\#1299](https://github.com/ably/ably-js/pull/1299), [\#1325](https://github.com/ably/ably-js/pull/1325), [\#1333](https://github.com/ably/ably-js/pull/1333) +- Remove `ably-commonjs*.js` files [\#1229](https://github.com/ably/ably-js/pull/1229) +- Remove deprecated APIs [\#1227](https://github.com/ably/ably-js/pull/1227) +- Remove deprecated `fromEncoded*` type declarations [\#1222](https://github.com/ably/ably-js/pull/1222) +- Remove deprecated `ClientOptions` parameters [\#1221](https://github.com/ably/ably-js/pull/1221) +- Remove the `ClientOptions.log` property and replace it with separate `logLevel` and `logHandler` properties [\#1216](https://github.com/ably/ably-js/pull/1216) +- Remove support for JSONP [\#1215](https://github.com/ably/ably-js/pull/1215) +- Fix `whenState` inconsistent behavior in `Connection` and `RealtimeChannel` [\#1640](https://github.com/ably/ably-js/pull/1640) +- Fix the type definition of `Crypto.getDefaultParams` [\#1352](https://github.com/ably/ably-js/pull/1352) + +### Features + +- Add `publish` function to the `useChannel` hook [\#1658](https://github.com/ably/ably-js/pull/1658) +- Add logs to all HTTP activity [\#1581](https://github.com/ably/ably-js/pull/1581) + +
+ +[Full Changelog](https://github.com/ably/ably-js/compare/1.2.50...2.0.0) + ## [1.2.50](https://github.com/ably/ably-js/tree/1.2.50) (2024-03-21) - Add new logging API to `ClientOptions` and add a deprecation warning for the old one [\#1671](https://github.com/ably/ably-js/pull/1671) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4dce45af00..7cfdd46c47 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,9 +8,8 @@ 4. Commit your changes (`git commit -am 'Add some feature'`) 5. Ensure you have added suitable tests and the test suite is passing(`npm test`) 6. Ensure the [type definitions](https://github.com/ably/ably-js/blob/main/ably.d.ts) have been updated if the public API has changed -7. Ensure you stick to the version of JS used by the library (currently ES5). (The minfication task (`npm run grunt -- closureCompiler:ably.js`) will enforce that you stick to ES5 syntax, but will not enforce that you don't use, for example, new methods) -8. Push the branch (`git push origin my-new-feature`) -9. Create a new Pull Request +7. Push the branch (`git push origin my-new-feature`) +8. Create a new Pull Request ## Release Process @@ -30,7 +29,7 @@ ## Building the library -To build the library, simply run `npm run build`. Building the library currently requires NodeJS <= v16. +To build the library, simply run `npm run build`. Building the library currently requires NodeJS >= v16. Since webpack builds are slow, commands are also available to only build the output for specific platforms (eg `npm run build:node`), see [package.json](./package.json) for the full list of available commands @@ -46,27 +45,27 @@ Run the Mocha test suite npm run test:node -Or run just one test file +You can pass any Mocha CLI arguments and flags to the test:node script after the `--` separator, for example running one test file: - npm run test:node -- --file=test/realtime/auth.test.js + npm run test:node -- test/realtime/auth.test.js Or run just one test - npm run test:node -- --file=test/rest/status.test.js --grep=test_name_here + npm run test:node -- --grep=test_name_here Or run test skipping the build - npm run test:node:skip-build -- --file=test/rest/status.test.js --grep=test_name_here + npm run test:node:skip-build -- test/rest/status.test.js --grep=test_name_here ### Debugging the mocha tests locally with a debugger Run the following command to launch tests with the debugger enabled. The tests will block until you attach a debugger. - node --inspect-brk=9229 node_modules/.bin/grunt test:node + node --inspect-brk=9229 node_modules/.bin/mocha Alternatively you can also run the tests for single file - node --inspect-brk=9229 node_modules/.bin/grunt test:node --test=test/realtime/auth.test.js + node --inspect-brk=9229 node_modules/.bin/mocha test/realtime/auth.test.js The included vscode launch config allows you to launch and attach the debugger in one step, simply open the test file you want to run and start debugging. Note that breakpoint setting for realtime code will be within the diff --git a/Gruntfile.js b/Gruntfile.js index a29bac491e..a19bc21ea3 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -3,11 +3,12 @@ var fs = require('fs'); var path = require('path'); var webpackConfig = require('./webpack.config'); +var esbuild = require('esbuild'); +var process = require('process'); +var MochaServer = require('./test/web_server'); +var esbuildConfig = require('./grunt/esbuild/build'); module.exports = function (grunt) { - grunt.loadNpmTasks('grunt-contrib-concat'); - grunt.loadNpmTasks('grunt-closure-tools'); - grunt.loadNpmTasks('grunt-bump'); grunt.loadNpmTasks('grunt-webpack'); var dirs = { @@ -16,75 +17,40 @@ module.exports = function (grunt) { fragments: 'src/platform/web/fragments', static: 'build', dest: 'build', - crypto_js: 'node_modules/crypto-js/src', - tools_compiler: __dirname + '/node_modules/google-closure-compiler/compiler.jar', }; - function compilerSpec(src, dest) { - return { - src: src, - dest: dest || src.replace(/\.js/, '.min.js'), - }; - } - - function execExternal(cmd) { - return function () { - var done = this.async(); - grunt.log.ok('Executing ' + cmd); + async function execExternalPromises(cmd) { + grunt.log.ok('Executing ' + cmd); + return new Promise(function (resolve, reject) { require('child_process').exec(cmd, function (err, stdout, stderr) { if (err) { - grunt.fatal('Error executing "' + cmd + '": ' + stderr); + grunt.fatal('Error executing "' + cmd + '":\nstderr:\n' + stderr + '\nstdout:\n' + stdout); + reject(err); } console.log(stdout); stderr && console.error(stderr); - done(); + resolve(); }); + }); + } + + function execExternal(cmd) { + return function () { + var done = this.async(); + execExternalPromises(cmd) + .then(() => done()) + .catch((error) => done(error)); }; } var gruntConfig = { dirs: dirs, - pkgVersion: grunt.file.readJSON('package.json').version, webpack: { all: Object.values(webpackConfig), - node: [webpackConfig.node, webpackConfig.mochaJUnitReporterNode], browser: [webpackConfig.browser, webpackConfig.browserMin, webpackConfig.mochaJUnitReporterBrowser], }, }; - gruntConfig['closureCompiler'] = { - options: { - compilerFile: dirs.tools_compiler, - compilerOpts: { - compilation_level: 'SIMPLE_OPTIMIZATIONS', - /* By default, the compiler assumes you're using es6 and transpiles to - * es5, adding various (unnecessary and undesired) polyfills. Specify - * both in and out to es5 to avoid transpilation */ - language_in: 'ECMASCRIPT5', - language_out: 'ECMASCRIPT5', - strict_mode_input: true, - checks_only: true, - warning_level: 'QUIET', - }, - }, - 'ably.js': compilerSpec('<%= dirs.static %>/ably.js'), - }; - - gruntConfig.bump = { - options: { - files: ['package.json', 'README.md'], - globalReplace: true, - commit: true, - commitMessage: 'Regenerate and release version %VERSION%', - commitFiles: [], // Add files manually as can't add new files with a commit flag - createTag: true, - tagName: '%VERSION%', - tagMessage: 'Version %VERSION%', - push: false, - prereleaseName: 'beta', - }, - }; - grunt.initConfig(gruntConfig); grunt.registerTask('checkGitSubmodules', 'Check, if git submodules are properly installed', function () { @@ -107,24 +73,54 @@ module.exports = function (grunt) { }); }); - grunt.registerTask('build', ['checkGitSubmodules', 'webpack:all']); + grunt.registerTask('build', ['checkGitSubmodules', 'webpack:all', 'build:browser', 'build:node']); - grunt.registerTask('build:node', ['checkGitSubmodules', 'webpack:node']); + grunt.registerTask('all', ['build', 'requirejs']); - grunt.registerTask('build:browser', ['checkGitSubmodules', 'webpack:browser']); + grunt.registerTask('mocha:webserver', 'Run the Mocha web server', function () { + const done = this.async(); + const server = new MochaServer(); + server.listen(); + + process.on('SIGTERM', () => { + server.close(); + done(); + }); + process.on('SIGINT', () => { + server.close(); + done(); + }); + }); - grunt.registerTask('check-closure-compiler', ['build', 'closureCompiler:ably.js']); + grunt.registerTask('build:node', function () { + const done = this.async(); - grunt.registerTask('all', ['build', 'check-closure-compiler', 'requirejs']); + esbuild + .build(esbuildConfig.nodeConfig) + .then(() => { + done(true); + }) + .catch((err) => { + done(err); + }); + }); - grunt.loadTasks('test/tasks'); + grunt.registerTask('build:browser', function () { + var done = this.async(); - grunt.registerTask('test', ['test:node']); - grunt.registerTask( - 'test:node', - 'Build the library and run the node test suite\nOptions\n --test [tests] e.g. --test test/rest/auth.js', - ['build:node', 'mocha'] - ); + Promise.all([ + esbuild.build(esbuildConfig.webConfig), + esbuild.build(esbuildConfig.minifiedWebConfig), + esbuild.build(esbuildConfig.modularConfig), + ]) + .then(() => { + console.log('esbuild succeeded'); + done(true); + }) + .catch((err) => { + done(err); + }); + }); grunt.registerTask('test:webserver', 'Launch the Mocha test web server on http://localhost:3000/', [ 'build:browser', @@ -132,58 +128,71 @@ module.exports = function (grunt) { 'mocha:webserver', ]); - grunt.registerTask('release:refresh-pkgVersion', 'Refreshes GruntConfig.pkgVersion', function () { - grunt.config('pkgVersion', grunt.file.readJSON('package.json').version); - grunt.log.ok('pkgVersion updated'); - }); + (function () { + const baseDir = path.join(__dirname, 'test', 'package', 'browser'); + const buildDir = path.join(baseDir, 'build'); + + grunt.registerTask( + 'test:package:browser:prepare-project', + 'Prepare an app to be used for testing the NPM package in a browser environment', + function () { + const done = this.async(); + + (async function () { + if (grunt.file.exists(buildDir)) { + grunt.file.delete(buildDir); + } + + // Create an app based on the template + grunt.file.copy(path.join(baseDir, 'template'), buildDir); + + // Use `npm pack` to generate a .tgz NPM package + await execExternalPromises('npm run build'); + await execExternalPromises('npm pack --pack-destination test/package/browser/build'); + const version = grunt.file.readJSON('package.json').version; + const packFileName = `ably-${version}.tgz`; + + // Configure app to consume the generated .tgz file + const pwd = process.cwd(); + process.chdir(buildDir); + await execExternalPromises(`npm install ${packFileName}`); + + // Install further dependencies required for testing the app + await execExternalPromises('npm run test:install-deps'); + process.chdir(pwd); + })() + .then(() => done(true)) + .catch((error) => done(error)); + }, + ); - grunt.registerTask('release:git-add-generated', 'Adds generated files to the git staging area', function () { - var done = this.async(); - var generatedFiles = [ - gruntConfig.dirs.common + '/lib/util/defaults.js', - gruntConfig.dirs.fragments + '/license.js', - 'package.json', - 'package-lock.json', - 'README.md', - 'test/support/browser_file_list.js', - ]; - var cmd = 'git add -A ' + generatedFiles.join(' '); - grunt.log.ok('Executing ' + cmd); + grunt.registerTask('test:package:browser:test', 'Test the NPM package in a browser environment', function () { + const done = this.async(); - require('child_process').exec(cmd, function (err, stdout, stderr) { - if (err) { - grunt.fatal('git add . -A failed with ' + stderr); - } - done(); - }); - }); + (async function () { + grunt.task.requires('test:package:browser:prepare-project'); - grunt.registerTask('release:git-push', 'Pushes to git', execExternal('git push origin main --follow-tags')); + const pwd = process.cwd(); + process.chdir(buildDir); - grunt.registerTask('release:ably-deploy', 'Deploys to ably CDN', function () { - var version = grunt.file.readJSON('package.json').version, - cmd = 'node scripts/cdn_deploy.js --skipCheckout --tag ' + version; - console.log('Publishing version ' + version + ' of the library to the CDN'); - execExternal(cmd).call(this); - }); + // Perform type checking on TypeScript code that imports ably-js + await execExternalPromises('npm run typecheck'); - grunt.registerTask('release:deploy', 'Pushes a new release to github and then deploys to the Ably CDN', function () { - grunt.task.run(['release:git-push', 'release:ably-deploy']); - }); + // Build bundle including ably-js + await execExternalPromises('npm run build'); + + // Test that the code which exercises ably-js behaves as expected + await execExternalPromises('npm run test'); + + process.chdir(pwd); + })() + .then(() => done(true)) + .catch((error) => done(error)); + }); + })(); - grunt.registerTask( - 'release', - 'Increments the version, regenerates, and makes a tagged commit. Run as "grunt release:type", where "type" is "major", "minor", "patch", "prepatch", etc.)', - function (versionType) { - grunt.task.run([ - 'bump-only:' + versionType, - 'release:refresh-pkgVersion', - 'all', - 'release:git-add-generated', - 'bump-commit', - ]); - } - ); + grunt.registerTask('test:package:browser', ['test:package:browser:prepare-project', 'test:package:browser:test']); + grunt.registerTask('test:package', ['test:package:browser']); grunt.registerTask('default', 'all'); }; diff --git a/README.md b/README.md index b9fb92c9f7..bfb9de6659 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,21 @@ This library currently targets the [Ably client library features spec](https://w This SDK supports the following platforms: -**Browsers:** All major desktop and mobile browsers, including (but not limited to) Chrome, Firefox, IE (only version 9 or newer), Safari on iOS and macOS, Opera, and Android browsers. +**Browsers:** All major desktop and mobile browsers, including (but not limited to) Chrome, Firefox, Edge, Safari on iOS and macOS, Opera, and Android browsers. IE is not supported. See compatibility table below for more information on minimum supported versions for major browsers: + +| Browser | Minimum supported version | Release date | +| ------- | :-----------------------: | -----------: | +| Chrome | 58 | Apr 19, 2017 | +| Firefox | 52 | Mar 7, 2017 | +| Edge | 79 | Dec 15, 2020 | +| Safari | 11 | Sep 19, 2017 | +| Opera | 45 | May 10, 2017 | **Webpack:** see [using Webpack in browsers](#using-webpack), or [our guide for serverside Webpack](#serverside-usage-with-webpack) -**Node.js:** version 8.17 or newer. (1.1.x versions work on Node.js 4.5 or newer). We do not currently provide an ESM bundle, please [contact us](https://www.ably.com/contact) if you would would like to use ably-js in a NodeJS ESM project. +**Node.js:** version 16.x or newer. (1.1.x versions work on Node.js 4.5 or newer, 1.2.x versions work on Node.js 8.17 or newer). We do not currently provide an ESM bundle, please [contact us](https://www.ably.com/contact) if you would would like to use ably-js in a NodeJS ESM project. -**React (release candidate)** We offer a set of React Hooks which make it seamless to use ably-js in your React application. See the [React Hooks documentation](./docs/react.md) for more details. +**React (release candidate):** We offer a set of React Hooks which make it seamless to use ably-js in your React application. See the [React Hooks documentation](./docs/react.md) for more details. **React Native:** We aim to support all platforms supported by React Native. If you find any issues please raise an issue or [contact us](https://www.ably.com/contact). @@ -28,18 +36,15 @@ This SDK supports the following platforms: **TypeScript:** see [below](#typescript) -**WebWorkers**: We build a separate bundle which supports running in a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) context. You can import it like this: +**WebWorkers:** The browser bundle supports running in a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) context. You can also use the [modular variant](#modular-tree-shakable-variant) of the library in Web Workers. -```js -import Ably from 'ably/build/ably-webworker.min'; -``` +We test the library against a selection of browsers using their latest versions. Please refer to [the test-browser GitHub workflow](./.github/workflows/test-browser.yml) for the set of browsers that currently undergo CI testing. -We regression-test the library against a selection of those (which will change over time, but usually consists of the versions that are supported upstream, plus old versions of IE). +We regression-test the library against a selection of Node.js versions, which will change over time. We will always support and test against current LTS Node.js versions, and optionally some older versions that are still supported by upstream dependencies. We reserve the right to drop support for non-LTS versions in a non-major release. We will update the `engines` field in [package.json](./package.json) whenever we change the Node.js versions supported by the project. Please refer to [the test-node GitHub workflow](./.github/workflows/test-node.yml) for the set of versions that currently undergo CI testing. However, we aim to be compatible with a much wider set of platforms and browsers than we can possibly test on. That means we'll happily support (and investigate reported problems with) any reasonably-widely-used browser. So if you find any compatibility issues, please do [raise an issue](https://github.com/ably/ably-js/issues) in this repository or [contact Ably customer support](https://support.ably.com) for advice. -Ably-js has fallback mechanisms in order to be able to support older browsers; specifically it supports comet-based connections for browsers that do not support websockets, and this includes JSONP for browsers that do not support cross-origin XHR. Each of these fallback transport mechanisms is supported and tested on all the browsers we test against, even when those browsers do not themselves require those fallbacks. These mean that the library should be compatible with nearly any browser on most platforms. -Known browser incompatibilities will be documented as an issue in this repository using the ["compatibility" label](https://github.com/ably/ably-js/issues?q=is%3Aissue+is%3Aopen+label%3A%22compatibility%22). +If you require support for older browsers and Node.js, you can use the security-maintained version 1 of the library. Install version 1 via [CDN link](https://cdn.ably.com/lib/ably.min-1.js), or from npm with `npm install ably@1 --save`. It supports IE versions 9 or newer, older versions of major browsers, and Node.js 8.17 or newer. Note that version 1 will only receive security updates and critical bug fixes, and won't include any new features. For complete API documentation, see the [Ably documentation](https://www.ably.com/docs). @@ -55,8 +60,6 @@ and require as: var Ably = require('ably'); ``` -For the version of the library where async methods return promises, use `var Ably = require('ably/promises');` instead. For the explicitly-callback-based variant use `require('ably/callbacks')`– see [Async API style](#async-api-style). - For usage, jump to [Using the Realtime API](#using-the-realtime-api) or [Using the REST API](#using-the-rest-api). #### Serverside usage with webpack @@ -81,7 +84,47 @@ For usage, jump to [Using the Realtime API](#using-the-realtime-api) or [Using t WebPack will search your `node_modules` folder by default, so if you include `ably` in your `package.json` file, when running Webpack the following will allow you to `require('ably')` (or if using typescript or ES6 modules, `import * as Ably from 'ably';`). If your webpack target is set to 'browser', this will automatically use the browser commonjs distribution. -If that doesn't work for some reason (e.g. you are using a custom webpack target), you can reference the `ably-commonjs.js` static file directly: `require('ably/build/ably-commonjs.js');` (or `import * as Ably from 'ably/build/ably-commonjs.js'` for typescript / ES6 modules). +If that doesn't work for some reason (e.g. you are using a custom webpack target), you can reference the `ably.js` static file directly: `require('ably/build/ably.js');` (or `import * as Ably from 'ably/build/ably.js'` for typescript / ES6 modules). + +#### Modular (tree-shakable) variant + +Aimed at those who are concerned about their app’s bundle size, the modular variant of the library allows you to create a client which has only the functionality that you choose. Unused functionality can then be tree-shaken by your module bundler. + +The modular variant of the library provides: + +- a `BaseRealtime` class; +- various plugins that add functionality to a `BaseRealtime` instance, such as `Rest`, `RealtimePresence`, etc. + +To use this variant of the library, import the `BaseRealtime` class from `ably/modular`, along with the plugins that you wish to use. Then, pass these plugins to the `BaseRealtime` constructor as shown in the example below: + +```javascript +import { BaseRealtime, WebSocketTransport, FetchRequest, RealtimePresence } from 'ably/modular'; + +const client = new BaseRealtime({ + key: 'YOUR_ABLY_API_KEY' /* Replace with a real key from the Ably dashboard */, + plugins: { + WebSocketTransport, + FetchRequest, + RealtimePresence, + }, +}); +``` + +You must provide: + +- at least one HTTP request implementation; that is, one of `FetchRequest` or `XHRRequest`; +- at least one realtime transport implementation; that is, one of `WebSocketTransport` or `XHRPolling`. + +`BaseRealtime` offers the same API as the `Realtime` class described in the rest of this `README`. This means that you can develop an application using the default variant of the SDK and switch to the modular version when you wish to optimize your bundle size. + +In order to further reduce bundle size, the modular variant of the SDK performs less logging than the default variant. It only logs: + +- messages that have a `logLevel` of 1 (that is, errors) +- a small number of other network events + +If you need more verbose logging, use the default variant of the SDK. + +For more information about the modular variant of the SDK, see the [generated documentation](https://sdk.ably.com/builds/ably/ably-js/main/typedoc/modules/modular.html) (this link points to the documentation for the `main` branch). ### TypeScript @@ -90,26 +133,16 @@ The TypeScript typings are included in the package and so all you have to do is: ```typescript import * as Ably from 'ably'; -let options: Ably.Types.ClientOptions = { key: 'foo' }; +let options: Ably.ClientOptions = { key: 'foo' }; let client = new Ably.Realtime(options); /* inferred type Ably.Realtime */ -let channel = client.channels.get('feed'); /* inferred type Ably.Types.RealtimeChannel */ +let channel = client.channels.get('feed'); /* inferred type Ably.RealtimeChannel */ ``` -For the version of the library where async methods return promises, use `import * as Ably from 'ably/promises';` instead. For the explicitly-callback-based variant use `import * as Ably from 'ably/callbacks'` – see [Async API style](#async-api-style). - Intellisense in IDEs with TypeScript support is supported: ![TypeScript suggestions](./resources/typescript-demo.gif) -If you need to explicitly import the type definitions, see [ably.d.ts](./ably.d.ts) (or `promises.d.ts` if you're requiring the library as `ably/promises`). - -## Async API style - -This library exposes two API variants. Firstly, the original (and presently the default) callback-based API, which follows the usual Node.js error-first callback style. Second, a promises-based API. With the promises variant, you can still pass a callback to methods and the callback will work as expected, but if you do not pass a callback, the method will return a promise. The API in use can be selected explicitly by requiring that specific variant when requiring/importing the library (or in the case of the browser version, when instantiating it). The usage instructions below make reference to both variants. - -For this library version, and for all future 1.x versions, the callback-based API will be the default. This means that the promises-based variant will need to be explicitly selected, to avoid breaking backwards compatibility. A move to the promises-based variant as the default is likely at the next major release (i.e. 2.x onwards). - -For usage, jump to [Using the async API style](#using-the-async-api-style). +If you need to explicitly import the type definitions, see [ably.d.ts](./ably.d.ts). ## NativeScript @@ -131,13 +164,6 @@ var client = new Ably.Realtime(key: string); // which must contain at least one auth option, i.e. at least // one of: key, token, tokenDetails, authUrl, or authCallback var client = new Ably.Realtime(options: ClientOptions); - -// For a version of the library where async methods return promises if -// you don't pass a callback: -var client = new Ably.Realtime.Promise(options: string | ClientOptions); - -// For the explicitly-callback-based variant (see 'Async API style' below): -var client = new Ably.Rest.Callbacks(options: string | ClientOptions); ``` ### Connection @@ -198,36 +224,26 @@ If you would like to inspect the `Message` instances in order to identify whethe ```javascript // Publish a single message with name and data -channel.publish('greeting', 'Hello World!'); - -// Optionally, you can use a callback to be notified of success or failure -channel.publish('greeting', 'Hello World!', function(err) { - if(err) { - console.log('publish failed with error ' + err); - } else { - console.log('publish succeeded'); - } -}) +await channel.publish('greeting', 'Hello World!'); // Publish several messages at once -channel.publish([{name: 'greeting', data: 'Hello World!'}, ...], callback); +await channel.publish([{name: 'greeting', data: 'Hello World!'}, ...]); ``` ### Querying the History ```javascript -channel.history(function(err, messagesPage) { - messagesPage // PaginatedResult - messagesPage.items // array of Message - messagesPage.items[0].data // payload for first message - messagesPage.items.length // number of messages in the current page of history - messagesPage.hasNext() // true if there are further pages - messagesPage.isLast() // true if this page is the last page - messagesPage.next(function(nextPage) { ... }); // retrieves the next page as PaginatedResult -}); +const messagesPage = channel.history() +messagesPage // PaginatedResult +messagesPage.items // array of Message +messagesPage.items[0].data // payload for first message +messagesPage.items.length // number of messages in the current page of history +messagesPage.hasNext() // true if there are further pages +messagesPage.isLast() // true if this page is the last page +const nextPage = await messagesPage.next(); // retrieves the next page as PaginatedResult // Can optionally take an options param, see https://www.ably.com/docs/rest-api/#message-history -channel.history({start: ..., end: ..., limit: ..., direction: ...}, function(err, messagesPage) { ...}); +const messagesPage = await channel.history({start: ..., end: ..., limit: ..., direction: ...}); ``` ### Presence on a channel @@ -235,9 +251,8 @@ channel.history({start: ..., end: ..., limit: ..., direction: ...}, function(err Getting presence: ```javascript -channel.presence.get(function (err, presenceSet) { - presenceSet; // array of PresenceMessages -}); +const presenceSet = channel.presence.get(); +presenceSet; // array of PresenceMessages ``` Note that presence#get on a realtime channel does not return a @@ -246,17 +261,14 @@ PaginatedResult, as the library maintains a local copy of the presence set. Entering (and leaving) the presence set: ```javascript -channel.presence.enter('my status', function (err) { - // now I am entered -}); +await channel.presence.enter('my status'); +// now I am entered -channel.presence.update('new status', function (err) { - // my presence data is updated -}); +await channel.presence.update('new status'); +// my presence data is updated -channel.presence.leave(function (err) { - // I've left the presence set -}); +await channel.presence.leave() +// I've left the presence set ``` If you are using a client which is allowed to use any clientId -- @@ -266,24 +278,23 @@ https://www.ably.com/docs/general/authentication for more information), you can use ```javascript -channel.presence.enterClient('myClientId', 'status', function(err) { ... }); +await channel.presence.enterClient('myClientId', 'status'); // and similarly, updateClient and leaveClient ``` ### Querying the Presence History ```javascript -channel.presence.history(function(err, messagesPage) { // PaginatedResult - messagesPage.items // array of PresenceMessage - messagesPage.items[0].data // payload for first message - messagesPage.items.length // number of messages in the current page of history - messagesPage.hasNext() // true if there are further pages - messagesPage.isLast() // true if this page is the last page - messagesPage.next(function(nextPage) { ... }); // retrieves the next page as PaginatedResult -}); +const messagesPage = channel.presence.history(); // PaginatedResult +messagesPage.items // array of PresenceMessage +messagesPage.items[0].data // payload for first message +messagesPage.items.length // number of messages in the current page of history +messagesPage.hasNext() // true if there are further pages +messagesPage.isLast() // true if this page is the last page +const nextPage = await messagesPage.next(); // retrieves the next page as PaginatedResult // Can optionally take an options param, see https://www.ably.com/docs/rest-api/#message-history -channel.presence.history({start: ..., end: ..., limit: ..., direction: ...}, function(err, messagesPage) { ...}); +const messagesPage = await channel.presence.history({start: ..., end: ..., limit: ..., direction: ...); ``` ### Symmetrical end-to-end encrypted payloads on a channel @@ -293,24 +304,22 @@ When a 128 bit or 256 bit key is provided to the library, the `data` attributes ```javascript // Generate a random 256-bit key for demonstration purposes (in // practice you need to create one and distribute it to clients yourselves) -Ably.Realtime.Crypto.generateRandomKey(function (err, key) { - var channel = client.channels.get('channelName', { cipher: { key: key } }); - - channel.subscribe(function (message) { - message.name; // 'name is not encrypted' - message.data; // 'sensitive data is encrypted' - }); +const key = await Ably.Realtime.Crypto.generateRandomKey(); +var channel = client.channels.get('channelName', { cipher: { key: key } }); - channel.publish('name is not encrypted', 'sensitive data is encrypted'); +channel.subscribe(function (message) { + message.name; // 'name is not encrypted' + message.data; // 'sensitive data is encrypted' }); + +channel.publish('name is not encrypted', 'sensitive data is encrypted'); ``` -You can also change the key on an existing channel using setOptions (which takes a callback which is called after the new encryption settings have taken effect): +You can also change the key on an existing channel using setOptions (which completes after the new encryption settings have taken effect): ```javascript -channel.setOptions({cipher: {key: }}, function() { - // New encryption settings are in effect -}) +await channel.setOptions({cipher: {key: }}); +// New encryption settings are in effect ``` ### Message interactions @@ -327,6 +336,14 @@ function sendReaction(emoji) { See https://www.ably.com/docs/realtime/messages#message-interactions for more detail. +### Fallback transport mechanisms + +Ably-js has fallback transport mechanisms to ensure its realtime capabilities can function in network conditions (such as firewalls or proxies) that might prevent the client from establishing a WebSocket connection. + +The default `Ably.Realtime` client includes these mechanisms by default. If you are using modular variant of the library, you may wish to provide the `BaseRealtime` instance with one or more alternative transport modules, namely `XHRStreaming` and/or `XHRPolling`, alongside `WebSocketTransport`, so your connection is less susceptible to these external conditions. For instructions on how to do this, refer to the [modular variant of the library](#modular-tree-shakable-variant) section. + +Each of these fallback transport mechanisms is supported and tested on all the browsers we test against, even when those browsers do not themselves require those fallbacks. + ## Using the REST API This readme gives some basic examples. For our full API documentation, please go to https://www.ably.com/docs . @@ -343,13 +360,6 @@ var client = new Ably.Rest(key: string); // which must contain at least one auth option, i.e. at least // one of: key, token, tokenDetails, authUrl, or authCallback var client = new Ably.Rest(options: ClientOptions); - -// For a version of the library where async methods return promises if -// you don't pass a callback: -var client = new Ably.Rest.Promise(options: string | ClientOptions); - -// For the explicitly-callback-based variant (see 'Async API style' above): -var client = new Ably.Rest.Callbacks(options: string | ClientOptions); ``` Given: @@ -362,75 +372,67 @@ var channel = client.channels.get('test'); ```javascript // Publish a single message with name and data -channel.publish('greeting', 'Hello World!'); - -// Optionally, you can use a callback to be notified of success or failure -channel.publish('greeting', 'Hello World!', function(err) { - if(err) { - console.log('publish failed with error ' + err); - } else { - console.log('publish succeeded'); - } -}) +try { + channel.publish('greeting', 'Hello World!'); + console.log('publish succeeded'); +} catch (err) { + console.log('publish failed with error ' + err); +} // Publish several messages at once -channel.publish([{name: 'greeting', data: 'Hello World!'}, ...], callback); +await channel.publish([{name: 'greeting', data: 'Hello World!'}, ...]); ``` ### Querying the History ```javascript -channel.history(function(err, messagesPage) { - messagesPage // PaginatedResult - messagesPage.items // array of Message - messagesPage.items[0].data // payload for first message - messagesPage.items.length // number of messages in the current page of history - messagesPage.hasNext() // true if there are further pages - messagesPage.isLast() // true if this page is the last page - messagesPage.next(function(nextPage) { ... }); // retrieves the next page as PaginatedResult -}); +const messagesPage = await channel.history(); +messagesPage // PaginatedResult +messagesPage.items // array of Message +messagesPage.items[0].data // payload for first message +messagesPage.items.length // number of messages in the current page of history +messagesPage.hasNext() // true if there are further pages +messagesPage.isLast() // true if this page is the last page +const nextPage = await messagesPage.next(); // retrieves the next page as PaginatedResult // Can optionally take an options param, see https://www.ably.com/docs/rest-api/#message-history -channel.history({start: ..., end: ..., limit: ..., direction: ...}, function(err, messagesPage) { ...}); +await channel.history({start: ..., end: ..., limit: ..., direction: ...}); ``` ### Presence on a channel ```javascript -channel.presence.get(function(err, presencePage) { // PaginatedResult - presencePage.items // array of PresenceMessage - presencePage.items[0].data // payload for first message - presencePage.items.length // number of messages in the current page of members - presencePage.hasNext() // true if there are further pages - presencePage.isLast() // true if this page is the last page - presencePage.next(function(nextPage) { ... }); // retrieves the next page as PaginatedResult -}); +const presencePage = await channel.presence.get() // PaginatedResult +presencePage.items // array of PresenceMessage +presencePage.items[0].data // payload for first message +presencePage.items.length // number of messages in the current page of members +presencePage.hasNext() // true if there are further pages +presencePage.isLast() // true if this page is the last page +const nextPage = await presencePage.next(); // retrieves the next page as PaginatedResult ``` ### Querying the Presence History ```javascript -channel.presence.history(function(err, messagesPage) { // PaginatedResult - messagesPage.items // array of PresenceMessage - messagesPage.items[0].data // payload for first message - messagesPage.items.length // number of messages in the current page of history - messagesPage.hasNext() // true if there are further pages - messagesPage.isLast() // true if this page is the last page - messagesPage.next(function(nextPage) { ... }); // retrieves the next page as PaginatedResult -}); +const messagesPage = channel.presence.history(); // PaginatedResult +messagesPage.items // array of PresenceMessage +messagesPage.items[0].data // payload for first message +messagesPage.items.length // number of messages in the current page of history +messagesPage.hasNext() // true if there are further pages +messagesPage.isLast() // true if this page is the last page +const nextPage = await messagesPage.next(); // retrieves the next page as PaginatedResult // Can optionally take an options param, see https://www.ably.com/docs/rest-api/#message-history -channel.history({start: ..., end: ..., limit: ..., direction: ...}, function(err, messagesPage) { ...}); +const messagesPage = channel.history({start: ..., end: ..., limit: ..., direction: ...}); ``` ### Getting the status of a channel ```javascript -channel.status(function(err, channelDetails) { - channelDetails.channelId // The name of the channel - channelDetails.status.isActive // A boolean indicating whether the channel is active - channelDetails.status.occupancy // Contains metadata relating to the occupants of the channel -}); +const channelDetails = await channel.status(); +channelDetails.channelId // The name of the channel +channelDetails.status.isActive // A boolean indicating whether the channel is active +channelDetails.status.occupancy // Contains metadata relating to the occupants of the channel ``` ### Generate Token and Token Request @@ -441,134 +443,49 @@ explanation of Ably's authentication mechanism. Requesting a token: ```javascript -client.auth.requestToken(function(err, tokenDetails) { - // tokenDetails is instance of TokenDetails - // see https://www.ably.com/docs/rest/authentication/#token-details for its properties +const tokenDetails = await client.auth.requestToken(); +// tokenDetails is instance of TokenDetails +// see https://www.ably.com/docs/rest/authentication/#token-details for its properties - // Now we have the token, we can send it to someone who can instantiate a client with it: - var clientUsingToken = new Ably.Realtime(tokenDetails.token); -}); +// Now we have the token, we can send it to someone who can instantiate a client with it: +var clientUsingToken = new Ably.Realtime(tokenDetails.token); // requestToken can take two optional params // tokenParams: https://www.ably.com/docs/rest/authentication/#token-params // authOptions: https://www.ably.com/docs/rest/authentication/#auth-options -client.auth.requestToken(tokenParams, authOptions, function(err, tokenDetails) { ... }); +const tokenDetails = await client.auth.requestToken(tokenParams, authOptions); ``` Creating a token request (for example, on a server in response to a request by a client using the `authCallback` or `authUrl` mechanisms): ```javascript -client.auth.createTokenRequest(function(err, tokenRequest) { - // now send the tokenRequest back to the client, which will - // use it to request a token and connect to Ably -}); +const tokenRequest = await client.auth.createTokenRequest(); +// now send the tokenRequest back to the client, which will +// use it to request a token and connect to Ably // createTokenRequest can take two optional params // tokenParams: https://www.ably.com/docs/rest/authentication/#token-params // authOptions: https://www.ably.com/docs/rest/authentication/#auth-options -client.auth.createTokenRequest(tokenParams, authOptions, function(err, tokenRequest) { ... }); +const tokenRequest = await client.auth.createTokenRequest(tokenParams, authOptions); ``` ### Fetching your application's stats ```javascript -client.stats(function(err, statsPage) { // statsPage as PaginatedResult - statsPage.items // array of Stats - statsPage.items[0].inbound.rest.messages.count; // total messages published over REST - statsPage.items.length; // number of stats in the current page of history - statsPage.hasNext() // true if there are further pages - statsPage.isLast() // true if this page is the last page - statsPage.next(function(nextPage) { ... }); // retrieves the next page as PaginatedResult -}); +const statsPage = await client.stats() // statsPage as PaginatedResult +statsPage.items // array of Stats +statsPage.items[0].inbound.rest.messages.count; // total messages published over REST +statsPage.items.length; // number of stats in the current page of history +statsPage.hasNext() // true if there are further pages +statsPage.isLast() // true if this page is the last page +const nextPage = await statsPage.next(); // retrieves the next page as PaginatedResult ``` ### Fetching the Ably service time ```javascript -client.time(function(err, time) { ... }); // time is in ms since epoch -``` - -## Using the async API style - -### Realtime Example - -```ts -import * as Ably from 'ably/promises'; - -const client = new Ably.Realtime.Promise(options); - -const ablyRealtimePromiseExample = async () => { - const channel = client.channels.get('myChannel'); - - // Attaching to a channel - await channel.attach(); - - // Getting presence on a channel - const presenceMessage = await channel.presence.get(); - console.log(presenceMessage); - - // Updating presence on a client - await channel.presence.enter(); - await channel.presence.update('new status'); - await channel.presence.leave(); - - // Publishing a message - await channel.publish('greeting', 'Hello, World!'); - - // Querying history - const history = await channel.history({ limit: 25 }); - console.log(history); - - client.close(); -}; - -ablyRealtimePromiseExample(); -``` - -### REST Example - -```ts -import * as Ably from 'ably/promises'; - -const client = new Ably.Rest.Promise(options); - -const ablyRestPromiseExample = async () => { - const channel = client.channels.get('myChannel'); - - // Publishing a message - await channel.publish('greeting', 'Hello, World!'); - - // Getting presence on a channel - const presenceMessage = await channel.presence.get(); - console.log(presenceMessage); - - // Querying history - const history = await channel.history({ limit: 25 }); - console.log(await history.current()); - - // Getting the status of a channel - const channelDetails = await channel.status(); - console.log(channelDetails); - - // Requesting a token - const token = await client.auth.requestToken(tokenParams); - - // Creating a token request - const tokenRequest = await client.auth.createTokenRequest(); - - // Fetching your application's stats - const stats = await client.stats(); - console.log(stats); - - // Fetching the Ably service time - const time = await client.time(); - console.log(`Ably service time: ${time}`); - - client.close(); -}; - -ablyRestPromiseExample(); +const time = await client.time(); // time is in ms since epoch ``` ## Delta Plugin diff --git a/ably.d.ts b/ably.d.ts index a0f9bc8142..1b21c736ab 100644 --- a/ably.d.ts +++ b/ably.d.ts @@ -4,3812 +4,2781 @@ // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped /** - * Type definitions for Ably Realtime and REST client library. + * You are currently viewing the default variant of the Ably JavaScript Client Library SDK. View the modular variant {@link modular | here}. + * + * To get started with the Ably JavaScript Client Library SDK, follow the [Quickstart Guide](https://ably.com/docs/quick-start-guide) or view the introductions to the [realtime](https://ably.com/docs/realtime/usage) and [REST](https://ably.com/docs/rest/usage) interfaces. + * + * @module */ -declare namespace Types { + +/** + * The `ChannelStates` namespace describes the possible values of the {@link ChannelState} type. + */ +declare namespace ChannelStates { /** - * The `ChannelState` namespace describes the possible values of the {@link ChannelState:type} type. + * The channel has been initialized but no attach has yet been attempted. */ - namespace ChannelState { - /** - * The channel has been initialized but no attach has yet been attempted. - */ - type INITIALIZED = 'initialized'; - /** - * An attach has been initiated by sending a request to Ably. This is a transient state, followed either by a transition to `ATTACHED`, `SUSPENDED`, or `FAILED`. - */ - type ATTACHING = 'attaching'; - /** - * The attach has succeeded. In the `ATTACHED` state a client may publish and subscribe to messages, or be present on the channel. - */ - type ATTACHED = 'attached'; - /** - * A detach has been initiated on an `ATTACHED` channel by sending a request to Ably. This is a transient state, followed either by a transition to `DETACHED` or `FAILED`. - */ - type DETACHING = 'detaching'; - /** - * The channel, having previously been `ATTACHED`, has been detached by the user. - */ - type DETACHED = 'detached'; - /** - * The channel, having previously been `ATTACHED`, has lost continuity, usually due to the client being disconnected from Ably for longer than two minutes. It will automatically attempt to reattach as soon as connectivity is restored. - */ - type SUSPENDED = 'suspended'; - /** - * An indefinite failure condition. This state is entered if a channel error has been received from the Ably service, such as an attempt to attach without the necessary access rights. - */ - type FAILED = 'failed'; - } + type INITIALIZED = 'initialized'; + /** + * An attach has been initiated by sending a request to Ably. This is a transient state, followed either by a transition to `ATTACHED`, `SUSPENDED`, or `FAILED`. + */ + type ATTACHING = 'attaching'; + /** + * The attach has succeeded. In the `ATTACHED` state a client may publish and subscribe to messages, or be present on the channel. + */ + type ATTACHED = 'attached'; + /** + * A detach has been initiated on an `ATTACHED` channel by sending a request to Ably. This is a transient state, followed either by a transition to `DETACHED` or `FAILED`. + */ + type DETACHING = 'detaching'; + /** + * The channel, having previously been `ATTACHED`, has been detached by the user. + */ + type DETACHED = 'detached'; + /** + * The channel, having previously been `ATTACHED`, has lost continuity, usually due to the client being disconnected from Ably for longer than two minutes. It will automatically attempt to reattach as soon as connectivity is restored. + */ + type SUSPENDED = 'suspended'; /** - * Describes the possible states of a {@link ChannelBase} or {@link RealtimeChannelBase} object. + * An indefinite failure condition. This state is entered if a channel error has been received from the Ably service, such as an attempt to attach without the necessary access rights. */ - type ChannelState = - | ChannelState.FAILED - | ChannelState.INITIALIZED - | ChannelState.SUSPENDED - | ChannelState.ATTACHED - | ChannelState.ATTACHING - | ChannelState.DETACHED - | ChannelState.DETACHING; + type FAILED = 'failed'; +} +/** + * Describes the possible states of a {@link Channel} or {@link RealtimeChannel} object. + */ +export type ChannelState = + | ChannelStates.FAILED + | ChannelStates.INITIALIZED + | ChannelStates.SUSPENDED + | ChannelStates.ATTACHED + | ChannelStates.ATTACHING + | ChannelStates.DETACHED + | ChannelStates.DETACHING; +/** + * The `ChannelEvents` namespace describes the possible values of the {@link ChannelEvent} type. + */ +declare namespace ChannelEvents { /** - * The `ChannelEvent` namespace describes the possible values of the {@link ChannelEvent:type} type. + * The channel has been initialized but no attach has yet been attempted. */ - namespace ChannelEvent { - /** - * The channel has been initialized but no attach has yet been attempted. - */ - type INITIALIZED = 'initialized'; - /** - * An attach has been initiated by sending a request to Ably. This is a transient state, followed either by a transition to `ATTACHED`, `SUSPENDED`, or `FAILED`. - */ - type ATTACHING = 'attaching'; - /** - * The attach has succeeded. In the `ATTACHED` state a client may publish and subscribe to messages, or be present on the channel. - */ - type ATTACHED = 'attached'; - /** - * A detach has been initiated on an `ATTACHED` channel by sending a request to Ably. This is a transient state, followed either by a transition to `DETACHED` or `FAILED`. - */ - type DETACHING = 'detaching'; - /** - * The channel, having previously been `ATTACHED`, has been detached by the user. - */ - type DETACHED = 'detached'; - /** - * The channel, having previously been `ATTACHED`, has lost continuity, usually due to the client being disconnected from Ably for longer than two minutes. It will automatically attempt to reattach as soon as connectivity is restored. - */ - type SUSPENDED = 'suspended'; - /** - * An indefinite failure condition. This state is entered if a channel error has been received from the Ably service, such as an attempt to attach without the necessary access rights. - */ - type FAILED = 'failed'; - /** - * An event for changes to channel conditions that do not result in a change in {@link ChannelState}. - */ - type UPDATE = 'update'; - } + type INITIALIZED = 'initialized'; /** - * Describes the events emitted by a {@link ChannelBase} or {@link RealtimeChannelBase} object. An event is either an `UPDATE` or a {@link ChannelState}. + * An attach has been initiated by sending a request to Ably. This is a transient state, followed either by a transition to `ATTACHED`, `SUSPENDED`, or `FAILED`. */ - type ChannelEvent = - | ChannelEvent.FAILED - | ChannelEvent.INITIALIZED - | ChannelEvent.SUSPENDED - | ChannelEvent.ATTACHED - | ChannelEvent.ATTACHING - | ChannelEvent.DETACHED - | ChannelEvent.DETACHING - | ChannelEvent.UPDATE; + type ATTACHING = 'attaching'; + /** + * The attach has succeeded. In the `ATTACHED` state a client may publish and subscribe to messages, or be present on the channel. + */ + type ATTACHED = 'attached'; + /** + * A detach has been initiated on an `ATTACHED` channel by sending a request to Ably. This is a transient state, followed either by a transition to `DETACHED` or `FAILED`. + */ + type DETACHING = 'detaching'; + /** + * The channel, having previously been `ATTACHED`, has been detached by the user. + */ + type DETACHED = 'detached'; + /** + * The channel, having previously been `ATTACHED`, has lost continuity, usually due to the client being disconnected from Ably for longer than two minutes. It will automatically attempt to reattach as soon as connectivity is restored. + */ + type SUSPENDED = 'suspended'; + /** + * An indefinite failure condition. This state is entered if a channel error has been received from the Ably service, such as an attempt to attach without the necessary access rights. + */ + type FAILED = 'failed'; + /** + * An event for changes to channel conditions that do not result in a change in {@link ChannelState}. + */ + type UPDATE = 'update'; +} +/** + * Describes the events emitted by a {@link Channel} or {@link RealtimeChannel} object. An event is either an `UPDATE` or a {@link ChannelState}. + */ +export type ChannelEvent = + | ChannelEvents.FAILED + | ChannelEvents.INITIALIZED + | ChannelEvents.SUSPENDED + | ChannelEvents.ATTACHED + | ChannelEvents.ATTACHING + | ChannelEvents.DETACHED + | ChannelEvents.DETACHING + | ChannelEvents.UPDATE; +/** + * The `ConnectionStates` namespace describes the possible values of the {@link ConnectionState} type. + */ +declare namespace ConnectionStates { /** - * The `ConnectionState` namespace describes the possible values of the {@link ConnectionState:type} type. + * A connection with this state has been initialized but no connection has yet been attempted. */ - namespace ConnectionState { - /** - * A connection with this state has been initialized but no connection has yet been attempted. - */ - type INITIALIZED = 'initialized'; - /** - * A connection attempt has been initiated. The connecting state is entered as soon as the library has completed initialization, and is reentered each time connection is re-attempted following disconnection. - */ - type CONNECTING = 'connecting'; - /** - * A connection exists and is active. - */ - type CONNECTED = 'connected'; - /** - * A temporary failure condition. No current connection exists because there is no network connectivity or no host is available. The disconnected state is entered if an established connection is dropped, or if a connection attempt was unsuccessful. In the disconnected state the library will periodically attempt to open a new connection (approximately every 15 seconds), anticipating that the connection will be re-established soon and thus connection and channel continuity will be possible. In this state, developers can continue to publish messages as they are automatically placed in a local queue, to be sent as soon as a connection is reestablished. Messages published by other clients while this client is disconnected will be delivered to it upon reconnection, so long as the connection was resumed within 2 minutes. After 2 minutes have elapsed, recovery is no longer possible and the connection will move to the `SUSPENDED` state. - */ - type DISCONNECTED = 'disconnected'; - /** - * A long term failure condition. No current connection exists because there is no network connectivity or no host is available. The suspended state is entered after a failed connection attempt if there has then been no connection for a period of two minutes. In the suspended state, the library will periodically attempt to open a new connection every 30 seconds. Developers are unable to publish messages in this state. A new connection attempt can also be triggered by an explicit call to {@link ConnectionBase.connect | `connect()`}. Once the connection has been re-established, channels will be automatically re-attached. The client has been disconnected for too long for them to resume from where they left off, so if it wants to catch up on messages published by other clients while it was disconnected, it needs to use the [History API](https://ably.com/docs/realtime/history). - */ - type SUSPENDED = 'suspended'; - /** - * An explicit request by the developer to close the connection has been sent to the Ably service. If a reply is not received from Ably within a short period of time, the connection is forcibly terminated and the connection state becomes `CLOSED`. - */ - type CLOSING = 'closing'; - /** - * The connection has been explicitly closed by the client. In the closed state, no reconnection attempts are made automatically by the library, and clients may not publish messages. No connection state is preserved by the service or by the library. A new connection attempt can be triggered by an explicit call to {@link ConnectionBase.connect | `connect()`}, which results in a new connection. - */ - type CLOSED = 'closed'; - /** - * This state is entered if the client library encounters a failure condition that it cannot recover from. This may be a fatal connection error received from the Ably service, for example an attempt to connect with an incorrect API key, or a local terminal error, for example the token in use has expired and the library does not have any way to renew it. In the failed state, no reconnection attempts are made automatically by the library, and clients may not publish messages. A new connection attempt can be triggered by an explicit call to {@link ConnectionBase.connect | `connect()`}. - */ - type FAILED = 'failed'; - } + type INITIALIZED = 'initialized'; + /** + * A connection attempt has been initiated. The connecting state is entered as soon as the library has completed initialization, and is reentered each time connection is re-attempted following disconnection. + */ + type CONNECTING = 'connecting'; + /** + * A connection exists and is active. + */ + type CONNECTED = 'connected'; + /** + * A temporary failure condition. No current connection exists because there is no network connectivity or no host is available. The disconnected state is entered if an established connection is dropped, or if a connection attempt was unsuccessful. In the disconnected state the library will periodically attempt to open a new connection (approximately every 15 seconds), anticipating that the connection will be re-established soon and thus connection and channel continuity will be possible. In this state, developers can continue to publish messages as they are automatically placed in a local queue, to be sent as soon as a connection is reestablished. Messages published by other clients while this client is disconnected will be delivered to it upon reconnection, so long as the connection was resumed within 2 minutes. After 2 minutes have elapsed, recovery is no longer possible and the connection will move to the `SUSPENDED` state. + */ + type DISCONNECTED = 'disconnected'; + /** + * A long term failure condition. No current connection exists because there is no network connectivity or no host is available. The suspended state is entered after a failed connection attempt if there has then been no connection for a period of two minutes. In the suspended state, the library will periodically attempt to open a new connection every 30 seconds. Developers are unable to publish messages in this state. A new connection attempt can also be triggered by an explicit call to {@link Connection.connect | `connect()`}. Once the connection has been re-established, channels will be automatically re-attached. The client has been disconnected for too long for them to resume from where they left off, so if it wants to catch up on messages published by other clients while it was disconnected, it needs to use the [History API](https://ably.com/docs/realtime/history). + */ + type SUSPENDED = 'suspended'; + /** + * An explicit request by the developer to close the connection has been sent to the Ably service. If a reply is not received from Ably within a short period of time, the connection is forcibly terminated and the connection state becomes `CLOSED`. + */ + type CLOSING = 'closing'; + /** + * The connection has been explicitly closed by the client. In the closed state, no reconnection attempts are made automatically by the library, and clients may not publish messages. No connection state is preserved by the service or by the library. A new connection attempt can be triggered by an explicit call to {@link Connection.connect | `connect()`}, which results in a new connection. + */ + type CLOSED = 'closed'; /** - * Describes the realtime {@link ConnectionBase} object states. + * This state is entered if the client library encounters a failure condition that it cannot recover from. This may be a fatal connection error received from the Ably service, for example an attempt to connect with an incorrect API key, or a local terminal error, for example the token in use has expired and the library does not have any way to renew it. In the failed state, no reconnection attempts are made automatically by the library, and clients may not publish messages. A new connection attempt can be triggered by an explicit call to {@link Connection.connect | `connect()`}. */ - type ConnectionState = - | ConnectionState.INITIALIZED - | ConnectionState.CONNECTED - | ConnectionState.CONNECTING - | ConnectionState.DISCONNECTED - | ConnectionState.SUSPENDED - | ConnectionState.CLOSED - | ConnectionState.CLOSING - | ConnectionState.FAILED; + type FAILED = 'failed'; +} +/** + * Describes the realtime {@link Connection} object states. + */ +export type ConnectionState = + | ConnectionStates.INITIALIZED + | ConnectionStates.CONNECTED + | ConnectionStates.CONNECTING + | ConnectionStates.DISCONNECTED + | ConnectionStates.SUSPENDED + | ConnectionStates.CLOSED + | ConnectionStates.CLOSING + | ConnectionStates.FAILED; +/** + * The `ConnectionEvents` namespace describes the possible values of the {@link ConnectionEvent} type. + */ +declare namespace ConnectionEvents { /** - * The `ConnectionEvent` namespace describes the possible values of the {@link ConnectionEvent:type} type. + * A connection with this state has been initialized but no connection has yet been attempted. */ - namespace ConnectionEvent { - /** - * A connection with this state has been initialized but no connection has yet been attempted. - */ - type INITIALIZED = 'initialized'; - /** - * A connection attempt has been initiated. The connecting state is entered as soon as the library has completed initialization, and is reentered each time connection is re-attempted following disconnection. - */ - type CONNECTING = 'connecting'; - /** - * A connection exists and is active. - */ - type CONNECTED = 'connected'; - /** - * A temporary failure condition. No current connection exists because there is no network connectivity or no host is available. The disconnected state is entered if an established connection is dropped, or if a connection attempt was unsuccessful. In the disconnected state the library will periodically attempt to open a new connection (approximately every 15 seconds), anticipating that the connection will be re-established soon and thus connection and channel continuity will be possible. In this state, developers can continue to publish messages as they are automatically placed in a local queue, to be sent as soon as a connection is reestablished. Messages published by other clients while this client is disconnected will be delivered to it upon reconnection, so long as the connection was resumed within 2 minutes. After 2 minutes have elapsed, recovery is no longer possible and the connection will move to the `SUSPENDED` state. - */ - type DISCONNECTED = 'disconnected'; - /** - * A long term failure condition. No current connection exists because there is no network connectivity or no host is available. The suspended state is entered after a failed connection attempt if there has then been no connection for a period of two minutes. In the suspended state, the library will periodically attempt to open a new connection every 30 seconds. Developers are unable to publish messages in this state. A new connection attempt can also be triggered by an explicit call to {@link ConnectionBase.connect | `connect()`}. Once the connection has been re-established, channels will be automatically re-attached. The client has been disconnected for too long for them to resume from where they left off, so if it wants to catch up on messages published by other clients while it was disconnected, it needs to use the [History API](https://ably.com/docs/realtime/history). - */ - type SUSPENDED = 'suspended'; - /** - * An explicit request by the developer to close the connection has been sent to the Ably service. If a reply is not received from Ably within a short period of time, the connection is forcibly terminated and the connection state becomes `CLOSED`. - */ - type CLOSING = 'closing'; - /** - * The connection has been explicitly closed by the client. In the closed state, no reconnection attempts are made automatically by the library, and clients may not publish messages. No connection state is preserved by the service or by the library. A new connection attempt can be triggered by an explicit call to {@link ConnectionBase.connect | `connect()`}, which results in a new connection. - */ - type CLOSED = 'closed'; - /** - * This state is entered if the client library encounters a failure condition that it cannot recover from. This may be a fatal connection error received from the Ably service, for example an attempt to connect with an incorrect API key, or a local terminal error, for example the token in use has expired and the library does not have any way to renew it. In the failed state, no reconnection attempts are made automatically by the library, and clients may not publish messages. A new connection attempt can be triggered by an explicit call to {@link ConnectionBase.connect | `connect()`}. - */ - type FAILED = 'failed'; - /** - * An event for changes to connection conditions for which the {@link ConnectionState} does not change. - */ - type UPDATE = 'update'; - } + type INITIALIZED = 'initialized'; + /** + * A connection attempt has been initiated. The connecting state is entered as soon as the library has completed initialization, and is reentered each time connection is re-attempted following disconnection. + */ + type CONNECTING = 'connecting'; + /** + * A connection exists and is active. + */ + type CONNECTED = 'connected'; + /** + * A temporary failure condition. No current connection exists because there is no network connectivity or no host is available. The disconnected state is entered if an established connection is dropped, or if a connection attempt was unsuccessful. In the disconnected state the library will periodically attempt to open a new connection (approximately every 15 seconds), anticipating that the connection will be re-established soon and thus connection and channel continuity will be possible. In this state, developers can continue to publish messages as they are automatically placed in a local queue, to be sent as soon as a connection is reestablished. Messages published by other clients while this client is disconnected will be delivered to it upon reconnection, so long as the connection was resumed within 2 minutes. After 2 minutes have elapsed, recovery is no longer possible and the connection will move to the `SUSPENDED` state. + */ + type DISCONNECTED = 'disconnected'; + /** + * A long term failure condition. No current connection exists because there is no network connectivity or no host is available. The suspended state is entered after a failed connection attempt if there has then been no connection for a period of two minutes. In the suspended state, the library will periodically attempt to open a new connection every 30 seconds. Developers are unable to publish messages in this state. A new connection attempt can also be triggered by an explicit call to {@link Connection.connect | `connect()`}. Once the connection has been re-established, channels will be automatically re-attached. The client has been disconnected for too long for them to resume from where they left off, so if it wants to catch up on messages published by other clients while it was disconnected, it needs to use the [History API](https://ably.com/docs/realtime/history). + */ + type SUSPENDED = 'suspended'; + /** + * An explicit request by the developer to close the connection has been sent to the Ably service. If a reply is not received from Ably within a short period of time, the connection is forcibly terminated and the connection state becomes `CLOSED`. + */ + type CLOSING = 'closing'; + /** + * The connection has been explicitly closed by the client. In the closed state, no reconnection attempts are made automatically by the library, and clients may not publish messages. No connection state is preserved by the service or by the library. A new connection attempt can be triggered by an explicit call to {@link Connection.connect | `connect()`}, which results in a new connection. + */ + type CLOSED = 'closed'; /** - * Describes the events emitted by a {@link ConnectionBase} object. An event is either an `UPDATE` or a {@link ConnectionState}. + * This state is entered if the client library encounters a failure condition that it cannot recover from. This may be a fatal connection error received from the Ably service, for example an attempt to connect with an incorrect API key, or a local terminal error, for example the token in use has expired and the library does not have any way to renew it. In the failed state, no reconnection attempts are made automatically by the library, and clients may not publish messages. A new connection attempt can be triggered by an explicit call to {@link Connection.connect | `connect()`}. */ - type ConnectionEvent = - | ConnectionEvent.INITIALIZED - | ConnectionEvent.CONNECTED - | ConnectionEvent.CONNECTING - | ConnectionEvent.DISCONNECTED - | ConnectionEvent.SUSPENDED - | ConnectionEvent.CLOSED - | ConnectionEvent.CLOSING - | ConnectionEvent.FAILED - | ConnectionEvent.UPDATE; + type FAILED = 'failed'; + /** + * An event for changes to connection conditions for which the {@link ConnectionState} does not change. + */ + type UPDATE = 'update'; +} +/** + * Describes the events emitted by a {@link Connection} object. An event is either an `UPDATE` or a {@link ConnectionState}. + */ +export type ConnectionEvent = + | ConnectionEvents.INITIALIZED + | ConnectionEvents.CONNECTED + | ConnectionEvents.CONNECTING + | ConnectionEvents.DISCONNECTED + | ConnectionEvents.SUSPENDED + | ConnectionEvents.CLOSED + | ConnectionEvents.CLOSING + | ConnectionEvents.FAILED + | ConnectionEvents.UPDATE; +/** + * The `PresenceActions` namespace describes the possible values of the {@link PresenceAction} type. + */ +declare namespace PresenceActions { /** - * The `PresenceAction` namespace describes the possible values of the {@link PresenceAction:type} type. + * A member is not present in the channel. */ - namespace PresenceAction { - /** - * A member is not present in the channel. - */ - type ABSENT = 'absent'; - /** - * When subscribing to presence events on a channel that already has members present, this event is emitted for every member already present on the channel before the subscribe listener was registered. - */ - type PRESENT = 'present'; - /** - * A new member has entered the channel. - */ - type ENTER = 'enter'; - /** - * A member who was present has now left the channel. This may be a result of an explicit request to leave or implicitly when detaching from the channel. Alternatively, if a member's connection is abruptly disconnected and they do not resume their connection within a minute, Ably treats this as a leave event as the client is no longer present. - */ - type LEAVE = 'leave'; - /** - * An already present member has updated their member data. Being notified of member data updates can be very useful, for example, it can be used to update the status of a user when they are typing a message. - */ - type UPDATE = 'update'; - } + type ABSENT = 'absent'; + /** + * When subscribing to presence events on a channel that already has members present, this event is emitted for every member already present on the channel before the subscribe listener was registered. + */ + type PRESENT = 'present'; + /** + * A new member has entered the channel. + */ + type ENTER = 'enter'; + /** + * A member who was present has now left the channel. This may be a result of an explicit request to leave or implicitly when detaching from the channel. Alternatively, if a member's connection is abruptly disconnected and they do not resume their connection within a minute, Ably treats this as a leave event as the client is no longer present. + */ + type LEAVE = 'leave'; /** - * Describes the possible actions members in the presence set can emit. + * An already present member has updated their member data. Being notified of member data updates can be very useful, for example, it can be used to update the status of a user when they are typing a message. */ - type PresenceAction = - | PresenceAction.ABSENT - | PresenceAction.PRESENT - | PresenceAction.ENTER - | PresenceAction.LEAVE - | PresenceAction.UPDATE; + type UPDATE = 'update'; +} +/** + * Describes the possible actions members in the presence set can emit. + */ +export type PresenceAction = + | PresenceActions.ABSENT + | PresenceActions.PRESENT + | PresenceActions.ENTER + | PresenceActions.LEAVE + | PresenceActions.UPDATE; +/** + * The `StatsIntervalGranularities` namespace describes the possible values of the {@link StatsIntervalGranularity} type. + */ +declare namespace StatsIntervalGranularities { /** - * The `StatsIntervalGranularity` namespace describes the possible values of the {@link StatsIntervalGranularity:type} type. + * Interval unit over which statistics are gathered as minutes. */ - namespace StatsIntervalGranularity { - /** - * Interval unit over which statistics are gathered as minutes. - */ - type MINUTE = 'minute'; - /** - * Interval unit over which statistics are gathered as hours. - */ - type HOUR = 'hour'; - /** - * Interval unit over which statistics are gathered as days. - */ - type DAY = 'day'; - /** - * Interval unit over which statistics are gathered as months. - */ - type MONTH = 'month'; - } + type MINUTE = 'minute'; + /** + * Interval unit over which statistics are gathered as hours. + */ + type HOUR = 'hour'; + /** + * Interval unit over which statistics are gathered as days. + */ + type DAY = 'day'; /** - * Describes the interval unit over which statistics are gathered. + * Interval unit over which statistics are gathered as months. */ - type StatsIntervalGranularity = - | StatsIntervalGranularity.MINUTE - | StatsIntervalGranularity.HOUR - | StatsIntervalGranularity.DAY - | StatsIntervalGranularity.MONTH; + type MONTH = 'month'; +} +/** + * Describes the interval unit over which statistics are gathered. + */ +export type StatsIntervalGranularity = + | StatsIntervalGranularities.MINUTE + | StatsIntervalGranularities.HOUR + | StatsIntervalGranularities.DAY + | StatsIntervalGranularities.MONTH; +/** + * HTTP Methods, used internally. + */ +declare namespace HTTPMethods { /** - * HTTP Methods, used internally. + * Represents a HTTP POST request. */ - namespace HTTPMethods { - /** - * Represents a HTTP POST request. - */ - type POST = 'POST'; - /** - * Represents a HTTP GET request. - */ - type GET = 'GET'; - } + type POST = 'POST'; /** - * HTTP Methods, used internally. + * Represents a HTTP GET request. */ - type HTTPMethods = HTTPMethods.GET | HTTPMethods.POST; + type GET = 'GET'; +} +/** + * HTTP Methods, used internally. + */ +export type HTTPMethod = HTTPMethods.GET | HTTPMethods.POST; +/** + * A type which specifies the valid transport names. [See here](https://faqs.ably.com/which-transports-are-supported) for more information. + */ +export type Transport = 'web_socket' | 'xhr_polling' | 'comet'; + +/** + * Contains the details of a {@link Channel} or {@link RealtimeChannel} object such as its ID and {@link ChannelStatus}. + */ +export interface ChannelDetails { + /** + * The identifier of the channel. + */ + channelId: string; /** - * A type which specifies the valid transport names. [See here](https://faqs.ably.com/which-transports-are-supported) for more information. + * A {@link ChannelStatus} object. */ - type Transport = 'web_socket' | 'xhr_streaming' | 'xhr_polling' | 'jsonp' | 'comet'; + status: ChannelStatus; +} +/** + * Contains the status of a {@link Channel} or {@link RealtimeChannel} object such as whether it is active and its {@link ChannelOccupancy}. + */ +export interface ChannelStatus { /** - * Contains the details of a {@link ChannelBase} or {@link RealtimeChannelBase} object such as its ID and {@link ChannelStatus}. + * If `true`, the channel is active, otherwise `false`. */ - interface ChannelDetails { - /** - * The identifier of the channel. - */ - channelId: string; - /** - * A {@link ChannelStatus} object. - */ - status: ChannelStatus; - } + isActive: boolean; + /** + * A {@link ChannelOccupancy} object. + */ + occupancy: ChannelOccupancy; +} +/** + * Contains the metrics of a {@link Channel} or {@link RealtimeChannel} object. + */ +export interface ChannelOccupancy { /** - * Contains the status of a {@link ChannelBase} or {@link RealtimeChannelBase} object such as whether it is active and its {@link ChannelOccupancy}. + * A {@link ChannelMetrics} object. */ - interface ChannelStatus { - /** - * If `true`, the channel is active, otherwise `false`. - */ - isActive: boolean; - /** - * A {@link ChannelOccupancy} object. - */ - occupancy: ChannelOccupancy; - } + metrics: ChannelMetrics; +} +/** + * Contains the metrics associated with a {@link Channel} or {@link RealtimeChannel}, such as the number of publishers, subscribers and connections it has. + */ +export interface ChannelMetrics { /** - * Contains the metrics of a {@link ChannelBase} or {@link RealtimeChannelBase} object. + * The number of realtime connections attached to the channel. */ - interface ChannelOccupancy { - /** - * A {@link ChannelMetrics} object. - */ - metrics: ChannelMetrics; - } + connections: number; + /** + * The number of realtime connections attached to the channel with permission to enter the presence set, regardless of whether or not they have entered it. This requires the `presence` capability and for a client to not have specified a {@link ChannelMode} flag that excludes {@link ChannelModes.PRESENCE}. + */ + presenceConnections: number; + /** + * The number of members in the presence set of the channel. + */ + presenceMembers: number; + /** + * The number of realtime attachments receiving presence messages on the channel. This requires the `subscribe` capability and for a client to not have specified a {@link ChannelMode} flag that excludes {@link ChannelModes.PRESENCE_SUBSCRIBE}. + */ + presenceSubscribers: number; + /** + * The number of realtime attachments permitted to publish messages to the channel. This requires the `publish` capability and for a client to not have specified a {@link ChannelMode} flag that excludes {@link ChannelModes.PUBLISH}. + */ + publishers: number; + /** + * The number of realtime attachments receiving messages on the channel. This requires the `subscribe` capability and for a client to not have specified a {@link ChannelMode} flag that excludes {@link ChannelModes.SUBSCRIBE}. + */ + subscribers: number; +} +/** + * Passes additional client-specific properties to the REST constructor or the Realtime constructor. + */ +export interface ClientOptions extends AuthOptions { /** - * Contains the metrics associated with a {@link ChannelBase} or {@link RealtimeChannelBase}, such as the number of publishers, subscribers and connections it has. + * When `true`, the client connects to Ably as soon as it is instantiated. You can set this to `false` and explicitly connect to Ably using the {@link Connection.connect | `connect()`} method. The default is `true`. + * + * @defaultValue `true` */ - interface ChannelMetrics { - /** - * The number of realtime connections attached to the channel. - */ - connections: number; - /** - * The number of realtime connections attached to the channel with permission to enter the presence set, regardless of whether or not they have entered it. This requires the `presence` capability and for a client to not have specified a {@link ChannelMode} flag that excludes {@link ChannelMode.PRESENCE}. - */ - presenceConnections: number; - /** - * The number of members in the presence set of the channel. - */ - presenceMembers: number; - /** - * The number of realtime attachments receiving presence messages on the channel. This requires the `subscribe` capability and for a client to not have specified a {@link ChannelMode} flag that excludes {@link ChannelMode.PRESENCE_SUBSCRIBE}. - */ - presenceSubscribers: number; - /** - * The number of realtime attachments permitted to publish messages to the channel. This requires the `publish` capability and for a client to not have specified a {@link ChannelMode} flag that excludes {@link ChannelMode.PUBLISH}. - */ - publishers: number; - /** - * The number of realtime attachments receiving messages on the channel. This requires the `subscribe` capability and for a client to not have specified a {@link ChannelMode} flag that excludes {@link ChannelMode.SUBSCRIBE}. - */ - subscribers: number; - } + autoConnect?: boolean; /** - * Passes additional client-specific properties to the REST {@link RestBase.constructor | `constructor()`} or the Realtime {@link RealtimeBase.constructor | `constructor()`}. + * When a {@link TokenParams} object is provided, it overrides the client library defaults when issuing new Ably Tokens or Ably {@link TokenRequest | `TokenRequest`s}. */ - interface ClientOptions extends AuthOptions { - /** - * When `true`, the client connects to Ably as soon as it is instantiated. You can set this to `false` and explicitly connect to Ably using the {@link ConnectionBase.connect | `connect()`} method. The default is `true`. - * - * @defaultValue `true` - */ - autoConnect?: boolean; + defaultTokenParams?: TokenParams; - /** - * When a {@link TokenParams} object is provided, it overrides the client library defaults when issuing new Ably Tokens or Ably {@link TokenRequest | `TokenRequest`s}. - */ - defaultTokenParams?: TokenParams; + /** + * If `false`, prevents messages originating from this connection being echoed back on the same connection. The default is `true`. + * + * @defaultValue `true` + */ + echoMessages?: boolean; - /** - * If `false`, prevents messages originating from this connection being echoed back on the same connection. The default is `true`. - * - * @defaultValue `true` - */ - echoMessages?: boolean; + /** + * Enables a [custom environment](https://ably.com/docs/platform-customization) to be used with the Ably service. + */ + environment?: string; - /** - * Enables a [custom environment](https://ably.com/docs/platform-customization) to be used with the Ably service. - */ - environment?: string; + /** + * Controls the verbosity of the logs output from the library. Valid values are: 0 (no logs), 1 (errors only), 2 (errors plus connection and channel state changes), 3 (high-level debug output), and 4 (full debug output). + */ + logLevel?: number; - /** - * Controls the verbosity of the logs output from the library. Valid values are: 0 (no logs), 1 (errors only), 2 (errors plus connection and channel state changes), 3 (high-level debug output), and 4 (full debug output). - */ - logLevel?: number; + /** + * Controls the log output of the library. This is a function to handle each line of log output. If you do not set this value, then `console.log` will be used. + * + * @param msg - The log message emitted by the library. + */ + logHandler?: (msg: string) => void; - /** - * Controls the log output of the library. This is a function to handle each line of log output. If you do not set this value, then `console.log` will be used. - * - * @param msg - The log message emitted by the library. - */ - logHandler?: (msg: string) => void; - - /** - * Parameters to control the log output of the library, such as the log handler and log level. - * - * @deprecated This property is deprecated and will be removed in a future version. Use the {@link ClientOptions.logLevel} and {@link ClientOptions.logHandler} client options instead. - */ - log?: LogInfo; - - /** - * Enables a non-default Ably port to be specified. For development environments only. The default value is 80. - * - * @defaultValue 80 - */ - port?: number; - - /** - * If `false`, this disables the default behavior whereby the library queues messages on a connection in the disconnected or connecting states. The default behavior enables applications to submit messages immediately upon instantiating the library without having to wait for the connection to be established. Applications may use this option to disable queueing if they wish to have application-level control over the queueing. The default is `true`. - * - * @defaultValue `true` - */ - queueMessages?: boolean; - - /** - * Enables a non-default Ably host to be specified. For development environments only. The default value is `rest.ably.io`. - * - * @defaultValue `"rest.ably.io"` - */ - restHost?: string; - - /** - * Enables a non-default Ably host to be specified for realtime connections. For development environments only. The default value is `realtime.ably.io`. - * - * @defaultValue `"realtime.ably.io"` - */ - realtimeHost?: string; - - /** - * An array of fallback hosts to be used in the case of an error necessitating the use of an alternative host. If you have been provided a set of custom fallback hosts by Ably, please specify them here. - * - * @defaultValue `['a.ably-realtime.com', 'b.ably-realtime.com', 'c.ably-realtime.com', 'd.ably-realtime.com', 'e.ably-realtime.com']`` - */ - fallbackHosts?: string[]; - - /** - * @deprecated This property is deprecated and will be removed in a future version. Enables default fallback hosts to be used. - */ - fallbackHostsUseDefault?: boolean; - - /** - * Set of configurable options to set on the HTTP(S) agent used for REST requests. - * - * See the [NodeJS docs](https://nodejs.org/api/http.html#new-agentoptions) for descriptions of these options. - */ - restAgentOptions?: { - /** - * See [NodeJS docs](https://nodejs.org/api/http.html#new-agentoptions) - */ - maxSockets?: number; - /** - * See [NodeJS docs](https://nodejs.org/api/http.html#new-agentoptions) - */ - keepAlive?: boolean; - }; - - /** - * Enables a connection to inherit the state of a previous connection that may have existed under a different instance of the Realtime library. This might typically be used by clients of the browser library to ensure connection state can be preserved when the user refreshes the page. A recovery key string can be explicitly provided, or alternatively if a callback function is provided, the client library will automatically persist the recovery key between page reloads and call the callback when the connection is recoverable. The callback is then responsible for confirming whether the connection should be recovered or not. See [connection state recovery](https://ably.com/docs/realtime/connection/#connection-state-recovery) for further information. - */ - recover?: string | recoverConnectionCallback; - - /** - * When `false`, the client will use an insecure connection. The default is `true`, meaning a TLS connection will be used to connect to Ably. - * - * @defaultValue `true` - */ - tls?: boolean; - - /** - * Enables a non-default Ably TLS port to be specified. For development environments only. The default value is 443. - * - * @defaultValue 443 - */ - tlsPort?: number; - - /** - * When `true`, the more efficient MsgPack binary encoding is used. When `false`, JSON text encoding is used. The default is `true` for Node.js, and `false` for all other platforms. - * - * @defaultValue `true` for Node.js, `false` for all other platforms - */ - useBinaryProtocol?: boolean; - - /** - * Override the URL used by the realtime client to check if the internet is available. - * - * In the event of a failure to connect to the primary endpoint, the client will send a - * GET request to this URL to check if the internet is available. If this request returns - * a success response the client will attempt to connect to a fallback host. - */ - connectivityCheckUrl?: string; - - /** - * Disable the check used by the realtime client to check if the internet - * is available before connecting to a fallback host. - */ - disableConnectivityCheck?: boolean; + /** + * Enables a non-default Ably port to be specified. For development environments only. The default value is 80. + * + * @defaultValue 80 + */ + port?: number; - /** - * If the connection is still in the {@link ConnectionState.DISCONNECTED} state after this delay in milliseconds, the client library will attempt to reconnect automatically. The default is 15 seconds. - * - * @defaultValue 15s - */ - disconnectedRetryTimeout?: number; + /** + * If `false`, this disables the default behavior whereby the library queues messages on a connection in the disconnected or connecting states. The default behavior enables applications to submit messages immediately upon instantiating the library without having to wait for the connection to be established. Applications may use this option to disable queueing if they wish to have application-level control over the queueing. The default is `true`. + * + * @defaultValue `true` + */ + queueMessages?: boolean; - /** - * When the connection enters the {@link ConnectionState.SUSPENDED} state, after this delay in milliseconds, if the state is still {@link ConnectionState.SUSPENDED | `SUSPENDED`}, the client library attempts to reconnect automatically. The default is 30 seconds. - * - * @defaultValue 30s - */ - suspendedRetryTimeout?: number; + /** + * Enables a non-default Ably host to be specified. For development environments only. The default value is `rest.ably.io`. + * + * @defaultValue `"rest.ably.io"` + */ + restHost?: string; - /** - * When `true`, the client library will automatically send a close request to Ably whenever the `window` [`beforeunload` event](https://developer.mozilla.org/en-US/docs/Web/API/BeforeUnloadEvent) fires. By enabling this option, the close request sent to Ably ensures the connection state will not be retained and all channels associated with the channel will be detached. This is commonly used by developers who want presence leave events to fire immediately (that is, if a user navigates to another page or closes their browser, then enabling this option will result in the presence member leaving immediately). Without this option or an explicit call to the `close` method of the `Connection` object, Ably expects that the abruptly disconnected connection could later be recovered and therefore does not immediately remove the user from presence. Instead, to avoid “twitchy” presence behaviour an abruptly disconnected client is removed from channels in which they are present after 15 seconds, and the connection state is retained for two minutes. Defaults to `true`. - */ - closeOnUnload?: boolean; + /** + * Enables a non-default Ably host to be specified for realtime connections. For development environments only. The default value is `realtime.ably.io`. + * + * @defaultValue `"realtime.ably.io"` + */ + realtimeHost?: string; - /** - * When `true`, enables idempotent publishing by assigning a unique message ID client-side, allowing the Ably servers to discard automatic publish retries following a failure such as a network fault. The default is `true`. - * - * @defaultValue `true` - */ - idempotentRestPublishing?: boolean; + /** + * An array of fallback hosts to be used in the case of an error necessitating the use of an alternative host. If you have been provided a set of custom fallback hosts by Ably, please specify them here. + * + * @defaultValue `['a.ably-realtime.com', 'b.ably-realtime.com', 'c.ably-realtime.com', 'd.ably-realtime.com', 'e.ably-realtime.com']`` + */ + fallbackHosts?: string[]; + /** + * Set of configurable options to set on the HTTP(S) agent used for REST requests. + * + * See the [NodeJS docs](https://nodejs.org/api/http.html#new-agentoptions) for descriptions of these options. + */ + restAgentOptions?: { /** - * A set of key-value pairs that can be used to pass in arbitrary connection parameters, such as [`heartbeatInterval`](https://ably.com/docs/realtime/connection#heartbeats) or [`remainPresentFor`](https://ably.com/docs/realtime/presence#unstable-connections). + * See [NodeJS docs](https://nodejs.org/api/http.html#new-agentoptions) */ - transportParams?: { [k: string]: string | number }; - + maxSockets?: number; /** - * An array of transports to use, in descending order of preference. In the browser environment the available transports are: `web_socket`, `xhr`, and `jsonp`. + * See [NodeJS docs](https://nodejs.org/api/http.html#new-agentoptions) */ - transports?: Transport[]; + keepAlive?: boolean; + }; - /** - * The maximum number of fallback hosts to use as a fallback when an HTTP request to the primary host is unreachable or indicates that it is unserviceable. The default value is 3. - * - * @defaultValue 3 - */ - httpMaxRetryCount?: number; + /** + * Enables a connection to inherit the state of a previous connection that may have existed under a different instance of the Realtime library. This might typically be used by clients of the browser library to ensure connection state can be preserved when the user refreshes the page. A recovery key string can be explicitly provided, or alternatively if a callback function is provided, the client library will automatically persist the recovery key between page reloads and call the callback when the connection is recoverable. The callback is then responsible for confirming whether the connection should be recovered or not. See [connection state recovery](https://ably.com/docs/realtime/connection/#connection-state-recovery) for further information. + */ + recover?: string | recoverConnectionCallback; - /** - * The maximum elapsed time in milliseconds in which fallback host retries for HTTP requests will be attempted. The default is 15 seconds. - * - * @defaultValue 15s - */ - httpMaxRetryDuration?: number; + /** + * When `false`, the client will use an insecure connection. The default is `true`, meaning a TLS connection will be used to connect to Ably. + * + * @defaultValue `true` + */ + tls?: boolean; - /** - * Timeout in milliseconds for opening a connection to Ably to initiate an HTTP request. The default is 4 seconds. - * - * @defaultValue 4s - */ - httpOpenTimeout?: number; + /** + * Enables a non-default Ably TLS port to be specified. For development environments only. The default value is 443. + * + * @defaultValue 443 + */ + tlsPort?: number; - /** - * Timeout in milliseconds for a client performing a complete HTTP request to Ably, including the connection phase. The default is 10 seconds. - * - * @defaultValue 10s - */ - httpRequestTimeout?: number; + /** + * When `true`, the more efficient MsgPack binary encoding is used. When `false`, JSON text encoding is used. The default is `true` for Node.js, and `false` for all other platforms. + * + * @defaultValue `true` for Node.js, `false` for all other platforms + */ + useBinaryProtocol?: boolean; - /** - * Timeout for the wait of acknowledgement for operations performed via a realtime connection, before the client library considers a request failed and triggers a failure condition. Operations include establishing a connection with Ably, or sending a `HEARTBEAT`, `CONNECT`, `ATTACH`, `DETACH` or `CLOSE` request. It is the equivalent of `httpRequestTimeout` but for realtime operations, rather than REST. The default is 10 seconds. - * - * @defaultValue 10s - */ - realtimeRequestTimeout?: number; + /** + * Override the URL used by the realtime client to check if the internet is available. + * + * In the event of a failure to connect to the primary endpoint, the client will send a + * GET request to this URL to check if the internet is available. If this request returns + * a success response the client will attempt to connect to a fallback host. + */ + connectivityCheckUrl?: string; - /** - * A map between a plugin type and a plugin object. - */ - plugins?: { - /** - * A plugin capable of decoding `vcdiff`-encoded messages. For more information on how to configure a channel to use delta encoding, see the [documentation for the `@ably-forks/vcdiff-decoder` package](https://github.com/ably-forks/vcdiff-decoder#usage). - */ - vcdiff?: any; - }; + /** + * Disable the check used by the realtime client to check if the internet + * is available before connecting to a fallback host. + */ + disableConnectivityCheck?: boolean; - /** - * The maximum message size is an attribute of an Ably account which represents the largest permitted payload size of a single message or set of messages published in a single operation. Publish requests whose payload exceeds this limit are rejected by the server. `maxMessageSize` enables the client to enforce, or further restrict, the maximum size of a single message or set of messages published via REST. The default value is `65536` (64 KiB). In the case of a realtime connection, the server may indicate the associated maximum message size on connection establishment; this value takes precedence over the client's default `maxMessageSize`. - * - * @defaultValue 65536 - */ - maxMessageSize?: number; - } + /** + * If the connection is still in the {@link ConnectionStates.DISCONNECTED} state after this delay in milliseconds, the client library will attempt to reconnect automatically. The default is 15 seconds. + * + * @defaultValue 15s + */ + disconnectedRetryTimeout?: number; /** - * Passes authentication-specific properties in authentication requests to Ably. Properties set using `AuthOptions` are used instead of the default values set when the client library is instantiated, as opposed to being merged with them. + * When the connection enters the {@link ConnectionStates.SUSPENDED} state, after this delay in milliseconds, if the state is still {@link ConnectionStates.SUSPENDED | `SUSPENDED`}, the client library attempts to reconnect automatically. The default is 30 seconds. + * + * @defaultValue 30s */ - interface AuthOptions { - /** - * Called when a new token is required. The role of the callback is to obtain a fresh token, one of: an Ably Token string (in plain text format); a signed {@link TokenRequest}; a {@link TokenDetails} (in JSON format); an [Ably JWT](https://ably.com/docs/core-features/authentication.ably-jwt). See [the authentication documentation](https://ably.com/docs/realtime/authentication) for details of the Ably {@link TokenRequest} format and associated API calls. - * - * @param data - The parameters that should be used to generate the token. - * @param callback - A function which, upon success, the `authCallback` should call with one of: an Ably Token string (in plain text format); a signed `TokenRequest`; a `TokenDetails` (in JSON format); an [Ably JWT](https://ably.com/docs/core-features/authentication#ably-jwt). Upon failure, the `authCallback` should call this function with information about the error. - */ - authCallback?( - data: TokenParams, - /** - * A function which, upon success, the `authCallback` should call with one of: an Ably Token string (in plain text format); a signed `TokenRequest`; a `TokenDetails` (in JSON format); an [Ably JWT](https://ably.com/docs/core-features/authentication#ably-jwt). Upon failure, the `authCallback` should call this function with information about the error. - * - * @param error - Should be `null` if the auth request completed successfully, or containing details of the error if not. - * @param tokenRequestOrDetails - A valid `TokenRequest`, `TokenDetails` or Ably JWT to be used for authentication. - */ - callback: ( - error: ErrorInfo | string | null, - tokenRequestOrDetails: TokenDetails | TokenRequest | string | null - ) => void - ): void; + suspendedRetryTimeout?: number; - /** - * A set of key-value pair headers to be added to any request made to the `authUrl`. Useful when an application requires these to be added to validate the request or implement the response. If the `authHeaders` object contains an `authorization` key, then `withCredentials` is set on the XHR request. - */ - authHeaders?: { [index: string]: string }; + /** + * When `true`, the client library will automatically send a close request to Ably whenever the `window` [`beforeunload` event](https://developer.mozilla.org/en-US/docs/Web/API/BeforeUnloadEvent) fires. By enabling this option, the close request sent to Ably ensures the connection state will not be retained and all channels associated with the channel will be detached. This is commonly used by developers who want presence leave events to fire immediately (that is, if a user navigates to another page or closes their browser, then enabling this option will result in the presence member leaving immediately). Without this option or an explicit call to the `close` method of the `Connection` object, Ably expects that the abruptly disconnected connection could later be recovered and therefore does not immediately remove the user from presence. Instead, to avoid “twitchy” presence behaviour an abruptly disconnected client is removed from channels in which they are present after 15 seconds, and the connection state is retained for two minutes. Defaults to `true`. + */ + closeOnUnload?: boolean; - /** - * The HTTP verb to use for any request made to the `authUrl`, either `GET` or `POST`. The default value is `GET`. - * - * @defaultValue `HTTPMethods.GET` - */ - authMethod?: HTTPMethods; + /** + * When `true`, enables idempotent publishing by assigning a unique message ID client-side, allowing the Ably servers to discard automatic publish retries following a failure such as a network fault. The default is `true`. + * + * @defaultValue `true` + */ + idempotentRestPublishing?: boolean; - /** - * A set of key-value pair params to be added to any request made to the `authUrl`. When the `authMethod` is `GET`, query params are added to the URL, whereas when `authMethod` is `POST`, the params are sent as URL encoded form data. Useful when an application requires these to be added to validate the request or implement the response. - */ - authParams?: { [index: string]: string }; + /** + * A set of key-value pairs that can be used to pass in arbitrary connection parameters, such as [`heartbeatInterval`](https://ably.com/docs/realtime/connection#heartbeats) or [`remainPresentFor`](https://ably.com/docs/realtime/presence#unstable-connections). + */ + transportParams?: { [k: string]: string | number }; - /** - * A URL that the library may use to obtain a token string (in plain text format), or a signed {@link TokenRequest} or {@link TokenDetails} (in JSON format) from. - */ - authUrl?: string; + /** + * An array of transports to use, in descending order of preference. In the browser environment the available transports are: `web_socket` and `xhr`. + */ + transports?: Transport[]; - /** - * The full API key string, as obtained from the [Ably dashboard](https://ably.com/dashboard). Use this option if you wish to use Basic authentication, or wish to be able to issue Ably Tokens without needing to defer to a separate entity to sign Ably {@link TokenRequest | `TokenRequest`s}. Read more about [Basic authentication](https://ably.com/docs/core-features/authentication#basic-authentication). - */ - key?: string; + /** + * The maximum number of fallback hosts to use as a fallback when an HTTP request to the primary host is unreachable or indicates that it is unserviceable. The default value is 3. + * + * @defaultValue 3 + */ + httpMaxRetryCount?: number; - /** - * If `true`, the library queries the Ably servers for the current time when issuing {@link TokenRequest | `TokenRequest`s} instead of relying on a locally-available time of day. Knowing the time accurately is needed to create valid signed Ably {@link TokenRequest | `TokenRequest`s}, so this option is useful for library instances on auth servers where for some reason the server clock cannot be kept synchronized through normal means, such as an [NTP daemon](https://en.wikipedia.org/wiki/Ntpd). The server is queried for the current time once per client library instance (which stores the offset from the local clock), so if using this option you should avoid instancing a new version of the library for each request. The default is `false`. - * - * @defaultValue `false` - */ - queryTime?: boolean; + /** + * The maximum elapsed time in milliseconds in which fallback host retries for HTTP requests will be attempted. The default is 15 seconds. + * + * @defaultValue 15s + */ + httpMaxRetryDuration?: number; - /** - * An authenticated token. This can either be a {@link TokenDetails} object or token string (obtained from the `token` property of a {@link TokenDetails} component of an Ably {@link TokenRequest} response, or a JSON Web Token satisfying [the Ably requirements for JWTs](https://ably.com/docs/core-features/authentication#ably-jwt)). This option is mostly useful for testing: since tokens are short-lived, in production you almost always want to use an authentication method that enables the client library to renew the token automatically when the previous one expires, such as `authUrl` or `authCallback`. Read more about [Token authentication](https://ably.com/docs/core-features/authentication#token-authentication). - */ - token?: TokenDetails | string; + /** + * Timeout in milliseconds for opening a connection to Ably to initiate an HTTP request. The default is 4 seconds. + * + * @defaultValue 4s + */ + httpOpenTimeout?: number; - /** - * An authenticated {@link TokenDetails} object (most commonly obtained from an Ably Token Request response). This option is mostly useful for testing: since tokens are short-lived, in production you almost always want to use an authentication method that enables the client library to renew the token automatically when the previous one expires, such as `authUrl` or `authCallback`. Use this option if you wish to use Token authentication. Read more about [Token authentication](https://ably.com/docs/core-features/authentication#token-authentication). - */ - tokenDetails?: TokenDetails; + /** + * Timeout in milliseconds for a client performing a complete HTTP request to Ably, including the connection phase. The default is 10 seconds. + * + * @defaultValue 10s + */ + httpRequestTimeout?: number; - /** - * When `true`, forces token authentication to be used by the library. If a `clientId` is not specified in the {@link ClientOptions} or {@link TokenParams}, then the Ably Token issued is [anonymous](https://ably.com/docs/core-features/authentication#identified-clients). - */ - useTokenAuth?: boolean; + /** + * Timeout for the wait of acknowledgement for operations performed via a realtime connection, before the client library considers a request failed and triggers a failure condition. Operations include establishing a connection with Ably, or sending a `HEARTBEAT`, `CONNECT`, `ATTACH`, `DETACH` or `CLOSE` request. It is the equivalent of `httpRequestTimeout` but for realtime operations, rather than REST. The default is 10 seconds. + * + * @defaultValue 10s + */ + realtimeRequestTimeout?: number; - /** - * A client ID, used for identifying this client when publishing messages or for presence purposes. The `clientId` can be any non-empty string, except it cannot contain a `*`. This option is primarily intended to be used in situations where the library is instantiated with a key. Note that a `clientId` may also be implicit in a token used to instantiate the library. An error will be raised if a `clientId` specified here conflicts with the `clientId` implicit in the token. Find out more about [client identities](https://ably.com/documentation/how-ably-works#client-identity). - */ - clientId?: string; - } + /** + * A map between a plugin type and a plugin object. + */ + plugins?: Plugins; /** - * Capabilities which are available for use within {@link TokenParams}. + * The maximum message size is an attribute of an Ably account which represents the largest permitted payload size of a single message or set of messages published in a single operation. Publish requests whose payload exceeds this limit are rejected by the server. `maxMessageSize` enables the client to enforce, or further restrict, the maximum size of a single message or set of messages published via REST. The default value is `65536` (64 KiB). In the case of a realtime connection, the server may indicate the associated maximum message size on connection establishment; this value takes precedence over the client's default `maxMessageSize`. + * + * @defaultValue 65536 */ - type capabilityOp = - | 'publish' - | 'subscribe' - | 'presence' - | 'history' - | 'stats' - | 'channel-metadata' - | 'push-subscribe' - | 'push-admin'; + maxMessageSize?: number; +} + +/** + * Describes the {@link ClientOptions.plugins | plugins} accepted by all variants of the SDK. + */ +export interface CorePlugins { /** - * Capabilities which are available for use within {@link TokenParams}. + * A plugin capable of decoding `vcdiff`-encoded messages. For more information on how to configure a channel to use delta encoding, see the [documentation for the `@ably-forks/vcdiff-decoder` package](https://github.com/ably-forks/vcdiff-decoder#usage). */ - type CapabilityOp = capabilityOp; + vcdiff?: any; +} +/** + * Passes authentication-specific properties in authentication requests to Ably. Properties set using `AuthOptions` are used instead of the default values set when the client library is instantiated, as opposed to being merged with them. + */ +export interface AuthOptions { /** - * Defines the properties of an Ably Token. + * Called when a new token is required. The role of the callback is to obtain a fresh token, one of: an Ably Token string (in plain text format); a signed {@link TokenRequest}; a {@link TokenDetails} (in JSON format); an [Ably JWT](https://ably.com/docs/core-features/authentication.ably-jwt). See [the authentication documentation](https://ably.com/docs/realtime/authentication) for details of the Ably {@link TokenRequest} format and associated API calls. + * + * @param data - The parameters that should be used to generate the token. + * @param callback - A function which, upon success, the `authCallback` should call with one of: an Ably Token string (in plain text format); a signed `TokenRequest`; a `TokenDetails` (in JSON format); an [Ably JWT](https://ably.com/docs/core-features/authentication#ably-jwt). Upon failure, the `authCallback` should call this function with information about the error. */ - interface TokenParams { - /** - * The capabilities associated with this Ably Token. The capabilities value is a JSON-encoded representation of the resource paths and associated operations. Read more about capabilities in the [capabilities docs](https://ably.com/docs/core-features/authentication/#capabilities-explained). - * - * @defaultValue `'{"*":["*"]}'` - */ - capability?: { [key: string]: capabilityOp[] } | string; - /** - * A client ID, used for identifying this client when publishing messages or for presence purposes. The `clientId` can be any non-empty string, except it cannot contain a `*`. This option is primarily intended to be used in situations where the library is instantiated with a key. Note that a `clientId` may also be implicit in a token used to instantiate the library. An error is raised if a `clientId` specified here conflicts with the `clientId` implicit in the token. Find out more about [identified clients](https://ably.com/docs/core-features/authentication#identified-clients). - */ - clientId?: string; + authCallback?( + data: TokenParams, /** - * A cryptographically secure random string of at least 16 characters, used to ensure the {@link TokenRequest} cannot be reused. - */ - nonce?: string; - /** - * The timestamp of this request as milliseconds since the Unix epoch. Timestamps, in conjunction with the `nonce`, are used to prevent requests from being replayed. `timestamp` is a "one-time" value, and is valid in a request, but is not validly a member of any default token params such as `ClientOptions.defaultTokenParams`. - */ - timestamp?: number; - /** - * Requested time to live for the token in milliseconds. The default is 60 minutes. + * A function which, upon success, the `authCallback` should call with one of: an Ably Token string (in plain text format); a signed `TokenRequest`; a `TokenDetails` (in JSON format); an [Ably JWT](https://ably.com/docs/core-features/authentication#ably-jwt). Upon failure, the `authCallback` should call this function with information about the error. * - * @defaultValue 60min + * @param error - Should be `null` if the auth request completed successfully, or containing details of the error if not. + * @param tokenRequestOrDetails - A valid `TokenRequest`, `TokenDetails` or Ably JWT to be used for authentication. */ - ttl?: number; - } + callback: ( + error: ErrorInfo | string | null, + tokenRequestOrDetails: TokenDetails | TokenRequest | string | null, + ) => void, + ): void; /** - * Sets the properties to configure encryption for a {@link ChannelBase} or {@link RealtimeChannelBase} object. + * A set of key-value pair headers to be added to any request made to the `authUrl`. Useful when an application requires these to be added to validate the request or implement the response. If the `authHeaders` object contains an `authorization` key, then `withCredentials` is set on the XHR request. */ - interface CipherParams { - /** - * The algorithm to use for encryption. Only `AES` is supported and is the default value. - * - * @defaultValue `"AES"` - */ - algorithm: string; - /** - * The private key used to encrypt and decrypt payloads. You should not set this value directly; rather, you should pass a `key` of type {@link Types.CipherKeyParam} to {@link Crypto.getDefaultParams}. - */ - key: CipherKey; - /** - * The length of the key in bits; either 128 or 256. - */ - keyLength: number; - /** - * The cipher mode. Only `CBC` is supported and is the default value. - * - * @defaultValue `"CBC"` - */ - mode: string; - } + authHeaders?: { [index: string]: string }; /** - * A generic Ably error object that contains an Ably-specific status code, and a generic status code. Errors returned from the Ably server are compatible with the `ErrorInfo` structure and should result in errors that inherit from `ErrorInfo`. + * The HTTP verb to use for any request made to the `authUrl`, either `GET` or `POST`. The default value is `GET`. + * + * @defaultValue `HTTPMethod.GET` */ - class ErrorInfo extends Error { - /** - * Ably [error code](https://github.com/ably/ably-common/blob/main/protocol/errors.json). - */ - code: number; - /** - * Additional message information, where available. - */ - message: string; - /** - * HTTP Status Code corresponding to this error, where applicable. - */ - statusCode: number; - /** - * The underlying cause of the error, where applicable. - */ - cause?: string | Error | ErrorInfo; + authMethod?: HTTPMethod; - /** - * Construct an ErrorInfo object. - * - * @param message - A string describing the error. - * @param code - Ably [error code](https://github.com/ably/ably-common/blob/main/protocol/errors.json). - * @param statusCode - HTTP Status Code corresponding to this error. - * @param cause - The underlying cause of the error. - */ - constructor(message: string, code: number, statusCode: number, cause?: string | Error | ErrorInfo); - } + /** + * A set of key-value pair params to be added to any request made to the `authUrl`. When the `authMethod` is `GET`, query params are added to the URL, whereas when `authMethod` is `POST`, the params are sent as URL encoded form data. Useful when an application requires these to be added to validate the request or implement the response. + */ + authParams?: { [index: string]: string }; /** - * Contains the aggregate counts for messages and data transferred. + * A URL that the library may use to obtain a token string (in plain text format), or a signed {@link TokenRequest} or {@link TokenDetails} (in JSON format) from. */ - interface StatsMessageCount { - /** - * The count of all messages. - */ - count: number; - /** - * The total number of bytes transferred for all messages. - */ - data: number; - } + authUrl?: string; /** - * Contains a breakdown of summary stats data for different (channel vs presence) message types. + * The full API key string, as obtained from the [Ably dashboard](https://ably.com/dashboard). Use this option if you wish to use Basic authentication, or wish to be able to issue Ably Tokens without needing to defer to a separate entity to sign Ably {@link TokenRequest | `TokenRequest`s}. Read more about [Basic authentication](https://ably.com/docs/core-features/authentication#basic-authentication). */ - interface StatsMessageTypes { - /** - * A {@link StatsMessageCount} object containing the count and byte value of messages and presence messages. - */ - all: StatsMessageCount; - /** - * A {@link StatsMessageCount} object containing the count and byte value of messages. - */ - messages: StatsMessageCount; - /** - * A {@link StatsMessageCount} object containing the count and byte value of presence messages. - */ - presence: StatsMessageCount; - } + key?: string; /** - * Contains the aggregate counts for requests made. + * If `true`, the library queries the Ably servers for the current time when issuing {@link TokenRequest | `TokenRequest`s} instead of relying on a locally-available time of day. Knowing the time accurately is needed to create valid signed Ably {@link TokenRequest | `TokenRequest`s}, so this option is useful for library instances on auth servers where for some reason the server clock cannot be kept synchronized through normal means, such as an [NTP daemon](https://en.wikipedia.org/wiki/Ntpd). The server is queried for the current time once per client library instance (which stores the offset from the local clock), so if using this option you should avoid instancing a new version of the library for each request. The default is `false`. + * + * @defaultValue `false` */ - interface StatsRequestCount { - /** - * The number of requests that failed. - */ - failed: number; - /** - * The number of requests that were refused, typically as a result of permissions or a limit being exceeded. - */ - refused: number; - /** - * The number of requests that succeeded. - */ - succeeded: number; - } + queryTime?: boolean; /** - * Contains the aggregate data for usage of a resource in a specific scope. + * An authenticated token. This can either be a {@link TokenDetails} object or token string (obtained from the `token` property of a {@link TokenDetails} component of an Ably {@link TokenRequest} response, or a JSON Web Token satisfying [the Ably requirements for JWTs](https://ably.com/docs/core-features/authentication#ably-jwt)). This option is mostly useful for testing: since tokens are short-lived, in production you almost always want to use an authentication method that enables the client library to renew the token automatically when the previous one expires, such as `authUrl` or `authCallback`. Read more about [Token authentication](https://ably.com/docs/core-features/authentication#token-authentication). */ - interface StatsResourceCount { - /** - * The average number of resources of this type used for this period. - */ - mean: number; - /** - * The minimum total resources of this type used for this period. - */ - min: number; - /** - * The total number of resources opened of this type. - */ - opened: number; - /** - * The peak number of resources of this type used for this period. - */ - peak: number; - /** - * The number of resource requests refused within this period. - */ - refused: number; - } + token?: TokenDetails | string; /** - * Contains a breakdown of summary stats data for different (TLS vs non-TLS) connection types. + * An authenticated {@link TokenDetails} object (most commonly obtained from an Ably Token Request response). This option is mostly useful for testing: since tokens are short-lived, in production you almost always want to use an authentication method that enables the client library to renew the token automatically when the previous one expires, such as `authUrl` or `authCallback`. Use this option if you wish to use Token authentication. Read more about [Token authentication](https://ably.com/docs/core-features/authentication#token-authentication). */ - interface StatsConnectionTypes { - /** - * A {@link StatsResourceCount} object containing a breakdown of usage by scope over TLS connections (both TLS and non-TLS). - */ - all: StatsResourceCount; - /** - * A {@link StatsResourceCount} object containing a breakdown of usage by scope over non-TLS connections. - */ - plain: StatsResourceCount; - /** - * A {@link StatsResourceCount} object containing a breakdown of usage by scope over TLS connections. - */ - tls: StatsResourceCount; - } + tokenDetails?: TokenDetails; /** - * Contains a breakdown of summary stats data for traffic over various transport types. + * When `true`, forces token authentication to be used by the library. If a `clientId` is not specified in the {@link ClientOptions} or {@link TokenParams}, then the Ably Token issued is [anonymous](https://ably.com/docs/core-features/authentication#identified-clients). */ - interface StatsMessageTraffic { - /** - * A {@link StatsMessageTypes} object containing a breakdown of usage by message type for all messages (includes `realtime`, `rest` and `webhook` messages). - */ - all: StatsMessageTypes; - /** - * A {@link StatsMessageTypes} object containing a breakdown of usage by message type for messages transferred over a realtime transport such as WebSocket. - */ - realtime: StatsMessageTypes; - /** - * A {@link StatsMessageTypes} object containing a breakdown of usage by message type for messages transferred over a rest transport such as WebSocket. - */ - rest: StatsMessageTypes; - /** - * A {@link StatsMessageTypes} object containing a breakdown of usage by message type for messages delivered using webhooks. - */ - webhook: StatsMessageTypes; - } + useTokenAuth?: boolean; /** - * Contains an Ably Token and its associated metadata. + * A client ID, used for identifying this client when publishing messages or for presence purposes. The `clientId` can be any non-empty string, except it cannot contain a `*`. This option is primarily intended to be used in situations where the library is instantiated with a key. Note that a `clientId` may also be implicit in a token used to instantiate the library. An error will be raised if a `clientId` specified here conflicts with the `clientId` implicit in the token. Find out more about [client identities](https://ably.com/documentation/how-ably-works#client-identity). */ - interface TokenDetails { - /** - * The capabilities associated with this Ably Token. The capabilities value is a JSON-encoded representation of the resource paths and associated operations. Read more about capabilities in the [capabilities docs](https://ably.com/docs/core-features/authentication/#capabilities-explained). - */ - capability: string; - /** - * The client ID, if any, bound to this Ably Token. If a client ID is included, then the Ably Token authenticates its bearer as that client ID, and the Ably Token may only be used to perform operations on behalf of that client ID. The client is then considered to be an [identified client](https://ably.com/docs/core-features/authentication#identified-clients). - */ - clientId?: string; - /** - * The timestamp at which this token expires as milliseconds since the Unix epoch. - */ - expires: number; - /** - * The timestamp at which this token was issued as milliseconds since the Unix epoch. - */ - issued: number; - /** - * The [Ably Token](https://ably.com/docs/core-features/authentication#ably-tokens) itself. A typical Ably Token string appears with the form `xVLyHw.A-pwh7wicf3afTfgiw4k2Ku33kcnSA7z6y8FjuYpe3QaNRTEo4`. - */ - token: string; - } + clientId?: string; +} +/** + * Capabilities which are available for use within {@link TokenParams}. + */ +export type capabilityOp = + | 'publish' + | 'subscribe' + | 'presence' + | 'history' + | 'stats' + | 'channel-metadata' + | 'push-subscribe' + | 'push-admin'; +/** + * Capabilities which are available for use within {@link TokenParams}. + */ +export type CapabilityOp = capabilityOp; + +/** + * Defines the properties of an Ably Token. + */ +export interface TokenParams { /** - * Contains the properties of a request for a token to Ably. Tokens are generated using {@link AuthCallbacks.requestToken} or {@link AuthPromise.requestToken}. + * The capabilities associated with this Ably Token. The capabilities value is a JSON-encoded representation of the resource paths and associated operations. Read more about capabilities in the [capabilities docs](https://ably.com/docs/core-features/authentication/#capabilities-explained). + * + * @defaultValue `'{"*":["*"]}'` */ - interface TokenRequest { - /** - * Capability of the requested Ably Token. If the Ably `TokenRequest` is successful, the capability of the returned Ably Token will be the intersection of this capability with the capability of the issuing key. The capabilities value is a JSON-encoded representation of the resource paths and associated operations. Read more about capabilities in the [capabilities docs](https://ably.com/docs/realtime/authentication). - */ - capability: string; - /** - * The client ID to associate with the requested Ably Token. When provided, the Ably Token may only be used to perform operations on behalf of that client ID. - */ - clientId?: string; - /** - * The name of the key against which this request is made. The key name is public, whereas the key secret is private. - */ - keyName: string; - /** - * The Message Authentication Code for this request. - */ - mac: string; - /** - * A cryptographically secure random string of at least 16 characters, used to ensure the `TokenRequest` cannot be reused. - */ - nonce: string; - /** - * The timestamp of this request as milliseconds since the Unix epoch. - */ - timestamp: number; - /** - * Requested time to live for the Ably Token in milliseconds. If the Ably `TokenRequest` is successful, the TTL of the returned Ably Token is less than or equal to this value, depending on application settings and the attributes of the issuing key. The default is 60 minutes. - * - * @defaultValue 60min - */ - ttl?: number; - } - + capability?: { [key: string]: capabilityOp[] } | string; /** - * [Channel Parameters](https://ably.com/docs/realtime/channels/channel-parameters/overview) used within {@link ChannelOptions}. + * A client ID, used for identifying this client when publishing messages or for presence purposes. The `clientId` can be any non-empty string, except it cannot contain a `*`. This option is primarily intended to be used in situations where the library is instantiated with a key. Note that a `clientId` may also be implicit in a token used to instantiate the library. An error is raised if a `clientId` specified here conflicts with the `clientId` implicit in the token. Find out more about [identified clients](https://ably.com/docs/core-features/authentication#identified-clients). */ - type ChannelParams = { [key: string]: string }; - + clientId?: string; /** - * The `ChannelMode` namespace describes the possible values of the {@link ChannelMode:type} type. + * A cryptographically secure random string of at least 16 characters, used to ensure the {@link TokenRequest} cannot be reused. */ - namespace ChannelMode { - /** - * The client can publish messages. - */ - type PUBLISH = 'PUBLISH'; - /** - * The client can subscribe to messages. - */ - type SUBSCRIBE = 'SUBSCRIBE'; - /** - * The client can enter the presence set. - */ - type PRESENCE = 'PRESENCE'; - /** - * The client can receive presence messages. - */ - type PRESENCE_SUBSCRIBE = 'PRESENCE_SUBSCRIBE'; - /** - * The client is resuming an existing connection. - */ - type ATTACH_RESUME = 'ATTACH_RESUME'; - } - + nonce?: string; /** - * Describes the possible flags used to configure client capabilities, using {@link ChannelOptions}. + * The timestamp of this request as milliseconds since the Unix epoch. Timestamps, in conjunction with the `nonce`, are used to prevent requests from being replayed. `timestamp` is a "one-time" value, and is valid in a request, but is not validly a member of any default token params such as `ClientOptions.defaultTokenParams`. */ - type ChannelMode = - | ChannelMode.PUBLISH - | ChannelMode.SUBSCRIBE - | ChannelMode.PRESENCE - | ChannelMode.PRESENCE_SUBSCRIBE - | ChannelMode.ATTACH_RESUME; + timestamp?: number; /** - * Describes the possible flags used to configure client capabilities, using {@link ChannelOptions}. + * Requested time to live for the token in milliseconds. The default is 60 minutes. + * + * @defaultValue 60min */ - type ChannelModes = Array; + ttl?: number; +} +/** + * Sets the properties to configure encryption for a {@link Channel} or {@link RealtimeChannel} object. + */ +export interface CipherParams { /** - * Passes additional properties to a {@link ChannelBase} or {@link RealtimeChannelBase} object, such as encryption, {@link ChannelMode} and channel parameters. + * The algorithm to use for encryption. Only `AES` is supported and is the default value. + * + * @defaultValue `"AES"` */ - interface ChannelOptions { - /** - * Requests encryption for this channel when not null, and specifies encryption-related parameters (such as algorithm, chaining mode, key length and key). See [an example](https://ably.com/docs/realtime/encryption#getting-started). - */ - cipher?: CipherParamOptions | CipherParams; - /** - * [Channel Parameters](https://ably.com/docs/realtime/channels/channel-parameters/overview) that configure the behavior of the channel. - */ - params?: ChannelParams; - /** - * An array of {@link ChannelMode} objects. - */ - modes?: ChannelModes; - } - + algorithm: string; /** - * Passes additional properties to a {@link RealtimeChannelBase} name to produce a new derived channel + * The private key used to encrypt and decrypt payloads. You should not set this value directly; rather, you should pass a `key` of type {@link CipherKeyParam} to {@link Crypto.getDefaultParams}. */ - interface DeriveOptions { - /** - * The JMESPath Query filter string to be used to derive new channel. - */ - filter?: string; - } - + key: unknown; /** - * The `RestHistoryParams` interface describes the parameters accepted by the following methods: - * - * - {@link PresenceCallbacks.history} - * - {@link PresencePromise.history} - * - {@link ChannelCallbacks.history} - * - {@link ChannelPromise.history} + * The length of the key in bits; either 128 or 256. */ - interface RestHistoryParams { - /** - * The time from which messages are retrieved, specified as milliseconds since the Unix epoch. - */ - start?: number; - /** - * The time until messages are retrieved, specified as milliseconds since the Unix epoch. - * - * @defaultValue The current time. - */ - end?: number; - /** - * The order for which messages are returned in. Valid values are `'backwards'` which orders messages from most recent to oldest, or `'forwards'` which orders messages from oldest to most recent. The default is `'backwards'`. - * - * @defaultValue `'backwards'` - */ - direction?: 'forwards' | 'backwards'; - /** - * An upper limit on the number of messages returned. The default is 100, and the maximum is 1000. - * - * @defaultValue 100 - */ - limit?: number; - } - + keyLength: number; /** - * The `RestPresenceParams` interface describes the parameters accepted by the following methods: + * The cipher mode. Only `CBC` is supported and is the default value. * - * - {@link PresenceCallbacks.get} - * - {@link PresencePromise.get} + * @defaultValue `"CBC"` */ - interface RestPresenceParams { - /** - * An upper limit on the number of messages returned. The default is 100, and the maximum is 1000. - * - * @defaultValue 100 - */ - limit?: number; - /** - * Filters the list of returned presence members by a specific client using its ID. - */ - clientId?: string; - /** - * Filters the list of returned presence members by a specific connection using its ID. - */ - connectionId?: string; - } + mode: string; +} +/** + * Contains an Ably Token and its associated metadata. + */ +export interface TokenDetails { /** - * The `RealtimePresenceParams` interface describes the parameters accepted by the following methods: - * - * - {@link RealtimePresenceCallbacks.get} - * - {@link RealtimePresencePromise.get} + * The capabilities associated with this Ably Token. The capabilities value is a JSON-encoded representation of the resource paths and associated operations. Read more about capabilities in the [capabilities docs](https://ably.com/docs/core-features/authentication/#capabilities-explained). */ - interface RealtimePresenceParams { - /** - * Sets whether to wait for a full presence set synchronization between Ably and the clients on the channel to complete before returning the results. Synchronization begins as soon as the channel is {@link ChannelState.ATTACHED}. When set to `true` the results will be returned as soon as the sync is complete. When set to `false` the current list of members will be returned without the sync completing. The default is `true`. - * - * @defaultValue `true` - */ - waitForSync?: boolean; - /** - * Filters the array of returned presence members by a specific client using its ID. - */ - clientId?: string; - /** - * Filters the array of returned presence members by a specific connection using its ID. - */ - connectionId?: string; - } - + capability: string; /** - * The `RealtimeHistoryParams` interface describes the parameters accepted by the following methods: - * - * - {@link RealtimePresenceCallbacks.history} - * - {@link RealtimePresencePromise.history} - * - {@link RealtimeChannelCallbacks.history} - * - {@link RealtimeChannelPromise.history} + * The client ID, if any, bound to this Ably Token. If a client ID is included, then the Ably Token authenticates its bearer as that client ID, and the Ably Token may only be used to perform operations on behalf of that client ID. The client is then considered to be an [identified client](https://ably.com/docs/core-features/authentication#identified-clients). */ - interface RealtimeHistoryParams { - /** - * The time from which messages are retrieved, specified as milliseconds since the Unix epoch. - */ - start?: number; - /** - * The time until messages are retrieved, specified as milliseconds since the Unix epoch. - * - * @defaultValue The current time. - */ - end?: number; - /** - * The order for which messages are returned in. Valid values are `'backwards'` which orders messages from most recent to oldest, or `'forwards'` which orders messages from oldest to most recent. The default is `'backwards'`. - * - * @defaultValue `'backwards'` - */ - direction?: 'forwards' | 'backwards'; - /** - * An upper limit on the number of messages returned. The default is 100, and the maximum is 1000. - * - * @defaultValue 100 - */ - limit?: number; - /** - * When `true`, ensures message history is up until the point of the channel being attached. See [continuous history](https://ably.com/docs/realtime/history#continuous-history) for more info. Requires the `direction` to be `backwards`. If the channel is not attached, or if `direction` is set to `forwards`, this option results in an error. - */ - untilAttach?: boolean; - } + clientId?: string; + /** + * The timestamp at which this token expires as milliseconds since the Unix epoch. + */ + expires: number; + /** + * The timestamp at which this token was issued as milliseconds since the Unix epoch. + */ + issued: number; + /** + * The [Ably Token](https://ably.com/docs/core-features/authentication#ably-tokens) itself. A typical Ably Token string appears with the form `xVLyHw.A-pwh7wicf3afTfgiw4k2Ku33kcnSA7z6y8FjuYpe3QaNRTEo4`. + */ + token: string; +} +/** + * Contains the properties of a request for a token to Ably. Tokens are generated using {@link Auth.requestToken}. + */ +export interface TokenRequest { + /** + * Capability of the requested Ably Token. If the Ably `TokenRequest` is successful, the capability of the returned Ably Token will be the intersection of this capability with the capability of the issuing key. The capabilities value is a JSON-encoded representation of the resource paths and associated operations. Read more about capabilities in the [capabilities docs](https://ably.com/docs/realtime/authentication). + */ + capability: string; + /** + * The client ID to associate with the requested Ably Token. When provided, the Ably Token may only be used to perform operations on behalf of that client ID. + */ + clientId?: string; + /** + * The name of the key against which this request is made. The key name is public, whereas the key secret is private. + */ + keyName: string; /** - * Settings which control the log output of the library. + * The Message Authentication Code for this request. + */ + mac: string; + /** + * A cryptographically secure random string of at least 16 characters, used to ensure the `TokenRequest` cannot be reused. + */ + nonce: string; + /** + * The timestamp of this request as milliseconds since the Unix epoch. + */ + timestamp: number; + /** + * Requested time to live for the Ably Token in milliseconds. If the Ably `TokenRequest` is successful, the TTL of the returned Ably Token is less than or equal to this value, depending on application settings and the attributes of the issuing key. The default is 60 minutes. * - * @deprecated This type is deprecated and will be removed in a future version. Use the {@link ClientOptions.logLevel} and {@link ClientOptions.logHandler} client options instead. + * @defaultValue 60min */ - interface LogInfo { - /** - * Controls the verbosity of the logs output from the library. Valid values are: 0 (no logs), 1 (errors only), 2 (errors plus connection and channel state changes), 3 (high-level debug output), and 4 (full debug output). - * - * @deprecated This property is deprecated and will be removed in a future version. Use the {@link ClientOptions.logLevel} client option instead. - */ - level?: number; + ttl?: number; +} - /** - * Controls the log output of the library. This is a function to handle each line of log output. If you do not set this value, then `console.log` will be used. - * - * @deprecated This property is deprecated and will be removed in a future version. Use the {@link ClientOptions.logHandler} client option instead. - * @param msg - The log message emitted by the library. - */ - handler?: (msg: string) => void; - } +/** + * [Channel Parameters](https://ably.com/docs/realtime/channels/channel-parameters/overview) used within {@link ChannelOptions}. + */ +export type ChannelParams = { [key: string]: string }; +/** + * The `ChannelModes` namespace describes the possible values of the {@link ChannelMode} type. + */ +declare namespace ChannelModes { /** - * Contains state change information emitted by {@link ChannelBase} and {@link RealtimeChannelBase} objects. + * The client can publish messages. */ - interface ChannelStateChange { - /** - * The new current {@link ChannelState}. - */ - current: ChannelState; - /** - * The previous state. For the {@link ChannelEvent.UPDATE} event, this is equal to the `current` {@link ChannelState}. - */ - previous: ChannelState; - /** - * An {@link ErrorInfo} object containing any information relating to the transition. - */ - reason?: ErrorInfo; - /** - * Indicates whether message continuity on this channel is preserved, see [Nonfatal channel errors](https://ably.com/docs/realtime/channels#nonfatal-errors) for more info. - */ - resumed: boolean; - /** - * Indicates whether the client can expect a backlog of messages from a rewind or resume. - */ - hasBacklog?: boolean; - } - + type PUBLISH = 'PUBLISH'; /** - * Contains {@link ConnectionState} change information emitted by the {@link ConnectionBase} object. + * The client can subscribe to messages. */ - interface ConnectionStateChange { - /** - * The new {@link ConnectionState}. - */ - current: ConnectionState; - /** - * The previous {@link ConnectionState}. For the {@link ConnectionEvent.UPDATE} event, this is equal to the current {@link ConnectionState}. - */ - previous: ConnectionState; - /** - * An {@link ErrorInfo} object containing any information relating to the transition. - */ - reason?: ErrorInfo; - /** - * Duration in milliseconds, after which the client retries a connection where applicable. - */ - retryIn?: number; - } - + type SUBSCRIBE = 'SUBSCRIBE'; /** - * The `DevicePlatform` namespace describes the possible values of the {@link DevicePlatform:type} type. + * The client can enter the presence set. */ - namespace DevicePlatform { - /** - * The device platform is Android. - */ - type ANDROID = 'android'; - /** - * The device platform is iOS. - */ - type IOS = 'ios'; - /** - * The device platform is a web browser. - */ - type BROWSER = 'browser'; - } - + type PRESENCE = 'PRESENCE'; /** - * Describes the device receiving push notifications. + * The client can receive presence messages. */ - type DevicePlatform = DevicePlatform.ANDROID | DevicePlatform.IOS | DevicePlatform.BROWSER; - + type PRESENCE_SUBSCRIBE = 'PRESENCE_SUBSCRIBE'; /** - * The `DeviceFormFactor` namespace describes the possible values of the {@link DeviceFormFactor:type} type. + * The client is resuming an existing connection. */ - namespace DeviceFormFactor { - /** - * The device is a phone. - */ - type PHONE = 'phone'; - /** - * The device is tablet. - */ - type TABLET = 'tablet'; - /** - * The device is a desktop. - */ - type DESKTOP = 'desktop'; - /** - * The device is a TV. - */ - type TV = 'tv'; - /** - * The device is a watch. - */ - type WATCH = 'watch'; - /** - * The device is a car. - */ - type CAR = 'car'; - /** - * The device is embedded. - */ - type EMBEDDED = 'embedded'; - /** - * The device is other. - */ - type OTHER = 'other'; - } + type ATTACH_RESUME = 'ATTACH_RESUME'; +} + +/** + * Describes the possible flags used to configure client capabilities, using {@link ChannelOptions}. + */ +export type ChannelMode = + | ChannelModes.PUBLISH + | ChannelModes.SUBSCRIBE + | ChannelModes.PRESENCE + | ChannelModes.PRESENCE_SUBSCRIBE + | ChannelModes.ATTACH_RESUME; +/** + * Passes additional properties to a {@link Channel} or {@link RealtimeChannel} object, such as encryption, {@link ChannelMode} and channel parameters. + */ +export interface ChannelOptions { /** - * Describes the type of device receiving a push notification. + * Requests encryption for this channel when not null, and specifies encryption-related parameters (such as algorithm, chaining mode, key length and key). See [an example](https://ably.com/docs/realtime/encryption#getting-started). When running in a browser, encryption is only available when the current environment is a [secure context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts). */ - type DeviceFormFactor = - | DeviceFormFactor.PHONE - | DeviceFormFactor.TABLET - | DeviceFormFactor.DESKTOP - | DeviceFormFactor.TV - | DeviceFormFactor.WATCH - | DeviceFormFactor.CAR - | DeviceFormFactor.EMBEDDED - | DeviceFormFactor.OTHER; - + cipher?: CipherParamOptions | CipherParams; /** - * Contains the properties of a device registered for push notifications. + * [Channel Parameters](https://ably.com/docs/realtime/channels/channel-parameters/overview) that configure the behavior of the channel. */ - interface DeviceDetails { - /** - * A unique ID generated by the device. - */ - id: string; - /** - * The client ID the device is connected to Ably with. - */ - clientId?: string; - /** - * The {@link DevicePlatform} associated with the device. Describes the platform the device uses, such as `android` or `ios`. - */ - platform: DevicePlatform; - /** - * The {@link DeviceFormFactor} object associated with the device. Describes the type of the device, such as `phone` or `tablet`. - */ - formFactor: DeviceFormFactor; - /** - * A JSON object of key-value pairs that contains metadata for the device. - */ - metadata?: any; - /** - * A unique device secret generated by the Ably SDK. - */ - deviceSecret?: string; - /** - * The {@link DevicePushDetails} object associated with the device. Describes the details of the push registration of the device. - */ - push: DevicePushDetails; - } - + params?: ChannelParams; /** - * Contains the subscriptions of a device, or a group of devices sharing the same `clientId`, has to a channel in order to receive push notifications. + * An array of {@link ChannelMode} objects. */ - interface PushChannelSubscription { - /** - * The channel the push notification subscription is for. - */ - channel: string; - /** - * The unique ID of the device. - */ - deviceId?: string; - /** - * The ID of the client the device, or devices are associated to. - */ - clientId?: string; - } + modes?: ChannelMode[]; +} +/** + * Passes additional properties to a {@link RealtimeChannel} name to produce a new derived channel + */ +export interface DeriveOptions { /** - * Valid states which a Push device may be in. + * The JMESPath Query filter string to be used to derive new channel. */ - type DevicePushState = 'ACTIVE' | 'FAILING' | 'FAILED'; + filter?: string; +} +/** + * The `RestHistoryParams` interface describes the parameters accepted by the following methods: + * + * - {@link Presence.history} + * - {@link Channel.history} + */ +export interface RestHistoryParams { /** - * Contains the details of the push registration of a device. + * The time from which messages are retrieved, specified as milliseconds since the Unix epoch. */ - interface DevicePushDetails { - /** - * A JSON object of key-value pairs that contains of the push transport and address. - */ - recipient: any; - /** - * The current state of the push registration. - */ - state?: DevicePushState; - /** - * An {@link ErrorInfo} object describing the most recent error when the `state` is `Failing` or `Failed`. - */ - error?: ErrorInfo; - } - + start?: number; /** - * The `DeviceRegistrationParams` interface describes the parameters accepted by the following methods: + * The time until messages are retrieved, specified as milliseconds since the Unix epoch. * - * - {@link PushDeviceRegistrationsCallbacks.list} - * - {@link PushDeviceRegistrationsCallbacks.removeWhere} - * - {@link PushDeviceRegistrationsPromise.list} - * - {@link PushDeviceRegistrationsPromise.removeWhere} + * @defaultValue The current time. */ - interface DeviceRegistrationParams { - /** - * Filter to restrict to devices associated with a client ID. - */ - clientId?: string; - /** - * Filter to restrict by the unique ID of the device. - */ - deviceId?: string; - /** - * A limit on the number of devices returned, up to 1,000. - */ - limit?: number; - /** - * Filter by the state of the device. - */ - state?: DevicePushState; - } - + end?: number; /** - * The `PushChannelSubscriptionParams` interface describes the parameters accepted by the following methods: + * The order for which messages are returned in. Valid values are `'backwards'` which orders messages from most recent to oldest, or `'forwards'` which orders messages from oldest to most recent. The default is `'backwards'`. * - * - {@link PushChannelSubscriptionsCallbacks.list} - * - {@link PushChannelSubscriptionsCallbacks.removeWhere} - * - {@link PushChannelSubscriptionsPromise.list} - * - {@link PushChannelSubscriptionsPromise.removeWhere} + * @defaultValue `'backwards'` */ - interface PushChannelSubscriptionParams { - /** - * Filter to restrict to subscriptions associated with the given channel. - */ - channel?: string; - /** - * Filter to restrict to devices associated with the given client identifier. Cannot be used with a deviceId param. - */ - clientId?: string; - /** - * Filter to restrict to devices associated with that device identifier. Cannot be used with a clientId param. - */ - deviceId?: string; - /** - * A limit on the number of devices returned, up to 1,000. - */ - limit?: number; - } - + direction?: 'forwards' | 'backwards'; /** - * The `PushChannelsParams` interface describes the parameters accepted by the following methods: + * An upper limit on the number of messages returned. The default is 100, and the maximum is 1000. * - * - {@link PushChannelSubscriptionsCallbacks.listChannels} - * - {@link PushChannelSubscriptionsPromise.listChannels} + * @defaultValue 100 */ - interface PushChannelsParams { - /** - * A limit on the number of channels returned, up to 1,000. - */ - limit?: number; - } + limit?: number; +} +/** + * The `RestPresenceParams` interface describes the parameters accepted by {@link Presence.get}. + */ +export interface RestPresenceParams { /** - * The `StatsParams` interface describes the parameters accepted by the following methods: + * An upper limit on the number of messages returned. The default is 100, and the maximum is 1000. * - * - {@link RestCallbacks.stats} - * - {@link RestPromise.stats} - * - {@link RealtimeCallbacks.stats} - * - {@link RealtimePromise.stats} + * @defaultValue 100 */ - interface StatsParams { - /** - * The time from which stats are retrieved, specified as milliseconds since the Unix epoch. - * - * @defaultValue The Unix epoch. - */ - start?: number; - /** - * The time until stats are retrieved, specified as milliseconds since the Unix epoch. - * - * @defaultValue The current time. - */ - end?: number; - /** - * The order for which stats are returned in. Valid values are `'backwards'` which orders stats from most recent to oldest, or `'forwards'` which orders stats from oldest to most recent. The default is `'backwards'`. - * - * @defaultValue `'backwards'` - */ - direction?: 'backwards' | 'forwards'; - /** - * An upper limit on the number of stats returned. The default is 100, and the maximum is 1000. - * - * @defaultValue 100 - */ - limit?: number; - /** - * Based on the unit selected, the given `start` or `end` times are rounded down to the start of the relevant interval depending on the unit granularity of the query. - * - * @defaultValue `StatsIntervalGranularity.MINUTE` - */ - unit?: StatsIntervalGranularity; - } - + limit?: number; /** - * Contains information about the results of a batch operation. + * Filters the list of returned presence members by a specific client using its ID. */ - interface BatchResult { - /** - * The number of successful operations in the request. - */ - successCount: number; - /** - * The number of unsuccessful operations in the request. - */ - failureCount: number; - /** - * An array of results for the batch operation. - */ - results: T[]; - } + clientId?: string; + /** + * Filters the list of returned presence members by a specific connection using its ID. + */ + connectionId?: string; +} +/** + * The `RealtimePresenceParams` interface describes the parameters accepted by {@link RealtimePresence.get}. + */ +export interface RealtimePresenceParams { /** - * Describes the messages that should be published by a batch publish operation, and the channels to which they should be published. + * Sets whether to wait for a full presence set synchronization between Ably and the clients on the channel to complete before returning the results. Synchronization begins as soon as the channel is {@link ChannelStates.ATTACHED}. When set to `true` the results will be returned as soon as the sync is complete. When set to `false` the current list of members will be returned without the sync completing. The default is `true`. + * + * @defaultValue `true` */ - interface BatchPublishSpec { - /** - * The names of the channels to publish the `messages` to. - */ - channels: string[]; - /** - * An array of {@link Message} objects. - */ - messages: Message[]; - } + waitForSync?: boolean; + /** + * Filters the array of returned presence members by a specific client using its ID. + */ + clientId?: string; + /** + * Filters the array of returned presence members by a specific connection using its ID. + */ + connectionId?: string; +} +/** + * The `RealtimeHistoryParams` interface describes the parameters accepted by the following methods: + * + * - {@link RealtimePresence.history} + * - {@link RealtimeChannel.history} + */ +export interface RealtimeHistoryParams { /** - * Contains information about the result of successful publishes to a channel requested by a single {@link Types.BatchPublishSpec}. + * The time from which messages are retrieved, specified as milliseconds since the Unix epoch. */ - interface BatchPublishSuccessResult { - /** - * The name of the channel the message(s) was published to. - */ - channel: string; - /** - * A unique ID prefixed to the {@link Message.id} of each published message. - */ - messageId: string; - } + start?: number; + /** + * The time until messages are retrieved, specified as milliseconds since the Unix epoch. + * + * @defaultValue The current time. + */ + end?: number; + /** + * The order for which messages are returned in. Valid values are `'backwards'` which orders messages from most recent to oldest, or `'forwards'` which orders messages from oldest to most recent. The default is `'backwards'`. + * + * @defaultValue `'backwards'` + */ + direction?: 'forwards' | 'backwards'; + /** + * An upper limit on the number of messages returned. The default is 100, and the maximum is 1000. + * + * @defaultValue 100 + */ + limit?: number; + /** + * When `true`, ensures message history is up until the point of the channel being attached. See [continuous history](https://ably.com/docs/realtime/history#continuous-history) for more info. Requires the `direction` to be `backwards`. If the channel is not attached, or if `direction` is set to `forwards`, this option results in an error. + */ + untilAttach?: boolean; +} +/** + * Contains state change information emitted by {@link Channel} and {@link RealtimeChannel} objects. + */ +export interface ChannelStateChange { /** - * Contains information about the result of unsuccessful publishes to a channel requested by a single {@link Types.BatchPublishSpec}. + * The new current {@link ChannelState}. */ - interface BatchPublishFailureResult { - /** - * The name of the channel the message(s) failed to be published to. - */ - channel: string; - /** - * Describes the reason for which the message(s) failed to publish to the channel as an {@link ErrorInfo} object. - */ - error: ErrorInfo; - } + current: ChannelState; + /** + * The previous state. For the {@link ChannelEvents.UPDATE} event, this is equal to the `current` {@link ChannelState}. + */ + previous: ChannelState; + /** + * An {@link ErrorInfo} object containing any information relating to the transition. + */ + reason?: ErrorInfo; + /** + * Indicates whether message continuity on this channel is preserved, see [Nonfatal channel errors](https://ably.com/docs/realtime/channels#nonfatal-errors) for more info. + */ + resumed: boolean; + /** + * Indicates whether the client can expect a backlog of messages from a rewind or resume. + */ + hasBacklog?: boolean; +} +/** + * Contains {@link ConnectionState} change information emitted by the {@link Connection} object. + */ +export interface ConnectionStateChange { /** - * Contains information about the result of a successful batch presence request for a single channel. + * The new {@link ConnectionState}. */ - interface BatchPresenceSuccessResult { - /** - * The channel name the presence state was retrieved for. - */ - channel: string; - /** - * An array of {@link PresenceMessage}s describing members present on the channel. - */ - presence: PresenceMessage[]; - } + current: ConnectionState; + /** + * The previous {@link ConnectionState}. For the {@link ConnectionEvents.UPDATE} event, this is equal to the current {@link ConnectionState}. + */ + previous: ConnectionState; + /** + * An {@link ErrorInfo} object containing any information relating to the transition. + */ + reason?: ErrorInfo; + /** + * Duration in milliseconds, after which the client retries a connection where applicable. + */ + retryIn?: number; +} +/** + * The `DevicePlatforms` namespace describes the possible values of the {@link DevicePlatform} type. + */ +declare namespace DevicePlatforms { /** - * Contains information about the result of an unsuccessful batch presence request for a single channel. + * The device platform is Android. */ - interface BatchPresenceFailureResult { - /** - * The channel name the presence state failed to be retrieved for. - */ - channel: string; - /** - * Describes the reason for which presence state could not be retrieved for the channel as an {@link ErrorInfo} object. - */ - error: ErrorInfo; - } + type ANDROID = 'android'; + /** + * The device platform is iOS. + */ + type IOS = 'ios'; + /** + * The device platform is a web browser. + */ + type BROWSER = 'browser'; +} + +/** + * Describes the device receiving push notifications. + */ +export type DevicePlatform = DevicePlatforms.ANDROID | DevicePlatforms.IOS | DevicePlatforms.BROWSER; +/** + * The `DeviceFormFactors` namespace describes the possible values of the {@link DeviceFormFactor} type. + */ +declare namespace DeviceFormFactors { /** - * The `TokenRevocationOptions` interface describes the additional options accepted by the following methods: - * - * - {@link AuthCallbacks.revokeTokens} - * - {@link AuthPromise.revokeTokens} + * The device is a phone. */ - interface TokenRevocationOptions { - /** - * A Unix timestamp in milliseconds where only tokens issued before this time are revoked. The default is the current time. Requests with an `issuedBefore` in the future, or more than an hour in the past, will be rejected. - */ - issuedBefore?: number; - /** - * If true, permits a token renewal cycle to take place without needing established connections to be dropped, by postponing enforcement to 30 seconds in the future, and sending any existing connections a hint to obtain (and upgrade the connection to use) a new token. The default is `false`, meaning that the effect is near-immediate. - */ - allowReauthMargin?: boolean; - } + type PHONE = 'phone'; + /** + * The device is tablet. + */ + type TABLET = 'tablet'; + /** + * The device is a desktop. + */ + type DESKTOP = 'desktop'; + /** + * The device is a TV. + */ + type TV = 'tv'; + /** + * The device is a watch. + */ + type WATCH = 'watch'; + /** + * The device is a car. + */ + type CAR = 'car'; + /** + * The device is embedded. + */ + type EMBEDDED = 'embedded'; + /** + * The device is other. + */ + type OTHER = 'other'; +} +/** + * Describes the type of device receiving a push notification. + */ +export type DeviceFormFactor = + | DeviceFormFactors.PHONE + | DeviceFormFactors.TABLET + | DeviceFormFactors.DESKTOP + | DeviceFormFactors.TV + | DeviceFormFactors.WATCH + | DeviceFormFactors.CAR + | DeviceFormFactors.EMBEDDED + | DeviceFormFactors.OTHER; + +/** + * Contains the properties of a device registered for push notifications. + */ +export interface DeviceDetails { /** - * Describes which tokens should be affected by a token revocation request. + * A unique ID generated by the device. */ - interface TokenRevocationTargetSpecifier { - /** - * The type of token revocation target specifier. Valid values include `clientId`, `revocationKey` and `channel`. - */ - type: string; - /** - * The value of the token revocation target specifier. - */ - value: string; - } + id: string; + /** + * The client ID the device is connected to Ably with. + */ + clientId?: string; + /** + * The {@link DevicePlatform} associated with the device. Describes the platform the device uses, such as `android` or `ios`. + */ + platform: DevicePlatform; + /** + * The {@link DeviceFormFactor} object associated with the device. Describes the type of the device, such as `phone` or `tablet`. + */ + formFactor: DeviceFormFactor; + /** + * A JSON object of key-value pairs that contains metadata for the device. + */ + metadata?: any; + /** + * A unique device secret generated by the Ably SDK. + */ + deviceSecret?: string; + /** + * The {@link DevicePushDetails} object associated with the device. Describes the details of the push registration of the device. + */ + push: DevicePushDetails; +} +/** + * Contains the subscriptions of a device, or a group of devices sharing the same `clientId`, has to a channel in order to receive push notifications. + */ +export interface PushChannelSubscription { /** - * Contains information about the result of a successful token revocation request for a single target specifier. + * The channel the push notification subscription is for. */ - interface TokenRevocationSuccessResult { - /** - * The target specifier. - */ - target: string; - /** - * The time at which the token revocation will take effect, as a Unix timestamp in milliseconds. - */ - appliesAt: number; - /** - * A Unix timestamp in milliseconds. Only tokens issued earlier than this time will be revoked. - */ - issuedBefore: number; - } + channel: string; + /** + * The unique ID of the device. + */ + deviceId?: string; + /** + * The ID of the client the device, or devices are associated to. + */ + clientId?: string; +} + +/** + * Valid states which a Push device may be in. + */ +export type DevicePushState = 'ACTIVE' | 'FAILING' | 'FAILED'; +/** + * Contains the details of the push registration of a device. + */ +export interface DevicePushDetails { /** - * Contains information about the result of an unsuccessful token revocation request for a single target specifier. + * A JSON object of key-value pairs that contains of the push transport and address. */ - interface TokenRevocationFailureResult { - /** - * The target specifier. - */ - target: string; - /** - * Describes the reason for which token revocation failed for the given `target` as an {@link ErrorInfo} object. - */ - error: ErrorInfo; - } + recipient: any; + /** + * The current state of the push registration. + */ + state?: DevicePushState; + /** + * An {@link ErrorInfo} object describing the most recent error when the `state` is `Failing` or `Failed`. + */ + error?: ErrorInfo; +} - // Common Listeners +/** + * The `DeviceRegistrationParams` interface describes the parameters accepted by the following methods: + * + * - {@link PushDeviceRegistrations.list} + * - {@link PushDeviceRegistrations.removeWhere} + */ +export interface DeviceRegistrationParams { /** - * A standard callback format used in most areas of the callback API. - * - * @param err - An error object if the request failed. - * @param result - The result of the request, if any. + * Filter to restrict to devices associated with a client ID. */ - type StandardCallback = (err: ErrorInfo | null, result?: T) => void; + clientId?: string; /** - * A {@link StandardCallback} which returns a {@link PaginatedResult}. + * Filter to restrict by the unique ID of the device. */ - type paginatedResultCallback = StandardCallback>; + deviceId?: string; /** - * A callback which returns only a single argument, used for {@link RealtimeChannelBase} subscriptions. - * - * @param message - The message which triggered the callback. + * A limit on the number of devices returned, up to 1,000. */ - type messageCallback = (message: T) => void; + limit?: number; /** - * A callback which returns only an error, or null, when complete. - * - * @param error - The error if the request failed, or null not. + * Filter by the state of the device. */ - type errorCallback = (error?: ErrorInfo | null) => void; + state?: DevicePushState; +} + +/** + * The `PushChannelSubscriptionParams` interface describes the parameters accepted by the following methods: + * + * - {@link PushChannelSubscriptions.list} + * - {@link PushChannelSubscriptions.removeWhere} + */ +export interface PushChannelSubscriptionParams { /** - * The callback used by {@link RealtimeChannelCallbacks.whenState}. - * - * @param changeStateChange - The state change that occurred. + * Filter to restrict to subscriptions associated with the given channel. */ - type channelEventCallback = (changeStateChange: ChannelStateChange) => void; + channel?: string; /** - * The callback used by {@link ConnectionCallbacks.whenState}. - * - * @param connectionStateChange - The state change that occurred. + * Filter to restrict to devices associated with the given client identifier. Cannot be used with a deviceId param. */ - type connectionEventCallback = (connectionStateChange: ConnectionStateChange) => void; + clientId?: string; /** - * The callback used by {@link RestCallbacks.time} and {@link RealtimeCallbacks.time}. - * - * @param timeCallback - The time in milliseconds since the Unix epoch. + * Filter to restrict to devices associated with that device identifier. Cannot be used with a clientId param. + */ + deviceId?: string; + /** + * A limit on the number of devices returned, up to 1,000. + */ + limit?: number; +} + +/** + * The `PushChannelsParams` interface describes the parameters accepted by {@link PushChannelSubscriptions.listChannels}. + */ +export interface PushChannelsParams { + /** + * A limit on the number of channels returned, up to 1,000. */ - type timeCallback = StandardCallback; + limit?: number; +} + +/** + * The `StatsParams` interface describes the parameters accepted by the following methods: + * + * - {@link RestClient.stats} + * - {@link RealtimeClient.stats} + */ +export interface StatsParams { /** - * The callback used by {@link RealtimePresenceCallbacks.get}. + * The time from which stats are retrieved, specified as milliseconds since the Unix epoch. * - * @param realtimePresenceGetCallback - An array of {@link PresenceMessage} objects. + * @defaultValue The Unix epoch. */ - type realtimePresenceGetCallback = StandardCallback; + start?: number; /** - * The callback used by {@link AuthCallbacks.authorize}. + * The time until stats are retrieved, specified as milliseconds since the Unix epoch. * - * @param tokenDetailsCallback - A {@link TokenDetails} object. + * @defaultValue The current time. */ - type tokenDetailsCallback = StandardCallback; + end?: number; /** - * The callback used by {@link AuthCallbacks.createTokenRequest}. + * The order for which stats are returned in. Valid values are `'backwards'` which orders stats from most recent to oldest, or `'forwards'` which orders stats from oldest to most recent. The default is `'backwards'`. * - * @param tokenRequestCallback - A {@link TokenRequest} object + * @defaultValue `'backwards'` */ - type tokenRequestCallback = StandardCallback; + direction?: 'backwards' | 'forwards'; /** - * The callback used by {@link recoverConnectionCallback}. + * An upper limit on the number of stats returned. The default is 100, and the maximum is 1000. * - * @param shouldRecover - Whether the connection should be recovered. + * @defaultValue 100 */ - type recoverConnectionCompletionCallback = (shouldRecover: boolean) => void; + limit?: number; /** - * Used in {@link ClientOptions} to configure connection recovery behaviour. + * Based on the unit selected, the given `start` or `end` times are rounded down to the start of the relevant interval depending on the unit granularity of the query. * - * @param lastConnectionDetails - Details of the connection used by the connection recovery process. - * @param callback - A callback which is called when a connection recovery attempt is complete. + * @defaultValue `StatsIntervalGranularity.MINUTE` + */ + unit?: StatsIntervalGranularity; +} + +/** + * Contains information about the results of a batch operation. + */ +export interface BatchResult { + /** + * The number of successful operations in the request. */ - type recoverConnectionCallback = ( - lastConnectionDetails: { - /** - * The recovery key can be used by another client to recover this connection’s state in the `recover` client options property. See [connection state recover options](https://ably.com/documentation/realtime/connection/#connection-state-recover-options) for more information. - */ - recoveryKey: string; - /** - * The time at which the previous client was abruptly disconnected before the page was unloaded. This is represented as milliseconds since Unix epoch. - */ - disconnectedAt: number; - /** - * A clone of the `location` object of the previous page’s document object before the page was unloaded. A common use case for this attribute is to ensure that the previous page URL is the same as the current URL before allowing the connection to be recovered. For example, you may want the connection to be recovered only for page reloads, but not when a user navigates to a different page. - */ - location: string; - /** - * The `clientId` of the client’s `Auth` object before the page was unloaded. A common use case for this attribute is to ensure that the current logged in user’s `clientId` matches the previous connection’s `clientId` before allowing the connection to be recovered. Ably prohibits changing a `clientId` for an existing connection, so any mismatch in `clientId` during a recover will result in the connection moving to the failed state. - */ - clientId: string | null; - }, - callback: recoverConnectionCompletionCallback - ) => void; + successCount: number; /** - * @ignore - * @deprecated No longer used by this library - kept here since it used to be part of our public API. Will be removed in next major version release. + * The number of unsuccessful operations in the request. */ - type fromEncoded = (JsonObject: any, channelOptions?: ChannelOptions) => T; + failureCount: number; /** - * @ignore - * @deprecated No longer used by this library - kept here since it used to be part of our public API. Will be removed in next major version release. + * An array of results for the batch operation. */ - type fromEncodedArray = (JsonArray: any[], channelOptions?: ChannelOptions) => T[]; - - // Internal Classes + results: T[]; +} - // To allow a uniform (callback) interface between on and once even in the - // promisified version of the lib, but still allow once to be used in a way - // that returns a Promise if desired, EventEmitter uses method overloading to - // present both methods +/** + * Describes the messages that should be published by a batch publish operation, and the channels to which they should be published. + */ +export interface BatchPublishSpec { /** - * A generic interface for event registration and delivery used in a number of the types in the Realtime client library. For example, the {@link ConnectionBase} object emits events for connection state using the `EventEmitter` pattern. + * The names of the channels to publish the `messages` to. */ - class EventEmitter { - /** - * Registers the provided listener for the specified event. If `on()` is called more than once with the same listener and event, the listener is added multiple times to its listener registry. Therefore, as an example, assuming the same listener is registered twice using `on()`, and an event is emitted once, the listener would be invoked twice. - * - * @param event - The named event to listen for. - * @param callback - The event listener. - */ - on(event: EventType, callback: CallbackType): void; - /** - * Registers the provided listener for the specified events. If `on()` is called more than once with the same listener and event, the listener is added multiple times to its listener registry. Therefore, as an example, assuming the same listener is registered twice using `on()`, and an event is emitted once, the listener would be invoked twice. - * - * @param events - The named events to listen for. - * @param callback - The event listener. - */ - on(events: EventType[], callback: CallbackType): void; - /** - * Registers the provided listener all events. If `on()` is called more than once with the same listener and event, the listener is added multiple times to its listener registry. Therefore, as an example, assuming the same listener is registered twice using `on()`, and an event is emitted once, the listener would be invoked twice. - * - * @param callback - The event listener. - */ - on(callback: CallbackType): void; - /** - * Registers the provided listener for the first occurrence of a single named event specified as the `Event` argument. If `once` is called more than once with the same listener, the listener is added multiple times to its listener registry. Therefore, as an example, assuming the same listener is registered twice using `once`, and an event is emitted once, the listener would be invoked twice. However, all subsequent events emitted would not invoke the listener as `once` ensures that each registration is only invoked once. - * - * @param event - The named event to listen for. - * @param callback - The event listener. - */ - once(event: EventType, callback: CallbackType): void; - /** - * Registers the provided listener for the first event that is emitted. If `once()` is called more than once with the same listener, the listener is added multiple times to its listener registry. Therefore, as an example, assuming the same listener is registered twice using `once()`, and an event is emitted once, the listener would be invoked twice. However, all subsequent events emitted would not invoke the listener as `once()` ensures that each registration is only invoked once. - * - * @param callback - The event listener. - */ - once(callback: CallbackType): void; - /** - * Returns a promise which resolves upon the first occurrence of a single named event specified as the `Event` argument. - * - * @param event - The named event to listen for. - * @returns A promise which resolves upon the first occurrence of the named event. - */ - once(event: EventType): Promise; - /** - * Returns a promise which resolves upon the first occurrence of an event. - * - * @returns A promise which resolves upon the first occurrence of an event. - */ - once(): Promise; - /** - * Removes all registrations that match both the specified listener and the specified event. - * - * @param event - The named event. - * @param callback - The event listener. - */ - off(event: EventType, callback: CallbackType): void; - /** - * Deregisters the specified listener. Removes all registrations matching the given listener, regardless of whether they are associated with an event or not. - * - * @param callback - The event listener. - */ - off(callback: CallbackType): void; - /** - * Deregisters all registrations, for all events and listeners. - */ - off(): void; - /** - * Returns the listeners for a specified `EventType`. - * - * @param eventName - The event name to retrieve the listeners for. - */ - listeners(eventName?: EventType): CallbackType[] | null; - } + channels: string[]; + /** + * An array of {@link Message} objects. + */ + messages: Message[]; +} - // Classes +/** + * Contains information about the result of successful publishes to a channel requested by a single {@link BatchPublishSpec}. + */ +export interface BatchPublishSuccessResult { /** - * The `RestBase` class acts as a base class for the {@link RestCallbacks} and {@link RestPromise} classes. + * The name of the channel the message(s) was published to. */ - class RestBase { - /** - * Construct a client object using an Ably {@link Types.ClientOptions} object. - * - * @param options - A {@link Types.ClientOptions} object to configure the client connection to Ably. - */ - constructor(options: Types.ClientOptions); - /** - * Constructs a client object using an Ably API key or token string. - * - * @param keyOrToken - The Ably API key or token string used to validate the client. - */ - constructor(keyOrToken: string); - /** - * The cryptographic functions available in the library. - */ - static Crypto: Types.Crypto; - /** - * Static utilities related to messages. - */ - static Message: Types.MessageStatic; - /** - * Static utilities related to presence messages. - */ - static PresenceMessage: Types.PresenceMessageStatic; - } + channel: string; + /** + * A unique ID prefixed to the {@link Message.id} of each published message. + */ + messageId: string; +} +/** + * Contains information about the result of unsuccessful publishes to a channel requested by a single {@link BatchPublishSpec}. + */ +export interface BatchPublishFailureResult { /** - * A client that offers a simple stateless API to interact directly with Ably's REST API. + * The name of the channel the message(s) failed to be published to. */ - class RestCallbacks extends RestBase { - /** - * A promisified version of the library (use this if you prefer to use Promises or async/await instead of callbacks) - */ - static Promise: typeof Types.RestPromise; - /** - * A callback based version of the library - */ - static Callbacks: typeof Types.RestCallbacks; - /** - * An {@link Types.AuthCallbacks} object. - */ - auth: Types.AuthCallbacks; - /** - * A {@link Types.Channels} object. - */ - channels: Types.Channels; - /** - * Makes a REST request to a provided path. This is provided as a convenience for developers who wish to use REST API functionality that is either not documented or is not yet included in the public API, without having to directly handle features such as authentication, paging, fallback hosts, MsgPack and JSON support. - * - * @param method - The request method to use, such as `GET`, `POST`. - * @param path - The request path. - * @param params - The parameters to include in the URL query of the request. The parameters depend on the endpoint being queried. See the [REST API reference](https://ably.com/docs/api/rest-api) for the available parameters of each endpoint. - * @param body - The JSON body of the request. - * @param headers - Additional HTTP headers to include in the request. - * @param callback - A function which, upon success, will be called with an {@link Types.HttpPaginatedResponse} response object returned by the HTTP request. This response object will contain an empty or JSON-encodable object. Upon failure, the function will be called with information about the error. - */ - request( - method: string, - path: string, - params?: any, - body?: any[] | any, - headers?: any, - callback?: Types.StandardCallback> - ): void; - /** - * Queries the REST `/stats` API and retrieves your application's usage statistics. Returns a {@link Types.PaginatedResult} object, containing an array of {@link Types.Stats} objects. See the [Stats docs](https://ably.com/docs/general/statistics). - * - * @param params - A set of parameters which are used to specify which statistics should be retrieved. This parameter should be a {@link Types.StatsParams} object. For reasons of backwards compatibility this parameter will also accept `any`; this ability will be removed in the next major release of this SDK. If you do not provide this argument, then this method will use the default parameters described in the {@link Types.StatsParams} interface. - * @param callback - A function which, upon success, will be called with a {@link Types.PaginatedResult} object containing an array of {@link Types.Stats} objects. Upon failure, the function will be called with information about the error. - */ - stats(params?: StatsParams | any, callback?: Types.paginatedResultCallback): void; - /** - * Queries the REST `/stats` API and retrieves your application's usage statistics, using the default parameters described in the {@link Types.StatsParams} interface. Returns a {@link Types.PaginatedResult} object, containing an array of {@link Types.Stats} objects. See the [Stats docs](https://ably.com/docs/general/statistics). - * - * @param callback - A function which, upon success, will be called with a {@link Types.PaginatedResult} object containing an array of {@link Types.Stats} objects. Upon failure, the function will be called with information about the error. - */ - stats(callback?: Types.paginatedResultCallback): void; - /** - * Retrieves the time from the Ably service as milliseconds since the Unix epoch. Clients that do not have access to a sufficiently well maintained time source and wish to issue Ably {@link Types.TokenRequest | `TokenRequest`s} with a more accurate timestamp should use the {@link Types.ClientOptions.queryTime} property instead of this method. - * - * @param callback - A function which, upon success, will be called with the time as milliseconds since the Unix epoch. Upon failure, the function will be called with information about the error. - */ - time(callback?: Types.timeCallback): void; - /** - * Publishes a {@link Types.BatchPublishSpec} object to one or more channels, up to a maximum of 100 channels. - * - * @param spec - A {@link Types.BatchPublishSpec} object. - * @param callback - A function which, upon success, will be called with a {@link Types.BatchResult} object containing information about the result of the batch publish for each requested channel. Upon failure, the function will be called with information about the error. - */ - batchPublish( - spec: BatchPublishSpec, - callback: StandardCallback> - ): void; - /** - * Publishes one or more {@link Types.BatchPublishSpec} objects to one or more channels, up to a maximum of 100 channels. - * - * @param specs - An array of {@link Types.BatchPublishSpec} objects. - * @param callback - A function which, upon success, will be called with an array of {@link Types.BatchResult} objects containing information about the result of the batch publish for each requested channel for each provided {@link Types.BatchPublishSpec}. This array is in the same order as the provided {@link Types.BatchPublishSpec} array. Upon failure, the function will be called with information about the error. - */ - batchPublish( - specs: BatchPublishSpec[], - callback: StandardCallback[]> - ): void; - /** - * Retrieves the presence state for one or more channels, up to a maximum of 100 channels. Presence state includes the `clientId` of members and their current {@link Types.PresenceAction}. - * - * @param channels - An array of one or more channel names, up to a maximum of 100 channels. - * @param callback - A function which, upon success, will be called with a {@link Types.BatchResult} object containing information about the result of the batch presence request for each requested channel. Upon failure, the function will be called with information about the error. - */ - batchPresence( - channels: string[], - callback: StandardCallback> - ): void; - /** - * A {@link Types.PushCallbacks} object. - */ - push: Types.PushCallbacks; - } - + channel: string; /** - * A client that offers a simple stateless API to interact directly with Ably's REST API. + * Describes the reason for which the message(s) failed to publish to the channel as an {@link ErrorInfo} object. */ - class RestPromise extends RestBase { - /** - * A promisified version of the library (use this if you prefer to use Promises or async/await instead of callbacks) - */ - static Promise: typeof Types.RestPromise; - /** - * A callback based version of the library - */ - static Callbacks: typeof Types.RestCallbacks; - /** - * An {@link Types.AuthPromise} object. - */ - auth: Types.AuthPromise; - /** - * A {@link Types.Channels} object. - */ - channels: Types.Channels; - /** - * Makes a REST request to a provided path. This is provided as a convenience for developers who wish to use REST API functionality that is either not documented or is not yet included in the public API, without having to directly handle features such as authentication, paging, fallback hosts, MsgPack and JSON support. - * - * @param method - The request method to use, such as `GET`, `POST`. - * @param path - The request path. - * @param params - The parameters to include in the URL query of the request. The parameters depend on the endpoint being queried. See the [REST API reference](https://ably.com/docs/api/rest-api) for the available parameters of each endpoint. - * @param body - The JSON body of the request. - * @param headers - Additional HTTP headers to include in the request. - * @returns A promise which, upon success, will be fulfilled with an {@link Types.HttpPaginatedResponse} response object returned by the HTTP request. This response object will contain an empty or JSON-encodable object. Upon failure, the promise will be rejected with an {@link Types.ErrorInfo} object which explains the error. - */ - request( - method: string, - path: string, - params?: any, - body?: any[] | any, - headers?: any - ): Promise>; - /** - * Queries the REST `/stats` API and retrieves your application's usage statistics. Returns a {@link Types.PaginatedResult} object, containing an array of {@link Types.Stats} objects. See the [Stats docs](https://ably.com/docs/general/statistics). - * - * @param params - A set of parameters which are used to specify which statistics should be retrieved. This parameter should be a {@link Types.StatsParams} object. For reasons of backwards compatibility this parameter will also accept `any`; this ability will be removed in the next major release of this SDK. If you do not provide this argument, then this method will use the default parameters described in the {@link Types.StatsParams} interface. - * @returns A promise which, upon success, will be fulfilled with a {@link Types.PaginatedResult} object containing an array of {@link Types.Stats} objects. Upon failure, the promise will be rejected with an {@link Types.ErrorInfo} object which explains the error. - */ - stats(params?: StatsParams | any): Promise>; - /** - * Retrieves the time from the Ably service as milliseconds since the Unix epoch. Clients that do not have access to a sufficiently well maintained time source and wish to issue Ably {@link Types.TokenRequest | `TokenRequest`s} with a more accurate timestamp should use the {@link Types.ClientOptions.queryTime} property instead of this method. - * - * @returns A promise which, upon success, will be fulfilled with the time as milliseconds since the Unix epoch. Upon failure, the promise will be rejected with an {@link Types.ErrorInfo} object which explains the error. - */ - time(): Promise; - - /** - * Publishes a {@link Types.BatchPublishSpec} object to one or more channels, up to a maximum of 100 channels. - * - * @param spec - A {@link Types.BatchPublishSpec} object. - * @returns A promise which, upon success, will be fulfilled with a {@link Types.BatchResult} object containing information about the result of the batch publish for each requested channel. Upon failure, the promise will be rejected with an {@link Types.ErrorInfo} object which explains the error. - */ - batchPublish(spec: BatchPublishSpec): Promise>; - /** - * Publishes one or more {@link Types.BatchPublishSpec} objects to one or more channels, up to a maximum of 100 channels. - * - * @param specs - An array of {@link Types.BatchPublishSpec} objects. - * @returns A promise which, upon success, will be fulfilled with an array of {@link Types.BatchResult} objects containing information about the result of the batch publish for each requested channel for each provided {@link Types.BatchPublishSpec}. This array is in the same order as the provided {@link Types.BatchPublishSpec} array. Upon failure, the promise will be rejected with an {@link Types.ErrorInfo} object which explains the error. - */ - batchPublish( - specs: BatchPublishSpec[] - ): Promise[]>; - /** - * Retrieves the presence state for one or more channels, up to a maximum of 100 channels. Presence state includes the `clientId` of members and their current {@link Types.PresenceAction}. - * - * @param channels - An array of one or more channel names, up to a maximum of 100 channels. - * @returns A promise which, upon success, will be fulfilled with a {@link Types.BatchResult} object containing information about the result of the batch presence request for each requested channel. Upon failure, the promise will be rejected with an {@link Types.ErrorInfo} object which explains the error. - */ - batchPresence(channels: string[]): Promise[]>; - /** - * A {@link Types.PushPromise} object. - */ - push: Types.PushPromise; - } + error: ErrorInfo; +} +/** + * Contains information about the result of a successful batch presence request for a single channel. + */ +export interface BatchPresenceSuccessResult { /** - * A base class used internally for Realtime APIs. + * The channel name the presence state was retrieved for. */ - class RealtimeBase extends RestBase { - /** - * A promisified version of the library (use this if you prefer to use Promises or async/await instead of callbacks) - */ - static Promise: typeof Types.RealtimePromise; - /** - * A callback based version of the library - */ - static Callbacks: typeof Types.RealtimeCallbacks; - /** - * A client ID, used for identifying this client when publishing messages or for presence purposes. The `clientId` can be any non-empty string, except it cannot contain a `*`. This option is primarily intended to be used in situations where the library is instantiated with a key. A `clientId` may also be implicit in a token used to instantiate the library; an error will be raised if a `clientId` specified here conflicts with the `clientId` implicit in the token. - */ - clientId: string; - /** - * Calls {@link Types.ConnectionBase.close | `connection.close()`} and causes the connection to close, entering the closing state. Once closed, the library will not attempt to re-establish the connection without an explicit call to {@link Types.ConnectionBase.connect | `connect()`}. - */ - close(): void; - /** - * Calls {@link Types.ConnectionBase.connect | `connection.connect()`} and causes the connection to open, entering the connecting state. Explicitly calling `connect()` is unnecessary unless the {@link Types.ClientOptions.autoConnect} property is disabled. - */ - connect(): void; - } - + channel: string; /** - * A client that extends the functionality of {@link RestCallbacks} and provides additional realtime-specific features. + * An array of {@link PresenceMessage}s describing members present on the channel. */ - class RealtimeCallbacks extends RealtimeBase { - /** - * An {@link Types.AuthCallbacks} object. - */ - auth: Types.AuthCallbacks; - /** - * A {@link Types.Channels} object. - */ - channels: Types.Channels; - /** - * A {@link Types.ConnectionCallbacks} object. - */ - connection: Types.ConnectionCallbacks; - /** - * Makes a REST request to a provided path. This is provided as a convenience for developers who wish to use REST API functionality that is either not documented or is not yet included in the public API, without having to directly handle features such as authentication, paging, fallback hosts, MsgPack and JSON support. - * - * @param method - The request method to use, such as `GET`, `POST`. - * @param path - The request path. - * @param params - The parameters to include in the URL query of the request. The parameters depend on the endpoint being queried. See the [REST API reference](https://ably.com/docs/api/rest-api) for the available parameters of each endpoint. - * @param body - The JSON body of the request. - * @param headers - Additional HTTP headers to include in the request. - * @param callback - A function which, upon success, will be called with the {@link Types.HttpPaginatedResponse} response object returned by the HTTP request. This response object will contain an empty or JSON-encodable object. Upon failure, the function will be called with information about the error. - */ - request( - method: string, - path: string, - params?: any, - body?: any[] | any, - headers?: any, - callback?: Types.StandardCallback> - ): void; - /** - * Queries the REST `/stats` API and retrieves your application's usage statistics. Returns a {@link Types.PaginatedResult} object, containing an array of {@link Types.Stats} objects. See the [Stats docs](https://ably.com/docs/general/statistics). - * - * @param params - A set of parameters which are used to specify which statistics should be retrieved. This parameter should be a {@link Types.StatsParams} object. For reasons of backwards compatibility this parameter will also accept `any`; this ability will be removed in the next major release of this SDK. - * @param callback - A function which, upon success, will be called with a {@link Types.PaginatedResult} object containing an array of {@link Types.Stats} objects. Upon failure, the function will be called with information about the error. - */ - stats(params: StatsParams | any, callback: Types.paginatedResultCallback): void; - /** - * Queries the REST `/stats` API and retrieves your application's usage statistics, using the default parameters described in the {@link Types.StatsParams} interface. Returns a {@link Types.PaginatedResult} object, containing an array of {@link Types.Stats} objects. See the [Stats docs](https://ably.com/docs/general/statistics). - * - * @param callback - A function which, upon success, will be called with a {@link Types.PaginatedResult} object containing an array of {@link Types.Stats} objects. Upon failure, the function will be called with information about the error. - */ - stats(callback: Types.paginatedResultCallback): void; - /** - * Retrieves the time from the Ably service as milliseconds since the Unix epoch. Clients that do not have access to a sufficiently well maintained time source and wish to issue Ably {@link Types.TokenRequest | `TokenRequest`s} with a more accurate timestamp should use the {@link Types.ClientOptions.queryTime} property instead of this method. - * - * @param callback - A function which, upon success, will be called with the time as milliseconds since the Unix epoch. Upon failure, the function will be called with information about the error. - */ - time(callback?: Types.timeCallback): void; - /** - * Publishes a {@link Types.BatchPublishSpec} object to one or more channels, up to a maximum of 100 channels. - * - * @param spec - A {@link Types.BatchPublishSpec} object. - * @param callback - A function which, upon success, will be called with a {@link Types.BatchResult} object containing information about the result of the batch publish for each requested channel. Upon failure, the function will be called with information about the error. - */ - batchPublish( - spec: BatchPublishSpec, - callback: StandardCallback> - ): void; - /** - * Publishes one or more {@link Types.BatchPublishSpec} objects to one or more channels, up to a maximum of 100 channels. - * - * @param specs - An array of {@link Types.BatchPublishSpec} objects. - * @param callback - A function which, upon success, will be called with an array of {@link Types.BatchResult} objects containing information about the result of the batch publish for each requested channel for each provided {@link Types.BatchPublishSpec}. This array is in the same order as the provided {@link Types.BatchPublishSpec} array. Upon failure, the function will be called with information about the error. - */ - batchPublish( - specs: BatchPublishSpec[], - callback: StandardCallback[]> - ): void; - /** - * Retrieves the presence state for one or more channels, up to a maximum of 100 channels. Presence state includes the `clientId` of members and their current {@link Types.PresenceAction}. - * - * @param channels - An array of one or more channel names, up to a maximum of 100 channels. - * @param callback - A function which, upon success, will be called with a {@link Types.BatchResult} object containing information about the result of the batch presence request for each requested channel. Upon failure, the function will be called with information about the error. - */ - batchPresence( - channels: string[], - callback: StandardCallback[]> - ): void; - /** - * A {@link Types.PushCallbacks} object. - */ - push: Types.PushCallbacks; - } + presence: PresenceMessage[]; +} +/** + * Contains information about the result of an unsuccessful batch presence request for a single channel. + */ +export interface BatchPresenceFailureResult { /** - * A client that extends the functionality of {@link RestPromise} and provides additional realtime-specific features. + * The channel name the presence state failed to be retrieved for. */ - class RealtimePromise extends RealtimeBase { - /** - * An {@link Types.AuthPromise} object. - */ - auth: Types.AuthPromise; - /** - * A {@link Types.Channels} object. - */ - channels: Types.Channels; - /** - * A {@link Types.ConnectionPromise} object. - */ - connection: Types.ConnectionPromise; - /** - * Makes a REST request to a provided path. This is provided as a convenience for developers who wish to use REST API functionality that is either not documented or is not yet included in the public API, without having to directly handle features such as authentication, paging, fallback hosts, MsgPack and JSON support. - * - * @param method - The request method to use, such as `GET`, `POST`. - * @param path - The request path. - * @param params - The parameters to include in the URL query of the request. The parameters depend on the endpoint being queried. See the [REST API reference](https://ably.com/docs/api/rest-api) for the available parameters of each endpoint. - * @param body - The JSON body of the request. - * @param headers - Additional HTTP headers to include in the request. - * @returns A promise which, upon success, will be fulfilled with the {@link Types.HttpPaginatedResponse} response object returned by the HTTP request. This response object will contain an empty or JSON-encodable object. Upon failure, the promise will be rejected with an {@link Types.ErrorInfo} object which explains the error. - */ - request( - method: string, - path: string, - params?: any, - body?: any[] | any, - headers?: any - ): Promise>; - /** - * Queries the REST `/stats` API and retrieves your application's usage statistics. Returns a {@link Types.PaginatedResult} object, containing an array of {@link Types.Stats} objects. See the [Stats docs](https://ably.com/docs/general/statistics). - * - * @param params - A set of parameters which are used to specify which statistics should be retrieved. This parameter should be a {@link Types.StatsParams} object. For reasons of backwards compatibility this parameter will also accept `any`; this ability will be removed in the next major release of this SDK. If you do not provide this argument, then this method will use the default parameters described in the {@link Types.StatsParams} interface. - * @returns A promise which, upon success, will be fulfilled with a {@link Types.PaginatedResult} object containing an array of {@link Types.Stats} objects. Upon failure, the promise will be rejected with an {@link Types.ErrorInfo} object which explains the error. - */ - stats(params?: StatsParams | any): Promise>; - /** - * Retrieves the time from the Ably service as milliseconds since the Unix epoch. Clients that do not have access to a sufficiently well maintained time source and wish to issue Ably {@link Types.TokenRequest | `TokenRequest`s} with a more accurate timestamp should use the {@link Types.ClientOptions.queryTime} property instead of this method. - * - * @returns A promise which, upon success, will be fulfilled with the time as milliseconds since the Unix epoch. Upon failure, the promise will be rejected with an {@link Types.ErrorInfo} object which explains the error. - */ - time(): Promise; - /** - * Publishes a {@link Types.BatchPublishSpec} object to one or more channels, up to a maximum of 100 channels. - * - * @param spec - A {@link Types.BatchPublishSpec} object. - * @returns A promise which, upon success, will be fulfilled with a {@link Types.BatchResult} object containing information about the result of the batch publish for each requested channel. Upon failure, the promise will be rejected with an {@link Types.ErrorInfo} object which explains the error. - */ - batchPublish(spec: BatchPublishSpec): Promise>; - /** - * Publishes one or more {@link Types.BatchPublishSpec} objects to one or more channels, up to a maximum of 100 channels. - * - * @param specs - An array of {@link Types.BatchPublishSpec} objects. - * @returns A promise which, upon success, will be fulfilled with an array of {@link Types.BatchResult} objects containing information about the result of the batch publish for each requested channel for each provided {@link Types.BatchPublishSpec}. This array is in the same order as the provided {@link Types.BatchPublishSpec} array. Upon failure, the promise will be rejected with an {@link Types.ErrorInfo} object which explains the error. - */ - batchPublish( - specs: BatchPublishSpec[] - ): Promise[]>; - /** - * Retrieves the presence state for one or more channels, up to a maximum of 100 channels. Presence state includes the `clientId` of members and their current {@link Types.PresenceAction}. - * - * @param channels - An array of one or more channel names, up to a maximum of 100 channels. - * @returns A promise which, upon success, will be fulfilled with a {@link Types.BatchResult} object containing information about the result of the batch presence request for each requested channel. Upon failure, the promise will be rejected with an {@link Types.ErrorInfo} object which explains the error. - */ - batchPresence(channels: string[]): Promise[]>; - /** - * A {@link Types.PushPromise} object. - */ - push: Types.PushPromise; - } - + channel: string; /** - * The `AuthBase` class acts as a base class for the {@link AuthCallbacks} and {@link AuthPromise} classes. + * Describes the reason for which presence state could not be retrieved for the channel as an {@link ErrorInfo} object. */ - class AuthBase { - /** - * A client ID, used for identifying this client when publishing messages or for presence purposes. The `clientId` can be any non-empty string, except it cannot contain a `*`. This option is primarily intended to be used in situations where the library is instantiated with a key. Note that a `clientId` may also be implicit in a token used to instantiate the library. An error is raised if a `clientId` specified here conflicts with the `clientId` implicit in the token. Find out more about [identified clients](https://ably.com/docs/core-features/authentication#identified-clients). - */ - clientId: string; - } + error: ErrorInfo; +} +/** + * The `TokenRevocationOptions` interface describes the additional options accepted by {@link Auth.revokeTokens}. + */ +export interface TokenRevocationOptions { /** - * Creates Ably {@link TokenRequest} objects and obtains Ably Tokens from Ably to subsequently issue to less trusted clients. + * A Unix timestamp in milliseconds where only tokens issued before this time are revoked. The default is the current time. Requests with an `issuedBefore` in the future, or more than an hour in the past, will be rejected. */ - class AuthCallbacks extends AuthBase { - /** - * Instructs the library to get a new token immediately. When using the realtime client, it upgrades the current realtime connection to use the new token, or if not connected, initiates a connection to Ably, once the new token has been obtained. Also stores any {@link TokenParams} and {@link AuthOptions} passed in as the new defaults, to be used for all subsequent implicit or explicit token requests. Any {@link TokenParams} and {@link AuthOptions} objects passed in entirely replace, as opposed to being merged with, the current client library saved values. - * - * @param tokenParams - A {@link TokenParams} object. - * @param authOptions - An {@link AuthOptions} object. - * @param callback - A function which, upon success, will be called with a {@link TokenDetails} object. Upon failure, the function will be called with information about the error. - */ - authorize(tokenParams?: TokenParams, authOptions?: AuthOptions, callback?: tokenDetailsCallback): void; - /** - * Instructs the library to get a new token immediately. When using the realtime client, it upgrades the current realtime connection to use the new token, or if not connected, initiates a connection to Ably, once the new token has been obtained. Also stores any {@link TokenParams} passed in as the new default, to be used for all subsequent implicit or explicit token requests. Any {@link TokenParams} object passed in entirely replaces, as opposed to being merged with, the current client library saved value. - * - * @param tokenParams - A {@link TokenParams} object. - * @param callback - A function which, upon success, will be called with a {@link TokenDetails} object. Upon failure, the function will be called with information about the error. - */ - authorize(tokenParams?: TokenParams, callback?: tokenDetailsCallback): void; - /** - * Instructs the library to get a new token immediately. When using the realtime client, it upgrades the current realtime connection to use the new token, or if not connected, initiates a connection to Ably, once the new token has been obtained. - * - * @param callback - A function which, upon success, will be called with a {@link TokenDetails} object. Upon failure, the function will be called with information about the error. - */ - authorize(callback?: tokenDetailsCallback): void; - /** - * Creates and signs an Ably {@link TokenRequest} based on the specified (or if none specified, the client library stored) {@link TokenParams} and {@link AuthOptions}. Note this can only be used when the API `key` value is available locally. Otherwise, the Ably {@link TokenRequest} must be obtained from the key owner. Use this to generate an Ably {@link TokenRequest} in order to implement an Ably Token request callback for use by other clients. Both {@link TokenParams} and {@link AuthOptions} are optional. When omitted or `null`, the default token parameters and authentication options for the client library are used, as specified in the {@link ClientOptions} when the client library was instantiated, or later updated with an explicit `authorize` request. Values passed in are used instead of, rather than being merged with, the default values. To understand why an Ably {@link TokenRequest} may be issued to clients in favor of a token, see [Token Authentication explained](https://ably.com/docs/core-features/authentication/#token-authentication). - * - * @param tokenParams - A {@link TokenParams} object. - * @param authOptions - An {@link AuthOptions} object. - * @param callback - A function which, upon success, will be called with a {@link TokenRequest} object. Upon failure, the function will be called with information about the error. - */ - createTokenRequest( - tokenParams?: TokenParams | null, - authOptions?: AuthOptions | null, - callback?: tokenRequestCallback - ): void; - /** - * Creates and signs an Ably {@link TokenRequest} based on the specified (or if none specified, the client library stored) {@link TokenParams}. Note this can only be used when the API `key` value is available locally. Otherwise, the Ably {@link TokenParams} must be obtained from the key owner. Use this to generate an Ably {@link TokenRequest} in order to implement an Ably Token request callback for use by other clients. When the {@link TokenRequest} is omitted or `null`, the default token parameters for the client library are used, as specified in the {@link ClientOptions} when the client library was instantiated, or later updated with an explicit `authorize` request. Values passed in are used instead of, rather than being merged with, the default values. To understand why an Ably {@link TokenRequest} may be issued to clients in favor of a token, see [Token Authentication explained](https://ably.com/docs/core-features/authentication/#token-authentication). - * - * @param tokenParams - A {@link TokenParams} object. - * @param callback - A function which, upon success, will be called with a {@link TokenRequest} object. Upon failure, the function will be called with information about the error. - */ - createTokenRequest(tokenParams?: TokenParams | null, callback?: tokenRequestCallback): void; - /** - * Creates and signs an Ably {@link TokenRequest} based on the the client library stored {@link TokenParams} and {@link AuthOptions}. Note this can only be used when the API `key` value is available locally. Otherwise, the Ably {@link TokenRequest} must be obtained from the key owner. Use this to generate an Ably {@link TokenRequest} in order to implement an Ably Token request callback for use by other clients. The default token parameters and authentication options for the client library are used, as specified in the {@link ClientOptions} when the client library was instantiated, or later updated with an explicit `authorize` request. To understand why an Ably {@link TokenRequest} may be issued to clients in favor of a token, see [Token Authentication explained](https://ably.com/docs/core-features/authentication/#token-authentication). - * - * @param callback - A function which, upon success, will be called with a {@link TokenRequest} object. Upon failure, the function will be called with information about the error. - */ - createTokenRequest(callback?: tokenRequestCallback): void; - /** - * Calls the `requestToken` REST API endpoint to obtain an Ably Token according to the specified {@link TokenParams} and {@link AuthOptions}. Both {@link TokenParams} and {@link AuthOptions} are optional. When omitted or `null`, the default token parameters and authentication options for the client library are used, as specified in the {@link ClientOptions} when the client library was instantiated, or later updated with an explicit `authorize` request. Values passed in are used instead of, rather than being merged with, the default values. To understand why an Ably {@link TokenRequest} may be issued to clients in favor of a token, see [Token Authentication explained](https://ably.com/docs/core-features/authentication/#token-authentication). - * - * @param TokenParams - A {@link TokenParams} object. - * @param authOptions - An {@link AuthOptions} object. - * @param callback - A function which, upon success, will be called with a {@link TokenDetails} object. Upon failure, the function will be called with information about the error. - */ - requestToken( - TokenParams?: TokenParams | null, - authOptions?: AuthOptions | null, - callback?: tokenDetailsCallback - ): void; - /** - * Calls the `requestToken` REST API endpoint to obtain an Ably Token according to the specified {@link TokenParams}. When omitted or `null`, the default token parameters and authentication options for the client library are used, as specified in the {@link ClientOptions} when the client library was instantiated, or later updated with an explicit `authorize` request. Values passed in are used instead of, rather than being merged with, the default values. To understand why an Ably {@link TokenRequest} may be issued to clients in favor of a token, see [Token Authentication explained](https://ably.com/docs/core-features/authentication/#token-authentication). - * - * @param TokenParams - A {@link TokenParams} object. - * @param callback - A function which, upon success, will be called with a {@link TokenDetails} object. Upon failure, the function will be called with information about the error. - */ - requestToken(TokenParams?: TokenParams | null, callback?: tokenDetailsCallback): void; - /** - * Calls the `requestToken` REST API endpoint to obtain an Ably Token. The default token parameters and authentication options for the client library are used, as specified in the {@link ClientOptions} when the client library was instantiated, or later updated with an explicit `authorize` request. To understand why an Ably {@link TokenRequest} may be issued to clients in favor of a token, see [Token Authentication explained](https://ably.com/docs/core-features/authentication/#token-authentication). - * - * @param callback - A function which, upon success, will be called with a {@link TokenDetails} object. Upon failure, the function will be called with information about the error. - */ - requestToken(callback?: tokenDetailsCallback): void; - /** - * Revokes the tokens specified by the provided array of {@link TokenRevocationTargetSpecifier}s. Only tokens issued by an API key that had revocable tokens enabled before the token was issued can be revoked. See the [token revocation docs](https://ably.com/docs/core-features/authentication#token-revocation) for more information. - * - * @param specifiers - An array of {@link TokenRevocationTargetSpecifier} objects. - * @param options - A set of options which are used to modify the revocation request. - * @param callback - A function which, upon success, will be called with a {@link Types.BatchResult} containing information about the result of the token revocation request for each provided [`TokenRevocationTargetSpecifier`]{@link TokenRevocationTargetSpecifier}. Upon failure, the function will be called with information about the error. - */ - revokeTokens( - specifiers: TokenRevocationTargetSpecifier[], - options?: TokenRevocationOptions, - callback?: StandardCallback> - ): void; - } - + issuedBefore?: number; /** - * Creates Ably {@link TokenRequest} objects and obtains Ably Tokens from Ably to subsequently issue to less trusted clients. + * If true, permits a token renewal cycle to take place without needing established connections to be dropped, by postponing enforcement to 30 seconds in the future, and sending any existing connections a hint to obtain (and upgrade the connection to use) a new token. The default is `false`, meaning that the effect is near-immediate. */ - class AuthPromise extends AuthBase { - /** - * Instructs the library to get a new token immediately. When using the realtime client, it upgrades the current realtime connection to use the new token, or if not connected, initiates a connection to Ably, once the new token has been obtained. Also stores any {@link TokenParams} and {@link AuthOptions} passed in as the new defaults, to be used for all subsequent implicit or explicit token requests. Any {@link TokenParams} and {@link AuthOptions} objects passed in entirely replace, as opposed to being merged with, the current client library saved values. - * - * @param tokenParams - A {@link TokenParams} object. - * @param authOptions - An {@link AuthOptions} object. - * @returns A promise which, upon success, will be fulfilled with a {@link TokenDetails} object. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. - */ - authorize(tokenParams?: TokenParams, authOptions?: AuthOptions): Promise; - /** - * Creates and signs an Ably {@link TokenRequest} based on the specified (or if none specified, the client library stored) {@link TokenParams} and {@link AuthOptions}. Note this can only be used when the API `key` value is available locally. Otherwise, the Ably {@link TokenRequest} must be obtained from the key owner. Use this to generate an Ably {@link TokenRequest} in order to implement an Ably Token request callback for use by other clients. Both {@link TokenParams} and {@link AuthOptions} are optional. When omitted or `null`, the default token parameters and authentication options for the client library are used, as specified in the {@link ClientOptions} when the client library was instantiated, or later updated with an explicit `authorize` request. Values passed in are used instead of, rather than being merged with, the default values. To understand why an Ably {@link TokenRequest} may be issued to clients in favor of a token, see [Token Authentication explained](https://ably.com/docs/core-features/authentication/#token-authentication). - * - * @param tokenParams - A {@link TokenParams} object. - * @param authOptions - An {@link AuthOptions} object. - * @returns A promise which, upon success, will be fulfilled with a {@link TokenRequest} object. Upon failure, the promise will be rejected with an {@link Types.ErrorInfo} object which explains the error. - */ - createTokenRequest(tokenParams?: TokenParams, authOptions?: AuthOptions): Promise; - /** - * Calls the `requestToken` REST API endpoint to obtain an Ably Token according to the specified {@link TokenParams} and {@link AuthOptions}. Both {@link TokenParams} and {@link AuthOptions} are optional. When omitted or `null`, the default token parameters and authentication options for the client library are used, as specified in the {@link ClientOptions} when the client library was instantiated, or later updated with an explicit `authorize` request. Values passed in are used instead of, rather than being merged with, the default values. To understand why an Ably {@link TokenRequest} may be issued to clients in favor of a token, see [Token Authentication explained](https://ably.com/docs/core-features/authentication/#token-authentication). - * - * @param TokenParams - A {@link TokenParams} object. - * @param authOptions - An {@link AuthOptions} object. - * @returns A promise which, upon success, will be fulfilled with a {@link TokenDetails} object. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. - */ - requestToken(TokenParams?: TokenParams, authOptions?: AuthOptions): Promise; - /** - * Revokes the tokens specified by the provided array of {@link TokenRevocationTargetSpecifier}s. Only tokens issued by an API key that had revocable tokens enabled before the token was issued can be revoked. See the [token revocation docs](https://ably.com/docs/core-features/authentication#token-revocation) for more information. - * - * @param specifiers - An array of {@link TokenRevocationTargetSpecifier} objects. - * @param options - A set of options which are used to modify the revocation request. - * @returns A promise which, upon success, will be fulfilled with a {@link Types.BatchResult} containing information about the result of the token revocation request for each provided [`TokenRevocationTargetSpecifier`]{@link TokenRevocationTargetSpecifier}. Upon failure, the promise will be rejected with an {@link Types.ErrorInfo} object which explains the error. - */ - revokeTokens( - specifiers: TokenRevocationTargetSpecifier[], - options?: TokenRevocationOptions - ): Promise>; - } + allowReauthMargin?: boolean; +} +/** + * Describes which tokens should be affected by a token revocation request. + */ +export interface TokenRevocationTargetSpecifier { /** - * Enables the retrieval of the current and historic presence set for a channel. + * The type of token revocation target specifier. Valid values include `clientId`, `revocationKey` and `channel`. */ - class PresenceCallbacks { - /** - * Retrieves the current members present on the channel and the metadata for each member, such as their {@link PresenceAction} and ID. Returns a {@link Types.PaginatedResult} object, containing an array of {@link PresenceMessage} objects. - * - * @param params - A set of parameters which are used to specify which presence members should be retrieved. - * @param callback - A function which, upon success, will be called with a {@link Types.PaginatedResult} object containing an array of {@link PresenceMessage} objects. Upon failure, the function will be called with information about the error. - */ - get(params?: RestPresenceParams, callback?: paginatedResultCallback): void; - /** - * Retrieves the current members present on the channel and the metadata for each member, such as their [PresenceAction]{@link PresenceAction} and ID. - * - * @param callback - A function which, upon success, will be called with a [PaginatedResult]{@link PaginatedResult} object, containing an array of [PresenceMessage]{@link PresenceMessage} objects. Upon failure, the function will be called with information about the error. - */ - get(callback?: paginatedResultCallback): void; - /** - * Retrieves a {@link Types.PaginatedResult} object, containing an array of historical {@link PresenceMessage} objects for the channel. If the channel is configured to persist messages, then presence messages can be retrieved from history for up to 72 hours in the past. If not, presence messages can only be retrieved from history for up to two minutes in the past. - * - * @param params - A set of parameters which are used to specify which messages should be retrieved. - * @param callback - A function which, upon success, will be called with a {@link Types.PaginatedResult} object containing an array of {@link PresenceMessage} objects. Upon failure, the function will be called with information about the error. - */ - history(params: RestHistoryParams, callback?: paginatedResultCallback): void; - /** - * Retrieves a {@link Types.PaginatedResult} object, containing an array of historical {@link PresenceMessage} objects for the channel. If the channel is configured to persist messages, then presence messages can be retrieved from history for up to 72 hours in the past. If not, presence messages can only be retrieved from history for up to two minutes in the past. - * - * @param callback - A function which, upon success, will be called with a {@link Types.PaginatedResult} object containing an array of {@link PresenceMessage} objects. Upon failure, the function will be called with information about the error. - */ - history(callback: paginatedResultCallback): void; - } + type: string; + /** + * The value of the token revocation target specifier. + */ + value: string; +} +/** + * Contains information about the result of a successful token revocation request for a single target specifier. + */ +export interface TokenRevocationSuccessResult { /** - * Enables the retrieval of the current and historic presence set for a channel. + * The target specifier. */ - class PresencePromise { - /** - * Retrieves the current members present on the channel and the metadata for each member, such as their {@link PresenceAction} and ID. Returns a {@link Types.PaginatedResult} object, containing an array of {@link PresenceMessage} objects. - * - * @param params - A set of parameters which are used to specify which presence members should be retrieved. - * @returns A promise which, upon success, will be fulfilled with a {@link Types.PaginatedResult} object containing an array of {@link PresenceMessage} objects. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. - */ - get(params?: RestPresenceParams): Promise>; - /** - * Retrieves a {@link Types.PaginatedResult} object, containing an array of historical {@link PresenceMessage} objects for the channel. If the channel is configured to persist messages, then presence messages can be retrieved from history for up to 72 hours in the past. If not, presence messages can only be retrieved from history for up to two minutes in the past. - * - * @param params - A set of parameters which are used to specify which messages should be retrieved. - * @returns A promise which, upon success, will be fulfilled with a {@link Types.PaginatedResult} object containing an array of {@link PresenceMessage} objects. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. - */ - history(params?: RestHistoryParams): Promise>; - } + target: string; + /** + * The time at which the token revocation will take effect, as a Unix timestamp in milliseconds. + */ + appliesAt: number; + /** + * A Unix timestamp in milliseconds. Only tokens issued earlier than this time will be revoked. + */ + issuedBefore: number; +} +/** + * Contains information about the result of an unsuccessful token revocation request for a single target specifier. + */ +export interface TokenRevocationFailureResult { /** - * The `RealtimePresenceBase` class acts as a base class for the {@link RealtimePresenceCallbacks} and {@link RealtimePresencePromise} classes. + * The target specifier. */ - class RealtimePresenceBase { - /** - * Indicates whether the presence set synchronization between Ably and the clients on the channel has been completed. Set to `true` when the sync is complete. - */ - syncComplete: boolean; - /** - * Deregisters a specific listener that is registered to receive {@link PresenceMessage} on the channel for a given {@link PresenceAction}. - * - * @param presence - A specific {@link PresenceAction} to deregister the listener for. - * @param listener - An event listener function. - */ - unsubscribe(presence: PresenceAction, listener: messageCallback): void; - /** - * Deregisters a specific listener that is registered to receive {@link PresenceMessage} on the channel for a given array of {@link PresenceAction} objects. - * - * @param presence - An array of {@link PresenceAction} objects to deregister the listener for. - * @param listener - An event listener function. - */ - unsubscribe(presence: Array, listener: messageCallback): void; + target: string; + /** + * Describes the reason for which token revocation failed for the given `target` as an {@link ErrorInfo} object. + */ + error: ErrorInfo; +} + +// Common Listeners +/** + * A callback which returns only a single argument, used for {@link RealtimeChannel} subscriptions. + * + * @param message - The message which triggered the callback. + */ +export type messageCallback = (message: T) => void; +/** + * The callback used for the events emitted by {@link RealtimeChannel}. + * + * @param changeStateChange - The state change that occurred. + */ +export type channelEventCallback = (changeStateChange: ChannelStateChange) => void; +/** + * The callback used for the events emitted by {@link Connection}. + * + * @param connectionStateChange - The state change that occurred. + */ +export type connectionEventCallback = (connectionStateChange: ConnectionStateChange) => void; +/** + * The callback used by {@link recoverConnectionCallback}. + * + * @param shouldRecover - Whether the connection should be recovered. + */ +export type recoverConnectionCompletionCallback = (shouldRecover: boolean) => void; +/** + * Used in {@link ClientOptions} to configure connection recovery behaviour. + * + * @param lastConnectionDetails - Details of the connection used by the connection recovery process. + * @param callback - A callback which is called when a connection recovery attempt is complete. + */ +export type recoverConnectionCallback = ( + lastConnectionDetails: { /** - * Deregisters any listener that is registered to receive {@link PresenceMessage} on the channel for a specific {@link PresenceAction} - * - * @param presence - A specific {@link PresenceAction} to deregister the listeners for. + * The recovery key can be used by another client to recover this connection’s state in the `recover` client options property. See [connection state recover options](https://ably.com/documentation/realtime/connection/#connection-state-recover-options) for more information. */ - unsubscribe(presence: PresenceAction): void; + recoveryKey: string; /** - * Deregisters any listener that is registered to receive {@link PresenceMessage} on the channel for an array of {@link PresenceAction} objects - * - * @param presence - An array of {@link PresenceAction} objects to deregister the listeners for. + * The time at which the previous client was abruptly disconnected before the page was unloaded. This is represented as milliseconds since Unix epoch. */ - unsubscribe(presence: Array): void; + disconnectedAt: number; /** - * Deregisters a specific listener that is registered to receive {@link PresenceMessage} on the channel. - * - * @param listener - An event listener function. + * A clone of the `location` object of the previous page’s document object before the page was unloaded. A common use case for this attribute is to ensure that the previous page URL is the same as the current URL before allowing the connection to be recovered. For example, you may want the connection to be recovered only for page reloads, but not when a user navigates to a different page. */ - unsubscribe(listener: messageCallback): void; + location: string; /** - * Deregisters all listeners currently receiving {@link PresenceMessage} for the channel. + * The `clientId` of the client’s `Auth` object before the page was unloaded. A common use case for this attribute is to ensure that the current logged in user’s `clientId` matches the previous connection’s `clientId` before allowing the connection to be recovered. Ably prohibits changing a `clientId` for an existing connection, so any mismatch in `clientId` during a recover will result in the connection moving to the failed state. */ - unsubscribe(): void; - } + clientId: string | null; + }, + callback: recoverConnectionCompletionCallback, +) => void; + +// Internal Interfaces +// To allow a uniform (callback) interface between on and once even in the +// promisified version of the lib, but still allow once to be used in a way +// that returns a Promise if desired, EventEmitter uses method overloading to +// present both methods +/** + * A generic interface for event registration and delivery used in a number of the types in the Realtime client library. For example, the {@link Connection} object emits events for connection state using the `EventEmitter` pattern. + */ +export declare interface EventEmitter { /** - * Enables the presence set to be entered and subscribed to, and the historic presence set to be retrieved for a channel. + * Registers the provided listener for the specified event. If `on()` is called more than once with the same listener and event, the listener is added multiple times to its listener registry. Therefore, as an example, assuming the same listener is registered twice using `on()`, and an event is emitted once, the listener would be invoked twice. + * + * @param event - The named event to listen for. + * @param callback - The event listener. */ - class RealtimePresenceCallbacks extends RealtimePresenceBase { - /** - * Retrieves the current members present on the channel and the metadata for each member, such as their {@link PresenceAction} and ID. Returns an array of {@link PresenceMessage} objects. - * - * @param params - A set of parameters which are used to specify which presence members should be retrieved. - * @param callback - A function which, upon success, will be called with an array of {@link PresenceMessage} objects. Upon failure, the function will be called with information about the error. - */ - get(params?: RealtimePresenceParams, callback?: realtimePresenceGetCallback): void; - /** - * Retrieves the current members present on the channel and the metadata for each member, such as their {@link PresenceAction} and ID. Returns an array of {@link PresenceMessage} objects. - * - * @param callback - A function which, upon success, will be called with an array of {@link PresenceMessage} objects. Upon failure, the function will be called with information about the error. - */ - get(callback?: realtimePresenceGetCallback): void; - /** - * Retrieves a {@link Types.PaginatedResult} object, containing an array of historical {@link PresenceMessage} objects for the channel. If the channel is configured to persist messages, then presence messages can be retrieved from history for up to 72 hours in the past. If not, presence messages can only be retrieved from history for up to two minutes in the past. - * - * @param params - A set of parameters which are used to specify which presence messages should be retrieved. - * @param callback - A function which, upon success, will be called with a {@link Types.PaginatedResult} object containing an array of {@link PresenceMessage} objects. Upon failure, the function will be called with information about the error. - */ - history(params?: RealtimeHistoryParams, callback?: paginatedResultCallback): void; - /** - * Retrieves a {@link Types.PaginatedResult} object, containing an array of historical {@link PresenceMessage} objects for the channel. If the channel is configured to persist messages, then presence messages can be retrieved from history for up to 72 hours in the past. If not, presence messages can only be retrieved from history for up to two minutes in the past. - * - * @param callback - A function which, upon success, will be called with a {@link Types.PaginatedResult} object containing an array of {@link PresenceMessage} objects. Upon failure, the function will be called with information about the error. - */ - history(callback?: paginatedResultCallback): void; - /** - * Registers a listener that is called each time a {@link PresenceMessage} matching a given {@link PresenceAction}, or an action within an array of {@link PresenceAction | `PresenceAction`s}, is received on the channel, such as a new member entering the presence set. - * - * @param presence - A {@link PresenceAction} or an array of {@link PresenceAction | `PresenceAction`s} to register the listener for. - * @param listener - An event listener function. - * @param callbackWhenAttached - A function which will be called upon completion of the channel {@link RealtimeChannelCallbacks.attach | `attach()`} operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - subscribe( - presence: PresenceAction | Array, - listener?: messageCallback, - callbackWhenAttached?: errorCallback - ): void; - /** - * Registers a listener that is called each time a {@link PresenceMessage} is received on the channel, such as a new member entering the presence set. - * - * @param listener - An event listener function. - * @param callbackWhenAttached - A function which will be called upon completion of the channel {@link RealtimeChannelCallbacks.attach | `attach()`} operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - subscribe(listener: messageCallback, callbackWhenAttached?: errorCallback): void; - /** - * Enters the presence set for the channel, passing a `data` payload. A `clientId` is required to be present on a channel. - * - * @param data - The data payload or {@link PresenceMessage} associated with the presence member. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - enter(data?: any, callback?: errorCallback): void; - /** - * Enters the presence set for the channel. A `clientId` is required to be present on a channel. - * - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - enter(callback?: errorCallback): void; - /** - * Updates the `data` payload for a presence member. If called before entering the presence set, this is treated as an {@link PresenceAction.ENTER} event. - * - * @param data - The data payload or {@link PresenceMessage} object to update for the presence member. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - update(data?: any, callback?: errorCallback): void; - /** - * Leaves the presence set for the channel. A client must have previously entered the presence set before they can leave it. - * - * @param data - The data payload or {@link PresenceMessage} associated with the presence member. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - leave(data?: any, callback?: errorCallback): void; - /** - * Leaves the presence set for the channel. A client must have previously entered the presence set before they can leave it. - * - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - leave(callback?: errorCallback): void; - /** - * Enters the presence set of the channel for a given `clientId`. Enables a single client to update presence on behalf of any number of clients using a single connection. The library must have been instantiated with an API key or a token bound to a wildcard `clientId`. - * - * @param clientId - The ID of the client to enter into the presence set. - * @param data - The data payload or {@link PresenceMessage} associated with the presence member. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - enterClient(clientId: string, data?: any, callback?: errorCallback): void; - /** - * Enters the presence set of the channel for a given `clientId`. Enables a single client to update presence on behalf of any number of clients using a single connection. The library must have been instantiated with an API key or a token bound to a wildcard `clientId`. - * - * @param clientId - The ID of the client to enter into the presence set. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - enterClient(clientId: string, callback?: errorCallback): void; - /** - * Updates the `data` payload for a presence member using a given `clientId`. Enables a single client to update presence on behalf of any number of clients using a single connection. The library must have been instantiated with an API key or a token bound to a wildcard `clientId`. - * - * @param clientId - The ID of the client to update in the presence set. - * @param data - The data payload or {@link PresenceMessage} object to update for the presence member. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - updateClient(clientId: string, data?: any, callback?: errorCallback): void; - /** - * Leaves the presence set of the channel for a given `clientId`. Enables a single client to update presence on behalf of any number of clients using a single connection. The library must have been instantiated with an API key or a token bound to a wildcard `clientId`. - * - * @param clientId - The ID of the client to leave the presence set for. - * @param data - The data payload or {@link PresenceMessage} associated with the presence member. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - leaveClient(clientId: string, data?: any, callback?: errorCallback): void; - /** - * Leaves the presence set of the channel for a given `clientId`. Enables a single client to update presence on behalf of any number of clients using a single connection. The library must have been instantiated with an API key or a token bound to a wildcard `clientId`. - * - * @param clientId - The ID of the client to leave the presence set for. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - leaveClient(clientId: string, callback?: errorCallback): void; - } + on(event: EventType, callback: CallbackType): void; + /** + * Registers the provided listener for the specified events. If `on()` is called more than once with the same listener and event, the listener is added multiple times to its listener registry. Therefore, as an example, assuming the same listener is registered twice using `on()`, and an event is emitted once, the listener would be invoked twice. + * + * @param events - The named events to listen for. + * @param callback - The event listener. + */ + on(events: EventType[], callback: CallbackType): void; + /** + * Registers the provided listener all events. If `on()` is called more than once with the same listener and event, the listener is added multiple times to its listener registry. Therefore, as an example, assuming the same listener is registered twice using `on()`, and an event is emitted once, the listener would be invoked twice. + * + * @param callback - The event listener. + */ + on(callback: CallbackType): void; + /** + * Registers the provided listener for the first occurrence of a single named event specified as the `Event` argument. If `once` is called more than once with the same listener, the listener is added multiple times to its listener registry. Therefore, as an example, assuming the same listener is registered twice using `once`, and an event is emitted once, the listener would be invoked twice. However, all subsequent events emitted would not invoke the listener as `once` ensures that each registration is only invoked once. + * + * @param event - The named event to listen for. + * @param callback - The event listener. + */ + once(event: EventType, callback: CallbackType): void; + /** + * Registers the provided listener for the first event that is emitted. If `once()` is called more than once with the same listener, the listener is added multiple times to its listener registry. Therefore, as an example, assuming the same listener is registered twice using `once()`, and an event is emitted once, the listener would be invoked twice. However, all subsequent events emitted would not invoke the listener as `once()` ensures that each registration is only invoked once. + * + * @param callback - The event listener. + */ + once(callback: CallbackType): void; + /** + * Returns a promise which resolves upon the first occurrence of a single named event specified as the `Event` argument. + * + * @param event - The named event to listen for. + * @returns A promise which resolves upon the first occurrence of the named event. + */ + once(event: EventType): Promise; + /** + * Returns a promise which resolves upon the first occurrence of an event. + * + * @returns A promise which resolves upon the first occurrence of an event. + */ + once(): Promise; + /** + * Removes all registrations that match both the specified listener and the specified event. + * + * @param event - The named event. + * @param callback - The event listener. + */ + off(event: EventType, callback: CallbackType): void; + /** + * Deregisters the specified listener. Removes all registrations matching the given listener, regardless of whether they are associated with an event or not. + * + * @param callback - The event listener. + */ + off(callback: CallbackType): void; + /** + * Deregisters all registrations, for all events and listeners. + */ + off(): void; + /** + * Returns the listeners for a specified `EventType`. + * + * @param eventName - The event name to retrieve the listeners for. + */ + listeners(eventName?: EventType): CallbackType[] | null; +} +// Interfaces +/** + * A client that offers a simple stateless API to interact directly with Ably's REST API. + */ +export declare interface RestClient { /** - * Enables the presence set to be entered and subscribed to, and the historic presence set to be retrieved for a channel. + * An {@link Auth} object. */ - class RealtimePresencePromise extends RealtimePresenceBase { - /** - * Retrieves the current members present on the channel and the metadata for each member, such as their {@link PresenceAction} and ID. Returns an array of {@link PresenceMessage} objects. - * - * @param params - A set of parameters which are used to specify which presence members should be retrieved. - * @returns A promise which, upon success, will be fulfilled with an array of {@link PresenceMessage} objects. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. - */ - get(params?: RealtimePresenceParams): Promise; - /** - * Retrieves a {@link Types.PaginatedResult} object, containing an array of historical {@link PresenceMessage} objects for the channel. If the channel is configured to persist messages, then presence messages can be retrieved from history for up to 72 hours in the past. If not, presence messages can only be retrieved from history for up to two minutes in the past. - * - * @param params - A set of parameters which are used to specify which presence messages should be retrieved. - * @returns A promise which, upon success, will be fulfilled with a {@link Types.PaginatedResult} object containing an array of {@link PresenceMessage} objects. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. - */ - history(params?: RealtimeHistoryParams): Promise>; - /** - * Registers a listener that is called each time a {@link PresenceMessage} matching a given {@link PresenceAction}, or an action within an array of {@link PresenceAction | `PresenceAction`s}, is received on the channel, such as a new member entering the presence set. - * - * @param action - A {@link PresenceAction} or an array of {@link PresenceAction | `PresenceAction`s} to register the listener for. - * @param listener - An event listener function. - * @returns A promise which resolves upon success of the channel {@link RealtimeChannelPromise.attach | `attach()`} operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - subscribe( - action: PresenceAction | Array, - listener?: messageCallback - ): Promise; - /** - * Registers a listener that is called each time a {@link PresenceMessage} is received on the channel, such as a new member entering the presence set. - * - * @param listener - An event listener function. - * @returns A promise which resolves upon success of the channel {@link RealtimeChannelPromise.attach | `attach()`} operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - subscribe(listener?: messageCallback): Promise; - /** - * Enters the presence set for the channel, optionally passing a `data` payload. A `clientId` is required to be present on a channel. - * - * @param data - The payload associated with the presence member. - * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - enter(data?: any): Promise; - /** - * Updates the `data` payload for a presence member. If called before entering the presence set, this is treated as an {@link PresenceAction.ENTER} event. - * - * @param data - The payload to update for the presence member. - * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - update(data?: any): Promise; - /** - * Leaves the presence set for the channel. A client must have previously entered the presence set before they can leave it. - * - * @param data - The payload associated with the presence member. - * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - leave(data?: any): Promise; - /** - * Enters the presence set of the channel for a given `clientId`. Enables a single client to update presence on behalf of any number of clients using a single connection. The library must have been instantiated with an API key or a token bound to a wildcard `clientId`. - * - * @param clientId - The ID of the client to enter into the presence set. - * @param data - The payload associated with the presence member. - * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - enterClient(clientId: string, data?: any): Promise; - /** - * Updates the `data` payload for a presence member using a given `clientId`. Enables a single client to update presence on behalf of any number of clients using a single connection. The library must have been instantiated with an API key or a token bound to a wildcard `clientId`. - * - * @param clientId - The ID of the client to update in the presence set. - * @param data - The payload to update for the presence member. - * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - updateClient(clientId: string, data?: any): Promise; - /** - * Leaves the presence set of the channel for a given `clientId`. Enables a single client to update presence on behalf of any number of clients using a single connection. The library must have been instantiated with an API key or a token bound to a wildcard `clientId`. - * - * @param clientId - The ID of the client to leave the presence set for. - * @param data - The payload associated with the presence member. - * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - leaveClient(clientId: string, data?: any): Promise; - } + auth: Auth; + /** + * A {@link Channels} object. + */ + channels: Channels; + /** + * Makes a REST request to a provided path. This is provided as a convenience for developers who wish to use REST API functionality that is either not documented or is not yet included in the public API, without having to directly handle features such as authentication, paging, fallback hosts, MsgPack and JSON support. + * + * @param method - The request method to use, such as `GET`, `POST`. + * @param path - The request path. + * @param version - The version of the Ably REST API to use. See the [REST API reference](https://ably.com/docs/api/rest-api#versioning) for information on versioning. + * @param params - The parameters to include in the URL query of the request. The parameters depend on the endpoint being queried. See the [REST API reference](https://ably.com/docs/api/rest-api) for the available parameters of each endpoint. + * @param body - The JSON body of the request. + * @param headers - Additional HTTP headers to include in the request. + * @returns A promise which, upon success, will be fulfilled with an {@link HttpPaginatedResponse} response object returned by the HTTP request. This response object will contain an empty or JSON-encodable object. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + request( + method: string, + path: string, + version: number, + params?: any, + body?: any[] | any, + headers?: any, + ): Promise>; + /** + * Queries the REST `/stats` API and retrieves your application's usage statistics. Returns a {@link PaginatedResult} object, containing an array of {@link Stats} objects. See the [Stats docs](https://ably.com/docs/general/statistics). + * + * @param params - A set of parameters which are used to specify which statistics should be retrieved. If you do not provide this argument, then this method will use the default parameters described in the {@link StatsParams} interface. + * @returns A promise which, upon success, will be fulfilled with a {@link PaginatedResult} object containing an array of {@link Stats} objects. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + stats(params?: StatsParams): Promise>; + /** + * Retrieves the time from the Ably service as milliseconds since the Unix epoch. Clients that do not have access to a sufficiently well maintained time source and wish to issue Ably {@link TokenRequest | `TokenRequest`s} with a more accurate timestamp should use the {@link ClientOptions.queryTime} property instead of this method. + * + * @returns A promise which, upon success, will be fulfilled with the time as milliseconds since the Unix epoch. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + time(): Promise; /** - * The `ChannelBase` class acts as a base class for the {@link ChannelCallbacks} and {@link ChannelPromise} classes. + * Publishes a {@link BatchPublishSpec} object to one or more channels, up to a maximum of 100 channels. + * + * @param spec - A {@link BatchPublishSpec} object. + * @returns A promise which, upon success, will be fulfilled with a {@link BatchResult} object containing information about the result of the batch publish for each requested channel. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. */ - class ChannelBase { - /** - * The channel name. - */ - name: string; - } + batchPublish(spec: BatchPublishSpec): Promise>; + /** + * Publishes one or more {@link BatchPublishSpec} objects to one or more channels, up to a maximum of 100 channels. + * + * @param specs - An array of {@link BatchPublishSpec} objects. + * @returns A promise which, upon success, will be fulfilled with an array of {@link BatchResult} objects containing information about the result of the batch publish for each requested channel for each provided {@link BatchPublishSpec}. This array is in the same order as the provided {@link BatchPublishSpec} array. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + batchPublish( + specs: BatchPublishSpec[], + ): Promise[]>; + /** + * Retrieves the presence state for one or more channels, up to a maximum of 100 channels. Presence state includes the `clientId` of members and their current {@link PresenceAction}. + * + * @param channels - An array of one or more channel names, up to a maximum of 100 channels. + * @returns A promise which, upon success, will be fulfilled with a {@link BatchResult} object containing information about the result of the batch presence request for each requested channel. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + batchPresence(channels: string[]): Promise[]>; + /** + * A {@link Push} object. + */ + push: Push; +} +/** + * A client that extends the functionality of {@link RestClient} and provides additional realtime-specific features. + */ +export declare interface RealtimeClient { /** - * Enables messages to be published and historic messages to be retrieved for a channel. + * A client ID, used for identifying this client when publishing messages or for presence purposes. The `clientId` can be any non-empty string, except it cannot contain a `*`. This option is primarily intended to be used in situations where the library is instantiated with a key. A `clientId` may also be implicit in a token used to instantiate the library; an error will be raised if a `clientId` specified here conflicts with the `clientId` implicit in the token. */ - class ChannelCallbacks extends ChannelBase { - /** - * A {@link PresenceCallbacks} object. - */ - presence: PresenceCallbacks; - /** - * Retrieves a {@link Types.PaginatedResult} object, containing an array of historical {@link Message} objects for the channel. If the channel is configured to persist messages, then messages can be retrieved from history for up to 72 hours in the past. If not, messages can only be retrieved from history for up to two minutes in the past. - * - * @param params - A set of parameters which are used to specify which messages should be retrieved. - * @param callback - A function which, upon success, will be called with a {@link Types.PaginatedResult} object containing an array of {@link Message} objects. Upon failure, the function will be called with information about the error. - */ - history(params?: RestHistoryParams, callback?: paginatedResultCallback): void; - /** - * Retrieves a {@link Types.PaginatedResult} object, containing an array of historical {@link Message} objects for the channel. If the channel is configured to persist messages, then messages can be retrieved from history for up to 72 hours in the past. If not, messages can only be retrieved from history for up to two minutes in the past. - * - * @param callback - A function which, upon success, will be called with a {@link Types.PaginatedResult} object containing an array of {@link Message} objects. Upon failure, the function will be called with information about the error. - */ - history(callback?: paginatedResultCallback): void; - /** - * Publishes a single message to the channel with the given event name and payload. - * - * @param name - The name of the message. - * @param data - The payload of the message. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - publish(name: string, data: any, callback?: errorCallback): void; - /** - * Publishes an array of messages to the channel. - * - * @param messages - An array of {@link Message} objects. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - publish(messages: any[], callback?: errorCallback): void; - /** - * Publishes a message to the channel. - * - * @param message - A {@link Message} object. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - publish(message: any, callback?: errorCallback): void; - /** - * Publishes a single message to the channel with the given event name and payload. - * - * @param name - The name of the message. - * @param data - The payload of the message. - * @param options - Optional parameters, such as [`quickAck`](https://faqs.ably.com/why-are-some-rest-publishes-on-a-channel-slow-and-then-typically-faster-on-subsequent-publishes) sent as part of the query string. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - publish(name: string, data: any, options?: PublishOptions, callback?: errorCallback): void; - /** - * Retrieves a {@link ChannelDetails} object for the channel, which includes status and occupancy metrics. - * - * @param callback - A function which, upon success, will be called a {@link ChannelDetails} object. Upon failure, the function will be called with information about the error. - */ - status(callback: StandardCallback): void; - } + clientId: string; + /** + * Calls {@link Connection.close | `connection.close()`} and causes the connection to close, entering the closing state. Once closed, the library will not attempt to re-establish the connection without an explicit call to {@link Connection.connect | `connect()`}. + */ + close(): void; + /** + * Calls {@link Connection.connect | `connection.connect()`} and causes the connection to open, entering the connecting state. Explicitly calling `connect()` is unnecessary unless the {@link ClientOptions.autoConnect} property is disabled. + */ + connect(): void; /** - * Enables messages to be published and historic messages to be retrieved for a channel. + * An {@link Auth} object. */ - class ChannelPromise extends ChannelBase { - /** - * A {@link PresencePromise} object. - */ - presence: PresencePromise; - /** - * Retrieves a {@link Types.PaginatedResult} object, containing an array of historical {@link Message} objects for the channel. If the channel is configured to persist messages, then messages can be retrieved from history for up to 72 hours in the past. If not, messages can only be retrieved from history for up to two minutes in the past. - * - * @param params - A set of parameters which are used to specify which messages should be retrieved. - * @returns A promise which, upon success, will be fulfilled with a {@link Types.PaginatedResult} object containing an array of {@link Message} objects. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. - */ - history(params?: RestHistoryParams): Promise>; - /** - * Publishes an array of messages to the channel. - * - * @param messages - An array of {@link Message} objects. - * @param options - Optional parameters, such as [`quickAck`](https://faqs.ably.com/why-are-some-rest-publishes-on-a-channel-slow-and-then-typically-faster-on-subsequent-publishes) sent as part of the query string. - * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - publish(messages: any[], options?: PublishOptions): Promise; - /** - * Publishes a message to the channel. - * - * @param message - A {@link Message} object. - * @param options - Optional parameters, such as [`quickAck`](https://faqs.ably.com/why-are-some-rest-publishes-on-a-channel-slow-and-then-typically-faster-on-subsequent-publishes) sent as part of the query string. - * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - publish(message: any, options?: PublishOptions): Promise; - /** - * Publishes a single message to the channel with the given event name and payload. - * - * @param name - The name of the message. - * @param data - The payload of the message. - * @param options - Optional parameters, such as [`quickAck`](https://faqs.ably.com/why-are-some-rest-publishes-on-a-channel-slow-and-then-typically-faster-on-subsequent-publishes) sent as part of the query string. - * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - publish(name: string, data: any, options?: PublishOptions): Promise; - /** - * Retrieves a {@link ChannelDetails} object for the channel, which includes status and occupancy metrics. - * - * @returns A promise which, upon success, will be fulfilled a {@link ChannelDetails} object. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. - */ - status(): Promise; - } + auth: Auth; + /** + * A {@link Channels} object. + */ + channels: Channels; + /** + * A {@link Connection} object. + */ + connection: Connection; + /** + * Makes a REST request to a provided path. This is provided as a convenience for developers who wish to use REST API functionality that is either not documented or is not yet included in the public API, without having to directly handle features such as authentication, paging, fallback hosts, MsgPack and JSON support. + * + * @param method - The request method to use, such as `GET`, `POST`. + * @param path - The request path. + * @param version - The version of the Ably REST API to use. See the [REST API reference](https://ably.com/docs/api/rest-api#versioning) for information on versioning. + * @param params - The parameters to include in the URL query of the request. The parameters depend on the endpoint being queried. See the [REST API reference](https://ably.com/docs/api/rest-api) for the available parameters of each endpoint. + * @param body - The JSON body of the request. + * @param headers - Additional HTTP headers to include in the request. + * @returns A promise which, upon success, will be fulfilled with the {@link HttpPaginatedResponse} response object returned by the HTTP request. This response object will contain an empty or JSON-encodable object. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + request( + method: string, + path: string, + version: number, + params?: any, + body?: any[] | any, + headers?: any, + ): Promise>; + /** + * Queries the REST `/stats` API and retrieves your application's usage statistics. Returns a {@link PaginatedResult} object, containing an array of {@link Stats} objects. See the [Stats docs](https://ably.com/docs/general/statistics). + * + * @param params - A set of parameters which are used to specify which statistics should be retrieved. If you do not provide this argument, then this method will use the default parameters described in the {@link StatsParams} interface. + * @returns A promise which, upon success, will be fulfilled with a {@link PaginatedResult} object containing an array of {@link Stats} objects. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + stats(params?: StatsParams): Promise>; + /** + * Retrieves the time from the Ably service as milliseconds since the Unix epoch. Clients that do not have access to a sufficiently well maintained time source and wish to issue Ably {@link TokenRequest | `TokenRequest`s} with a more accurate timestamp should use the {@link ClientOptions.queryTime} property instead of this method. + * + * @returns A promise which, upon success, will be fulfilled with the time as milliseconds since the Unix epoch. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + time(): Promise; + /** + * Publishes a {@link BatchPublishSpec} object to one or more channels, up to a maximum of 100 channels. + * + * @param spec - A {@link BatchPublishSpec} object. + * @returns A promise which, upon success, will be fulfilled with a {@link BatchResult} object containing information about the result of the batch publish for each requested channel. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + batchPublish(spec: BatchPublishSpec): Promise>; + /** + * Publishes one or more {@link BatchPublishSpec} objects to one or more channels, up to a maximum of 100 channels. + * + * @param specs - An array of {@link BatchPublishSpec} objects. + * @returns A promise which, upon success, will be fulfilled with an array of {@link BatchResult} objects containing information about the result of the batch publish for each requested channel for each provided {@link BatchPublishSpec}. This array is in the same order as the provided {@link BatchPublishSpec} array. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + batchPublish( + specs: BatchPublishSpec[], + ): Promise[]>; + /** + * Retrieves the presence state for one or more channels, up to a maximum of 100 channels. Presence state includes the `clientId` of members and their current {@link PresenceAction}. + * + * @param channels - An array of one or more channel names, up to a maximum of 100 channels. + * @returns A promise which, upon success, will be fulfilled with a {@link BatchResult} object containing information about the result of the batch presence request for each requested channel. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + batchPresence(channels: string[]): Promise[]>; + /** + * A {@link Push} object. + */ + push: Push; +} +/** + * Creates Ably {@link TokenRequest} objects and obtains Ably Tokens from Ably to subsequently issue to less trusted clients. + */ +export declare interface Auth { /** - * The `RealtimeChannelBase` class acts as a base class for the {@link RealtimeChannelCallbacks} and {@link RealtimeChannelPromise} classes. + * A client ID, used for identifying this client when publishing messages or for presence purposes. The `clientId` can be any non-empty string, except it cannot contain a `*`. This option is primarily intended to be used in situations where the library is instantiated with a key. Note that a `clientId` may also be implicit in a token used to instantiate the library. An error is raised if a `clientId` specified here conflicts with the `clientId` implicit in the token. Find out more about [identified clients](https://ably.com/docs/core-features/authentication#identified-clients). */ - class RealtimeChannelBase extends EventEmitter { - /** - * The channel name. - */ - readonly name: string; - /** - * An {@link ErrorInfo} object describing the last error which occurred on the channel, if any. - */ - errorReason: ErrorInfo; - /** - * The current {@link ChannelState} of the channel. - */ - readonly state: ChannelState; - /** - * Optional [channel parameters](https://ably.com/docs/realtime/channels/channel-parameters/overview) that configure the behavior of the channel. - */ - params: ChannelParams; - /** - * An array of {@link ChannelMode} objects. - */ - modes: ChannelModes; - /** - * Deregisters the given listener for the specified event name. This removes an earlier event-specific subscription. - * - * @param event - The event name. - * @param listener - An event listener function. - */ - unsubscribe(event: string, listener: messageCallback): void; - /** - * Deregisters the given listener from all event names in the array. - * - * @param events - An array of event names. - * @param listener - An event listener function. - */ - unsubscribe(events: Array, listener: messageCallback): void; - /** - * Deregisters all listeners for the given event name. - * - * @param event - The event name. - */ - unsubscribe(event: string): void; - /** - * Deregisters all listeners for all event names in the array. - * - * @param events - An array of event names. - */ - unsubscribe(events: Array): void; - /** - * Deregisters all listeners to messages on this channel that match the supplied filter. - * - * @param filter - A {@link MessageFilter}. - * @param listener - An event listener function. - */ - unsubscribe(filter: MessageFilter, listener?: messageCallback): void; - /** - * Deregisters the given listener (for any/all event names). This removes an earlier subscription. - * - * @param listener - An event listener function. - */ - unsubscribe(listener: messageCallback): void; - /** - * Deregisters all listeners to messages on this channel. This removes all earlier subscriptions. - */ - unsubscribe(): void; - } + clientId: string; + + /** + * Instructs the library to get a new token immediately. When using the realtime client, it upgrades the current realtime connection to use the new token, or if not connected, initiates a connection to Ably, once the new token has been obtained. Also stores any {@link TokenParams} and {@link AuthOptions} passed in as the new defaults, to be used for all subsequent implicit or explicit token requests. Any {@link TokenParams} and {@link AuthOptions} objects passed in entirely replace, as opposed to being merged with, the current client library saved values. + * + * @param tokenParams - A {@link TokenParams} object. + * @param authOptions - An {@link AuthOptions} object. + * @returns A promise which, upon success, will be fulfilled with a {@link TokenDetails} object. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + authorize(tokenParams?: TokenParams, authOptions?: AuthOptions): Promise; + /** + * Creates and signs an Ably {@link TokenRequest} based on the specified (or if none specified, the client library stored) {@link TokenParams} and {@link AuthOptions}. Note this can only be used when the API `key` value is available locally. Otherwise, the Ably {@link TokenRequest} must be obtained from the key owner. Use this to generate an Ably {@link TokenRequest} in order to implement an Ably Token request callback for use by other clients. Both {@link TokenParams} and {@link AuthOptions} are optional. When omitted or `null`, the default token parameters and authentication options for the client library are used, as specified in the {@link ClientOptions} when the client library was instantiated, or later updated with an explicit `authorize` request. Values passed in are used instead of, rather than being merged with, the default values. To understand why an Ably {@link TokenRequest} may be issued to clients in favor of a token, see [Token Authentication explained](https://ably.com/docs/core-features/authentication/#token-authentication). + * + * @param tokenParams - A {@link TokenParams} object. + * @param authOptions - An {@link AuthOptions} object. + * @returns A promise which, upon success, will be fulfilled with a {@link TokenRequest} object. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + createTokenRequest(tokenParams?: TokenParams, authOptions?: AuthOptions): Promise; + /** + * Calls the `requestToken` REST API endpoint to obtain an Ably Token according to the specified {@link TokenParams} and {@link AuthOptions}. Both {@link TokenParams} and {@link AuthOptions} are optional. When omitted or `null`, the default token parameters and authentication options for the client library are used, as specified in the {@link ClientOptions} when the client library was instantiated, or later updated with an explicit `authorize` request. Values passed in are used instead of, rather than being merged with, the default values. To understand why an Ably {@link TokenRequest} may be issued to clients in favor of a token, see [Token Authentication explained](https://ably.com/docs/core-features/authentication/#token-authentication). + * + * @param TokenParams - A {@link TokenParams} object. + * @param authOptions - An {@link AuthOptions} object. + * @returns A promise which, upon success, will be fulfilled with a {@link TokenDetails} object. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + requestToken(TokenParams?: TokenParams, authOptions?: AuthOptions): Promise; + /** + * Revokes the tokens specified by the provided array of {@link TokenRevocationTargetSpecifier}s. Only tokens issued by an API key that had revocable tokens enabled before the token was issued can be revoked. See the [token revocation docs](https://ably.com/docs/core-features/authentication#token-revocation) for more information. + * + * @param specifiers - An array of {@link TokenRevocationTargetSpecifier} objects. + * @param options - A set of options which are used to modify the revocation request. + * @returns A promise which, upon success, will be fulfilled with a {@link BatchResult} containing information about the result of the token revocation request for each provided [`TokenRevocationTargetSpecifier`]{@link TokenRevocationTargetSpecifier}. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + revokeTokens( + specifiers: TokenRevocationTargetSpecifier[], + options?: TokenRevocationOptions, + ): Promise>; +} + +/** + * Enables the retrieval of the current and historic presence set for a channel. + */ +export declare interface Presence { + /** + * Retrieves the current members present on the channel and the metadata for each member, such as their {@link PresenceAction} and ID. Returns a {@link PaginatedResult} object, containing an array of {@link PresenceMessage} objects. + * + * @param params - A set of parameters which are used to specify which presence members should be retrieved. + * @returns A promise which, upon success, will be fulfilled with a {@link PaginatedResult} object containing an array of {@link PresenceMessage} objects. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + get(params?: RestPresenceParams): Promise>; + /** + * Retrieves a {@link PaginatedResult} object, containing an array of historical {@link PresenceMessage} objects for the channel. If the channel is configured to persist messages, then presence messages can be retrieved from history for up to 72 hours in the past. If not, presence messages can only be retrieved from history for up to two minutes in the past. + * + * @param params - A set of parameters which are used to specify which messages should be retrieved. + * @returns A promise which, upon success, will be fulfilled with a {@link PaginatedResult} object containing an array of {@link PresenceMessage} objects. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + history(params?: RestHistoryParams): Promise>; +} + +/** + * Enables the presence set to be entered and subscribed to, and the historic presence set to be retrieved for a channel. + */ +export declare interface RealtimePresence { + /** + * Indicates whether the presence set synchronization between Ably and the clients on the channel has been completed. Set to `true` when the sync is complete. + */ + syncComplete: boolean; + /** + * Deregisters a specific listener that is registered to receive {@link PresenceMessage} on the channel for a given {@link PresenceAction}. + * + * @param presence - A specific {@link PresenceAction} to deregister the listener for. + * @param listener - An event listener function. + */ + unsubscribe(presence: PresenceAction, listener: messageCallback): void; + /** + * Deregisters a specific listener that is registered to receive {@link PresenceMessage} on the channel for a given array of {@link PresenceAction} objects. + * + * @param presence - An array of {@link PresenceAction} objects to deregister the listener for. + * @param listener - An event listener function. + */ + unsubscribe(presence: Array, listener: messageCallback): void; + /** + * Deregisters any listener that is registered to receive {@link PresenceMessage} on the channel for a specific {@link PresenceAction} + * + * @param presence - A specific {@link PresenceAction} to deregister the listeners for. + */ + unsubscribe(presence: PresenceAction): void; + /** + * Deregisters any listener that is registered to receive {@link PresenceMessage} on the channel for an array of {@link PresenceAction} objects + * + * @param presence - An array of {@link PresenceAction} objects to deregister the listeners for. + */ + unsubscribe(presence: Array): void; + /** + * Deregisters a specific listener that is registered to receive {@link PresenceMessage} on the channel. + * + * @param listener - An event listener function. + */ + unsubscribe(listener: messageCallback): void; + /** + * Deregisters all listeners currently receiving {@link PresenceMessage} for the channel. + */ + unsubscribe(): void; + + /** + * Retrieves the current members present on the channel and the metadata for each member, such as their {@link PresenceAction} and ID. Returns an array of {@link PresenceMessage} objects. + * + * @param params - A set of parameters which are used to specify which presence members should be retrieved. + * @returns A promise which, upon success, will be fulfilled with an array of {@link PresenceMessage} objects. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + get(params?: RealtimePresenceParams): Promise; + /** + * Retrieves a {@link PaginatedResult} object, containing an array of historical {@link PresenceMessage} objects for the channel. If the channel is configured to persist messages, then presence messages can be retrieved from history for up to 72 hours in the past. If not, presence messages can only be retrieved from history for up to two minutes in the past. + * + * @param params - A set of parameters which are used to specify which presence messages should be retrieved. + * @returns A promise which, upon success, will be fulfilled with a {@link PaginatedResult} object containing an array of {@link PresenceMessage} objects. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + history(params?: RealtimeHistoryParams): Promise>; + /** + * Registers a listener that is called each time a {@link PresenceMessage} matching a given {@link PresenceAction}, or an action within an array of {@link PresenceAction | `PresenceAction`s}, is received on the channel, such as a new member entering the presence set. + * + * @param action - A {@link PresenceAction} or an array of {@link PresenceAction | `PresenceAction`s} to register the listener for. + * @param listener - An event listener function. + * @returns A promise which resolves upon success of the channel {@link RealtimeChannel.attach | `attach()`} operation and rejects with an {@link ErrorInfo} object upon its failure. + */ + subscribe(action: PresenceAction | Array, listener?: messageCallback): Promise; + /** + * Registers a listener that is called each time a {@link PresenceMessage} is received on the channel, such as a new member entering the presence set. + * + * @param listener - An event listener function. + * @returns A promise which resolves upon success of the channel {@link RealtimeChannel.attach | `attach()`} operation and rejects with an {@link ErrorInfo} object upon its failure. + */ + subscribe(listener?: messageCallback): Promise; + /** + * Enters the presence set for the channel, optionally passing a `data` payload. A `clientId` is required to be present on a channel. + * + * @param data - The payload associated with the presence member. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. + */ + enter(data?: any): Promise; + /** + * Updates the `data` payload for a presence member. If called before entering the presence set, this is treated as an {@link PresenceActions.ENTER} event. + * + * @param data - The payload to update for the presence member. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. + */ + update(data?: any): Promise; + /** + * Leaves the presence set for the channel. A client must have previously entered the presence set before they can leave it. + * + * @param data - The payload associated with the presence member. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. + */ + leave(data?: any): Promise; + /** + * Enters the presence set of the channel for a given `clientId`. Enables a single client to update presence on behalf of any number of clients using a single connection. The library must have been instantiated with an API key or a token bound to a wildcard `clientId`. + * + * @param clientId - The ID of the client to enter into the presence set. + * @param data - The payload associated with the presence member. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. + */ + enterClient(clientId: string, data?: any): Promise; + /** + * Updates the `data` payload for a presence member using a given `clientId`. Enables a single client to update presence on behalf of any number of clients using a single connection. The library must have been instantiated with an API key or a token bound to a wildcard `clientId`. + * + * @param clientId - The ID of the client to update in the presence set. + * @param data - The payload to update for the presence member. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. + */ + updateClient(clientId: string, data?: any): Promise; + /** + * Leaves the presence set of the channel for a given `clientId`. Enables a single client to update presence on behalf of any number of clients using a single connection. The library must have been instantiated with an API key or a token bound to a wildcard `clientId`. + * + * @param clientId - The ID of the client to leave the presence set for. + * @param data - The payload associated with the presence member. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. + */ + leaveClient(clientId: string, data?: any): Promise; +} + +/** + * Enables messages to be published and historic messages to be retrieved for a channel. + */ +export declare interface Channel { + /** + * The channel name. + */ + name: string; + + /** + * A {@link Presence} object. + */ + presence: Presence; + /** + * Retrieves a {@link PaginatedResult} object, containing an array of historical {@link InboundMessage} objects for the channel. If the channel is configured to persist messages, then messages can be retrieved from history for up to 72 hours in the past. If not, messages can only be retrieved from history for up to two minutes in the past. + * + * @param params - A set of parameters which are used to specify which messages should be retrieved. + * @returns A promise which, upon success, will be fulfilled with a {@link PaginatedResult} object containing an array of {@link InboundMessage} objects. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + history(params?: RestHistoryParams): Promise>; + /** + * Publishes an array of messages to the channel. + * + * @param messages - An array of {@link Message} objects. + * @param options - Optional parameters, such as [`quickAck`](https://faqs.ably.com/why-are-some-rest-publishes-on-a-channel-slow-and-then-typically-faster-on-subsequent-publishes) sent as part of the query string. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. + */ + publish(messages: Message[], options?: PublishOptions): Promise; + /** + * Publishes a message to the channel. + * + * @param message - A {@link Message} object. + * @param options - Optional parameters, such as [`quickAck`](https://faqs.ably.com/why-are-some-rest-publishes-on-a-channel-slow-and-then-typically-faster-on-subsequent-publishes) sent as part of the query string. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. + */ + publish(message: Message, options?: PublishOptions): Promise; + /** + * Publishes a single message to the channel with the given event name and payload. + * + * @param name - The name of the message. + * @param data - The payload of the message. + * @param options - Optional parameters, such as [`quickAck`](https://faqs.ably.com/why-are-some-rest-publishes-on-a-channel-slow-and-then-typically-faster-on-subsequent-publishes) sent as part of the query string. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. + */ + publish(name: string, data: any, options?: PublishOptions): Promise; + /** + * Retrieves a {@link ChannelDetails} object for the channel, which includes status and occupancy metrics. + * + * @returns A promise which, upon success, will be fulfilled a {@link ChannelDetails} object. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + status(): Promise; +} + +/** + * Enables messages to be published and subscribed to. Also enables historic messages to be retrieved and provides access to the {@link RealtimePresence} object of a channel. + */ +export declare interface RealtimeChannel extends EventEmitter { + /** + * The channel name. + */ + readonly name: string; + /** + * An {@link ErrorInfo} object describing the last error which occurred on the channel, if any. + */ + errorReason: ErrorInfo; + /** + * The current {@link ChannelState} of the channel. + */ + readonly state: ChannelState; + /** + * Optional [channel parameters](https://ably.com/docs/realtime/channels/channel-parameters/overview) that configure the behavior of the channel. + */ + params: ChannelParams; + /** + * An array of {@link ChannelMode} objects. + */ + modes: ChannelMode[]; + /** + * Deregisters the given listener for the specified event name. This removes an earlier event-specific subscription. + * + * @param event - The event name. + * @param listener - An event listener function. + */ + unsubscribe(event: string, listener: messageCallback): void; + /** + * Deregisters the given listener from all event names in the array. + * + * @param events - An array of event names. + * @param listener - An event listener function. + */ + unsubscribe(events: Array, listener: messageCallback): void; + /** + * Deregisters all listeners for the given event name. + * + * @param event - The event name. + */ + unsubscribe(event: string): void; + /** + * Deregisters all listeners for all event names in the array. + * + * @param events - An array of event names. + */ + unsubscribe(events: Array): void; + /** + * Deregisters all listeners to messages on this channel that match the supplied filter. + * + * @param filter - A {@link MessageFilter}. + * @param listener - An event listener function. + */ + unsubscribe(filter: MessageFilter, listener?: messageCallback): void; + /** + * Deregisters the given listener (for any/all event names). This removes an earlier subscription. + * + * @param listener - An event listener function. + */ + unsubscribe(listener: messageCallback): void; + /** + * Deregisters all listeners to messages on this channel. This removes all earlier subscriptions. + */ + unsubscribe(): void; + + /** + * A {@link RealtimePresence} object. + */ + presence: RealtimePresence; + /** + * Attach to this channel ensuring the channel is created in the Ably system and all messages published on the channel are received by any channel listeners registered using {@link RealtimeChannel.subscribe | `subscribe()`}. Any resulting channel state change will be emitted to any listeners registered using the {@link EventEmitter.on | `on()`} or {@link EventEmitter.once | `once()`} methods. As a convenience, `attach()` is called implicitly if {@link RealtimeChannel.subscribe | `subscribe()`} for the channel is called, or {@link RealtimePresence.enter | `enter()`} or {@link RealtimePresence.subscribe | `subscribe()`} are called on the {@link RealtimePresence} object for this channel. + * + * @returns A promise which, upon success, if the channel became attached will be fulfilled with a {@link ChannelStateChange} object. If the channel was already attached the promise will be fulfilled with `null`. Upon failure, the promise will be rejected with an {@link ErrorInfo} object. + */ + attach(): Promise; + /** + * Detach from this channel. Any resulting channel state change is emitted to any listeners registered using the {@link EventEmitter.on | `on()`} or {@link EventEmitter.once | `once()`} methods. Once all clients globally have detached from the channel, the channel will be released in the Ably service within two minutes. + * + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. + */ + detach(): Promise; + /** + * Retrieves a {@link PaginatedResult} object, containing an array of historical {@link InboundMessage} objects for the channel. If the channel is configured to persist messages, then messages can be retrieved from history for up to 72 hours in the past. If not, messages can only be retrieved from history for up to two minutes in the past. + * + * @param params - A set of parameters which are used to specify which presence members should be retrieved. + * @returns A promise which, upon success, will be fulfilled with a {@link PaginatedResult} object containing an array of {@link InboundMessage} objects. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + history(params?: RealtimeHistoryParams): Promise>; + /** + * Sets the {@link ChannelOptions} for the channel. + * + * @param options - A {@link ChannelOptions} object. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. + */ + setOptions(options: ChannelOptions): Promise; + /** + * Registers a listener for messages with a given event name on this channel. The caller supplies a listener function, which is called each time one or more matching messages arrives on the channel. + * + * @param event - The event name. + * @param listener - An event listener function. + * @returns A promise which, upon successful attachment to the channel, will be fulfilled with a {@link ChannelStateChange} object. If the channel was already attached the promise will be resolved with `null`. Upon failure, the promise will be rejected with an {@link ErrorInfo} object. + */ + subscribe(event: string, listener?: messageCallback): Promise; + /** + * Registers a listener for messages on this channel for multiple event name values. + * + * @param events - An array of event names. + * @param listener - An event listener function. + * @returns A promise which, upon successful attachment to the channel, will be fulfilled with a {@link ChannelStateChange} object. If the channel was already attached the promise will be resolved with `null`. Upon failure, the promise will be rejected with an {@link ErrorInfo} object. + */ + subscribe(events: Array, listener?: messageCallback): Promise; + /** + * {@label WITH_MESSAGE_FILTER} + * + * Registers a listener for messages on this channel that match the supplied filter. + * + * @param filter - A {@link MessageFilter}. + * @param listener - An event listener function. + * @returns A promise which, upon successful attachment to the channel, will be fulfilled with a {@link ChannelStateChange} object. If the channel was already attached the promise will be resolved with `null`. Upon failure, the promise will be rejected with an {@link ErrorInfo} object. + */ + subscribe(filter: MessageFilter, listener?: messageCallback): Promise; + /** + * Registers a listener for messages on this channel. The caller supplies a listener function, which is called each time one or more messages arrives on the channel. + * + * @param callback - An event listener function. + * @returns A promise which, upon successful attachment to the channel, will be fulfilled with a {@link ChannelStateChange} object. If the channel was already attached the promise will be resolved with `null`. Upon failure, the promise will be rejected with an {@link ErrorInfo} object. + */ + subscribe(callback: messageCallback): Promise; + /** + * Publishes a single message to the channel with the given event name and payload. When publish is called with this client library, it won't attempt to implicitly attach to the channel, so long as [transient publishing](https://ably.com/docs/realtime/channels#transient-publish) is available in the library. Otherwise, the client will implicitly attach. + * + * @param name - The event name. + * @param data - The message payload. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. + */ + publish(name: string, data: any): Promise; + /** + * Publishes an array of messages to the channel. When publish is called with this client library, it won't attempt to implicitly attach to the channel. + * + * @param messages - An array of {@link Message} objects. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. + */ + publish(messages: Message[]): Promise; + /** + * Publish a message to the channel. When publish is called with this client library, it won't attempt to implicitly attach to the channel. + * + * @param message - A {@link Message} object. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. + */ + publish(message: Message): Promise; + /** + * If the channel is already in the given state, returns a promise which immediately resolves to `null`. Else, calls {@link EventEmitter.once | `once()`} to return a promise which resolves the next time the channel transitions to the given state. + * + * @param targetState - The channel state to wait for. + */ + whenState(targetState: ChannelState): Promise; +} + +/** + * Optional parameters for message publishing. + */ +export type PublishOptions = { + /** + * See [here](https://faqs.ably.com/why-are-some-rest-publishes-on-a-channel-slow-and-then-typically-faster-on-subsequent-publishes). + */ + quickAck?: boolean; +}; + +/** + * Contains properties to filter messages with when calling {@link RealtimeChannel.subscribe | `RealtimeChannel.subscribe()`}. + */ +export type MessageFilter = { + /** + * Filters messages by a specific message `name`. + */ + name?: string; + /** + * Filters messages by a specific `extras.ref.timeserial` value. + */ + refTimeserial?: string; + /** + * Filters messages by a specific `extras.ref.type` value. + */ + refType?: string; + /** + * Filters messages based on whether they contain an `extras.ref`. + */ + isRef?: boolean; + /** + * Filters messages by a specific message `clientId`. + */ + clientId: string; +}; + +/** + * Creates and destroys {@link Channel} and {@link RealtimeChannel} objects. + */ +export declare interface Channels { + /** + * Creates a new {@link Channel} or {@link RealtimeChannel} object, with the specified {@link ChannelOptions}, or returns the existing channel object. + * + * @param name - The channel name. + * @param channelOptions - A {@link ChannelOptions} object. + * @returns A {@link Channel} or {@link RealtimeChannel} object. + */ + get(name: string, channelOptions?: ChannelOptions): T; + /** + * Creates a new {@link Channel} or {@link RealtimeChannel} object, with the specified channel {@link DeriveOptions} + * and {@link ChannelOptions}, or returns the existing channel object. + * + * @experimental This is a preview feature and may change in a future non-major release. + * This experimental method allows you to create custom realtime data feeds by selectively subscribing + * to receive only part of the data from the channel. + * See the [announcement post](https://pages.ably.com/subscription-filters-preview) for more information. + * + * @param name - The channel name. + * @param deriveOptions - A {@link DeriveOptions} object. + * @param channelOptions - A {@link ChannelOptions} object. + * @returns A {@link RealtimeChannel} object. + */ + getDerived(name: string, deriveOptions: DeriveOptions, channelOptions?: ChannelOptions): T; + /** + * Releases a {@link Channel} or {@link RealtimeChannel} object, deleting it, and enabling it to be garbage collected. It also removes any listeners associated with the channel. To release a channel, the {@link ChannelState} must be `INITIALIZED`, `DETACHED`, or `FAILED`. + * + * @param name - The channel name. + */ + release(name: string): void; +} + +/** + * Contains an individual message that is sent to, or received from, Ably. + */ +export interface Message { + /** + * The client ID of the publisher of this message. + */ + clientId?: string; + /** + * The connection ID of the publisher of this message. + */ + connectionId?: string; + /** + * The message payload, if provided. + */ + data?: any; + /** + * This is typically empty, as all messages received from Ably are automatically decoded client-side using this value. However, if the message encoding cannot be processed, this attribute contains the remaining transformations not applied to the `data` payload. + */ + encoding?: string; + /** + * A JSON object of arbitrary key-value pairs that may contain metadata, and/or ancillary payloads. Valid payloads include `push`, `delta`, `ref` and `headers`. + */ + extras?: any; + /** + * Unique ID assigned by Ably to this message. + */ + id?: string; + /** + * The event name. + */ + name?: string; + /** + * Timestamp of when the message was received by Ably, as milliseconds since the Unix epoch. + */ + timestamp?: number; +} + +/** + * A message received from Ably. + */ +export type InboundMessage = Message & Required>; + +/** + * Static utilities related to messages. + */ +export interface MessageStatic { + /** + * A static factory method to create an `InboundMessage` object from a deserialized InboundMessage-like object encoded using Ably's wire protocol. + * + * @param JsonObject - A `InboundMessage`-like deserialized object. + * @param channelOptions - A {@link ChannelOptions} object. If you have an encrypted channel, use this to allow the library to decrypt the data. + * @returns A promise which will be fulfilled with an `InboundMessage` object. + */ + fromEncoded: (JsonObject: any, channelOptions?: ChannelOptions) => Promise; + /** + * A static factory method to create an array of `InboundMessage` objects from an array of deserialized InboundMessage-like object encoded using Ably's wire protocol. + * + * @param JsonArray - An array of `InboundMessage`-like deserialized objects. + * @param channelOptions - A {@link ChannelOptions} object. If you have an encrypted channel, use this to allow the library to decrypt the data. + * @returns A promise which will be fulfilled with an array of {@link InboundMessage} objects. + */ + fromEncodedArray: (JsonArray: any[], channelOptions?: ChannelOptions) => Promise; +} + +/** + * Contains an individual presence update sent to, or received from, Ably. + */ +export declare interface PresenceMessage { + /** + * The type of {@link PresenceAction} the `PresenceMessage` is for. + */ + action: PresenceAction; + /** + * The ID of the client that published the `PresenceMessage`. + */ + clientId: string; + /** + * The ID of the connection associated with the client that published the `PresenceMessage`. + */ + connectionId: string; + /** + * The payload of the `PresenceMessage`. + */ + data: any; + /** + * This will typically be empty as all presence messages received from Ably are automatically decoded client-side using this value. However, if the message encoding cannot be processed, this attribute will contain the remaining transformations not applied to the data payload. + */ + encoding: string; + /** + * A JSON object of arbitrary key-value pairs that may contain metadata, and/or ancillary payloads. Valid payloads include `headers`. + */ + extras: any; + /** + * A unique ID assigned to each `PresenceMessage` by Ably. + */ + id: string; + /** + * The time the `PresenceMessage` was received by Ably, as milliseconds since the Unix epoch. + */ + timestamp: number; +} + +/** + * Static utilities related to presence messages. + */ +export interface PresenceMessageStatic { + /** + * Decodes and decrypts a deserialized `PresenceMessage`-like object using the cipher in {@link ChannelOptions}. Any residual transforms that cannot be decoded or decrypted will be in the `encoding` property. Intended for users receiving messages from a source other than a REST or Realtime channel (for example a queue) to avoid having to parse the encoding string. + * + * @param JsonObject - The deserialized `PresenceMessage`-like object to decode and decrypt. + * @param channelOptions - A {@link ChannelOptions} object containing the cipher. + */ + fromEncoded: (JsonObject: any, channelOptions?: ChannelOptions) => Promise; + /** + * Decodes and decrypts an array of deserialized `PresenceMessage`-like object using the cipher in {@link ChannelOptions}. Any residual transforms that cannot be decoded or decrypted will be in the `encoding` property. Intended for users receiving messages from a source other than a REST or Realtime channel (for example a queue) to avoid having to parse the encoding string. + * + * @param JsonArray - An array of deserialized `PresenceMessage`-like objects to decode and decrypt. + * @param channelOptions - A {@link ChannelOptions} object containing the cipher. + */ + fromEncodedArray: (JsonArray: any[], channelOptions?: ChannelOptions) => Promise; + + /** + * Initialises a `PresenceMessage` from a `PresenceMessage`-like object. + * + * @param values - The values to intialise the `PresenceMessage` from. + * @param stringifyAction - Whether to convert the `action` field from a number to a string. + */ + fromValues(values: PresenceMessage | Record, stringifyAction?: boolean): PresenceMessage; +} + +/** + * Cipher Key used in {@link CipherParamOptions}. If set to a `string`, the value must be base64 encoded. + */ +export type CipherKeyParam = ArrayBuffer | Uint8Array | string; // if string must be base64-encoded +/** + * The type of the key returned by {@link Crypto.generateRandomKey}. Typed differently depending on platform (`Buffer` in Node.js, `ArrayBuffer` elsewhere). + */ +export type CipherKey = ArrayBuffer | Buffer; +/** + * Contains the properties used to generate a {@link CipherParams} object. + */ +export type CipherParamOptions = { /** - * Optional parameters for message publishing. + * The private key used to encrypt and decrypt payloads. */ - type PublishOptions = { - /** - * See [here](https://faqs.ably.com/why-are-some-rest-publishes-on-a-channel-slow-and-then-typically-faster-on-subsequent-publishes). - */ - quickAck?: boolean; - }; - + key: CipherKeyParam; /** - * Contains properties to filter messages with when calling {@link RealtimeChannelCallbacks.subscribe | `RealtimeChannelCallbacks.subscribe()`} or {@link RealtimeChannelPromise.subscribe | `RealtimeChannelPromise.subscribe()`}. + * The algorithm to use for encryption. Only `AES` is supported. */ - type MessageFilter = { - /** - * Filters messages by a specific message `name`. - */ - name?: string; - /** - * Filters messages by a specific `extras.ref.timeserial` value. - */ - refTimeserial?: string; - /** - * Filters messages by a specific `extras.ref.type` value. - */ - refType?: string; - /** - * Filters messages based on whether they contain an `extras.ref`. - */ - isRef?: boolean; - /** - * Filters messages by a specific message `clientId`. - */ - clientId: string; - }; - + algorithm?: 'aes'; /** - * Enables messages to be published and subscribed to. Also enables historic messages to be retrieved and provides access to the {@link RealtimePresenceCallbacks} object of a channel. + * The length of the key in bits; for example 128 or 256. */ - class RealtimeChannelCallbacks extends RealtimeChannelBase { - /** - * A {@link RealtimePresenceCallbacks} object. - */ - presence: RealtimePresenceCallbacks; - /** - * Attach to this channel ensuring the channel is created in the Ably system and all messages published on the channel are received by any channel listeners registered using {@link RealtimeChannelCallbacks.subscribe | `subscribe()`}. Any resulting channel state change will be emitted to any listeners registered using the {@link EventEmitter.on | `on()`} or {@link EventEmitter.once | `once()`} methods. As a convenience, `attach()` is called implicitly if {@link RealtimeChannelCallbacks.subscribe | `subscribe()`} for the channel is called, or {@link RealtimePresenceCallbacks.enter | `enter()`} or {@link RealtimePresenceCallbacks.subscribe | `subscribe()`} are called on the {@link RealtimePresenceCallbacks} object for this channel. - * - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded and the channel became attached, then the function will be called with a {@link ChannelStateChange} object. If the channel was already attached the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - attach(callback?: StandardCallback): void; - /** - * Detach from this channel. Any resulting channel state change is emitted to any listeners registered using the {@link EventEmitter.on | `on()`} or {@link EventEmitter.once | `once()`} methods. Once all clients globally have detached from the channel, the channel will be released in the Ably service within two minutes. - * - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - detach(callback?: errorCallback): void; - /** - * Retrieves a {@link Types.PaginatedResult} object, containing an array of historical {@link Message} objects for the channel. If the channel is configured to persist messages, then messages can be retrieved from history for up to 72 hours in the past. If not, messages can only be retrieved from history for up to two minutes in the past. - * - * @param params - A set of parameters which are used to specify which presence members should be retrieved. - * @param callback - A function which, upon success, will be called with a {@link Types.PaginatedResult} object containing an array of {@link Message} objects. Upon failure, the function will be called with information about the error. - */ - history(params?: RealtimeHistoryParams, callback?: paginatedResultCallback): void; - /** - * Retrieves a {@link Types.PaginatedResult} object, containing an array of historical {@link Message} objects for the channel. If the channel is configured to persist messages, then messages can be retrieved from history for up to 72 hours in the past. If not, messages can only be retrieved from history for up to two minutes in the past. - * - * @param callback - A function which, upon success, will be called with a {@link Types.PaginatedResult} object containing an array of {@link Message} objects. Upon failure, the function will be called with information about the error. - */ - history(callback?: paginatedResultCallback): void; - /** - * Sets the {@link ChannelOptions} for the channel. - * - * @param options - A {@link ChannelOptions} object. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - setOptions(options: ChannelOptions, callback?: errorCallback): void; - /** - * Registers a listener for messages with a given event name on this channel. The caller supplies a listener function, which is called each time one or more matching messages arrives on the channel. - * - * @param event - The event name. - * @param listener - An event listener function. - * @param callbackWhenAttached - A function which will be called upon completion of the channel {@link RealtimeChannelCallbacks.attach | `attach()`} operation. If the operation succeeded and the channel became attached, then the function will be called with a {@link ChannelStateChange} object. If the channel was already attached the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - subscribe( - event: string, - listener?: messageCallback, - callbackWhenAttached?: StandardCallback - ): void; - /** - * Registers a listener for messages on this channel for multiple event name values. - * - * @param events - An array of event names. - * @param listener - An event listener function. - * @param callbackWhenAttached - A function which will be called upon completion of the channel {@link RealtimeChannelCallbacks.attach | `attach()`} operation. If the operation succeeded and the channel became attached, then the function will be called with a {@link ChannelStateChange} object. If the channel was already attached the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - subscribe( - events: Array, - listener?: messageCallback, - callbackWhenAttached?: StandardCallback - ): void; - /** - * Registers a listener for messages on this channel that match the supplied filter. - * - * @param filter - A {@link MessageFilter}. - * @param listener - An event listener function. - * @param callbackWhenAttached - A function which will be called upon completion of the channel {@link RealtimeChannelCallbacks.attach | `attach()`} operation. If the operation succeeded and the channel became attached, then the function will be called with a {@link ChannelStateChange} object. If the channel was already attached the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - subscribe( - filter: MessageFilter, - listener?: messageCallback, - callbackWhenAttached?: StandardCallback - ): void; - /** - * Registers a listener for messages on this channel. The caller supplies a listener function, which is called each time one or more messages arrives on the channel. - * - * @param listener - An event listener function. - * @param callbackWhenAttached - A function which will be called upon completion of the channel {@link RealtimeChannelCallbacks.attach | `attach()`} operation. If the operation succeeded and the channel became attached, then the function will be called with a {@link ChannelStateChange} object. If the channel was already attached the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - subscribe(listener: messageCallback, callbackWhenAttached?: StandardCallback): void; - /** - * Publishes a single message to the channel with the given event name and payload. When publish is called with this client library, it won't attempt to implicitly attach to the channel, so long as [transient publishing](https://ably.com/docs/realtime/channels#transient-publish) is available in the library. Otherwise, the client will implicitly attach. - * - * @param name - The event name. - * @param data - The message payload. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - publish(name: string, data: any, callback?: errorCallback): void; - /** - * Publishes an array of messages to the channel. When publish is called with this client library, it won't attempt to implicitly attach to the channel. - * - * @param messages - An array of {@link Message} objects. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - publish(messages: any[], callback?: errorCallback): void; - /** - * Publish a message to the channel. When publish is called with this client library, it won't attempt to implicitly attach to the channel. - * - * @param message - A {@link Message} object. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - publish(message: any, callback?: errorCallback): void; - /** - * Publishes a single message to the channel with the given event name and payload. When publish is called with this client library, it won't attempt to implicitly attach to the channel, so long as [transient publishing](https://ably.com/docs/realtime/channels#transient-publish) is available in the library. Otherwise, the client will implicitly attach. - * - * @param name - The event name. - * @param data - The message payload. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - publish(name: string, data: any, callback?: errorCallback): void; - /** - * Calls the supplied function when the channel reaches the specified {@link ChannelState}. If the channel is already in the specified state, the callback is called immediately. - * - * @param targetState - The state which should be reached. - * @param callback - A function which will be called when the channel has reached the specified {@link ChannelState} with a {@link ChannelStateChange} object as the first argument. - */ - whenState(targetState: ChannelState, callback: channelEventCallback): void; - } - + keyLength?: number; /** - * Enables messages to be published and subscribed to. Also enables historic messages to be retrieved and provides access to the {@link RealtimePresencePromise} object of a channel. + * The cipher mode. Only `CBC` is supported. */ - class RealtimeChannelPromise extends RealtimeChannelBase { - /** - * A {@link RealtimePresencePromise} object. - */ - presence: RealtimePresencePromise; - /** - * Attach to this channel ensuring the channel is created in the Ably system and all messages published on the channel are received by any channel listeners registered using {@link RealtimeChannelPromise.subscribe | `subscribe()`}. Any resulting channel state change will be emitted to any listeners registered using the {@link EventEmitter.on | `on()`} or {@link EventEmitter.once | `once()`} methods. As a convenience, `attach()` is called implicitly if {@link RealtimeChannelPromise.subscribe | `subscribe()`} for the channel is called, or {@link RealtimePresencePromise.enter | `enter()`} or {@link RealtimePresencePromise.subscribe | `subscribe()`} are called on the {@link RealtimePresencePromise} object for this channel. - * - * @returns A promise which, upon success, if the channel became attached will be fulfilled with a {@link ChannelStateChange} object. If the channel was already attached the promise will be fulfilled with `null`. Upon failure, the promise will be rejected with an {@link ErrorInfo} object. - */ - attach(): Promise; - /** - * Detach from this channel. Any resulting channel state change is emitted to any listeners registered using the {@link EventEmitter.on | `on()`} or {@link EventEmitter.once | `once()`} methods. Once all clients globally have detached from the channel, the channel will be released in the Ably service within two minutes. - * - * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - detach(): Promise; - /** - * Retrieves a {@link Types.PaginatedResult} object, containing an array of historical {@link Message} objects for the channel. If the channel is configured to persist messages, then messages can be retrieved from history for up to 72 hours in the past. If not, messages can only be retrieved from history for up to two minutes in the past. - * - * @param params - A set of parameters which are used to specify which presence members should be retrieved. - * @returns A promise which, upon success, will be fulfilled with a {@link Types.PaginatedResult} object containing an array of {@link Message} objects. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. - */ - history(params?: RealtimeHistoryParams): Promise>; - /** - * Sets the {@link ChannelOptions} for the channel. - * - * @param options - A {@link ChannelOptions} object. - * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - setOptions(options: ChannelOptions): Promise; - /** - * Registers a listener for messages with a given event name on this channel. The caller supplies a listener function, which is called each time one or more matching messages arrives on the channel. - * - * @param event - The event name. - * @param listener - An event listener function. - * @returns A promise which, upon successful attachment to the channel, will be fulfilled with a {@link ChannelStateChange} object. If the channel was already attached the promise will be resolved with `null`. Upon failure, the promise will be rejected with an {@link ErrorInfo} object. - */ - subscribe(event: string, listener?: messageCallback): Promise; - /** - * Registers a listener for messages on this channel for multiple event name values. - * - * @param events - An array of event names. - * @param listener - An event listener function. - * @returns A promise which, upon successful attachment to the channel, will be fulfilled with a {@link ChannelStateChange} object. If the channel was already attached the promise will be resolved with `null`. Upon failure, the promise will be rejected with an {@link ErrorInfo} object. - */ - subscribe(events: Array, listener?: messageCallback): Promise; - /** - * Registers a listener for messages on this channel that match the supplied filter. - * - * @param filter - A {@link MessageFilter}. - * @param listener - An event listener function. - * @returns A promise which, upon successful attachment to the channel, will be fulfilled with a {@link ChannelStateChange} object. If the channel was already attached the promise will be resolved with `null`. Upon failure, the promise will be rejected with an {@link ErrorInfo} object. - */ - subscribe(filter: MessageFilter, listener?: messageCallback): Promise; - /** - * Registers a listener for messages on this channel. The caller supplies a listener function, which is called each time one or more messages arrives on the channel. - * - * @param callback - An event listener function. - * @returns A promise which, upon successful attachment to the channel, will be fulfilled with a {@link ChannelStateChange} object. If the channel was already attached the promise will be resolved with `null`. Upon failure, the promise will be rejected with an {@link ErrorInfo} object. - */ - subscribe(callback: messageCallback): Promise; - /** - * Publishes a single message to the channel with the given event name and payload. When publish is called with this client library, it won't attempt to implicitly attach to the channel, so long as [transient publishing](https://ably.com/docs/realtime/channels#transient-publish) is available in the library. Otherwise, the client will implicitly attach. - * - * @param name - The event name. - * @param data - The message payload. - * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - publish(name: string, data: any): Promise; - /** - * Publishes an array of messages to the channel. When publish is called with this client library, it won't attempt to implicitly attach to the channel. - * - * @param messages - An array of {@link Message} objects. - * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - publish(messages: any[]): Promise; - /** - * Publish a message to the channel. When publish is called with this client library, it won't attempt to implicitly attach to the channel. - * - * @param message - A {@link Message} object. - * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - publish(message: any): Promise; - /** - * Returns a promise which is resolved when the channel reaches the specified {@link ChannelState}. If the channel is already in the specified state, the promise is resolved immediately. - * - * @param targetState - The state which should be reached. - */ - whenState(targetState: ChannelState): Promise; - } + mode?: 'cbc'; +}; +/** + * Contains the properties required to configure the encryption of {@link Message} payloads. + */ +export interface Crypto { /** - * Creates and destroys {@link ChannelBase} and {@link RealtimeChannelBase} objects. + * Generates a random key to be used in the encryption of the channel. If the language cryptographic randomness primitives are blocking or async, a callback is used. The callback returns a generated binary key. + * + * @param keyLength - The length of the key, in bits, to be generated. If not specified, this is equal to the default `keyLength` of the default algorithm: for AES this is 256 bits. + * @returns A promise which, upon success, will be fulfilled with the generated key as a binary, for example, a byte array. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. */ - class Channels { - /** - * Creates a new {@link ChannelBase} or {@link RealtimeChannelBase} object, with the specified {@link ChannelOptions}, or returns the existing channel object. - * - * @param name - The channel name. - * @param channelOptions - A {@link ChannelOptions} object. - * @returns A {@link ChannelBase} or {@link RealtimeChannelBase} object. - */ - get(name: string, channelOptions?: ChannelOptions): T; - /** - * Creates a new {@link ChannelBase} or {@link RealtimeChannelBase} object, with the specified channel {@link DeriveOptions} - * and {@link ChannelOptions}, or returns the existing channel object. - * - * @experimental This is a preview feature and may change in a future non-major release. - * This experimental method allows you to create custom realtime data feeds by selectively subscribing - * to receive only part of the data from the channel. - * See the [announcement post](https://pages.ably.com/subscription-filters-preview) for more information. - * @param name - The channel name. - * @param deriveOptions - A {@link DeriveOptions} object. - * @param channelOptions - A {@link ChannelOptions} object. - * @returns A {@link RealtimeChannelBase} object. - */ - getDerived(name: string, deriveOptions: DeriveOptions, channelOptions?: ChannelOptions): T; - /** - * Releases a {@link ChannelBase} or {@link RealtimeChannelBase} object, deleting it, and enabling it to be garbage collected. It also removes any listeners associated with the channel. To release a channel, the {@link ChannelState} must be `INITIALIZED`, `DETACHED`, or `FAILED`. - * - * @param name - The channel name. - */ - release(name: string): void; - } - + generateRandomKey(keyLength?: number): Promise; /** - * Contains an individual message that is sent to, or received from, Ably. + * Returns a {@link CipherParams} object, using the default values for any fields not supplied by the {@link CipherParamOptions} object. + * + * @param params - A {@link CipherParamOptions} object. + * @returns A {@link CipherParams} object, using the default values for any fields not supplied. */ - class Message { - /** - * Constructor for internal use. - * - * @internal - */ - constructor(); - /** - * A static factory method to create a `Message` object from a deserialized Message-like object encoded using Ably's wire protocol. - * - * @param JsonObject - A `Message`-like deserialized object. - * @param channelOptions - A {@link ChannelOptions} object. If you have an encrypted channel, use this to allow the library to decrypt the data. - * @returns A `Message` object. - */ - static fromEncoded: (JsonObject: any, channelOptions?: ChannelOptions) => Message; - /** - * A static factory method to create an array of `Message` objects from an array of deserialized Message-like object encoded using Ably's wire protocol. - * - * @param JsonArray - An array of `Message`-like deserialized objects. - * @param channelOptions - A {@link ChannelOptions} object. If you have an encrypted channel, use this to allow the library to decrypt the data. - * @returns An array of {@link Message} objects. - */ - static fromEncodedArray: (JsonArray: any[], channelOptions?: ChannelOptions) => Message[]; - /** - * The client ID of the publisher of this message. - */ - clientId: string; - /** - * The connection ID of the publisher of this message. - */ - connectionId?: string; - /** - * The message payload, if provided. - */ - data: any; - /** - * This is typically empty, as all messages received from Ably are automatically decoded client-side using this value. However, if the message encoding cannot be processed, this attribute contains the remaining transformations not applied to the `data` payload. - */ - encoding: string; - /** - * A JSON object of arbitrary key-value pairs that may contain metadata, and/or ancillary payloads. Valid payloads include `push`, `delta`, `ref` and `headers`. - */ - extras: any; - /** - * Unique ID assigned by Ably to this message. - */ - id: string; - /** - * The event name. - */ - name: string; - /** - * Timestamp of when the message was received by Ably, as milliseconds since the Unix epoch. - */ - timestamp: number; - } + getDefaultParams(params: CipherParamOptions): CipherParams; +} +/** + * Enables the management of a connection to Ably. + */ +export declare interface Connection + extends EventEmitter { /** - * Static utilities related to messages. + * An {@link ErrorInfo} object describing the last error received if a connection failure occurs. */ - interface MessageStatic { - /** - * A static factory method to create a `Message` object from a deserialized Message-like object encoded using Ably's wire protocol. - * - * @param JsonObject - A `Message`-like deserialized object. - * @param channelOptions - A {@link ChannelOptions} object. If you have an encrypted channel, use this to allow the library to decrypt the data. - * @returns A `Message` object. - */ - fromEncoded: (JsonObject: any, channelOptions?: ChannelOptions) => Message; - /** - * A static factory method to create an array of `Message` objects from an array of deserialized Message-like object encoded using Ably's wire protocol. - * - * @param JsonArray - An array of `Message`-like deserialized objects. - * @param channelOptions - A {@link ChannelOptions} object. If you have an encrypted channel, use this to allow the library to decrypt the data. - * @returns An array of {@link Message} objects. - */ - fromEncodedArray: (JsonArray: any[], channelOptions?: ChannelOptions) => Message[]; - } - + errorReason: ErrorInfo; /** - * Contains an individual presence update sent to, or received from, Ably. + * A unique public identifier for this connection, used to identify this member. */ - class PresenceMessage { - /** - * Constructor for internal use. - * - * @internal - */ - constructor(); - /** - * Decodes and decrypts a deserialized `PresenceMessage`-like object using the cipher in {@link ChannelOptions}. Any residual transforms that cannot be decoded or decrypted will be in the `encoding` property. Intended for users receiving messages from a source other than a REST or Realtime channel (for example a queue) to avoid having to parse the encoding string. - * - * @param JsonObject - The deserialized `PresenceMessage`-like object to decode and decrypt. - * @param channelOptions - A {@link ChannelOptions} object containing the cipher. - */ - static fromEncoded: (JsonObject: any, channelOptions?: ChannelOptions) => PresenceMessage; - /** - * Decodes and decrypts an array of deserialized `PresenceMessage`-like object using the cipher in {@link ChannelOptions}. Any residual transforms that cannot be decoded or decrypted will be in the `encoding` property. Intended for users receiving messages from a source other than a REST or Realtime channel (for example a queue) to avoid having to parse the encoding string. - * - * @param JsonArray - An array of deserialized `PresenceMessage`-like objects to decode and decrypt. - * @param channelOptions - A {@link ChannelOptions} object containing the cipher. - */ - static fromEncodedArray: (JsonArray: any[], channelOptions?: ChannelOptions) => PresenceMessage[]; - /** - * The type of {@link PresenceAction} the `PresenceMessage` is for. - */ - action: PresenceAction; - /** - * The ID of the client that published the `PresenceMessage`. - */ - clientId: string; - /** - * The ID of the connection associated with the client that published the `PresenceMessage`. - */ - connectionId: string; - /** - * The payload of the `PresenceMessage`. - */ - data: any; - /** - * This will typically be empty as all presence messages received from Ably are automatically decoded client-side using this value. However, if the message encoding cannot be processed, this attribute will contain the remaining transformations not applied to the data payload. - */ - encoding: string; - /** - * A JSON object of arbitrary key-value pairs that may contain metadata, and/or ancillary payloads. Valid payloads include `headers`. - */ - extras: any; - /** - * A unique ID assigned to each `PresenceMessage` by Ably. - */ - id: string; - /** - * The time the `PresenceMessage` was received by Ably, as milliseconds since the Unix epoch. - */ - timestamp: number; - } + id?: string; + /** + * A unique private connection key used to recover or resume a connection, assigned by Ably. This private connection key can also be used by other REST clients to publish on behalf of this client. See the [publishing over REST on behalf of a realtime client docs](https://ably.com/docs/rest/channels#publish-on-behalf) for more info. (If you want to explicitly recover a connection in a different SDK instance, see createRecoveryKey() instead) + */ + key?: string; + /** + * createRecoveryKey method returns a string that can be used by another client to recover this connection's state in the recover client options property. See [connection state recover options](https://ably.com/docs/connect/states?lang=javascript#connection-state-recovery) for more information. + */ + createRecoveryKey(): string | null; + /** + * The current {@link ConnectionState} of the connection. + */ + readonly state: ConnectionState; + /** + * Causes the connection to close, entering the {@link ConnectionStates.CLOSING} state. Once closed, the library does not attempt to re-establish the connection without an explicit call to {@link Connection.connect | `connect()`}. + */ + close(): void; + /** + * Explicitly calling `connect()` is unnecessary unless the `autoConnect` attribute of the {@link ClientOptions} object is `false`. Unless already connected or connecting, this method causes the connection to open, entering the {@link ConnectionStates.CONNECTING} state. + */ + connect(): void; /** - * Static utilities related to presence messages. + * When connected, sends a heartbeat ping to the Ably server and executes the callback with any error and the response time in milliseconds when a heartbeat ping request is echoed from the server. This can be useful for measuring true round-trip latency to the connected Ably server. + * + * @returns A promise which, upon success, will be fulfilled with the response time in milliseconds. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. */ - interface PresenceMessageStatic { - /** - * Decodes and decrypts a deserialized `PresenceMessage`-like object using the cipher in {@link ChannelOptions}. Any residual transforms that cannot be decoded or decrypted will be in the `encoding` property. Intended for users receiving messages from a source other than a REST or Realtime channel (for example a queue) to avoid having to parse the encoding string. - * - * @param JsonObject - The deserialized `PresenceMessage`-like object to decode and decrypt. - * @param channelOptions - A {@link ChannelOptions} object containing the cipher. - */ - fromEncoded: (JsonObject: any, channelOptions?: ChannelOptions) => PresenceMessage; - /** - * Decodes and decrypts an array of deserialized `PresenceMessage`-like object using the cipher in {@link ChannelOptions}. Any residual transforms that cannot be decoded or decrypted will be in the `encoding` property. Intended for users receiving messages from a source other than a REST or Realtime channel (for example a queue) to avoid having to parse the encoding string. - * - * @param JsonArray - An array of deserialized `PresenceMessage`-like objects to decode and decrypt. - * @param channelOptions - A {@link ChannelOptions} object containing the cipher. - */ - fromEncodedArray: (JsonArray: any[], channelOptions?: ChannelOptions) => PresenceMessage[]; + ping(): Promise; + /** + * If the connection is already in the given state, returns a promise which immediately resolves to `null`. Else, calls {@link EventEmitter.once | `once()`} to return a promise which resolves the next time the connection transitions to the given state. + * + * @param targetState - The connection state to wait for. + */ + whenState(targetState: ConnectionState): Promise; +} - /** - * Initialises a `PresenceMessage` from a `PresenceMessage`-like object. - * - * @param values - The values to intialise the `PresenceMessage` from. - * @param stringifyAction - Whether to convert the `action` field from a number to a string. - */ - fromValues(values: PresenceMessage | Record, stringifyAction?: boolean): PresenceMessage; - } +/** + * Contains application statistics for a specified time interval and time period. + */ +export declare interface Stats { + /** + * The UTC time at which the time period covered begins. If `unit` is set to `minute` this will be in the format `YYYY-mm-dd:HH:MM`, if `hour` it will be `YYYY-mm-dd:HH`, if `day` it will be `YYYY-mm-dd:00` and if `month` it will be `YYYY-mm-01:00`. + */ + intervalId: string; + /** + * For entries that are still in progress, such as the current month: the last sub-interval included in this entry (in format yyyy-mm-dd:hh:mm:ss), else undefined. + */ + inProgress?: string; + /** + * The statistics for this time interval and time period. See the JSON schema which the {@link Stats.schema | `schema`} property points to for more information. + */ + entries: Partial>; + /** + * The URL of a [JSON Schema](https://json-schema.org/) which describes the structure of this `Stats` object. + */ + schema: string; + /** + * The ID of the Ably application the statistics are for. + */ + appId: string; +} +/** + * Contains a page of results for message or presence history, stats, or REST presence requests. A `PaginatedResult` response from a REST API paginated query is also accompanied by metadata that indicates the relative queries available to the `PaginatedResult` object. + */ +export declare interface PaginatedResult { + /** + * Contains the current page of results; for example, an array of {@link InboundMessage} or {@link PresenceMessage} objects for a channel history request. + */ + items: T[]; + /** + * Returns a new `PaginatedResult` for the first page of results. + * + * @returns A promise which, upon success, will be fulfilled with a page of results for message and presence history, stats, and REST presence requests. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + first(): Promise>; + /** + * Returns a new `PaginatedResult` loaded with the next page of results. If there are no further pages, then `null` is returned. + * + * @returns A promise which, upon success, will be fulfilled with a page of results for message and presence history, stats, and REST presence requests. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + next(): Promise | null>; + /** + * Returns the `PaginatedResult` for the current page of results. + */ + current(): Promise>; /** - * Cipher Key used in {@link CipherParamOptions}. If set to a `string`, the value must be base64 encoded. + * Returns `true` if there are more pages available by calling next and returns `false` if this page is the last page available. + * + * @returns Whether or not there are more pages of results. */ - type CipherKeyParam = ArrayBuffer | Uint8Array | string; // if string must be base64-encoded + hasNext(): boolean; /** - * Typed differently depending on platform. (`WordArray` in browser, `Buffer` in node) + * Returns `true` if this page is the last page and returns `false` if there are more pages available by calling next available. * - * @internal + * @returns Whether or not this is the last page of results. */ - type CipherKey = unknown; // WordArray on browsers, Buffer on node, using unknown as - // user should not be interacting with it - output of getDefaultParams should be used opaquely + isLast(): boolean; +} +/** + * A superset of {@link PaginatedResult} which represents a page of results plus metadata indicating the relative queries available to it. `HttpPaginatedResponse` additionally carries information about the response to an HTTP request. + */ +export declare interface HttpPaginatedResponse extends PaginatedResult { /** - * Contains the properties used to generate a {@link CipherParams} object. + * The HTTP status code of the response. */ - type CipherParamOptions = { - /** - * The private key used to encrypt and decrypt payloads. - */ - key: CipherKeyParam; - /** - * The algorithm to use for encryption. Only `AES` is supported. - */ - algorithm?: 'aes'; - /** - * The length of the key in bits; for example 128 or 256. - */ - keyLength?: number; - /** - * The cipher mode. Only `CBC` is supported. - */ - mode?: 'cbc'; - }; + statusCode: number; + /** + * Whether `statusCode` indicates success. This is equivalent to `200 <= statusCode < 300`. + */ + success: boolean; + /** + * The error code if the `X-Ably-Errorcode` HTTP header is sent in the response. + */ + errorCode: number; + /** + * The error message if the `X-Ably-Errormessage` HTTP header is sent in the response. + */ + errorMessage: string; + /** + * The headers of the response. + */ + headers: any; +} +/** + * Enables a device to be registered and deregistered from receiving push notifications. + */ +export declare interface Push { /** - * Contains the properties required to configure the encryption of {@link Message} payloads. + * A {@link PushAdmin} object. */ - interface Crypto { - /** - * Generates a random key to be used in the encryption of the channel. If the language cryptographic randomness primitives are blocking or async, a callback is used. The callback returns a generated binary key. - * - * @param keyLength - The length of the key, in bits, to be generated. If not specified, this is equal to the default `keyLength` of the default algorithm: for AES this is 256 bits. - * @param callback - A function which, upon success, will be called with the generated key as a binary, for example, a byte array. Upon failure, the function will be called with information about the error. - */ - generateRandomKey(keyLength?: number, callback?: Types.StandardCallback): void; - /** - * Returns a {@link CipherParams} object, using the default values for any fields not supplied by the {@link CipherParamOptions} object. - * - * @param params - A {@link CipherParamOptions} object. - * @returns A {@link CipherParams} object, using the default values for any fields not supplied. - */ - getDefaultParams(params: CipherParamOptions): CipherParams; - } + admin: PushAdmin; +} +/** + * Enables the management of device registrations and push notification subscriptions. Also enables the publishing of push notifications to devices. + */ +export declare interface PushAdmin { /** - * The `ConnectionBase` class acts as a base class for the {@link ChannelCallbacks} and {@link ChannelPromise} classes. + * A {@link PushDeviceRegistrations} object. */ - class ConnectionBase extends EventEmitter { - /** - * An {@link ErrorInfo} object describing the last error received if a connection failure occurs. - */ - errorReason: ErrorInfo; - /** - * A unique public identifier for this connection, used to identify this member. - */ - id?: string; - /** - * A unique private connection key used to recover or resume a connection, assigned by Ably. When recovering a connection explicitly, the `recoveryKey` is used in the recover client options as it contains both the key and the last message serial. This private connection key can also be used by other REST clients to publish on behalf of this client. See the [publishing over REST on behalf of a realtime client docs](https://ably.com/docs/rest/channels#publish-on-behalf) for more info. - */ - key?: string; - /** - * The recovery key string can be used by another client to recover this connection's state in the recover client options property. See [connection state recover options](https://ably.com/docs/realtime/connection#connection-state-recover-options) for more information. - */ - recoveryKey: string | null; - /** - * The serial number of the last message to be received on this connection, used automatically by the library when recovering or resuming a connection. When recovering a connection explicitly, the `recoveryKey` is used in the recover client options as it contains both the key and the last message serial. - */ - serial: number; - /** - * The current {@link ConnectionState} of the connection. - */ - readonly state: ConnectionState; - /** - * Causes the connection to close, entering the {@link ConnectionState.CLOSING} state. Once closed, the library does not attempt to re-establish the connection without an explicit call to {@link ConnectionBase.connect | `connect()`}. - */ - close(): void; - /** - * Explicitly calling `connect()` is unnecessary unless the `autoConnect` attribute of the {@link ClientOptions} object is `false`. Unless already connected or connecting, this method causes the connection to open, entering the {@link ConnectionState.CONNECTING} state. - */ - connect(): void; - } - + deviceRegistrations: PushDeviceRegistrations; /** - * Enables the management of a connection to Ably. + * A {@link PushChannelSubscriptions} object. */ - class ConnectionCallbacks extends ConnectionBase { - /** - * When connected, sends a heartbeat ping to the Ably server and executes the callback with any error and the response time in milliseconds when a heartbeat ping request is echoed from the server. This can be useful for measuring true round-trip latency to the connected Ably server. - * - * @param callback - A function which, upon success, will be called with the response time in milliseconds. Upon failure, the function will be called with information about the error. - */ - ping(callback?: Types.StandardCallback): void; - /** - * Calls the supplied function when the connection reaches the specified {@link ConnectionState}. If the connection is already in the specified state, the callback is called immediately. - * - * @param targetState - The state which should be reached. - * @param callback - A function which will be called when the connection has reached the specified {@link ConnectionState} with a {@link ConnectionStateChange} object as the first argument. - */ - whenState(targetState: ConnectionState, callback: connectionEventCallback): void; - } - + channelSubscriptions: PushChannelSubscriptions; /** - * Enables the management of a connection to Ably. + * Sends a push notification directly to a device, or a group of devices sharing the same `clientId`. + * + * @param recipient - A JSON object containing the recipient details using `clientId`, `deviceId` or the underlying notifications service. + * @param payload - A JSON object containing the push notification payload. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. */ - class ConnectionPromise extends ConnectionBase { - /** - * When connected, sends a heartbeat ping to the Ably server and executes the callback with any error and the response time in milliseconds when a heartbeat ping request is echoed from the server. This can be useful for measuring true round-trip latency to the connected Ably server. - * - * @returns A promise which, upon success, will be fulfilled with the response time in milliseconds. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. - */ - ping(): Promise; - /** - * Returns a promise which is resolved when the connection reaches the specified {@link ConnectionState}. If the connection is already in the specified state, the promise is resolved immediately. - * - * @param targetState - The state which should be reached. - */ - whenState(targetState: ConnectionState): Promise; - } + publish(recipient: any, payload: any): Promise; +} +/** + * Enables the management of push notification registrations with Ably. + */ +export declare interface PushDeviceRegistrations { /** - * Contains application statistics for a specified time interval and time period. + * Registers or updates a {@link DeviceDetails} object with Ably. Returns the new, or updated {@link DeviceDetails} object. + * + * @param deviceDetails - The {@link DeviceDetails} object to create or update. + * @returns A promise which, upon success, will be fulfilled with a {@link DeviceDetails} object. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. */ - class Stats { - /** - * A {@link StatsMessageTypes} object containing the aggregate count of all message stats. - */ - all: StatsMessageTypes; - /** - * A {@link StatsRequestCount} object containing a breakdown of API Requests. - */ - apiRequests: StatsRequestCount; - /** - * A {@link StatsResourceCount} object containing a breakdown of channels. - */ - channels: StatsResourceCount; - /** - * A {@link StatsConnectionTypes} object containing a breakdown of connection related stats, such as min, mean and peak connections. - */ - connections: StatsConnectionTypes; - /** - * A {@link StatsMessageTraffic} object containing the aggregate count of inbound message stats. - */ - inbound: StatsMessageTraffic; - /** - * The UTC time at which the time period covered begins. If `unit` is set to `minute` this will be in the format `YYYY-mm-dd:HH:MM`, if `hour` it will be `YYYY-mm-dd:HH`, if `day` it will be `YYYY-mm-dd:00` and if `month` it will be `YYYY-mm-01:00`. - */ - intervalId: string; - /** - * A {@link StatsMessageTraffic} object containing the aggregate count of outbound message stats. - */ - outbound: StatsMessageTraffic; - /** - * A {@link StatsMessageTypes} object containing the aggregate count of persisted message stats. - */ - persisted: StatsMessageTypes; - /** - * A {@link StatsRequestCount} object containing a breakdown of Ably Token requests. - */ - tokenRequests: StatsRequestCount; - } - + save(deviceDetails: DeviceDetails): Promise; /** - * Contains a page of results for message or presence history, stats, or REST presence requests. A `PaginatedResult` response from a REST API paginated query is also accompanied by metadata that indicates the relative queries available to the `PaginatedResult` object. + * Retrieves the {@link DeviceDetails} of a device registered to receive push notifications using its `deviceId`. + * + * @param deviceId - The unique ID of the device. + * @returns A promise which, upon success, will be fulfilled with a {@link DeviceDetails} object. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. */ - class PaginatedResult { - /** - * Contains the current page of results; for example, an array of {@link Message} or {@link PresenceMessage} objects for a channel history request. - */ - items: T[]; - /** - * Returns a new `PaginatedResult` for the first page of results. - * - * @param results - A function which, upon success, will be called with a page of results for message and presence history, stats, and REST presence requests. Upon failure, the function will be called with information about the error. - */ - first(results: paginatedResultCallback): void; - /** - * Returns a new `PaginatedResult` for the first page of results. - * - * @returns A promise which, upon success, will be fulfilled with a page of results for message and presence history, stats, and REST presence requests. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. - */ - first(): Promise>; - /** - * Returns a new `PaginatedResult` loaded with the next page of results. If there are no further pages, then `null` is returned. - * - * @param results - A function which, upon success, will be fulfilled with a page of results for message and presence history, stats, and REST presence requests. Upon failure, the function will be called with information about the error. - */ - next(results: StandardCallback | null>): void; - /** - * Returns a new `PaginatedResult` loaded with the next page of results. If there are no further pages, then `null` is returned. - * - * @returns A promise which, upon success, will be fulfilled with a page of results for message and presence history, stats, and REST presence requests. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. - */ - next(): Promise | null>; - /** - * Returns the `PaginatedResult` for the current page of results. - * - * @param results - A function which, upon success, will be fulfilled with a page of results for message and presence history, stats, and REST presence requests. Upon failure, the function will be called with information about the error. - */ - current(results: paginatedResultCallback): void; - /** - * Returns the `PaginatedResult` for the current page of results. - */ - current(): Promise>; - /** - * Returns `true` if there are more pages available by calling next and returns `false` if this page is the last page available. - * - * @returns Whether or not there are more pages of results. - */ - hasNext(): boolean; - /** - * Returns `true` if this page is the last page and returns `false` if there are more pages available by calling next available. - * - * @returns Whether or not this is the last page of results. - */ - isLast(): boolean; - } - + get(deviceId: string): Promise; /** - * A superset of {@link Types.PaginatedResult} which represents a page of results plus metadata indicating the relative queries available to it. `HttpPaginatedResponse` additionally carries information about the response to an HTTP request. + * Retrieves the {@link DeviceDetails} of a device registered to receive push notifications using the `id` property of a {@link DeviceDetails} object. + * + * @param deviceDetails - The {@link DeviceDetails} object containing the `id` property of the device. + * @returns A promise which, upon success, will be fulfilled with a {@link DeviceDetails} object. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. */ - class HttpPaginatedResponse extends PaginatedResult { - /** - * The HTTP status code of the response. - */ - statusCode: number; - /** - * Whether `statusCode` indicates success. This is equivalent to `200 <= statusCode < 300`. - */ - success: boolean; - /** - * The error code if the `X-Ably-Errorcode` HTTP header is sent in the response. - */ - errorCode: number; - /** - * The error message if the `X-Ably-Errormessage` HTTP header is sent in the response. - */ - errorMessage: string; - /** - * The headers of the response. - */ - headers: any; - } - + get(deviceDetails: DeviceDetails): Promise; /** - * Enables a device to be registered and deregistered from receiving push notifications. + * Retrieves all devices matching the filter `params` provided. Returns a {@link PaginatedResult} object, containing an array of {@link DeviceDetails} objects. + * + * @param params - An object containing key-value pairs to filter devices by. + * @returns A promise which, upon success, will be fulfilled with a {@link PaginatedResult} object containing an array of {@link DeviceDetails} objects. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. */ - class PushCallbacks { - /** - * A {@link PushAdminCallbacks} object. - */ - admin: PushAdminCallbacks; - } - + list(params: DeviceRegistrationParams): Promise>; /** - * Enables a device to be registered and deregistered from receiving push notifications. + * Removes a device registered to receive push notifications from Ably using its `deviceId`. + * + * @param deviceId - The unique ID of the device. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. */ - class PushPromise { - /** - * A {@link PushAdminPromise | `PushAdmin`} object. - */ - admin: PushAdminPromise; - } - + remove(deviceId: string): Promise; /** - * Enables the management of device registrations and push notification subscriptions. Also enables the publishing of push notifications to devices. + * Removes a device registered to receive push notifications from Ably using the `id` property of a {@link DeviceDetails} object. + * + * @param deviceDetails - The {@link DeviceDetails} object containing the `id` property of the device. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. */ - class PushAdminCallbacks { - /** - * A {@link PushDeviceRegistrationsCallbacks} object. - */ - deviceRegistrations: PushDeviceRegistrationsCallbacks; - /** - * A {@link PushChannelSubscriptionsCallbacks} object. - */ - channelSubscriptions: PushChannelSubscriptionsCallbacks; - /** - * Sends a push notification directly to a device, or a group of devices sharing the same `clientId`. - * - * @param recipient - A JSON object containing the recipient details using `clientId`, `deviceId` or the underlying notifications service. - * @param payload - A JSON object containing the push notification payload. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - publish(recipient: any, payload: any, callback?: errorCallback): void; - } - + remove(deviceDetails: DeviceDetails): Promise; /** - * Enables the management of device registrations and push notification subscriptions. Also enables the publishing of push notifications to devices. + * Removes all devices registered to receive push notifications from Ably matching the filter `params` provided. + * + * @param params - An object containing key-value pairs to filter devices by. This object’s {@link DeviceRegistrationParams.limit} property will be ignored. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. */ - class PushAdminPromise { - /** - * A {@link PushDeviceRegistrationsPromise} object. - */ - deviceRegistrations: PushDeviceRegistrationsPromise; - /** - * A {@link PushChannelSubscriptionsPromise} object. - */ - channelSubscriptions: PushChannelSubscriptionsPromise; - /** - * Sends a push notification directly to a device, or a group of devices sharing the same `clientId`. - * - * @param recipient - A JSON object containing the recipient details using `clientId`, `deviceId` or the underlying notifications service. - * @param payload - A JSON object containing the push notification payload. - * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - publish(recipient: any, payload: any): Promise; - } + removeWhere(params: DeviceRegistrationParams): Promise; +} +/** + * Enables device push channel subscriptions. + */ +export declare interface PushChannelSubscriptions { /** - * Enables the management of push notification registrations with Ably. + * Subscribes a device, or a group of devices sharing the same `clientId` to push notifications on a channel. Returns a {@link PushChannelSubscription} object. + * + * @param subscription - A {@link PushChannelSubscription} object. + * @returns A promise which, upon success, will be fulfilled with a {@link PushChannelSubscription} object describing the new or updated subscriptions. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. */ - class PushDeviceRegistrationsCallbacks { - /** - * Registers or updates a {@link DeviceDetails} object with Ably. Returns the new, or updated {@link DeviceDetails} object. - * - * @param deviceDetails - The {@link DeviceDetails} object to create or update. - * @param callback - A function which, upon success, will be called with a {@link DeviceDetails} object. Upon failure, the function will be called with information about the error. - */ - save(deviceDetails: DeviceDetails, callback?: Types.StandardCallback): void; - /** - * Retrieves the {@link DeviceDetails} of a device registered to receive push notifications using its `deviceId`. - * - * @param deviceId - The unique ID of the device. - * @param callback - A function which, upon success, will be called with a {@link DeviceDetails} object. Upon failure, the function will be called with information about the error. - */ - get(deviceId: string, callback: Types.StandardCallback): void; - /** - * Retrieves the {@link DeviceDetails} of a device registered to receive push notifications using the `id` property of a {@link DeviceDetails} object. - * - * @param deviceDetails - The {@link DeviceDetails} object containing the `id` property of the device. - * @param callback - A function which, upon success, will be called with a {@link DeviceDetails} object. Upon failure, the function will be called with information about the error. - */ - get(deviceDetails: DeviceDetails, callback: Types.StandardCallback): void; - /** - * Retrieves all devices matching the filter `params` provided. Returns a {@link Types.PaginatedResult} object, containing an array of {@link DeviceDetails} objects. - * - * @param params - An object containing key-value pairs to filter devices by. - * @param callback - A function which, upon success, will be called with a {@link Types.PaginatedResult} object containing an array of {@link DeviceDetails} objects. Upon failure, the function will be called with information about the error. - */ - list(params: DeviceRegistrationParams, callback: paginatedResultCallback): void; - /** - * Removes a device registered to receive push notifications from Ably using its `deviceId`. - * - * @param deviceId - The unique ID of the device. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - remove(deviceId: string, callback?: errorCallback): void; - /** - * Removes a device registered to receive push notifications from Ably using the `id` property of a {@link DeviceDetails} object. - * - * @param deviceDetails - The {@link DeviceDetails} object containing the `id` property of the device. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - remove(deviceDetails: DeviceDetails, callback?: errorCallback): void; - /** - * Removes all devices registered to receive push notifications from Ably matching the filter `params` provided. - * - * @param params - An object containing key-value pairs to filter devices by. This object’s {@link DeviceRegistrationParams.limit} property will be ignored. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - removeWhere(params: DeviceRegistrationParams, callback?: errorCallback): void; - } - + save(subscription: PushChannelSubscription): Promise; /** - * Enables the management of push notification registrations with Ably. + * Retrieves all push channel subscriptions matching the filter `params` provided. Returns a {@link PaginatedResult} object, containing an array of {@link PushChannelSubscription} objects. + * + * @param params - An object containing key-value pairs to filter subscriptions by. + * @returns A promise which, upon success, will be fulfilled with a {@link PaginatedResult} object containing an array of {@link PushChannelSubscription} objects. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. */ - class PushDeviceRegistrationsPromise { - /** - * Registers or updates a {@link DeviceDetails} object with Ably. Returns the new, or updated {@link DeviceDetails} object. - * - * @param deviceDetails - The {@link DeviceDetails} object to create or update. - * @returns A promise which, upon success, will be fulfilled with a {@link DeviceDetails} object. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. - */ - save(deviceDetails: DeviceDetails): Promise; - /** - * Retrieves the {@link DeviceDetails} of a device registered to receive push notifications using its `deviceId`. - * - * @param deviceId - The unique ID of the device. - * @returns A promise which, upon success, will be fulfilled with a {@link DeviceDetails} object. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. - */ - get(deviceId: string): Promise; - /** - * Retrieves the {@link DeviceDetails} of a device registered to receive push notifications using the `id` property of a {@link DeviceDetails} object. - * - * @param deviceDetails - The {@link DeviceDetails} object containing the `id` property of the device. - * @returns A promise which, upon success, will be fulfilled with a {@link DeviceDetails} object. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. - */ - get(deviceDetails: DeviceDetails): Promise; - /** - * Retrieves all devices matching the filter `params` provided. Returns a {@link Types.PaginatedResult} object, containing an array of {@link DeviceDetails} objects. - * - * @param params - An object containing key-value pairs to filter devices by. - * @returns A promise which, upon success, will be fulfilled with a {@link Types.PaginatedResult} object containing an array of {@link DeviceDetails} objects. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. - */ - list(params: DeviceRegistrationParams): Promise>; - /** - * Removes a device registered to receive push notifications from Ably using its `deviceId`. - * - * @param deviceId - The unique ID of the device. - * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - remove(deviceId: string): Promise; - /** - * Removes a device registered to receive push notifications from Ably using the `id` property of a {@link DeviceDetails} object. - * - * @param deviceDetails - The {@link DeviceDetails} object containing the `id` property of the device. - * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - remove(deviceDetails: DeviceDetails): Promise; - /** - * Removes all devices registered to receive push notifications from Ably matching the filter `params` provided. - * - * @param params - An object containing key-value pairs to filter devices by. This object’s {@link DeviceRegistrationParams.limit} property will be ignored. - * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - removeWhere(params: DeviceRegistrationParams): Promise; - } - + list(params: PushChannelSubscriptionParams): Promise>; /** - * Enables device push channel subscriptions. + * Retrieves all channels with at least one device subscribed to push notifications. Returns a {@link PaginatedResult} object, containing an array of channel names. + * + * @param params - An object containing key-value pairs to filter channels by. + * @returns A promise which, upon success, will be fulfilled with a {@link PaginatedResult} object containing an array of channel names. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. */ - class PushChannelSubscriptionsCallbacks { - /** - * Subscribes a device, or a group of devices sharing the same `clientId` to push notifications on a channel. Returns a {@link PushChannelSubscription} object. - * - * @param subscription - A {@link PushChannelSubscription} object. - * @param callback - A function which, upon success, will be called with a {@link PushChannelSubscription} object describing the new or updated subscriptions. Upon failure, the function will be called with information about the error. - */ - save(subscription: PushChannelSubscription, callback?: Types.StandardCallback): void; - /** - * Retrieves all push channel subscriptions matching the filter `params` provided. Returns a {@link Types.PaginatedResult} object, containing an array of {@link PushChannelSubscription} objects. - * - * @param params - An object containing key-value pairs to filter subscriptions by. - * @param callback - A function which, upon success, will be called with a {@link Types.PaginatedResult} object containing an array of {@link PushChannelSubscription} objects. Upon failure, the function will be called with information about the error. - */ - list(params: PushChannelSubscriptionParams, callback: paginatedResultCallback): void; - /** - * Retrieves all channels with at least one device subscribed to push notifications. Returns a {@link Types.PaginatedResult} object, containing an array of channel names. - * - * @param params - An object containing key-value pairs to filter channels by. - * @param callback - A function which, upon success, will be called with a {@link Types.PaginatedResult} object containing an array of channel names. Upon failure, the function will be called with information about the error. - */ - listChannels(params: PushChannelsParams, callback: paginatedResultCallback): void; - /** - * Unsubscribes a device, or a group of devices sharing the same `clientId` from receiving push notifications on a channel. - * - * @param subscription - A {@link PushChannelSubscription} object. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - remove(subscription: PushChannelSubscription, callback?: errorCallback): void; - /** - * Unsubscribes all devices from receiving push notifications on a channel that match the filter `params` provided. - * - * @param params - An object containing key-value pairs to filter subscriptions by. Can contain `channel`, and optionally either `clientId` or `deviceId`. - * @param callback - A function which will be called upon completion of the operation. If the operation succeeded, then the function will be called with `null`. If it failed, the function will be called with information about the error. - */ - removeWhere(params: PushChannelSubscriptionParams, callback?: errorCallback): void; - } - + listChannels(params: PushChannelsParams): Promise>; /** - * Enables device push channel subscriptions. + * Unsubscribes a device, or a group of devices sharing the same `clientId` from receiving push notifications on a channel. + * + * @param subscription - A {@link PushChannelSubscription} object. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. */ - class PushChannelSubscriptionsPromise { - /** - * Subscribes a device, or a group of devices sharing the same `clientId` to push notifications on a channel. Returns a {@link PushChannelSubscription} object. - * - * @param subscription - A {@link PushChannelSubscription} object. - * @returns A promise which, upon success, will be fulfilled with a {@link PushChannelSubscription} object describing the new or updated subscriptions. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. - */ - save(subscription: PushChannelSubscription): Promise; - /** - * Retrieves all push channel subscriptions matching the filter `params` provided. Returns a {@link Types.PaginatedResult} object, containing an array of {@link PushChannelSubscription} objects. - * - * @param params - An object containing key-value pairs to filter subscriptions by. - * @returns A promise which, upon success, will be fulfilled with a {@link Types.PaginatedResult} object containing an array of {@link PushChannelSubscription} objects. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. - */ - list(params: PushChannelSubscriptionParams): Promise>; - /** - * Retrieves all channels with at least one device subscribed to push notifications. Returns a {@link Types.PaginatedResult} object, containing an array of channel names. - * - * @param params - An object containing key-value pairs to filter channels by. - * @returns A promise which, upon success, will be fulfilled with a {@link Types.PaginatedResult} object containing an array of channel names. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. - */ - listChannels(params: PushChannelsParams): Promise>; - /** - * Unsubscribes a device, or a group of devices sharing the same `clientId` from receiving push notifications on a channel. - * - * @param subscription - A {@link PushChannelSubscription} object. - * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - remove(subscription: PushChannelSubscription): Promise; - /** - * Unsubscribes all devices from receiving push notifications on a channel that match the filter `params` provided. - * - * @param params - An object containing key-value pairs to filter subscriptions by. Can contain `channel`, and optionally either `clientId` or `deviceId`. - * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. - */ - removeWhere(params: PushChannelSubscriptionParams): Promise; - } + remove(subscription: PushChannelSubscription): Promise; + /** + * Unsubscribes all devices from receiving push notifications on a channel that match the filter `params` provided. + * + * @param params - An object containing key-value pairs to filter subscriptions by. Can contain `channel`, and optionally either `clientId` or `deviceId`. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. + */ + removeWhere(params: PushChannelSubscriptionParams): Promise; } /** * A client that offers a simple stateless API to interact directly with Ably's REST API. */ -export declare class Rest extends Types.RestCallbacks {} +export declare class Rest implements RestClient { + /** + * Construct a client object using an Ably {@link ClientOptions} object. + * + * @param options - A {@link ClientOptions} object to configure the client connection to Ably. + */ + constructor(options: ClientOptions); + /** + * Constructs a client object using an Ably API key or token string. + * + * @param keyOrToken - The Ably API key or token string used to validate the client. + */ + constructor(keyOrToken: string); + /** + * The cryptographic functions available in the library. + */ + static Crypto: Crypto; + /** + * Static utilities related to messages. + */ + static Message: MessageStatic; + /** + * Static utilities related to presence messages. + */ + static PresenceMessage: PresenceMessageStatic; + + // Requirements of RestClient + + auth: Auth; + channels: Channels; + request( + method: string, + path: string, + version: number, + params?: any, + body?: any[] | any, + headers?: any, + ): Promise>; + stats(params?: StatsParams): Promise>; + time(): Promise; + batchPublish(spec: BatchPublishSpec): Promise>; + batchPublish( + specs: BatchPublishSpec[], + ): Promise[]>; + batchPresence(channels: string[]): Promise[]>; + push: Push; +} /** * A client that extends the functionality of {@link Rest} and provides additional realtime-specific features. */ -export declare class Realtime extends Types.RealtimeCallbacks {} +export declare class Realtime implements RealtimeClient { + /** + * Construct a client object using an Ably {@link ClientOptions} object. + * + * @param options - A {@link ClientOptions} object to configure the client connection to Ably. + */ + constructor(options: ClientOptions); + /** + * Constructs a client object using an Ably API key or token string. + * + * @param keyOrToken - The Ably API key or token string used to validate the client. + */ + constructor(keyOrToken: string); + /** + * The cryptographic functions available in the library. + */ + static Crypto: Crypto; + /** + * Static utilities related to messages. + */ + static Message: MessageStatic; + /** + * Static utilities related to presence messages. + */ + static PresenceMessage: PresenceMessageStatic; + + // Requirements of RealtimeClient + + clientId: string; + close(): void; + connect(): void; + auth: Auth; + channels: Channels; + connection: Connection; + request( + method: string, + path: string, + version: number, + params?: any, + body?: any[] | any, + headers?: any, + ): Promise>; + stats(params?: StatsParams): Promise>; + time(): Promise; + batchPublish(spec: BatchPublishSpec): Promise>; + batchPublish( + specs: BatchPublishSpec[], + ): Promise[]>; + batchPresence(channels: string[]): Promise[]>; + push: Push; +} /** * A generic Ably error object that contains an Ably-specific status code, and a generic status code. Errors returned from the Ably server are compatible with the `ErrorInfo` structure and should result in errors that inherit from `ErrorInfo`. */ -export declare class ErrorInfo extends Types.ErrorInfo {} +export declare class ErrorInfo { + /** + * Ably [error code](https://github.com/ably/ably-common/blob/main/protocol/errors.json). + */ + code: number; + /** + * Additional message information, where available. + */ + message: string; + /** + * HTTP Status Code corresponding to this error, where applicable. + */ + statusCode: number; + /** + * The underlying cause of the error, where applicable. + */ + cause?: string | Error | ErrorInfo; + + /** + * Construct an ErrorInfo object. + * + * @param message - A string describing the error. + * @param code - Ably [error code](https://github.com/ably/ably-common/blob/main/protocol/errors.json). + * @param statusCode - HTTP Status Code corresponding to this error. + * @param cause - The underlying cause of the error. + */ + constructor(message: string, code: number, statusCode: number, cause?: string | Error | ErrorInfo); +} diff --git a/callbacks.d.ts b/callbacks.d.ts deleted file mode 100644 index e0a434a3dd..0000000000 --- a/callbacks.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -import Ably = require('./ably'); -export = Ably; diff --git a/callbacks.js b/callbacks.js deleted file mode 100644 index 8bfe2c458f..0000000000 --- a/callbacks.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; -/* When returning promises becomes the default in some future version, this - * file will start modifying the exports as promises.js does today. - * Please note that the file imported below is only generated after running - * the build task. */ -module.exports = require('./build/ably-node'); diff --git a/docs/landing-pages/choose-library.html b/docs/landing-pages/choose-library.html deleted file mode 100644 index c4243efaf1..0000000000 --- a/docs/landing-pages/choose-library.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - Ably JavaScript Client Library SDK API Reference - - -

Ably JavaScript Client Library SDK API Reference

- -

- The JavaScript Client Library SDK supports a realtime and a REST interface. The JavaScript API references are generated from the Ably JavaScript Client Library SDK source code using TypeDoc and structured by classes. -

- -

- The realtime interface enables a client to maintain a persistent connection to Ably and publish, subscribe and be present on channels. The REST interface is stateless and typically implemented server-side. It is used to make requests such as retrieving statistics, token authentication and publishing to a channel. -

- -

- There are two API variants of the Ably JavaScript Client Library SDK: - -

-

- -

- View the Ably docs for conceptual information on using Ably, and for API references featuring all languages. The combined API references are organized by features and split between the realtime and REST interfaces. -

- - diff --git a/docs/landing-pages/default.md b/docs/landing-pages/default.md deleted file mode 100644 index 994b06cd18..0000000000 --- a/docs/landing-pages/default.md +++ /dev/null @@ -1,7 +0,0 @@ -# Ably JavaScript Client Library SDK API Reference for Callbacks - -You are currently viewing the callback-based variant of the Ably JavaScript Client Library SDK. View the promise-based variant [here](../promises/index.html). - -The callback-based variant of the API implements the common Node.js error-first callback style. - -To get started with the Ably JavaScript Client Library SDK, follow the [Quickstart Guide](https://ably.com/docs/quick-start-guide) or view the introductions to the [realtime](https://ably.com/docs/realtime/usage) and [REST](https://ably.com/docs/rest/usage) interfaces. diff --git a/docs/landing-pages/promises.md b/docs/landing-pages/promises.md deleted file mode 100644 index c312a44660..0000000000 --- a/docs/landing-pages/promises.md +++ /dev/null @@ -1,7 +0,0 @@ -# Ably JavaScript Client Library SDK API Reference for Promises - -You are currently viewing the promise-based variant of the Ably JavaScript Client Library SDK. View the callback-based variant [here](../default/index.html). - -Passing callbacks to methods when using the promise-based variant of the API is still possible, however, if a method does not receive a callback then it will instead return a promise. - -To get started with the Ably JavaScript Client Library SDK, follow the [Quickstart Guide](https://ably.com/docs/quick-start-guide) or view the introductions to the [realtime](https://ably.com/docs/realtime/usage) and [REST](https://ably.com/docs/rest/usage) interfaces. diff --git a/docs/react-migration-guide.md b/docs/migration-guides/v1/react-hooks.md similarity index 73% rename from docs/react-migration-guide.md rename to docs/migration-guides/v1/react-hooks.md index 156f2158b5..cf39b9bdf3 100644 --- a/docs/react-migration-guide.md +++ b/docs/migration-guides/v1/react-hooks.md @@ -1,6 +1,6 @@ # React hooks upgrade / migration guide -## Version 2.x to 3.x +## `@ably-labs/react-hooks` to `ably 1.x` ### Hooks now return object @@ -9,12 +9,14 @@ Since these hooks now return more values we've opted to change them to return ob You can still access the return values using simple destructuring syntax like in the below example: ```jsx -const { channel, ably } = useChannel("your-channel-name", (message) => { /* ... */ }); +const { channel, ably } = useChannel('your-channel-name', (message) => { + /* ... */ +}); -const { presenceData, updateStatus } = usePresence("your-channel-name"); +const { presenceData, updateStatus } = usePresence('your-channel-name'); ``` -### Replacing `configureAbly` with `AblyProvider` +### Replacing `configureAbly` with `AblyProvider` In versions 1 and 2 of our react-hooks, we exported a function called `configureAbly` which was used to register an Ably client instance to global state. This caused a few issues (most notably it made the hooks difficult to use with hot module reloading), so we have replaced the global configuration function with a context provider (`AblyProvider`) @@ -22,29 +24,32 @@ The simplest way to use the context provider is to create your own ably-js clien All child components of the `AblyProvider` will then be able to use the hooks, making use of the provided Ably client instance. For this reason, we recommend putting the `AblyProvider` high up in your component tree, surrounding all components which may need to use Ably hooks. For example, replace this: + ```jsx configureAbly(options); ``` With this: + ```jsx -const client = new Ably.Realtime.Promise(options); +const client = new Ably.Realtime(options); -return - {children} - +return {children}; ``` If you were already using multiple Ably clients in the same react application, you may pass in an optional `id` prop to the provider, which you can then pass to the hooks to specify which Ably client instance the hook should use: + ```jsx -const client = new Ably.Realtime.Promise(options); +const client = new Ably.Realtime(options); -return - {children} - +return ( + + {children} + +); // in a child component: -useChannel({channelName: 'my_channel', id: 'foo'}, (msg) => { +useChannel({ channelName: 'my_channel', id: 'foo' }, (msg) => { console.log(msg); }); ``` diff --git a/docs/migration-guides/v2/lib.md b/docs/migration-guides/v2/lib.md new file mode 100644 index 0000000000..b0ae1dd788 --- /dev/null +++ b/docs/migration-guides/v2/lib.md @@ -0,0 +1,412 @@ +# Migration guide for ably-js v2 + +Here’s how to migrate from ably-js v1 to v2: + +1. [Stop using functionality that v1 deprecated and which v2 removes](#stop-using-v1-deprecated). +2. [Stop using other functionality that v2 removes](#stop-using-v2-removed). +3. [Update to v2 and handle its breaking changes](#update-to-v2-and-handle-breaking-changes). +4. (Optional) [Stop using functionality that v2 deprecates](#stop-using-v2-deprecated). +5. (Optional) [Take advantage of new features that v2 introduces](#use-v2-new-features). + +

Stop using functionality that v1 deprecated and which v2 removes

+ +Begin by updating to ably-js 1.2.50 or later, to make sure you see the deprecation log messages [described below](#stop-using-v1-deprecated-general). + +Now, you need to stop using the functionality that is deprecated by v1 and which is removed in v2. Here we explain how. + +The changes below are split into: + +- general changes +- changes that only affect TypeScript users + +If you’re not using any of the deprecated functionality described below, then this section does not affect you. + +

General changes

+ +In ably-js 1.2.50 or later, use of the following APIs will trigger a deprecation warning at runtime. + +**Note about deprecation warnings:** These deprecation warnings take the form of error-level log messages emitted through the library’s logging mechanism (i.e. `ClientOptions.log.handler` or `ClientOptions.logHandler` if you’ve set these properties, or else `console.log()`). To find them in your logs, search for `Ably: Deprecation warning`. + +- The `log` client option has been removed in v2. Equivalent functionality is provided by the `logLevel` and `logHandler` client options. Update your client options code of the form `{ log: { level: logLevel, handler: logHandler } }` to instead be `{ logLevel, logHandler }`. +- Changes to `Crypto.getDefaultParams()`: + - The ability to pass a callback to this method has been removed in v2. This method now directly returns its result, instead of returning it asynchronously. Update your code so that it uses the return value of this method instead of passing a callback. + - The ability to call this method without specifying an encryption key has been removed in v2. Update your code so that it instead passes an object whose `key` property contains an encryption key. That is, replace `Crypto.getDefaultParams()` with `Crypto.getDefaultParams({ key })`, where `key` is an encryption key that you have generated (for example from the `Crypto.generateRandomKey()` method). + - The ability to pass the encryption key as the first argument of this method has been removed in v2. Update your code so that it instead passes an object whose `key` property contains the key. That is, replace `Crypto.getDefaultParams(key)` with `Crypto.getDefaultParams({ key })`. +- The `fallbackHostsUseDefault` client option has been removed in v2. + - If you’re using this client option to force the library to make use of fallback hosts even though you’ve set the `environment` client option, then this is no longer necessary: remove your usage of the `fallbackHostsUseDefault` client option and the library will then automatically choose the correct fallback hosts to use for the specified environment. + - If you’re using this client option to force the library to make use of fallback hosts even though you’re not using the primary Ably environment, then stop using `fallbackHostsUseDefault`, and update your code to either pass the `environment` client option (in which case the library will automatically choose the correct fallback hosts to use for the specified environment), or to pass the `fallbackHosts` client option to specify a custom list of fallback hosts to use (for example, if you’re using a custom CNAME, in which case Ably will have provided you with an explicit list of fallback hosts). +- The ability to use a boolean value for the `recover` client option has been removed in v2. If you wish for the connection to always be recovered, replace `{ recover: true }` with a function that always passes `true` to its callback: `{ recover: function(lastConnectionDetails, cb) { cb(true); } }`. +- The ability to pass an array of channel mode flags as the first argument of `RealtimeChannel.attach()` has been removed in v2. To set channel mode flags, populate the `modes` property of the channel options object that you pass to `Channels.get()` or `RealtimeChannel.setOptions()`. +- The `force` auth option has been removed in v2. If you’re using this option to force `authorize()` to fetch a new token even if the current token has not expired, this is no longer necessary, as `authorize()` now always fetches a new token. Update your code to no longer pass the `force` auth option. Note that, in general, passing an auth options argument to `authorize()` will overwrite the library’s stored auth options, which may not be what you want. In v1, the library contains a special case behavior where passing an auth options object which only contains `{ force: true }` will _not_ overwrite the stored options. This special case behavior has been removed in v2, so if you’re currently passing `authorize()` an auth options object which only contains `{ force: true }`, you should stop passing it an auth options object entirely. +- The `host` client option has been renamed to `restHost`. Update your code to use `restHost`. +- The `wsHost` client option has been renamed to `realtimeHost`. Update your code to use `realtimeHost`. +- The `queueEvents` client option has been renamed to `queueMessages`. Update your code to use `queueMessages`. +- `RealtimePresence`’s `on` method has been renamed to `subscribe`. Update your code to use `subscribe`. +- `RealtimePresence`’s `off` method has been renamed to `unsubscribe`. Update your code to use `unsubscribe`. +- `Auth`’s `authorise` method has been renamed to `authorize`. Update your code to use `authorize`. +- The `headers` client option has been removed in v2. Remove your use of this client option. + +### Only TypeScript users + +- In v2, the `stats()` method on `Rest` and `Realtime` no longer accepts an argument of type `any`. Make sure that any argument you pass to this method implements the `StatsParams` interface. +- In v2, `fromEncoded` and `fromEncodedArray` types, which were already not being used by the library, are no longer exported by the library. Remove your references to these types. + +

Stop using other functionality that v2 removes

+ +

Switch from the callbacks-based variant of the library to the promise-based variant

+ +**Note:** This section is only relevant if you’re not already using v1’s promise-based API. + +ably-js v1 offered a choice between styles of asynchronous programming. There was a variant of the library that implemented asynchronous function calls via callbacks, and another that implemented them via promises. Now that promises are widely supported across modern JavaScript engines, ably-js v2 removes the callbacks variant of the library, and only offers promise-based asynchronicity. + +So, if you’re currently using the callbacks variant of the library, then before upgrading to v2 you should switch to using the promises variant. Here, we explain how to do this. + +#### Choose the promises variant of the library instead of the callbacks variant + +First, you should stop choosing the callbacks variant of the library, and instead choose the promises variant. How exactly you should change your code to achieve this depends on how you’re currently choosing the callbacks variant: + +- If you’re implicitly choosing the callbacks variant of the library by writing `require('ably')`, then update your code to `require('ably/promises')`. +- If you’re explicitly choosing the callbacks variant through the subpath of the imported module — that is, if you’re writing `require('ably/callbacks')` — then update your code to `require('ably/promises')`. +- If you’re explicitly choosing the callbacks variant at the moment of instantiating the client — that is, if you’re writing `new Ably.Rest.Callbacks(…)` or `new Ably.Realtime.Callbacks(…)` — then update your code to use the `Promise` property instead, i.e. write `new Ably.Realtime.Promise(…)` or `new Ably.Rest.Promise(…)`. + +#### Update your code to use the promise-based API + +Now, update your code to use ably-js’s promise-based API instead of the callback-based API. The best way to do this is to consult the [documentation for v1’s promise-based API](https://ably.com/docs/sdk/js/v1.2/promises), to find the documentation for the promise-based version of each method that you’re using in your code. If you don’t want to do this, it’s generally sufficient to understand that the promise-based API differs from the callback-based API in a way that’s consistent across all methods. What follows is a description of this difference. + +Given a method which, in the callback-based API, takes a callback of the form `(err, result)` as its final argument, its equivalent in the promise-based API does not take this final argument. Instead, it returns a promise. If the operation succeeds then this promise will be resolved with a value equivalent to the callback’s `result` argument, and if it fails then this promise will be rejected with a value equivalent to the callback’s `err` argument. + +So, you need to update your code to stop passing this callback, and then make use of the returned promise. In general, JavaScript offers a couple of approaches for interacting with promises, and so now we’ll demonstrate how these apply to an example ably-js method call. + +For example, given the following call to the callbacks-based version of `RestChannel.history()`: + +```javascript +channel.history({ direction: 'forwards' }, (err, paginatedResult) => { + if (err) { + // Perform some sort of error handling + return; + } + + // Make use of paginatedResult +}); +``` + +We could do one of the following: + +1. Use JavaScript’s `await` keyword to retrieve the result of the operation, combined with the `catch` keyword for error handling: + + ```javascript + try { + const paginatedResult = await channel.history({ direction: 'forwards' }); + // Make use of paginatedResult + } catch (err) { + // Perform some sort of error handling + } + ``` + +2. Use the promise’s `then` method to retrieve the result of the operation, combined with its `catch` method for error handling: + + ```javascript + channel + .history({ direction: 'forwards' }) + .then((paginatedResult) => { + // Make use of paginatedResult + }) + .catch((err) => { + // Perform some sort of error handling + }); + ``` + +#### A caveat regarding `Crypto.generateRandomKey()` + +**Important:** For historical reasons, the `Crypto.generateRandomKey()` method does not have a promise-based version in v1. That is, even in the promise-based variant of the SDK, it implements asynchronicity via callbacks. So, if you’re using this method, then you’ll need to keep using the callback-based version of this method until upgrading to v2, which replaces the callback-based variant of this method with a promise-based one. For more information see [here](#switch-to-promise-based-generateRandomKey). + +

Update to v2 and handle its breaking changes

+ +Next, update to ably-js version 2.0.0 or later. + +Now, you need to address the other breaking changes introduced by v2. Here we explain how. + +The changes below are split into: + +- general changes +- changes that only affect TypeScript users + +Some of these changes are only relevant if you’re using specific features of the library. The guidance below makes it clear when this is the case. + +### General changes + +#### Stop explicitly selecting the promise-based variant of the library + +As [mentioned above](#switch-from-callbacks-to-promises), v2 of the library no longer offers a choice between a callbacks-based API and a promises-based API. This means that v1’s mechanism for choosing which variant to use has been removed, so you should stop using this mechanism. + +- If you’re explicitly choosing the promises variant of the library through the subpath of the imported module — that is, if you’re writing `require('ably/promises')` — then update your code to `require('ably')`. +- If you’re explicitly choosing the promises variant at the moment of instantiating the client — that is, if you’re writing `new Ably.Rest.Promise(…)` or `new Ably.Realtime.Promise(…)` — then update your code to remove the use of the `Promise` property, i.e. write `new Ably.Realtime(…)` or `new Ably.Rest(…)`. + +

Be aware of platforms that are no longer supported

+ +v2 of the library drops support for some platforms that v1 supported. Most notably, it no longer supports Internet Explorer or Node.js versions below 16. For more information, see [this section of the Readme](../../../README.md#supported-platforms). + +#### Be aware of a new endpoint to add to firewall whitelists + +**Note:** This change is most likely to affect you if you’re already explicitly configuring your firewall (or instructing your users to configure their firewall) to allow realtime connections to Ably, as described in [this FAQ on ably.com](https://faqs.ably.com/if-i-need-to-whitelist-ablys-servers-from-a-firewall-which-ports-ips-and/or-domains-should-i-add). + +When attempting to establish a realtime connection using WebSocket, v2 uses the `wss://ws-up.ably-realtime.com` endpoint to check if WebSocket connectivity is available. Update your firewall whitelist to allow connectivity to that endpoint. + +#### Be aware of changes affecting environments where WebSocket connections may be blocked + +In v1 of ably-js, realtime clients with multiple available transports would initially attempt to connect with the transport most likely to succeed (`xhr_polling` in web browsers). Upon the success of this initial connection, they would subsequently attempt to "upgrade" to a preferable transport such as WebSocket. + +This behaviour has been changed in v2. Now, realtime clients will instead first attempt to connect to Ably using WebSocket, and only failover to alternative transports if this WebSocket connection attempt fails. For the vast majority of users this will result in a smoother initial connection sequence, however in environments where WebSocket connections are unavailable and time out instead of failing immediately (such as when using a corporate proxy which strips WebSocket headers) this may result in a slower initial connection. Once a connection is first established, transport preference will be cached in the web browser local storage so subsequent connections will use the best available transport. If you expect WebSocket connection attempts to always fail in your enviornment, you can skip the WebSocket connection step by explicitly providing a list of transports which omits the `web_socket` transport via the `transports` client option. + +

Switch to using the new promise-based API of Crypto.generateRandomKey()

+ +**Note:** This section is only relevant if you’re using the `Crypto.generateRandomKey()` method. + +If you’re using the `Crypto.generateRandomKey()` method, you’ll need to change how you call this method. In v1, this method required that you pass a callback. In v2, it communicates its result by returning a promise. + +So you need to change code that looks like this: + +```javascript +Ably.Realtime.Crypto.generateRandomKey((err, key) => { … }) +``` + +into code that makes use of the returned promise, for example by using the `await` keyword on it: + +```javascript +const key = await Ably.Realtime.Crypto.generateRandomKey(); +``` + +#### Update your usage of `request()` to pass a `version` argument + +**Note:** This section is only relevant if you’re using the `Rest.request()` or `Realtime.request()` methods; that is, if you’re using the library to manually make a request to the Ably REST API. + +The signature of this method has been changed; it now requires that you pass a `version` argument to specify the version of the REST API to use. For compatibility with v1 of this library, specify a version of `2`. + +As an example, given the current code to [get the service time](https://ably.com/docs/api/rest-api#time): + +```javascript +const time = await rest.request('get', '/time'); +``` + +add an argument to specify the API version: + +```javascript +const time = await rest.request('get', '/time', 2); +``` + +#### Update your usage of the result of the `stats()` method + +**Note:** This section is only relevant if you’re using the `Rest.stats()` or `Realtime.stats()` method; that is, if you’re using the library to retrieve your application’s usage statistics. + +The `Stats` type returned by the `Rest.stats()` and `Realtime.stats()` method has been simplified in v2. Specifically, there is a new property called `entries`, which is an object all of whose properties have numeric values. The `entries` property replaces the following properties: + +- `all` +- `inbound` +- `outbound` +- `persisted` +- `connections` +- `channels` +- `apiRequests` +- `tokenRequests` +- `xchgProducer` +- `xchgConsumer` +- `pushStats` +- `processed` + +You should migrate your code to use the `entries` property instead of these properties. + +The main differences between the v1 `Stats` type and the `entries` property in v2 are: + +- the various stats that count messages in different ways (`all`, `inbound`, `outbound`, `persisted`, `processed`) are now under `messages` instead of all in the top level +- `connections` is no longer broken down into `plain` and `tls` +- instead of top-level `apiRequests` and `tokenRequests`, which doesn't make much sense because token requests are API requests, you now have a top level `apiRequests` that’s broken down currently into `tokenRequests` and `other` with an `all` aggregate, with potential to split other out into more specific types later +- `push` is completely reorganised in a way that makes more sense + +For more detailed information on `entries`, see the `Stats` type’s new `schema` property. It provides you with the URL of a [JSON Schema](https://json-schema.org/) which describes the structure of this `Stats` object. (Alternatively, if you wish to view this schema now, you can find it [here](https://github.com/ably/ably-common/blob/main/json-schemas/src/app-stats.json).) + +As an example, given the following v1 code that uses the `stats()` API: + +```javascript +const stats = await rest.stats(); +const inboundMessageCount = stats[0].inbound.all.messages.count; +``` + +This is the equivalent v2 code: + +```javascript +const stats = await rest.stats(); +const inboundMessageCount = stats[0].entries['messages.inbound.all.messages.count'] ?? 0; +``` + +Notice that a given property may be absent from `entries`. If a property is absent, this is equivalent to its value being 0. + +#### Be aware of changed `whenState()` behaviour + +**Note:** This section is only relevant if you’re using the `RealtimeChannel.whenState()` or `Connection.whenState()` methods. + +The `RealtimeChannel.whenState()` and `Connection.whenState()` methods now return `null` when the connection is already in the given state, instead of attempting to synthesize an artificial state change. + +#### Be aware that the `fromEncoded()` and `fromEncodedArray()` methods are now async + +**Note:** This section is only relevant if you’re using `Message` or `PresenceMessage`’s `fromEncoded` or `fromEncodedArray` methods. + +`Message` and `PresenceMessage`’s `fromEncoded` and `fromEncodedArray` methods now operate asynchronously. That is, instead of returning the decoding result, they return a promise. Update your code to retrieve the result of these promises, for example by using the `await` keyword. + +

Be aware that symmetric encryption in a browser now requires a secure context

+ +**Note:** This section is only relevant if you’re using the `cipher` client option and running in a browser. + +If you’re making use of the `cipher` client option to enable symmetric encryption on a channel, be aware that when running in a browser this functionality is now implemented using the [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API). Hence, this functionality is only available when this API is available; namely, when the current environment is a [secure context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts). Roughly speaking, this means that you can now only use Ably channel encryption inside a web page when serving your page over HTTPS or from `localhost`. + +

Be aware that the library no longer uses CryptoJS and hence no longer works with WordArray

+ +**Note:** This section is only relevant if you’re using the `cipher` client option and running in a browser. + +When running in a browser, the library previously used the CryptoJS library for implementing symmetric encryption on a channel. As mentioned [here](#secure-context), the library now instead uses the built-in Web Crypto API. + +This means that the library is no longer capable of interacting with CryptoJS’s `WordArray` type. Specifically: + +- `Crypto.generateRandomKey()` now returns an `ArrayBuffer` instead of a `WordArray`. +- `Crypto.getDefaultParams({ key })` no longer accepts a `WordArray` key; pass an `ArrayBuffer` or `Uint8Array` instead. + +#### Stop requesting the `xhr_streaming` and `xhr` transports + +**Note:** This section is only relevant if you’re explicitly writing `"xhr_streaming"` or `"xhr"` as part of your `transports` client option. + +v1 offered the `xhr_streaming` transport, also known as `xhr`, primarily to provide a performant realtime transport for browsers which did not support WebSocket connections. Since this limitation does not apply to any of the [browsers supported by v2](#supported-platforms), the `xhr_streaming` transport has been removed. If your `transports` client option contains `"xhr_streaming"` or `"xhr"`, remove this value. If you still wish to explicitly request a non-WebSocket transport, request `xhr_polling` instead. + +#### Stop requesting the `jsonp` transport + +**Note:** This section is only relevant if you’re explicitly writing `"jsonp"` as part of your `transports` client option. + +v1 offered JSONP as a fallback transport for browsers that did not support cross-origin XHR. Since this limitation does not apply to any of the [browsers supported by v2](#supported-platforms), the JSONP transport has been removed. If your `transports` client option contains `"jsonp"`, remove this value. + +#### Stop using the `noencryption` variant of the library + +**Note:** This section is only relevant if you’re importing `ably/build/ably.noencryption.min.js` or using the `ably.noencryption.min-1.js` CDN build. + +In v1, we provided a separate version of the library that did not support the `cipher` channel option. This was offered for those who did not wish to bloat their app’s bundle size with encryption code. + +Since v2 [no longer uses the CryptoJS library](#no-more-CryptoJS), the `cipher` channel option functionality now has a much smaller impact on your app’s bundle size. So, we no longer offer the `noencryption` variant of the library. + +Furthermore, v2 introduces the [modular variant of the library](#modular-variant), which is specifically aimed at those who are concerned about their app’s bundle size. It provides a general mechanism for choosing which Ably functionality you wish to include in your app’s bundle. So, if you do not wish to incur even the small bundle size overhead that the `cipher` channel option imposes, consider using the modular variant of the library without the `Crypto` module. + +#### Stop using the special Web Worker build of the library + +**Note:** This section is only relevant if you’re using ably-js inside a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API). + +In v1, if you wished to use ably-js inside a Web Worker, you had to use a special build of the library, by writing `import Ably from 'ably/build/ably-webworker.min'`. + +In v2, the library can be used inside a Web Worker without needing to use a special build, and this special `ably-webworker` build no longer exists. So, update your code to `import Ably from 'ably'`. + +#### Stop importing the `ably/build/ably-commonjs.js` or `ably/build/ably-commonjs.noencryption.js` files + +**Note:** This section is only relevant if you’re importing `ably/build/ably-commonjs.js` or `ably/build/ably-commonjs.noencryption.js`. + +The `ably/build/ably-commonjs.js` file no longer exists. + +- If you’re currently writing `require('ably/build/ably-commonjs.js')`, write `require('ably')` instead. +- If you’re currently writing `import * as Ably from 'ably/build/ably-commonjs.js'`, write `import * as Ably from 'ably'` instead. + +(The same applies for `ably-commonjs.noencryption.js`.) + +### Only TypeScript users + +#### Stop referring to the `Types` namespace + +v1 exported many of its types inside the `Types` TypeScript namespace. v2 removes this namespace, and the types it contained are now exported at the top level. Remove your references to the `Types` namespace. + +For example, code like this: + +```typescript +let options: Ably.Types.ClientOptions = { key: 'foo' }; +``` + +should now be written + +```typescript +let options: Ably.ClientOptions = { key: 'foo' }; +``` + +In order to avoid a clash with the existing top-level types of the same type, the `Types.Rest` and `Types.Realtime` types have been renamed to `RestClient` and `RealtimeClient`. If you’re referring to these types in your code (which is probably unlikely) then update these references. + +

Stop referring to the *Base, *Callbacks and *Promise types

+ +The v1 type declarations contained various sets of three related types in order to reflect the different asynchronicity styles offered by the SDK. For example, it had a `RealtimeChannelBase` type which contained functionality common to the callbacks-based and promises-based API of a realtime channel, and then `RealtimeChannelCallbacks` and `RealtimeChannelPromises` types which contained the APIs unique to those asynchronicity styles. + +Now that [the callbacks-based variant of the library has been removed](#switch-from-callbacks-to-promises), we’ve simplified the types, removing the `*Callbacks` types and combining each pair of `*Base` and `*Promise` types into a single type. For example, where previously there existed three types that described a realtime channel, now there is just one, named `RealtimeChannel`. You should update your code to stop referring to the suffixed types. In general, this is a case of removing these suffixes from the type names. + +#### Be aware that some properties might not be populated on messages received from Ably + +The following properties of `Message` are now declared as optional: + +- `clientId` +- `data` +- `encoding` +- `extras` +- `name` + +This update to the type declarations reflects the fact that these properties are not (and never have been, despite what the v1 declarations suggested) guaranteed to be populated on a message received from Ably. + +#### Be aware that the `Message` type now refers to a message that you publish to Ably + +The `Message` type has been changed so that it represents a message that you publish to Ably (see [migration guidance for the changes to the type declaration for the `publish()` method](#publish-type-changes)). Since you are not required to populate the following properties of a message that you publish to Ably, they are now optional: + +- `id` +- `timestamp` + +If you were previously using the `Message` type to refer to a message received from Ably (e.g. from the `RealtimeChannel.subscribe()` or `RealtimeChannel.history()` methods), then switch to using the new `InboundMessage` type, which represents a message received from Ably; it’s a version of `Message` in which these two properties are _not_ optional (matching the v1 `Message` type). + +

Be aware that publish() is stricter about its arguments

+ +In v1, the type declarations for the publishing methods `Channel.publish()` and `RealtimeChannel.publish()` stated that they accepted an argument of type `any`. This was inaccurate, as in fact there were expectations about the shape of the message or messages that you pass to these methods. Now, the type declarations state you must pass an object that satisfies the `Message` interface, or an array of such objects. + +#### Be aware that enum-like namespaces have changed name + +**Note:** It’s unlikely that you’re affected by this change. + +In v1, there existed a pattern in the type declarations where a single name was used for a namespace and also a type whose possible values were the members of that namespace. For example: + +```typescript +declare namespace ChannelState { + type INITIALIZED = 'initialized'; + type ATTACHING = 'attaching'; + type ATTACHED = 'attached'; + type DETACHING = 'detaching'; + type DETACHED = 'detached'; + type SUSPENDED = 'suspended'; +} + +export type ChannelState = + | ChannelState.FAILED + | ChannelState.INITIALIZED + | ChannelState.SUSPENDED + | ChannelState.ATTACHED + | ChannelState.ATTACHING + | ChannelState.DETACHED + | ChannelState.DETACHING; +``` + +In v2, the namespace has been changed so that its name has a plural inflection. For example, whilst the `ChannelState` _type_ maintains its name, the `ChannelState` _namespace_ is now called `ChannelStates`. Update your code accordingly. + +Also, there was a type in v1 called `ChannelModes` which was just an alias for `Array`. In order to accommodate the naming scheme described above, the `ChannelModes` type no longer has its v1 meaning. + +#### Stop relying on the declared inheritance relationship between REST and realtime types + +In v1, the `Types.RealtimeBase` class is declared as inheriting from the `Types.RestBase` class. [As mentioned above](#stop-referring-to-suffixed-types), these classes no longer exist. Their replacements (the `RealtimeClient` and `RestClient` interfaces) do not declare an inheritance relationship. + +

Stop using functionality that v2 deprecates

+ +#### Use `Connection.createRecoveryKey()` instead of `Connection.recoveryKey` + +**Note:** This section only applies if you’re using the `Connection.recoveryKey` property. You’re likely only using this if making use of the library’s connection recovery functionality, and have opted out of the library’s recovery key persistance functionality; that is, if you’re populating the `recover` client option with a string (as opposed to a callback). + +The `Connection.recoveryKey` property is deprecated and will be removed in a future version. It has been replaced by the `Connection.createRecoveryKey()` method. The return value of this method is identical to the value of the `Connection.recoveryKey` property. Update your code to use this return value. + +

Take advantage of new features that v2 introduces

+ +

Using the modular variant of the library

+ +Aimed at those who are concerned about their app’s bundle size, the modular variant of the library allows you to create a client which has only the functionality that you choose. Unused functionality can then be tree-shaken by your module bundler. + +To get started with the modular variant of the library, see [this section of the Readme](../../../README.md#modular-tree-shakable-variant). diff --git a/docs/migration-guides/v2/react-hooks.md b/docs/migration-guides/v2/react-hooks.md new file mode 100644 index 0000000000..c8fe37b115 --- /dev/null +++ b/docs/migration-guides/v2/react-hooks.md @@ -0,0 +1,223 @@ +# React hooks migration guide for ably-js v2 + +Here’s how to migrate React hooks from ably-js v1 to v2: + +1. [Use new `ChannelProvider` component](#use-new-channelprovider-component) +2. [Update usage of the `usePresence` hook, which has been split into two separate hooks](#update-usage-of-the-usepresence-hook-which-has-been-split-into-two-separate-hooks) +3. [Rename optional `id` field to `ablyId`](#rename-optional-id-field-to-ablyid) +4. (Optional) [Use new convenience function `publish` in `useChannel`](#optional-use-new-convenience-function-publish-in-usechannel) + +## Use new `ChannelProvider` component + +In previous versions, you were able to provide channel options as a parameter in the `useChannel`/`usePresence` hooks. This could, in some cases, lead to errors when using hooks with the same channel name but different options, or when attempting to dynamically change options for a channel. + +To address these issues, the new version introduces the `ChannelProvider` component to define the channels you wish to use and their options. The ability to provide channel options using the `options` or `deriveOptions` parameters in `useChannel`/`usePresence` hooks has been removed. + +Replace code that used `options` in the `useChannel` hook: + +```jsx +const { channel } = useChannel( + { channelName: 'your-channel-name', options: { params: { rewind: '1' } } }, + (message) => { + console.log(message); + }, +); +``` + +With this: + +```jsx +// in a parent component: +return ( + + {children} + +); + +// in a child component: +const { channel } = useChannel({ channelName: 'your-channel-name' }, (message) => { + console.log(message); +}); +``` + +Replace code that used `deriveOptions` in `useChannel` hook: + +```jsx +useChannel( + { + channelName: 'your-derived-channel-name', + deriveOptions: { filter: 'headers.role == `"marketing"`' }, + }, + (message) => { + console.log(message); + }, +); +``` + +With this: + +```jsx +// in a parent component: +return ( + + {children} + +); + +// in a child component: +useChannel({ channelName: 'your-derived-channel-name' }, (message) => { + console.log(message); +}); +``` + +Replace code that used `options` in `usePresence` hook: + +```jsx +const { updateStatus } = usePresence( + { channelName: 'presence-channel-name', options: { modes: ['PRESENCE'] } }, + { foo: 'bar' }, +); +``` + +With this: + +```jsx +// in a parent component: +return ( + + {children} + +); + +// in a child component: +const { updateStatus } = usePresence({ channelName: 'presence-channel-name' }, { foo: 'bar' }); +``` + +Additionally, if you were calling `.setOptions()` on a channel instance returned by the `useChannel` hook before, you must remove those calls and instead modify options provided to the `ChannelProvider` component if you want to change channel options during runtime. + +Change this: + +```jsx +const { channel } = useChannel({ channelName: 'your-channel-name' }, (message) => { + console.log(message); +}); +channel.setOptions({ params: { rewind: '1' } }); +``` + +To this: + +```jsx +// in a parent component: +// change channel options during runtime using state +const [options, setOptions] = useState({}); +return ( + + {children} + +); + +// in a child component: +const { channel } = useChannel({ channelName: 'your-channel-name' }, (message) => { + console.log(message); +}); + +// use the useState setter to change the options provided to the `ChannelProvider` component +setOptions({ params: { rewind: '1' } }); +``` + +## Update usage of the `usePresence` hook, which has been split into two separate hooks + +The functionality of the `usePresence` hook has been split into two separate hooks. + +The `usePresence` hook can now only be used to enter the presence with optional initial state and update the presence status for the current client. It no longer returns the `presenceData` value and does not accept the `onPresenceUpdated` callback as its third parameter. + +To listen for presence updates, a new hook called `usePresenceListener` has been introduced. This hook returns the `presenceData` object previously returned by `usePresence` and accepts an `onPresenceMessageReceived` callback as its second parameter, which is called on new presence messages. + +Replace this: + +```jsx +const { presenceData, updateStatus } = usePresence( + { channelName: 'presence-channel-name' }, + { foo: 'bar' }, + (update) => { + console.log(update); + }, +); +``` + +With this: + +```jsx +const { updateStatus } = usePresence({ channelName: 'presence-channel-name' }, { foo: 'bar' }); +const { presenceData } = usePresenceListener({ channelName: 'presence-channel-name' }, (update) => { + console.log(update); +}); +``` + +## Rename optional `id` field to `ablyId` + +All instances of the `id` field, which optionally were used to specify the `AblyProvider` component and the underlying Ably client to use, have been renamed to `ablyId`. + +Replace this: + +```jsx +// in a parent component: +const client = new Ably.Realtime(options); + +return ( + + {children} + +); + +// in a child component: +useChannel({ channelName: 'your-channel-name', id: 'foo' }, (message) => { + console.log(message); +}); +``` + +With this: + +```jsx +// in a parent component: +const client = new Ably.Realtime(options); + +return ( + + + {children} + + +); + +// in a child component: +useChannel({ channelName: 'your-channel-name', ablyId: 'foo' }, (message) => { + console.log(message); +}); +``` + +## (Optional) Use new convenience function `publish` in `useChannel` + +A new convenience function, `publish`, is now being returned by the `useChannel` hook. It performs the same function as calling `channel.publish()`. Additionally, using this dedicated `publish` function allows you to send messages to derived channels (channels with a filter qualifier) without attaching to the channel or using other workarounds. + +It is recommended to use the dedicated `publish` function returned by the `useChannel` hook instead of calling `channel.publish()`. + +Replace this: + +```jsx +const { channel } = useChannel({ channelName: 'your-channel-name' }); + +channel.publish('test-message', { + text: 'message text', +}); +``` + +With this: + +```jsx +const { publish } = useChannel({ channelName: 'your-channel-name' }); + +publish('test-message', { + text: 'message text', +}); +``` diff --git a/docs/react.md b/docs/react.md index 171a2fffab..68995dbbd0 100644 --- a/docs/react.md +++ b/docs/react.md @@ -8,9 +8,9 @@ Use Ably in your React application using idiomatic, easy to use, React Hooks! Using this module you can: - Interact with [Ably channels](https://ably.com/docs/channels) using a React Hook. -- [Publish messages](https://ably.com/docs/channels#publish) via Ably using the channel instances the hooks provide +- [Publish messages](https://ably.com/docs/channels#publish) via Ably using the publish function the hooks provide - Get notifications of user [presence on channels](https://ably.com/docs/presence-occupancy/presence) -- Send presence updates +- Enter presence and send presence updates The hooks provide a simplified syntax for interacting with Ably, and manage the lifecycle of the Ably SDK instances for you taking care to subscribe and unsubscribe to channels and events when your react components re-render. @@ -26,9 +26,9 @@ The hooks provide a simplified syntax for interacting with Ably, and manage the - [Usage](#usage) - [useChannel](#usechannel) - [usePresence](#usepresence) + - [usePresenceListener](#usepresencelistener) - ---- +## ### Compatible React Versions @@ -36,34 +36,74 @@ The hooks are compatible with all versions of React above 16.8.0 ## Usage -Start by connecting your app to Ably using the `AblyProvider` component. See the [`ClientOptions` documentation](https://ably.com/docs/api/realtime-sdk/types?lang=javascript) for information about what options are available when creating an Ably client. If you want to use the `usePresence` hook, you'll need to explicitly provide a `clientId`. +Start by connecting your app to Ably using the `AblyProvider` component. See the [`ClientOptions` documentation](https://ably.com/docs/api/realtime-sdk/types?lang=javascript) for information about what options are available when creating an Ably client. If you want to use the `usePresence` or `usePresenceListener` hooks, you'll need to explicitly provide a `clientId`. The `AblyProvider` should be high in your component tree, wrapping every component which needs to access Ably. ```jsx -import { AblyProvider } from "ably/react"; -import * as Ably from "ably"; +import { AblyProvider } from 'ably/react'; +import * as Ably from 'ably'; -const client = new Ably.Realtime.Promise({ key: "your-ably-api-key", clientId: 'me' }); +const client = new Ably.Realtime({ key: 'your-ably-api-key', clientId: 'me' }); root.render( - -) + , +); +``` + +### Define Ably channels + +Once you've set up `AblyProvider`, define Ably channels you want to use by utilizing the `ChannelProvider` component: + +```jsx + + + ``` -Once you've done this, you can use the `hooks` in your code. The simplest example is as follows: +After setting up `ChannelProvider`, you can employ the provided `hooks` in your code. Here's a basic example: ```javascript -const { channel } = useChannel("your-channel-name", (message) => { - console.log(message); +const { channel } = useChannel('your-channel-name', (message) => { + console.log(message); }); ``` Every time a message is sent to `your-channel-name` it'll be logged to the console. You can do whatever you need to with those messages. -Our react hooks are designed to run on the client-side, so if you are using server-side rendering, make sure that your components which use Ably react hooks are only rendered on the client side. +> [!NOTE] +> Our react hooks are designed to run on the client-side, so if you are using server-side rendering, make sure that your components which use Ably react hooks are only rendered on the client side. + +### Channel Options + +We support providing [ChannelOptions](https://ably.com/docs/api/realtime-sdk/types#channel-options) for the `ChannelProvider` component: + +This means you can use features like `rewind`: + +```jsx + + + +``` + +[Subscription filters](https://ably.com/docs/channels#filter-subscribe) are also supported: + +```jsx +const deriveOptions = { filter: 'headers.email == `"rob.pike@domain.com"` || headers.company == `"domain"`' } + +return ( + + + +) +``` + +> [!NOTE] +> Please note that attempts to publish to a derived channel (the one created or retrieved with a filter expression) +> using channel instance will fail, since derived channels support only `subscribe` capability. +> Use `publish` function returned by `useChannel` hook instead. --- @@ -72,8 +112,8 @@ Our react hooks are designed to run on the client-side, so if you are using serv The useChannel hook lets you subscribe to an [Ably Channel](https://ably.com/docs/channels) and receive messages from it. ```javascript -const { channel, ably } = useChannel("your-channel-name", (message) => { - console.log(message); +const { channel, ably } = useChannel('your-channel-name', (message) => { + console.log(message); }); ``` @@ -83,8 +123,8 @@ const { channel, ably } = useChannel("your-channel-name", (message) => { ```javascript const [messages, updateMessages] = useState([]); -const { channel } = useChannel("your-channel-name", (message) => { - updateMessages((prev) => [...prev, message]); +const { channel } = useChannel('your-channel-name', (message) => { + updateMessages((prev) => [...prev, message]); }); // Convert the messages to list items to render in a react component @@ -94,129 +134,155 @@ const messagePreviews = messages.map((msg, index) =>
  • {msg.data.s `useChannel` supports all of the parameter combinations of a regular call to `channel.subscribe`, so you can filter the messages you subscribe to by providing a `message type` to the `useChannel` function: ```javascript -const { channel } = useChannel("your-channel-name", "test-message", (message) => { - console.log(message); // Only logs messages sent using the `test-message` message type +useChannel('your-channel-name', 'test-message', (message) => { + console.log(message); // Only logs messages sent using the `test-message` message type }); ``` -The `channel` instance returned by `useChannel` can be used to send messages to the channel. It's just a regular Ably JavaScript SDK `channel` instance. +#### useChannel `publish` function + +The `publish` function returned by `useChannel` can be used to send messages to the channel. ```javascript -channel.publish("test-message", { text: "message text" }); +const { publish } = useChannel('your-channel-name'); +publish('test-message', { text: 'message text' }); ``` -Because we're returning the channel instance, and Ably SDK instance from our `useChannel` hook, you can subsequently use these to perform any operations you like on the channel. +#### useChannel `channel` instance -For example, you could retrieve history like this: +The `useChannel` hook returns an instance of the channel, which is part of the Ably JavaScript SDK. This allows you to access the standard Ably JavaScript SDK functionalities associated with channels. + +By providing both the channel instance and the Ably SDK instance through our useChannel hook, you gain the flexibility to execute various operations on the channel. + +For instance, you can easily fetch the history of the channel using the following method: ```javascript -const { channel } = useChannel("your-channel-name", (message) => { - console.log(message); +const { channel } = useChannel('your-channel-name', (message) => { + console.log(message); }); const history = channel.history((err, result) => { - var lastMessage = resultPage.items[0]; - console.log('Last message: ' + lastMessage.id + ' - ' + lastMessage.data); + var lastMessage = resultPage.items[0]; + console.log('Last message: ' + lastMessage.id + ' - ' + lastMessage.data); }); ``` -We support providing [ChannelOptions](https://ably.com/docs/api/realtime-sdk/types#channel-options) to the `useChannel` hook: - -This means you can use features like `rewind`: +--- -```javascript -const { channel } = useChannel({ channelName: "your-channel-name", options: { params: { rewind: '1' } } }, (message) => { - ... -}); -``` +### usePresence -[Subscription filters](https://ably.com/docs/channels#filter-subscribe) are also supported: +The usePresence hook [enters the presence set on a channel](https://ably.com/docs/presence-occupancy/presence) and enables you to send presence updates for current client. To find out more about Presence, see the [Presence documentation](https://ably.com/docs/presence-occupancy/presence). ```javascript -const deriveOptions = { filter: 'headers.email == `"rob.pike@domain.com"` || headers.company == `"domain"`' } -const { channel } = useChannel({ channelName: "your-derived-channel-name", options: { ... }, deriveOptions }, (message) => { - ... -}); +const { updateStatus } = usePresence('your-channel-name'); + +// The `updateStatus` function can be used to update the presence data for the current client +updateStatus('status'); ``` -Please note that attempts to publish to a derived channel (the one created or retrieved with a filter expression) will fail. In order to send messages to the channel called _"your-derived-channel-name"_ from the example above, you will need to create another channel instance without a filter expression. +You can optionally provide a second parameter when you `usePresence` to set an initial `presence data`. ```javascript -const channelName = "your-derived-channel-name"; -const options = { ... }; -const deriveOptions = { filter: 'headers.email == `"rob.pike@domain.com"` || headers.company == `"domain"`' } -const callback = (message) => { ... }; +const { updateStatus } = usePresence('your-channel-name', 'initial state'); -const { channel: readOnlyChannelInstance } = useChannel({ channelName, options, deriveOptions }, callback); -const { channel: readWriteChannelInstance } = useChannel({ channelName, options }, callback); // NB! No 'deriveOptions' passed here - -readWriteChannelInstance.publish("test-message", { text: "message text" }); +// The `updateStatus` function can be used to update the presence data for the current client +updateStatus('new status'); ``` ---- - -### usePresence +The new state will be sent to the channel, and all clients subscribed to the channel (including current, if you've subscribed to updates using [`usePresenceListener` hook](#usepresencelistener)) will be notified of the change immediately. -The usePresence hook lets you [subscribe to presence events on a channel](https://ably.com/docs/presence-occupancy/presence?lang=javascript#subscribe) - this will allow you to get notified when a user joins or leaves the channel. To find out more about Presence, see the [presence documentation](https://ably.com/docs/presence-occupancy/presence). - -**Please note** that fetching present members is executed as an effect, so it'll load in *after* your component renders for the first time. +`usePresence` supports objects and numbers, as well as strings: ```javascript -const { presenceData, updateStatus } = usePresence("your-channel-name"); - -// Convert presence data to list items to render -const peers = presenceData.map((msg, index) =>
  • {msg.clientId}: {msg.data}
  • ); +usePresence('your-channel-name', { foo: 'bar' }); +usePresence('another-channel-name', 123); +usePresence('third-channel-name', 'initial status'); ``` -`usePresence` returns an array of presence messages - again each message is a regular Ably JavaScript SDK `presenceMessage` instance. - -You can optionally provide a string when you `usePresence` to set an initial `presence data` string. +If you're using `TypeScript` there are type hints to make sure that value passed to `updateStatus` is of the same `type` as your initial constraint, or a provided generic type parameter: -```javascript -const { presenceData, updateStatus } = usePresence("your-channel-name", "initial state"); +```tsx +const TypedUsePresenceComponent = () => { + // In this example MyPresenceType will be used for type checking updateStatus function. + // If omitted, the shape of the initial value will be used, and if that's omitted, `any` will be the default. + + const { updateStatus } = usePresence('testChannelName', { foo: 'bar' }); + + return ( + + ); +}; -// The `updateStatus` function can be used to update the presence data for the current client -updateStatus("new status"); +interface MyPresenceType { + foo: string; +} ``` -The new state will be sent to the channel, and any other clients subscribed to the channel will be notified of the change immediately. +--- -If you don't want to use the `presenceData` returned from usePresence, you can configure a callback +### usePresenceListener + +The usePresenceListener hook [subscribes you to presence 'enter', 'update' and 'leave' events on a channel](https://ably.com/docs/presence-occupancy/presence?lang=javascript#subscribe) - this will allow you to get notified when a user joins or leaves the channel, or updates its presence data. To find out more about Presence, see the [Presence documentation](https://ably.com/docs/presence-occupancy/presence). + +**Please note** that fetching present members is executed as an effect, so it'll load in _after_ your component renders for the first time. ```javascript -const { updateStatus } = usePresence("your-channel-name", "initial state", (presenceUpdate) => { - console.log(presenceUpdate); -}); +const { presenceData } = usePresenceListener('your-channel-name'); + +// Convert presence data to the list of items to render +const membersData = presenceData.map((msg, index) => ( +
  • + {msg.clientId}: {msg.data} +
  • +)); ``` -usePresence supports objects, as well as strings +The `usePresenceListener` hook returns an array of presence messages - each message is a regular Ably JavaScript SDK `PresenceMessage` instance. + +If you don't want to use the `presenceData` returned from `usePresenceListener`, you can configure a callback: ```javascript -usePresence("your-channel-name", { foo: "bar" }); +usePresenceListener('your-channel-name', (presenceUpdate) => { + console.log(presenceUpdate); +}); ``` -and if you're using `TypeScript` there are type hints to make sure that updates are of the same `type` as your initial constraint, or a provided generic type parameter: +If you're using `TypeScript` there are type hints to make sure that presence data received are of the same `type` as a provided generic type parameter: ```tsx -const TypedUsePresenceComponent = () => { - // In this example MyPresenceType will be checked - if omitted, the shape of the initial - // value will be used ...and if that's omitted, `any` will be the default. +const TypedUsePresenceListenerComponent = () => { + // In this example MyPresenceType will be used for presenceData type hints. + // If that's omitted, `any` will be the default. - const { val } = usePresence("testChannelName", { foo: "bar" }); + const { presenceData } = usePresenceListener('testChannelName'); + const membersData = presenceData.map((presenceMsg, index) => { return ( -
    - {JSON.stringify(val)} -
    +
  • + {/* you will have Intellisense for presenceMsg.data of type MyPresenceType here */} + {presenceMsg.clientId} - {presenceMsg.data.foo} +
  • ); -} + }); + + return
      {membersData}
    ; +}; interface MyPresenceType { - foo: string; + foo: string; } ``` -`PresenceData` is a good way to store synchronised, per-client metadata, so types here are especially valuable. +`presenceData` is a good way to store synchronised, per-client metadata, so types here are especially valuable. ### useConnectionStateListener @@ -224,10 +290,10 @@ The `useConnectionStateListener` hook lets you attach a listener to be notified ```javascript useConnectionStateListener((stateChange) => { - console.log(stateChange.current) // the new connection state - console.log(stateChange.previous) // the previous connection state - console.log(stateChange.reason) // if applicable, an error indicating the reason for the connection state change -}) + console.log(stateChange.current); // the new connection state + console.log(stateChange.previous); // the previous connection state + console.log(stateChange.reason); // if applicable, an error indicating the reason for the connection state change +}); ``` You can also pass in a filter to only listen to a set of connection states: @@ -243,10 +309,10 @@ The `useChannelStateListener` hook lets you attach a listener to be notified of ```javascript useChannelStateListener((stateChange) => { - console.log(stateChange.current) // the new channel state - console.log(stateChange.previous) // the previous channel state - console.log(stateChange.reason) // if applicable, an error indicating the reason for the channel state change -}) + console.log(stateChange.current); // the new channel state + console.log(stateChange.previous); // the previous channel state + console.log(stateChange.reason); // if applicable, an error indicating the reason for the channel state change +}); ``` You can also pass in a filter to only listen to a set of channel states: @@ -270,7 +336,7 @@ client.authorize(); When using the Ably react hooks, your Ably client may encounter a variety of errors, for example if it doesn't have permissions to attach to a channel it may encounter a channel error, or if it loses connection from the Ably network it may encounter a connection error. -To allow you to handle these errors, the `useChannel` and `usePresence` hooks return connection and channel errors so that you can react to them in your components: +To allow you to handle these errors, the `useChannel`, `usePresence` and `usePresenceListener` hooks return connection and channel errors so that you can react to them in your components: ```jsx const { connectionError, channelError } = useChannel('my_channel', messageHandler); @@ -280,48 +346,147 @@ if (connectionError) { } else if (channelError) { // TODO: handle channel errors } else { - return + return ; } ``` Alternatively, you can also pass callbacks to the hooks to be called when the client encounters an error: ```js -useChannel({ - channelName: 'my_channel', - onConnectionError: (err) => { /* handle connection error */ }, - onChannelError: (err) => { /* handle channel error */ }, -}, messageHandler); +useChannel( + { + channelName: 'my_channel', + onConnectionError: (err) => { + /* handle connection error */ + }, + onChannelError: (err) => { + /* handle channel error */ + }, + }, + messageHandler, +); ``` ### Usage with multiple clients -If you need to use multiple Ably clients on the same page, the easiest way to do so is to keep your clients in separate `AblyProvider` components. However, if you need to nest `AblyProvider`s, you can pass a string id for each client as a prop to the provider. +If you need to use multiple Ably clients on the same page, the easiest way to do so is to keep your clients in separate `AblyProvider` components. However, if you need to nest `AblyProvider`s, you can pass a string `ablyId` for each client as a prop to the provider. ```jsx root.render( - - + + - -) + , +); ``` -This `id` can then be passed in to each hook to specify which client to use. +This `ablyId` can then be passed in to each hook to specify which client to use. ```javascript -const ablyContextId = 'providerOne'; +const ablyId = 'providerOne'; + +const client = useAbly(ablyId); + +useChannel({ channelName: 'your-channel-name', ablyId }, (message) => { + console.log(message); +}); + +usePresence({ channelName: 'your-channel-name', ablyId }, 'initial state'); + +usePresenceListener({ channelName: 'your-channel-name', ablyId }, (presenceUpdate) => { + // ... +}); +``` + +### Skip attaching to a channel using `skip` parameter + +By default, `usePresenceEnter` and `usePresenceListener` automatically attach to a channel upon component mount, and `useChannel` does so if a callback for receiving messages is provided. This means that these hooks will attempt to establish a connection to the Ably server using the credentials currently set in the corresponding `Ably.RealtimeClient` from `AblyProvider`. However, there may be scenarios where your user authentication is asynchronous or when certain parts of your application are conditionally accessible (e.g., premium features). In these instances, you might not have a valid auth token yet, and an attempt to attach to a channel would result in an error. + +To address this, the `skip` parameter allows you to control the mounting behavior of the `useChannel`, `usePresenceEnter`, and `usePresenceListener` hooks, specifically determining whether they should attach to a channel upon the component's mount. -const client = useAbly(ablyContextId); +```tsx +const [skip, setSkip] = useState(true); -useChannel({ channelName: "your-channel-name", id: ablyContextId }, (message) => { - console.log(message); +const { channel, publish } = useChannel({ channelName: 'your-channel-name', skip }, (message) => { + updateMessages((prev) => [...prev, message]); }); +``` + +By setting the `skip` parameter, you can prevent the hooks from attempting to attach to a channel, thereby avoiding errors and avoiding unnecessary messages being send (which reduces your messages consumption in your Ably app). + +The `skip` parameter accepts a boolean value. When set to `true`, it instructs the hooks not to attach to the channel upon the component's mount. This behavior is dynamically responsive; meaning that, once the conditions change (e.g., the user gets authenticated), updating the `skip` parameter to `false` will trigger the hooks to attach to the channel. + +This parameter is useful in next situations: + +**Asynchronous Authentication**: Users are not immediately authorized upon loading the component, and acquiring a valid auth token is asynchronous. + +Consider a scenario where a component uses the `useChannel` hook, but the user's authentication status is determined asynchronously: + +```tsx +import React, { useEffect, useState } from 'react'; +import { useChannel } from 'ably/react'; +import * as Ably from 'ably'; + +const ChatComponent = () => { + const [isUserAuthenticated, setIsUserAuthenticated] = useState(false); + const [messages, updateMessages] = useState([]); + + // Simulate asynchronous authentication + useEffect(() => { + async function authenticate() { + const isAuthenticated = await someAuthenticationFunction(); + setIsUserAuthenticated(isAuthenticated); + } + authenticate(); + }, []); + + // useChannel with skip parameter + useChannel({ channelName: 'your-channel-name', skip: !isUserAuthenticated }, (message) => { + updateMessages((prev) => [...prev, message]); + }); + + if (!isUserAuthenticated) { + return

    Please log in to join the chat.

    ; + } + + return ( +
    + {messages.map((message, index) => ( +

    {message.data.text}

    + ))} +
    + ); +}; +``` + +**Conditional Feature Access**: Certain features or parts of your application are behind a paywall or require specific user privileges that not all users possess. + +In an application with both free and premium features, you might want to conditionally use channels based on the user's subscription status: + +```tsx +import React from 'react'; +import { useChannel } from 'ably/react'; +import * as Ably from 'ably'; + +interface PremiumFeatureComponentProps { + isPremiumUser: boolean; +} -usePresence({ channelName: "your-channel-name", id: ablyContextId }, (presenceUpdate) => { - ... -}) +const PremiumFeatureComponent = ({ isPremiumUser }: PremiumFeatureComponentProps) => { + const [messages, updateMessages] = useState([]); + + // Skip attaching to the channel if the user is not a premium subscriber + useChannel({ channelName: 'premium-feature-channel', skip: !isPremiumUser }, (message) => { + updateMessages((prev) => [...prev, message]); + }); + + if (!isPremiumUser) { + return

    This feature is available for premium users only.

    ; + } + + return
    {/* Render premium feature based on messages */}
    ; +}; ``` ## NextJS warnings @@ -356,9 +521,9 @@ module.exports = { webpack: (config) => { config.externals.push({ 'utf-8-validate': 'commonjs utf-8-validate', - 'bufferutil': 'commonjs bufferutil', - }) - return config + bufferutil: 'commonjs bufferutil', + }); + return config; }, -} +}; ``` diff --git a/grunt/esbuild/build.js b/grunt/esbuild/build.js new file mode 100644 index 0000000000..9be1152642 --- /dev/null +++ b/grunt/esbuild/build.js @@ -0,0 +1,53 @@ +const banner = require('../../src/fragments/license'); +const umdWrapper = require('esbuild-plugin-umd-wrapper'); +const stripLogsPlugin = require('./strip-logs').default; + +// We need to create a new copy of the base config each time, because calling +// esbuild.build() with the base config causes it to mutate the passed +// config’s `banner.js` property to add some weird modules shim code, +// which we don’t want here. +function createBaseConfig() { + return { + bundle: true, + sourcemap: true, + format: 'umd', + banner: { js: '/*' + banner + '*/' }, + plugins: [umdWrapper.default({ libraryName: 'Ably', amdNamedModule: false })], + target: 'es2017', + }; +} + +const webConfig = { + ...createBaseConfig(), + entryPoints: ['src/platform/web/index.ts'], + outfile: 'build/ably.js', +}; + +const minifiedWebConfig = { + ...createBaseConfig(), + entryPoints: ['src/platform/web/index.ts'], + outfile: 'build/ably.min.js', + minify: true, +}; + +const modularConfig = { + ...createBaseConfig(), + entryPoints: ['src/platform/web/modular.ts'], + outfile: 'build/modular/index.mjs', + format: 'esm', + plugins: [stripLogsPlugin], +}; + +const nodeConfig = { + ...createBaseConfig(), + platform: 'node', + entryPoints: ['src/platform/nodejs/index.ts'], + outfile: 'build/ably-node.js', +}; + +module.exports = { + webConfig, + minifiedWebConfig, + modularConfig, + nodeConfig, +}; diff --git a/grunt/esbuild/strip-logs.js b/grunt/esbuild/strip-logs.js new file mode 100644 index 0000000000..6fc755513d --- /dev/null +++ b/grunt/esbuild/strip-logs.js @@ -0,0 +1,111 @@ +var path = require('path'); +var fs = require('fs'); +var babel = { + types: require('@babel/types'), + parser: require('@babel/parser'), + traverse: require('@babel/traverse'), + generator: require('@babel/generator'), +}; + +// This function is copied from +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +// This esbuild plugin strips all log messages from the modular variant of +// the library, except for error-level logs and other logging statements +// explicitly marked as not to be stripped. +const stripLogsPlugin = { + name: 'stripLogs', + setup(build) { + let foundLogToStrip = false; + let foundErrorLog = false; + let foundNoStripLog = false; + + const filter = new RegExp(`^${escapeRegExp(path.join(__dirname, '..', '..', 'src') + path.sep)}.*\\.[tj]s$`); + build.onLoad({ filter }, async (args) => { + const contents = (await fs.promises.readFile(args.path)).toString(); + const lines = contents.split('\n'); + const ast = babel.parser.parse(contents, { sourceType: 'module', plugins: ['typescript'] }); + const errors = []; + + babel.traverse.default(ast, { + enter(path) { + if ( + path.isCallExpression() && + babel.types.isMemberExpression(path.node.callee) && + babel.types.isIdentifier(path.node.callee.object, { name: 'Logger' }) + ) { + if (babel.types.isIdentifier(path.node.callee.property, { name: 'logAction' })) { + const firstArgument = path.node.arguments[0]; + + if ( + babel.types.isMemberExpression(firstArgument) && + babel.types.isIdentifier(firstArgument.object, { name: 'Logger' }) && + firstArgument.property.name.startsWith('LOG_') + ) { + if (firstArgument.property.name === 'LOG_ERROR') { + // `path` is a call to `Logger.logAction(Logger.LOG_ERROR, ...)`; preserve it. + foundErrorLog = true; + } else { + // `path` is a call to `Logger.logAction(Logger.LOG_*, ...) for some other log level; strip it. + foundLogToStrip = true; + path.remove(); + } + } else { + // `path` is a call to `Logger.logAction(...)` with some argument other than a `Logger.LOG_*` expression; raise an error because we can’t determine whether to strip it. + errors.push({ + location: { + file: args.path, + column: firstArgument.loc.start.column, + line: firstArgument.loc.start.line, + lineText: lines[firstArgument.loc.start.line - 1], + }, + text: `First argument passed to Logger.logAction() must be Logger.LOG_*, got \`${ + babel.generator.default(firstArgument).code + }\``, + }); + } + } else if (babel.types.isIdentifier(path.node.callee.property, { name: 'logActionNoStrip' })) { + // `path` is a call to `Logger.logActionNoStrip(...)`; preserve it. + foundNoStripLog = true; + } + } + }, + }); + + return { contents: babel.generator.default(ast).code, loader: 'ts', errors }; + }); + + build.onEnd(() => { + const errorMessages = []; + + // Perform a sense check to make sure that we found some logging + // calls to strip (to protect us against accidentally changing the + // internal logging API in such a way that would cause us to no + // longer strip any calls). + + if (!foundLogToStrip) { + errorMessages.push('Did not find any Logger.logAction(...) calls to strip'); + } + + // Perform a sense check to make sure that we found some logging + // calls to preserve (to protect us against accidentally changing the + // internal logging API in such a way that would cause us to + // accidentally strip all logging calls). + + if (!foundErrorLog) { + errorMessages.push('Did not find any Logger.logAction(Logger.LOG_ERROR, ...) calls to preserve'); + } + + if (!foundNoStripLog) { + errorMessages.push('Did not find any Logger.logActionNoStrip(...) calls to preserve'); + } + + return { errors: errorMessages.map((text) => ({ text })) }; + }); + }, +}; + +exports.default = stripLogsPlugin; diff --git a/modular.d.ts b/modular.d.ts new file mode 100644 index 0000000000..a62de692dc --- /dev/null +++ b/modular.d.ts @@ -0,0 +1,356 @@ +/** + * You are currently viewing the modular (tree-shakable) variant of the Ably JavaScript Client Library SDK. View the default variant {@link ably | here}. + * + * To get started with the Ably JavaScript Client Library SDK, follow the [Quickstart Guide](https://ably.com/docs/quick-start-guide) or view the introductions to the [realtime](https://ably.com/docs/realtime/usage) and [REST](https://ably.com/docs/rest/usage) interfaces. + * + * ## No `static` class functionality + * + * In contrast to the default variant of the SDK, the modular variant does not expose any functionality via `static` class properties or methods, since they cannot be tree-shaken by module bundlers. Instead, it exports free-standing functions which provide the same functionality. These are: + * + * | `static` version | Replacement in modular variant | + * | ------------------------------------------ | ---------------------------------------------------------------------------------- | + * | `Crypto.generateRandomKey()` | [`generateRandomKey()`](../functions/modular.generateRandomKey.html) | + * | `Crypto.getDefaultParams()` | [`getDefaultCryptoParams()`](../functions/modular.getDefaultCryptoParams.html) | + * | `MessageStatic.fromEncoded()` | [`decodeMessage()`](../functions/modular.decodeMessage.html) | + * | `MessageStatic.fromEncoded()` | [`decodeEncryptedMessage()`](../functions/modular.decodeEncryptedMessage.html) | + * | `MessageStatic.fromEncodedArray()` | [`decodeMessages()`](../functions/modular.decodeMessages.html) | + * | `MessageStatic.fromEncodedArray()` | [`decodeEncryptedMessages()`](../functions/modular.decodeEncryptedMessages.html) | + * | `PresenceMessageStatic.fromEncoded()` | [`decodePresenceMessage()`](../functions/modular.decodePresenceMessage.html) | + * | `PresenceMessageStatic.fromEncodedArray()` | [`decodePresenceMessages()`](../functions/modular.decodePresenceMessages.html) | + * | `PresenceMessageStatic.fromValues()` | [`constructPresenceMessage()`](../functions/modular.constructPresenceMessage.html) | + * + * @module + */ + +import { + ErrorInfo, + RestClient, + ClientOptions, + Crypto as CryptoClass, + MessageStatic, + PresenceMessageStatic, + RealtimeClient, + Auth, + Channels, + Channel, + HttpPaginatedResponse, + StatsParams, + PaginatedResult, + Stats, + BatchPublishSpec, + BatchResult, + BatchPublishSuccessResult, + BatchPresenceFailureResult, + BatchPresenceSuccessResult, + BatchPublishFailureResult, + Push, + RealtimeChannel, + Connection, + CorePlugins, + // The ESLint warning is triggered because we only use this type in a documentation comment. + // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars + AuthOptions, +} from './ably'; + +export declare const generateRandomKey: CryptoClass['generateRandomKey']; +export declare const getDefaultCryptoParams: CryptoClass['getDefaultParams']; +export declare const decodeMessage: MessageStatic['fromEncoded']; +export declare const decodeEncryptedMessage: MessageStatic['fromEncoded']; +export declare const decodeMessages: MessageStatic['fromEncodedArray']; +export declare const decodeEncryptedMessages: MessageStatic['fromEncodedArray']; +export declare const decodePresenceMessage: PresenceMessageStatic['fromEncoded']; +export declare const decodePresenceMessages: PresenceMessageStatic['fromEncodedArray']; +export declare const constructPresenceMessage: PresenceMessageStatic['fromValues']; + +/** + * Provides REST-related functionality to a {@link BaseRealtime} client. + * + * To create a client that includes this plugin, include it in the client options that you pass to the {@link BaseRealtime.constructor}: + * + * ```javascript + * import { BaseRealtime, WebSocketTransport, FetchRequest, Rest } from 'ably/modular'; + * const realtime = new BaseRealtime({ ...options, plugins: { WebSocketTransport, FetchRequest, Rest } }); + * ``` + * + * When provided, the following functionality becomes available: + * + * - { @link ably!Push | push admin } + * - { @link BaseRealtime.time | retrieving Ably service time } + * - { @link ably!Auth.createTokenRequest | creating a token request } using the { @link ably!AuthOptions.queryTime } option + * - { @link BaseRealtime.stats | retrieving your application’s usage statistics } + * - { @link BaseRealtime.request | making arbitrary REST requests } + * - { @link BaseRealtime.batchPublish | batch publishing of messages } + * - { @link BaseRealtime.batchPresence | batch retrieval of channel presence state } + * - { @link ably!Auth.revokeTokens | requesting the revocation of tokens } + * - { @link ably!RealtimeChannel.history | retrieving the message history of a channel } + * - { @link ably!RealtimePresence.history | retrieving the presence history of a channel } + * + * If this plugin is not provided, then trying to use the above functionality will cause a runtime error. + */ +export declare const Rest: unknown; + +/** + * Provides a {@link BaseRest} or {@link BaseRealtime} instance with the ability to encrypt and decrypt {@link ably!Message} payloads. + * + * To create a client that includes this plugin, include it in the client options that you pass to the {@link BaseRealtime.constructor}: + * + * ```javascript + * import { BaseRealtime, WebSocketTransport, FetchRequest, Crypto } from 'ably/modular'; + * const realtime = new BaseRealtime({ ...options, plugins: { WebSocketTransport, FetchRequest, Crypto } }); + * ``` + * + * When provided, you can configure message encryption on a channel via the {@link ably!ChannelOptions.cipher} property of the `ChannelOptions` that you pass when {@link ably!Channels.get | fetching a channel}. If this plugin is not provided, then passing a `ChannelOptions` with a `cipher` property will cause a runtime error. + */ +export declare const Crypto: unknown; + +/** + * Provides a {@link BaseRest} or {@link BaseRealtime} instance with the ability to communicate with the Ably service using the more space-efficient [MessagePack](https://msgpack.org/index.html) format. + * + * To create a client that includes this plugin, include it in the client options that you pass to the {@link BaseRealtime.constructor}: + * + * ```javascript + * import { BaseRealtime, WebSocketTransport, FetchRequest, MsgPack } from 'ably/modular'; + * const realtime = new BaseRealtime({ ...options, plugins: { WebSocketTransport, FetchRequest, MsgPack } }); + * ``` + * + * When provided, you can control whether the client uses MessagePack via the {@link ClientOptions.useBinaryProtocol} client option. If you do not provide this plugin, then the library will always JSON format for encoding messages. + */ +export declare const MsgPack: unknown; + +/** + * Provides a {@link BaseRealtime} instance with the ability to interact with a channel’s presence set. + * + * To create a client that includes this plugin, include it in the client options that you pass to the {@link BaseRealtime.constructor}: + * + * ```javascript + * import { BaseRealtime, WebSocketTransport, FetchRequest, RealtimePresence } from 'ably/modular'; + * const realtime = new BaseRealtime({ ...options, plugins: { WebSocketTransport, FetchRequest, RealtimePresence } }); + * ``` + * + * If you do not provide this plugin, then attempting to access a channel’s {@link ably!RealtimeChannel.presence} property will cause a runtime error. + */ +export declare const RealtimePresence: unknown; + +/** + * Provides a {@link BaseRealtime} instance with the ability to establish a connection with the Ably realtime service using a [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) connection. + * + * To create a client that includes this plugin, include it in the client options that you pass to the {@link BaseRealtime.constructor}: + * + * ```javascript + * import { BaseRealtime, WebSocketTransport, FetchRequest } from 'ably/modular'; + * const realtime = new BaseRealtime({ ...options, plugins: { WebSocketTransport, FetchRequest } }); + * ``` + * + * Note that network conditions, such as firewalls or proxies, might prevent the client from establishing a WebSocket connection. For this reason, you may wish to provide the `BaseRealtime` instance with the ability to alternatively establish a connection using long polling which is less susceptible to these external conditions. You do this by passing in the {@link XHRPolling} module, alongside `WebSocketTransport`: + * + * ```javascript + * import { BaseRealtime, WebSocketTransport, FetchRequest } from 'ably/modules'; + * const realtime = new BaseRealtime(options, { WebSocketTransport, XHRPolling, FetchRequest }); + * ``` + */ +export declare const WebSocketTransport: unknown; + +/** + * Provides a {@link BaseRealtime} instance with the ability to establish a connection with the Ably realtime service using the browser’s [XMLHttpRequest API](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest). + * + * `XHRPolling` uses HTTP long polling; that is, it will make a new HTTP request each time a message is received from Ably. + * + * ```javascript + * import { BaseRealtime, WebSocketTransport, FetchRequest } from 'ably/modular'; + * const realtime = new BaseRealtime({ ...options, plugins: { XHRPolling, FetchRequest } }); + * ``` + * + * Provide this plugin if, for example, you wish the client to have an alternative mechanism for connecting to Ably if it’s unable to establish a WebSocket connection. + */ +export declare const XHRPolling: unknown; + +/** + * Provides a {@link BaseRest} or {@link BaseRealtime} instance with the ability to make HTTP requests using the browser’s [XMLHttpRequest API](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest). + * + * To create a client that includes this plugin, include it in the client options that you pass to the {@link BaseRealtime.constructor}: + * + * ```javascript + * import { BaseRealtime, WebSocketTransport, XHRRequest } from 'ably/modular'; + * const realtime = new BaseRealtime({ ...options, plugins: { WebSocketTransport, XHRRequest } }); + * ``` + */ +export declare const XHRRequest: unknown; + +/** + * Provides a {@link BaseRest} or {@link BaseRealtime} instance with the ability to make HTTP requests using the browser’s [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). + * + * To create a client that includes this plugin, include it in the client options that you pass to the {@link BaseRealtime.constructor}: + * + * ```javascript + * import { BaseRealtime, WebSocketTransport, FetchRequest } from 'ably/modular'; + * const realtime = new BaseRealtime({ ...options, plugins: { WebSocketTransport, FetchRequest } }); + * ``` + */ +export declare const FetchRequest: unknown; + +/** + * Provides a {@link BaseRealtime} instance with the ability to filter channel subscriptions at runtime using { @link ably!RealtimeChannel.subscribe:WITH_MESSAGE_FILTER | the overload of `subscribe()` that accepts a `MessageFilter` }. + * + * To create a client that includes this plugin, include it in the client options that you pass to the {@link BaseRealtime.constructor}: + * + * ```javascript + * import { BaseRealtime, WebSocketTransport, FetchRequest, MessageInteractions } from 'ably/modular'; + * const realtime = new BaseRealtime({ ...options, plugins: { WebSocketTransport, FetchRequest, MessageInteractions } }); + * ``` + * + * If you do not provide this plugin, then attempting to use this overload of `subscribe()` will cause a runtime error. + */ +export declare const MessageInteractions: unknown; + +/** + * Pass a `ModularPlugins` in the {@link ClientOptions.plugins} property of the options that you pass to { @link BaseRest.constructor | the constructor of BaseRest } or {@link BaseRealtime.constructor | that of BaseRealtime} to specify which functionality should be made available to that client. + */ +export interface ModularPlugins { + /** + * See {@link Rest | documentation for the `Rest` plugin}. + */ + Rest?: typeof Rest; + + /** + * See {@link Crypto | documentation for the `Crypto` plugin}. + */ + Crypto?: typeof Crypto; + + /** + * See {@link MsgPack | documentation for the `MsgPack` plugin}. + */ + MsgPack?: typeof MsgPack; + + /** + * See {@link RealtimePresence | documentation for the `RealtimePresence` plugin}. + */ + RealtimePresence?: typeof RealtimePresence; + + /** + * See {@link WebSocketTransport | documentation for the `WebSocketTransport` plugin}. + */ + WebSocketTransport?: typeof WebSocketTransport; + + /** + * See {@link XHRPolling | documentation for the `XHRPolling` plugin}. + */ + XHRPolling?: typeof XHRPolling; + + /** + * See {@link XHRRequest | documentation for the `XHRRequest` plugin}. + */ + XHRRequest?: typeof XHRRequest; + + /** + * See {@link FetchRequest | documentation for the `FetchRequest` plugin}. + */ + FetchRequest?: typeof FetchRequest; + + /** + * See {@link MessageInteractions | documentation for the `MessageInteractions` plugin}. + */ + MessageInteractions?: typeof MessageInteractions; +} + +/** + * A client that offers a simple stateless API to interact directly with Ably's REST API. + * + * `BaseRest` is the equivalent, in the modular variant of the Ably Client Library SDK, of the [`Rest`](../../default/classes/Rest.html) class in the default variant of the SDK. The difference is that its constructor allows you to decide exactly which functionality the client should include. This allows unused functionality to be tree-shaken, reducing bundle size. + * + * > **Note** + * > + * > In order to further reduce bundle size, `BaseRest` performs less logging than the `Rest` class exported by the default variant of the SDK. It only logs: + * > + * > - messages that have a {@link ClientOptions.logLevel | `logLevel`} of 1 (that is, errors) + * > - a small number of other network events + * > + * > If you need more verbose logging, use the default variant of the SDK. + */ +export declare class BaseRest implements RestClient { + /** + * Construct a client object using an Ably {@link ClientOptions} object. + * + * @param options - A {@link ClientOptions} object to configure the client connection to Ably. Its {@link ClientOptions.plugins} property should be an object describing which functionality the client should offer. See the documentation for {@link ModularPlugins}. + * + * You must provide at least one HTTP request implementation; that is, one of {@link FetchRequest} or {@link XHRRequest}. For minimum bundle size, favour `FetchRequest`. + * + * The {@link Rest} plugin is always implicitly included. + */ + constructor(options: ClientOptions); + + // Requirements of RestClient + + auth: Auth; + channels: Channels; + request( + method: string, + path: string, + version: number, + params?: any, + body?: any[] | any, + headers?: any, + ): Promise>; + stats(params?: StatsParams): Promise>; + time(): Promise; + batchPublish(spec: BatchPublishSpec): Promise>; + batchPublish( + specs: BatchPublishSpec[], + ): Promise[]>; + batchPresence(channels: string[]): Promise[]>; + push: Push; +} + +/** + * A client that extends the functionality of {@link BaseRest} and provides additional realtime-specific features. + * + * `BaseRealtime` is the equivalent, in the modular variant of the Ably Client Library SDK, of the [`Realtime`](../../default/classes/Realtime.html) class in the default variant of the SDK. The difference is that its constructor allows you to decide exactly which functionality the client should include. This allows unused functionality to be tree-shaken, reducing bundle size. + * + * > **Note** + * > + * > In order to further reduce bundle size, `BaseRealtime` performs less logging than the `Realtime` class exported by the default variant of the SDK. It only logs: + * > + * > - messages that have a {@link ClientOptions.logLevel | `logLevel`} of 1 (that is, errors) + * > - a small number of other network events + * > + * > If you need more verbose logging, use the default variant of the SDK. + */ +export declare class BaseRealtime implements RealtimeClient { + /** + * Construct a client object using an Ably {@link ClientOptions} object. + * + * @param options - A {@link ClientOptions} object to configure the client connection to Ably. Its {@link ClientOptions.plugins} property should be an object describing which functionality the client should offer. See the documentation for {@link ModularPlugins}. + * + * You must provide: + * + * - at least one HTTP request implementation; that is, one of {@link FetchRequest} or {@link XHRRequest} — for minimum bundle size, favour `FetchRequest`; + * - at least one realtime transport implementation; that is, one of {@link WebSocketTransport} or {@link XHRPolling} — for minimum bundle size, favour `WebSocketTransport`. + */ + constructor(options: ClientOptions); + + // Requirements of RealtimeClient + + clientId: string; + close(): void; + connect(): void; + auth: Auth; + channels: Channels; + connection: Connection; + request( + method: string, + path: string, + version: number, + params?: any, + body?: any[] | any, + headers?: any, + ): Promise>; + stats(params?: StatsParams): Promise>; + time(): Promise; + batchPublish(spec: BatchPublishSpec): Promise>; + batchPublish( + specs: BatchPublishSpec[], + ): Promise[]>; + batchPresence(channels: string[]): Promise[]>; + push: Push; +} + +export { ErrorInfo }; diff --git a/package-lock.json b/package-lock.json index 16a1b795fc..19d7c78759 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ably", - "version": "1.2.50", + "version": "2.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ably", - "version": "1.2.50", + "version": "2.0.0", "license": "Apache-2.0", "dependencies": { "@ably/msgpack-js": "^0.4.0", @@ -14,25 +14,28 @@ "ws": "^8.14.2" }, "devDependencies": { - "@ably/vcdiff-decoder": "1.0.4", + "@ably/vcdiff-decoder": "1.0.6", "@arethetypeswrong/cli": "^0.13.1", - "@babel/preset-env": "^7.23.6", + "@babel/generator": "^7.23.6", + "@babel/parser": "^7.23.6", + "@babel/traverse": "^7.23.7", "@testing-library/react": "^13.3.0", - "@types/crypto-js": "^4.0.1", + "@types/cli-table": "^0.3.4", "@types/jmespath": "^0.15.2", - "@types/node": "^15.0.0", + "@types/node": "^18.0.0", "@types/request": "^2.48.7", "@types/ws": "^8.2.0", - "@typescript-eslint/eslint-plugin": "^5.14.0", - "@typescript-eslint/parser": "^5.14.0", + "@typescript-eslint/eslint-plugin": "^5.59.6", + "@typescript-eslint/parser": "^5.59.6", "@vitejs/plugin-react": "^1.3.2", "async": "ably-forks/async#requirejs", "aws-sdk": "^2.1413.0", - "babel-loader": "^8.3.0", "chai": "^4.2.0", - "copy-webpack-plugin": "^6.4.1", + "cli-table": "^0.3.11", "cors": "^2.8.5", - "crypto-js": "ably-forks/crypto-js#crypto-lite", + "esbuild": "^0.18.10", + "esbuild-plugin-umd-wrapper": "ably-forks/esbuild-plugin-umd-wrapper#1.0.7-optional-amd-named-module", + "esbuild-runner": "^2.2.2", "eslint": "^7.13.0", "eslint-plugin-import": "^2.28.0", "eslint-plugin-jsdoc": "^40.0.0", @@ -41,42 +44,39 @@ "eslint-plugin-security": "^1.4.0", "express": "^4.17.1", "glob": "~4.4", - "google-closure-compiler": "^20180610.0.1", "grunt": "^1.6.1", - "grunt-bump": "^0.3.1", "grunt-cli": "~1.2.0", - "grunt-closure-tools": "^1.0.0", - "grunt-contrib-concat": "~0.5", "grunt-shell": "~1.1", - "grunt-webpack": "^4.0.2", + "grunt-webpack": "^5.0.0", "hexy": "~0.2", "jmespath": "^0.16.0", "jsdom": "^20.0.0", - "kexec": "ably-forks/node-kexec#update-for-node-12", "minimist": "^1.2.5", "mocha": "^8.1.3", "mocha-junit-reporter": "^2.2.1", - "null-loader": "^4.0.1", + "path-browserify": "^1.0.1", "playwright": "^1.39.0", - "prettier": "^2.5.1", + "prettier": "^2.8.8", + "process": "^0.11.10", "react": ">=18.1.0", "react-dom": ">=18.1.0", "requirejs": "~2.1", "shelljs": "~0.8", "source-map-explorer": "^2.5.2", - "ts-loader": "^8.2.0", + "source-map-support": "^0.5.21", + "stream-browserify": "^3.0.0", + "ts-loader": "^9.4.2", "tsconfig-paths-webpack-plugin": "^4.0.1", "tslib": "^2.3.1", - "typedoc": "^0.23.8", - "typescript": "^4.6.4", + "typedoc": "^0.24.7", + "typescript": "^4.9.5", "vite": "^4.4.9", "vitest": "^0.18.0", - "webpack": "^4.44.2", - "webpack-cli": "^4.2.0", - "webpack-node-externals": "^3.0.0" + "webpack": "^5.79.0", + "webpack-cli": "^5.0.1" }, "engines": { - "node": ">=5.10.x" + "node": ">=16" }, "peerDependencies": { "react": ">=16.8.0", @@ -91,6 +91,15 @@ } } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@ably/msgpack-js": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@ably/msgpack-js/-/msgpack-js-0.4.0.tgz", @@ -100,9 +109,9 @@ } }, "node_modules/@ably/vcdiff-decoder": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@ably/vcdiff-decoder/-/vcdiff-decoder-1.0.4.tgz", - "integrity": "sha512-D2j7j+keGWOI48ahTjtKmPEahSXtnm2PLVSp1fDCctibyGd/ywRWyJP2TzNtVMwL1IDD9PAaKPPK3+cTo0WP3g==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@ably/vcdiff-decoder/-/vcdiff-decoder-1.0.6.tgz", + "integrity": "sha512-VdA8vat5GIaG7gQaUL4ZpF5x3iHH9IolfcztEtQXJa7OSw++G/x9UrNQFXsdv4ZyiMAti6bRMR70G1zNkL67/g==", "dev": true }, "node_modules/@ampproject/remapping": { @@ -125,12 +134,12 @@ "dev": true }, "node_modules/@arethetypeswrong/cli": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/@arethetypeswrong/cli/-/cli-0.13.1.tgz", - "integrity": "sha512-nmaSMnVb1iHvJIJ4UmrNMRwtxxjnuOL2IELTuYLP4QatnebrGmqeRWOzbsuwh9OT48LX4fxp+wnJMGdrUf/nvw==", + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/@arethetypeswrong/cli/-/cli-0.13.5.tgz", + "integrity": "sha512-S5vPcInsCwt6QJlPi1CxxkVfemVrapeeySv5a1jeodwUzfkG/rLjZTYd1uhwgqDMCgQK4sR8z+W9GZITdqLIew==", "dev": true, "dependencies": { - "@arethetypeswrong/core": "0.13.0", + "@arethetypeswrong/core": "0.13.5", "chalk": "^4.1.2", "cli-table3": "^0.6.3", "commander": "^10.0.1", @@ -145,37 +154,17 @@ "node": ">=18" } }, - "node_modules/@arethetypeswrong/cli/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@arethetypeswrong/cli/node_modules/marked": { - "version": "9.1.6", - "resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz", - "integrity": "sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==", - "dev": true, - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 16" - } - }, "node_modules/@arethetypeswrong/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@arethetypeswrong/core/-/core-0.13.0.tgz", - "integrity": "sha512-dEDo+Vq/Kt12ZZJANuUDKWZzogswcmndcY293LVgswKv69YXq9jMR8HjVTDGhwnlGJ8fAiJC+XYhg/wfTwAFCA==", + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/@arethetypeswrong/core/-/core-0.13.5.tgz", + "integrity": "sha512-Ahk+vBUK9RBBx4zle2Y1imrbl68GCtLEl0/UBxsJeDd2j5HkbWcgGtgE2tEPMy4qAgWxj2JEptDPl6Jucbov2w==", "dev": true, "dependencies": { "@andrewbranch/untar.js": "^1.0.3", "fflate": "^0.7.4", "semver": "^7.5.4", - "typescript": "^5.2.2", + "ts-expose-internals-conditionally": "1.0.0-empty.0", + "typescript": "5.3.3", "validate-npm-package-name": "^5.0.0" }, "engines": { @@ -183,9 +172,9 @@ } }, "node_modules/@arethetypeswrong/core/node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -196,18 +185,30 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.13", + "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/code-frame/node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -222,6 +223,51 @@ "node": ">=4" } }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/compat-data": { "version": "7.23.5", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", @@ -232,22 +278,22 @@ } }, "node_modules/@babel/core": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.11.tgz", - "integrity": "sha512-lh7RJrtPdhibbxndr6/xx0w8+CVlY5FJZiaSz908Fpy+G0xkBFTvwLcKJFF4PJxVfGhVWNebikpWGnOoC71juQ==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz", + "integrity": "sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-compilation-targets": "^7.22.10", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.11", - "@babel/parser": "^7.22.11", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.11", - "@babel/types": "^7.22.11", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.7", + "@babel/parser": "^7.23.6", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", @@ -261,18 +307,6 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -283,12 +317,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", - "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dev": true, "dependencies": { - "@babel/types": "^7.22.10", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -309,18 +343,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", - "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-compilation-targets": { "version": "7.23.6", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", @@ -346,97 +368,6 @@ "semver": "bin/semver.js" } }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.6.tgz", - "integrity": "sha512-cBXU1vZni/CpGF29iTu4YRbOZt3Wat6zCoMDxRF1MayiEc4URxOj31tT65HUM0CRpMowA3HCJaAOVOUnMf96cw==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-member-expression-to-functions": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/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/@babel/helper-create-regexp-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", - "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "regexpu-core": "^5.3.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin/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/@babel/helper-define-polyfill-provider": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.4.tgz", - "integrity": "sha512-QcJMILQCu2jm5TFPGA3lCpJJTeEP+mqeXooG/NZbg/h5FTFi6V0+99ahlRsW8/kRLyb24LZVCCiclDedhLKcBA==", - "dev": true, - "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/helper-define-polyfill-provider/node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/@babel/helper-environment-visitor": { "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", @@ -471,18 +402,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", - "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-module-imports": { "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", @@ -514,18 +433,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-plugin-utils": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", @@ -535,40 +442,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", - "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-wrap-function": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", - "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.22.15", - "@babel/helper-optimise-call-expression": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, "node_modules/@babel/helper-simple-access": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", @@ -581,18 +454,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-split-export-declaration": { "version": "7.22.6", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", @@ -632,46 +493,44 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", - "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", + "node_modules/@babel/helpers": { + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.8.tgz", + "integrity": "sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ==", "dev": true, "dependencies": { - "@babel/helper-function-name": "^7.22.5", "@babel/template": "^7.22.15", - "@babel/types": "^7.22.19" + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helpers": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.11.tgz", - "integrity": "sha512-vyOXC8PBWaGc5h7GMsNx68OH33cypkEDJCHvYVVgVbbxJDROYVtexSk0gK5iCF1xNjRIN2s8ai7hwkWDq5szWg==", + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.11", - "@babel/types": "^7.22.11" + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", - "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "color-convert": "^1.9.0" }, "engines": { - "node": ">=6.9.0" + "node": ">=4" } }, "node_modules/@babel/highlight/node_modules/chalk": { @@ -688,109 +547,89 @@ "node": ">=4" } }, - "node_modules/@babel/parser": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", - "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" + "dependencies": { + "color-name": "1.1.3" } }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz", - "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==", + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">=0.8.0" } }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz", - "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==", + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.23.3" - }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" + "node": ">=4" } }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.3.tgz", - "integrity": "sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w==", + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.22.5" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">=4" } }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "node_modules/@babel/parser": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", "dev": true, - "engines": { - "node": ">=6.9.0" + "bin": { + "parser": "bin/babel-parser.js" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/helper-plugin-utils": "^7.22.5" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz", + "integrity": "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/types": "^7.23.4" }, "engines": { "node": ">=6.9.0" @@ -799,34 +638,25 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", + "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/plugin-transform-react-jsx": "^7.22.5" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-import-assertions": { + "node_modules/@babel/plugin-transform-react-jsx-self": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz", - "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz", + "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -838,10 +668,10 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-import-attributes": { + "node_modules/@babel/plugin-transform-react-jsx-source": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz", - "integrity": "sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz", + "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -853,2834 +683,2896 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "node_modules/@babel/runtime": { + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.8.tgz", + "integrity": "sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "regenerator-runtime": "^0.14.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "node_modules/@babel/traverse": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz", + "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", + "globals": "^11.1.0" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "node_modules/@babel/types": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "optional": true, + "engines": { + "node": ">=0.1.90" } }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=10.0.0" } }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "node_modules/@es-joy/jsdoccomment": { + "version": "0.37.1", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.37.1.tgz", + "integrity": "sha512-5vxWJ1gEkEF0yRd0O+uK6dHJf7adrxwQSX8PuRiPfFSAbNLnY0ZJfXaZucoz14Jj2N11xn2DnlEPwWRpYpvRjg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "comment-parser": "1.3.1", + "esquery": "^1.5.0", + "jsdoc-type-pratt-parser": "~4.0.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": "^14 || ^16 || ^17 || ^18 || ^19 || ^20" } }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=12" } }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=12" } }, - "node_modules/@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">=12" } }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz", - "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=12" } }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.4.tgz", - "integrity": "sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.20", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=12" } }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", - "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.20" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=12" } }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz", - "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==", + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=12" } }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz", - "integrity": "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==", + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=12" } }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", - "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=12" } }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz", - "integrity": "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" + "node": ">=12" } }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.5.tgz", - "integrity": "sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg==", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", - "@babel/helper-split-export-declaration": "^7.22.6", - "globals": "^11.1.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=12" } }, - "node_modules/@babel/plugin-transform-classes/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=4" + "node": ">=12" } }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz", - "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==", + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/template": "^7.22.15" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=12" } }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz", - "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==", + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=12" } }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz", - "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==", + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=12" } }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz", - "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==", + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=12" } }, - "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz", - "integrity": "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==", + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - }, + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=12" } }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz", - "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==", + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=12" } }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", - "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=12" } }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz", - "integrity": "sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==", + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=12" } }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz", - "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5" + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz", - "integrity": "sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==", + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz", - "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==", + "node_modules/@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", - "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + "type-fest": "^0.20.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=8" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz", - "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==", + "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==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 4" } }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz", - "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==", + "node_modules/@eslint/eslintrc/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==", "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5" - }, "engines": { - "node": ">=6.9.0" + "node": ">=10" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", - "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", + "node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=10.10.0" } }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz", - "integrity": "sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==", + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "dependencies": { - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz", - "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5" - }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", - "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz", - "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==", + "node_modules/@jridgewell/source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" } }, - "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", - "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.21.tgz", + "integrity": "sha512-SRfKmRe1KvYnxjEMtxEr+J4HIeMX5YBg/qhRHpxEIGjhX1rshcHlnFUE9K0GazhVKWM7B+nARSkV8LuvJdJ5/g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz", - "integrity": "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==", + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 8" } }, - "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz", - "integrity": "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, - "dependencies": { - "@babel/compat-data": "^7.23.3", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.23.3" - }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 8" } }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz", - "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==", + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 8" } }, - "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz", - "integrity": "sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==", + "node_modules/@rollup/pluginutils": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 8.0.0" } }, - "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", - "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "engines": { - "node": ">=6.9.0" + "node": ">=10" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz", - "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==", - "dev": true, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "defer-to-connect": "^2.0.0" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=10" } }, - "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", - "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", + "node_modules/@testing-library/dom": { + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz", + "integrity": "sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=12" } }, - "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", - "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", + "node_modules/@testing-library/react": { + "version": "13.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", + "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.5.0", + "@types/react-dom": "^18.0.0" }, "engines": { - "node": ">=6.9.0" + "node": ">=12" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz", - "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==", + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 10" } }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.5.tgz", - "integrity": "sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA==", - "dev": true, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.22.5", - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" } }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", - "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", + "node_modules/@types/caseless": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", + "dev": true + }, + "node_modules/@types/chai": { + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.11.tgz", + "integrity": "sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ==", + "dev": true + }, + "node_modules/@types/chai-subset": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.5.tgz", + "integrity": "sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==", "dev": true, "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@types/chai": "*" } }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.22.5.tgz", - "integrity": "sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==", + "node_modules/@types/cli-table": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@types/cli-table/-/cli-table-0.3.4.tgz", + "integrity": "sha512-GsALrTL69mlwbAw/MHF1IPTadSLZQnsxe7a80G8l4inN/iEXCOcVeT/S7aRc6hbhqzL9qZ314kHPDQnQ3ev+HA==", + "dev": true + }, + "node_modules/@types/eslint": { + "version": "8.56.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.2.tgz", + "integrity": "sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@types/estree": "*", + "@types/json-schema": "*" } }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.22.5.tgz", - "integrity": "sha512-yIiRO6yobeEIaI0RTbIr8iAK9FcBHLtZq0S89ZPjDLQXBA4xvghaKqI0etp/tF3htTM0sazJKKLz9oEiGRtu7w==", + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@types/eslint": "*", + "@types/estree": "*" } }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz", - "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==", - "dev": true, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" + }, + "node_modules/@types/jmespath": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@types/jmespath/-/jmespath-0.15.2.tgz", + "integrity": "sha512-pegh49FtNsC389Flyo9y8AfkVIZn9MMPE9yJrO9svhq6Fks2MwymULWjZqySuxmctd3ZH4/n7Mr98D+1Qo5vGA==", + "dev": true + }, + "node_modules/@types/json-schema": { + "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/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "regenerator-transform": "^0.15.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@types/node": "*" } }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz", - "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==", - "dev": true, + "node_modules/@types/node": { + "version": "18.19.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.8.tgz", + "integrity": "sha512-g1pZtPhsvGVTwmeVoexWZLTQaOvXwoSq//pTL0DHeNzUDrFnir4fgETdhjhIxjVnN+hKOuh98+E1eMLnUXstFg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "undici-types": "~5.26.4" } }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz", - "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==", + "node_modules/@types/prop-types": { + "version": "15.7.11", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.2.48", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.48.tgz", + "integrity": "sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" } }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz", - "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==", + "node_modules/@types/react-dom": { + "version": "18.2.18", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.18.tgz", + "integrity": "sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@types/react": "*" } }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz", - "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==", + "node_modules/@types/request": { + "version": "2.48.12", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", + "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" } }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz", - "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==", - "dev": true, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@types/node": "*" } }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz", - "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==", + "node_modules/@types/scheduler": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "dev": true + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true + }, + "node_modules/@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@types/node": "*" } }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz", - "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==", + "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": { - "@babel/helper-plugin-utils": "^7.22.5" + "@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": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz", - "integrity": "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==", + "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": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@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": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz", - "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==", + "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": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" }, "engines": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz", - "integrity": "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==", + "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": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@babel/preset-env": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.6.tgz", - "integrity": "sha512-2XPn/BqKkZCpzYhUUNZ1ssXw7DcXfKQEjv/uXZUXgaebCMYmkEsfZ2yY+vv+xtXv50WmL5SGhyB6/xsWxIvvOQ==", + "node_modules/@typescript-eslint/types": { + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "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": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.3", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.23.3", - "@babel/plugin-syntax-import-attributes": "^7.23.3", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.23.3", - "@babel/plugin-transform-async-generator-functions": "^7.23.4", - "@babel/plugin-transform-async-to-generator": "^7.23.3", - "@babel/plugin-transform-block-scoped-functions": "^7.23.3", - "@babel/plugin-transform-block-scoping": "^7.23.4", - "@babel/plugin-transform-class-properties": "^7.23.3", - "@babel/plugin-transform-class-static-block": "^7.23.4", - "@babel/plugin-transform-classes": "^7.23.5", - "@babel/plugin-transform-computed-properties": "^7.23.3", - "@babel/plugin-transform-destructuring": "^7.23.3", - "@babel/plugin-transform-dotall-regex": "^7.23.3", - "@babel/plugin-transform-duplicate-keys": "^7.23.3", - "@babel/plugin-transform-dynamic-import": "^7.23.4", - "@babel/plugin-transform-exponentiation-operator": "^7.23.3", - "@babel/plugin-transform-export-namespace-from": "^7.23.4", - "@babel/plugin-transform-for-of": "^7.23.6", - "@babel/plugin-transform-function-name": "^7.23.3", - "@babel/plugin-transform-json-strings": "^7.23.4", - "@babel/plugin-transform-literals": "^7.23.3", - "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", - "@babel/plugin-transform-member-expression-literals": "^7.23.3", - "@babel/plugin-transform-modules-amd": "^7.23.3", - "@babel/plugin-transform-modules-commonjs": "^7.23.3", - "@babel/plugin-transform-modules-systemjs": "^7.23.3", - "@babel/plugin-transform-modules-umd": "^7.23.3", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.23.3", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", - "@babel/plugin-transform-numeric-separator": "^7.23.4", - "@babel/plugin-transform-object-rest-spread": "^7.23.4", - "@babel/plugin-transform-object-super": "^7.23.3", - "@babel/plugin-transform-optional-catch-binding": "^7.23.4", - "@babel/plugin-transform-optional-chaining": "^7.23.4", - "@babel/plugin-transform-parameters": "^7.23.3", - "@babel/plugin-transform-private-methods": "^7.23.3", - "@babel/plugin-transform-private-property-in-object": "^7.23.4", - "@babel/plugin-transform-property-literals": "^7.23.3", - "@babel/plugin-transform-regenerator": "^7.23.3", - "@babel/plugin-transform-reserved-words": "^7.23.3", - "@babel/plugin-transform-shorthand-properties": "^7.23.3", - "@babel/plugin-transform-spread": "^7.23.3", - "@babel/plugin-transform-sticky-regex": "^7.23.3", - "@babel/plugin-transform-template-literals": "^7.23.3", - "@babel/plugin-transform-typeof-symbol": "^7.23.3", - "@babel/plugin-transform-unicode-escapes": "^7.23.3", - "@babel/plugin-transform-unicode-property-regex": "^7.23.3", - "@babel/plugin-transform-unicode-regex": "^7.23.3", - "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.6", - "babel-plugin-polyfill-corejs3": "^0.8.5", - "babel-plugin-polyfill-regenerator": "^0.5.3", - "core-js-compat": "^3.31.0", - "semver": "^6.3.1" + "@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.7", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "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, - "bin": { - "semver": "bin/semver.js" + "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/@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "node_modules/@typescript-eslint/visitor-keys": { + "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": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", + "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==", "dev": true }, - "node_modules/@babel/runtime": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz", - "integrity": "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==", + "node_modules/@vitejs/plugin-react": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-1.3.2.tgz", + "integrity": "sha512-aurBNmMo0kz1O4qRoY+FM4epSA39y3ShWGuqfLRA/3z0oEJAdtoSfgA3aO98/PCCHAqMaduLxIxErWrVKIFzXA==", "dev": true, "dependencies": { - "regenerator-runtime": "^0.14.0" + "@babel/core": "^7.17.10", + "@babel/plugin-transform-react-jsx": "^7.17.3", + "@babel/plugin-transform-react-jsx-development": "^7.16.7", + "@babel/plugin-transform-react-jsx-self": "^7.16.7", + "@babel/plugin-transform-react-jsx-source": "^7.16.7", + "@rollup/pluginutils": "^4.2.1", + "react-refresh": "^0.13.0", + "resolve": "^1.22.0" }, "engines": { - "node": ">=6.9.0" + "node": ">=12.0.0" } }, - "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "node_modules/@webassemblyjs/ast": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, - "node_modules/@babel/traverse": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.11.tgz", - "integrity": "sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==", + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.11", - "@babel/types": "^7.22.11", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" } }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", "dev": true, - "engines": { - "node": ">=4" + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" } }, - "node_modules/@babel/types": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", - "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" + "@xtuc/ieee754": "^1.2.0" } }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "dev": true, - "optional": true, - "engines": { - "node": ">=0.1.90" + "dependencies": { + "@xtuc/long": "4.2.2" } }, - "node_modules/@es-joy/jsdoccomment": { - "version": "0.36.1", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.36.1.tgz", - "integrity": "sha512-922xqFsTpHs6D0BUiG4toiyPOMc8/jafnWKxz1KWgS4XzKPy2qXf1Pe6UFuNSCQqt6tOuhAWXBNuuyUhJmw9Vg==", + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", "dev": true, "dependencies": { - "comment-parser": "1.3.1", - "esquery": "^1.4.0", - "jsdoc-type-pratt-parser": "~3.1.0" - }, - "engines": { - "node": "^14 || ^16 || ^17 || ^18 || ^19" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz", - "integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==", - "cpu": [ - "arm" - ], + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", - "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", - "cpu": [ - "arm64" - ], + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", - "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", - "cpu": [ - "x64" - ], + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", - "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", - "cpu": [ - "arm64" - ], + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@xtuc/long": "4.2.2" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", - "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", - "cpu": [ - "x64" - ], + "node_modules/@webpack-cli/configtest": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": ">=12" + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", - "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", - "cpu": [ - "arm64" - ], + "node_modules/@webpack-cli/info": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], "engines": { - "node": ">=12" + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", - "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", - "cpu": [ - "x64" - ], + "node_modules/@webpack-cli/serve": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], "engines": { - "node": ">=12" + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", - "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", - "cpu": [ - "arm" - ], + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, "engines": { - "node": ">=12" + "node": ">= 0.6" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", - "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", - "cpu": [ - "arm64" - ], + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "bin": { + "acorn": "bin/acorn" + }, "engines": { - "node": ">=12" + "node": ">=0.4.0" } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", - "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", - "cpu": [ - "ia32" - ], + "node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz", - "integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==", - "cpu": [ - "loong64" - ], + "node_modules/acorn-globals/node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "bin": { + "acorn": "bin/acorn" + }, "engines": { - "node": ">=12" + "node": ">=0.4.0" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", - "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", - "cpu": [ - "mips64el" - ], + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", - "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", - "cpu": [ - "ppc64" - ], + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", "dev": true, - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=12" + "node": ">=0.4.0" } }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", - "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", - "cpu": [ - "riscv64" - ], + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "debug": "4" + }, "engines": { - "node": ">=12" + "node": ">= 6.0.0" } }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", - "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", - "cpu": [ - "s390x" - ], + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", - "cpu": [ - "x64" - ], + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "peerDependencies": { + "ajv": "^6.9.1" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", - "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", - "cpu": [ - "x64" - ], + "node_modules/ansi-colors": { + "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, - "optional": true, - "os": [ - "netbsd" - ], "engines": { - "node": ">=12" + "node": ">=6" } }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", - "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", - "cpu": [ - "x64" - ], + "node_modules/ansi-escapes": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", + "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", "dev": true, - "optional": true, - "os": [ - "openbsd" - ], + "dependencies": { + "type-fest": "^3.0.0" + }, "engines": { - "node": ">=12" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", - "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", - "cpu": [ - "x64" - ], + "node_modules/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, - "optional": true, - "os": [ - "sunos" - ], "engines": { - "node": ">=12" + "node": ">=8" } }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", - "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } + "node_modules/ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", + "dev": true }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", - "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", - "cpu": [ - "ia32" - ], + "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, - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=12" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@esbuild/win32-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", - "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } + "node_modules/ansicolors": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", + "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==", + "dev": true }, - "node_modules/@eslint/eslintrc": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.1.tgz", - "integrity": "sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA==", + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "lodash": "^4.17.19", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">= 8" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" + "sprintf-js": "~1.0.2" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", "dev": true, - "engines": { - "node": ">=6.0.0" + "dependencies": { + "deep-equal": "^2.0.5" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", "dev": true, - "engines": { - "node": ">=6.0.0" + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "node_modules/array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==", "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "dev": true }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", - "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "node_modules/array-includes": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "2.0.4", - "run-parallel": "^1.1.9" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-string": "^1.0.7" }, "engines": { - "node": ">= 8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", - "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", + "node_modules/array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", "dev": true, "engines": { - "node": ">= 8" + "node": ">=0.10.0" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", - "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "node_modules/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, - "dependencies": { - "@nodelib/fs.scandir": "2.1.4", - "fastq": "^1.6.0" - }, "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/@npmcli/move-file": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", - "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "node_modules/array.prototype.findlastindex": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", + "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", "dev": true, "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@npmcli/move-file/node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" }, "engines": { - "node": "*" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@npmcli/move-file/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@npmcli/move-file/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/array.prototype.tosorted": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz", + "integrity": "sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==", "dev": true, "dependencies": { - "glob": "^7.1.3" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@rollup/pluginutils": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", - "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "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, - "dependencies": { - "estree-walker": "^2.0.1", - "picomatch": "^2.2.2" - }, "engines": { - "node": ">= 8.0.0" + "node": "*" } }, - "node_modules/@sindresorhus/is": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.4.0.tgz", - "integrity": "sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ==", + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=8" + } + }, + "node_modules/async": { + "version": "1.5.2", + "resolved": "git+ssh://git@github.com/ably-forks/async.git#76306396b3d3a25316be0170ab6483ccbaaaa097", + "dev": true, + "license": "MIT" + }, + "node_modules/asynciterator.prototype": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", + "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "node_modules/aws-sdk": { + "version": "2.1539.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1539.0.tgz", + "integrity": "sha512-gqQgZPFWSfYFkkTDI0JEF9vs8ASYFm8z1+hH933sXMhnTqUB5jgaujcicpe3PJ3cy/SLAvNSzc6MhKoZiIU9+g==", + "dev": true, "dependencies": { - "defer-to-connect": "^2.0.0" + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.5.0" }, "engines": { - "node": ">=10" + "node": ">= 10.0.0" } }, - "node_modules/@testing-library/dom": { - "version": "8.20.1", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz", - "integrity": "sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==", + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.0.2.tgz", + "integrity": "sha512-ZXBDPMt/v/8fsIqn+Z5VwrhdR6jVka0bYobHdGia0Nxi7BJ9i/Uvml3AocHIBtIIBhZjBw5MR0aR4ROs/8+SNg==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true, - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.1.3", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" - }, "engines": { - "node": ">=12" + "node": ">=8" } }, - "node_modules/@testing-library/react": { - "version": "13.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", - "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==", + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dev": true, "dependencies": { - "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^8.5.0", - "@types/react-dom": "^18.0.0" + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">=12" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, - "engines": { - "node": ">= 10" + "dependencies": { + "ms": "2.0.0" } }, - "node_modules/@types/aria-query": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", - "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==", + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/@types/cacheable-request": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", - "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", + "node_modules/bops": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bops/-/bops-1.0.1.tgz", + "integrity": "sha512-qCMBuZKP36tELrrgXpAfM+gHzqa0nLsWZ+L37ncsb8txYlnAoxOPpVp+g7fK0sGkMXfA0wl8uQkESqw3v4HNag==", "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "*", - "@types/node": "*", - "@types/responselike": "*" + "base64-js": "1.0.2", + "to-utf8": "0.0.1" } }, - "node_modules/@types/caseless": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", - "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", - "dev": true - }, - "node_modules/@types/chai": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", - "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==", - "dev": true + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } }, - "node_modules/@types/chai-subset": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.3.tgz", - "integrity": "sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==", + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "dependencies": { - "@types/chai": "*" + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@types/crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==", + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" - }, - "node_modules/@types/jmespath": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@types/jmespath/-/jmespath-0.15.2.tgz", - "integrity": "sha512-pegh49FtNsC389Flyo9y8AfkVIZn9MMPE9yJrO9svhq6Fks2MwymULWjZqySuxmctd3ZH4/n7Mr98D+1Qo5vGA==", - "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==", - "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/keyv": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", - "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", + "node_modules/browserslist": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "@types/node": "*" + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/@types/node": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.0.tgz", - "integrity": "sha512-YN1d+ae2MCb4U0mMa+Zlb5lWTdpFShbAj5nmte6lel27waMMBfivrm0prC16p/Di3DyTrmerrYUT8/145HXxVw==" - }, - "node_modules/@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "dev": true - }, - "node_modules/@types/react": { - "version": "18.2.21", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.21.tgz", - "integrity": "sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA==", + "node_modules/btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", "dev": true, - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" + "bin": { + "btoa": "bin/btoa.js" + }, + "engines": { + "node": ">= 0.4.0" } }, - "node_modules/@types/react-dom": { - "version": "18.2.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", - "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", "dev": true, "dependencies": { - "@types/react": "*" + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" } }, - "node_modules/@types/request": { - "version": "2.48.8", - "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.8.tgz", - "integrity": "sha512-whjk1EDJPcAR2kYHRbFl/lKeeKYTi05A15K9bnLInCVroNDCtXce57xKdI0/rQaA3K+6q0eFyUBPmqfSndUZdQ==", + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", "dev": true, "dependencies": { - "@types/caseless": "*", - "@types/node": "*", - "@types/tough-cookie": "*", - "form-data": "^2.5.0" + "semver": "^7.0.0" } }, - "node_modules/@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "dependencies": { - "@types/node": "*" + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" } }, - "node_modules/@types/scheduler": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", - "dev": true - }, - "node_modules/@types/tough-cookie": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz", - "integrity": "sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg==", - "dev": true + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "engines": { + "node": ">=10.6.0" + } }, - "node_modules/@types/ws": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.0.tgz", - "integrity": "sha512-mTClfhq5cuGyW4jthaFuig6Q8OVfB3IRyZfN/9SCyJtiM5H0SubwM89cHoT9UngO6HyUFic88HvT1zSNLNyxWA==", - "dev": true, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", "dependencies": { - "@types/node": "*" + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.14.0.tgz", - "integrity": "sha512-ir0wYI4FfFUDfLcuwKzIH7sMVA+db7WYen47iRSaCGl+HMAZI9fpBwfDo45ZALD3A45ZGyHWDNLhbg8tZrMX4w==", + "node_modules/call-bind": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.14.0", - "@typescript-eslint/type-utils": "5.14.0", - "@typescript-eslint/utils": "5.14.0", - "debug": "^4.3.2", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.2.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "engines": { - "node": ">= 4" + "node": ">=6" } }, - "node_modules/@typescript-eslint/parser": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.14.0.tgz", - "integrity": "sha512-aHJN8/FuIy1Zvqk4U/gcO/fxeMKyoSv/rS46UXMXOJKVsLQ+iYPuXNbpbH7cBLcpSbmyyFbwrniLx5+kutu1pw==", + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.14.0", - "@typescript-eslint/types": "5.14.0", - "@typescript-eslint/typescript-estree": "5.14.0", - "debug": "^4.3.2" - }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001579", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz", + "integrity": "sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } + ] + }, + "node_modules/cardinal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", + "integrity": "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==", + "dev": true, + "dependencies": { + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" + }, + "bin": { + "cdl": "bin/cdl.js" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.14.0.tgz", - "integrity": "sha512-LazdcMlGnv+xUc5R4qIlqH0OWARyl2kaP8pVCS39qSL3Pd1F7mI10DbdXeARcE62sVQE4fHNvEqMWsypWO+yEw==", + "node_modules/chai": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.14.0", - "@typescript-eslint/visitor-keys": "5.14.0" + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=4" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.14.0.tgz", - "integrity": "sha512-d4PTJxsqaUpv8iERTDSQBKUCV7Q5yyXjqXUl3XF7Sd9ogNLuKLkxz82qxokqQ4jXdTPZudWpmNtr/JjbbvUixw==", + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.14.0", - "debug": "^4.3.2", - "tsutils": "^3.21.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@typescript-eslint/types": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.14.0.tgz", - "integrity": "sha512-BR6Y9eE9360LNnW3eEUqAg6HxS9Q35kSIs4rp4vNHRdfg0s+/PgHgskvu5DFTM7G5VKAVjuyaN476LCPrdA7Mw==", + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=10" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.14.0.tgz", - "integrity": "sha512-QGnxvROrCVtLQ1724GLTHBTR0lZVu13izOp9njRvMkCBgWX26PKvmMP8k82nmXBRD3DQcFFq2oj3cKDwr0FaUA==", + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.14.0", - "@typescript-eslint/visitor-keys": "5.14.0", - "debug": "^4.3.2", - "globby": "^11.0.4", - "is-glob": "^4.0.3", - "semver": "^7.3.5", - "tsutils": "^3.21.0" + "get-func-name": "^2.0.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": "*" } }, - "node_modules/@typescript-eslint/utils": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.14.0.tgz", - "integrity": "sha512-EHwlII5mvUA0UsKYnVzySb/5EE/t03duUTweVy8Zqt3UQXBrpEVY144OTceFKaOe4xQXZJrkptCf7PjEBeGK4w==", + "node_modules/chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.14.0", - "@typescript-eslint/types": "5.14.0", - "@typescript-eslint/typescript-estree": "5.14.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">= 8.10.0" }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "optionalDependencies": { + "fsevents": "~2.3.1" } }, - "node_modules/@typescript-eslint/utils/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/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "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" + "node": ">=6.0" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.14.0.tgz", - "integrity": "sha512-yL0XxfzR94UEkjBqyymMLgCBdojzEuy/eim7N9/RIcTNxpJudAcqsU8eRyfzBbcEzGoPWfdM3AGak3cN08WOIw==", + "node_modules/cli-table": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.11.tgz", + "integrity": "sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.14.0", - "eslint-visitor-keys": "^3.0.0" + "colors": "1.0.3" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">= 0.2.0" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "node_modules/cli-table/node_modules/colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=0.1.90" } }, - "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==", - "dev": true - }, - "node_modules/@vitejs/plugin-react": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-1.3.2.tgz", - "integrity": "sha512-aurBNmMo0kz1O4qRoY+FM4epSA39y3ShWGuqfLRA/3z0oEJAdtoSfgA3aO98/PCCHAqMaduLxIxErWrVKIFzXA==", + "node_modules/cli-table3": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", + "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", "dev": true, "dependencies": { - "@babel/core": "^7.17.10", - "@babel/plugin-transform-react-jsx": "^7.17.3", - "@babel/plugin-transform-react-jsx-development": "^7.16.7", - "@babel/plugin-transform-react-jsx-self": "^7.16.7", - "@babel/plugin-transform-react-jsx-source": "^7.16.7", - "@rollup/pluginutils": "^4.2.1", - "react-refresh": "^0.13.0", - "resolve": "^1.22.0" + "string-width": "^4.2.0" }, "engines": { - "node": ">=12.0.0" + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" } }, - "node_modules/@vitejs/plugin-react/node_modules/resolve": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", - "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "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": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" }, - "bin": { - "resolve": "bin/resolve" + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dependencies": { + "mimic-response": "^1.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@webassemblyjs/ast": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", - "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "@webassemblyjs/helper-module-context": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", - "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", - "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", - "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", - "dev": true + "node_modules/colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha512-ENwblkFQpqqia6b++zLD/KUWafYlVY/UNnAp7oz7LY7E924wmpye416wBOmvv/HMWzl8gL1kJlfvId/1Dg176w==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } }, - "node_modules/@webassemblyjs/helper-code-frame": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", - "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, "dependencies": { - "@webassemblyjs/wast-printer": "1.9.0" + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "node_modules/@webassemblyjs/helper-fsm": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", - "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", - "dev": true + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } }, - "node_modules/@webassemblyjs/helper-module-context": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", - "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "node_modules/comment-parser": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", + "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0" + "engines": { + "node": ">= 12.0.0" } }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", - "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", - "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0" + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" } }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", - "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true, - "dependencies": { - "@xtuc/ieee754": "^1.2.0" + "engines": { + "node": ">= 0.6" } }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", - "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "dev": true, - "dependencies": { - "@xtuc/long": "4.2.2" + "engines": { + "node": ">= 0.6" } }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", - "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "dev": true }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", - "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/helper-wasm-section": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0", - "@webassemblyjs/wasm-opt": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0", - "@webassemblyjs/wast-printer": "1.9.0" + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" } }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", - "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/ieee754": "1.9.0", - "@webassemblyjs/leb128": "1.9.0", - "@webassemblyjs/utf8": "1.9.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", - "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0" + "engines": { + "node": "*" } }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", - "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-api-error": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/ieee754": "1.9.0", - "@webassemblyjs/leb128": "1.9.0", - "@webassemblyjs/utf8": "1.9.0" - } + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true }, - "node_modules/@webassemblyjs/wast-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", - "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/floating-point-hex-parser": "1.9.0", - "@webassemblyjs/helper-api-error": "1.9.0", - "@webassemblyjs/helper-code-frame": "1.9.0", - "@webassemblyjs/helper-fsm": "1.9.0", - "@xtuc/long": "4.2.2" + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", - "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true + }, + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0", - "@xtuc/long": "4.2.2" + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" } }, - "node_modules/@webpack-cli/info": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.1.0.tgz", - "integrity": "sha512-uNWSdaYHc+f3LdIZNwhdhkjjLDDl3jP2+XBqAq9H8DjrJUvlOKdP8TNruy1yEaDfgpAIgbSAN7pye4FEHg9tYQ==", + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", "dev": true, - "dependencies": { - "envinfo": "^7.7.3" - }, - "peerDependencies": { - "webpack-cli": "4.x.x" + "engines": { + "node": "*" } }, - "node_modules/@webpack-cli/serve": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.1.0.tgz", - "integrity": "sha512-7RfnMXCpJ/NThrhq4gYQYILB18xWyoQcBey81oIyVbmgbc6m5ZHHyFK+DyH7pLHJf0p14MxL4mTsoPAgBSTpIg==", + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, - "peerDependencies": { - "webpack-cli": "4.x.x" + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" }, "peerDependenciesMeta": { - "webpack-dev-server": { + "supports-color": { "optional": true } } }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "dev": true + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", "dev": true }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "mimic-response": "^3.1.0" }, "engines": { - "node": ">= 0.6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "engines": { - "node": ">=0.4.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/acorn-globals": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", - "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", "dev": true, "dependencies": { - "acorn": "^8.1.0", - "acorn-walk": "^8.0.2" + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" } }, - "node_modules/acorn-globals/node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "node_modules/deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", "dev": true, - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" }, "engines": { - "node": ">=0.4.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "node_modules/deep-equal/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/deep-for-each": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/deep-for-each/-/deep-for-each-3.0.0.tgz", + "integrity": "sha512-pPN+0f8jlnNP+z90qqOdxGghJU5XM6oBDhvAR+qdQzjCg5pk/7VPPvKK1GqoXEFkHza6ZS+Otzzvmr0g3VUaKw==", "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "dependencies": { + "lodash.isplainobject": "^4.0.6" } }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "engines": { - "node": ">=0.4.0" + "node": ">=10" } }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", "dev": true, "dependencies": { - "debug": "4" + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" }, "engines": { - "node": ">= 6.0.0" + "node": ">= 0.4" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "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": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "node": ">= 0.4" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ajv-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, - "peerDependencies": { - "ajv": ">=5.0.0" + "engines": { + "node": ">=0.4.0" } }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" + "engines": { + "node": ">= 0.8" } }, - "node_modules/amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "dev": true, "engines": { - "node": ">=0.4.2" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "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==", + "node_modules/detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", "dev": true, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/ansi-escapes": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", - "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", + "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==", "dev": true, - "dependencies": { - "type-fest": "^3.0.0" - }, "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.3.1" } }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, - "engines": { - "node": ">=14.16" + "dependencies": { + "path-type": "^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "dependencies": { - "color-convert": "^1.9.0" + "esutils": "^2.0.2" }, "engines": { - "node": ">=4" + "node": ">=6.0.0" } }, - "node_modules/ansicolors": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", - "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==", + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true }, - "node_modules/anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", "dev": true, - "optional": true, "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "node_modules/anymatch/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "node_modules/ejs": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", + "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", "dev": true, - "optional": true, "dependencies": { - "remove-trailing-separator": "^1.0.1" + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "node_modules/electron-to-chromium": { + "version": "1.4.639", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.639.tgz", + "integrity": "sha512-CkKf3ZUVZchr+zDpAlNLEEy2NJJ9T64ULWaDgy3THXXlPVPkLu3VOs9Bac44nebVtdwl2geSj6AxTtGDOxoXhg==", "dev": true }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "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 + }, + "node_modules/emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dependencies": { - "sprintf-js": "~1.0.2" + "once": "^1.4.0" } }, - "node_modules/aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "node_modules/enhanced-resolve": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", "dev": true, "dependencies": { - "deep-equal": "^2.0.5" + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" } }, - "node_modules/arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8.6" } }, - "node_modules/arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "node_modules/envinfo": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.11.0.tgz", + "integrity": "sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg==", "dev": true, + "bin": { + "envinfo": "dist/cli.js" + }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/array-back": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.1.tgz", - "integrity": "sha512-Z/JnaVEXv+A9xabHzN43FiiiWEE7gPCRXMrVmRm00tWbjZRul1iHm7ECzlyNq1p4a4ATXz+G9FJ3GqGOkOV3fg==", + "node_modules/es-abstract": { + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.5", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.2", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.12", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.13" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", - "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", + "node_modules/es-get-iterator/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/es-iterator-helpers": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", + "integrity": "sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "asynciterator.prototype": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.1", + "es-set-tostringtag": "^2.0.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.2.1", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "iterator.prototype": "^1.1.2", + "safe-array-concat": "^1.0.1" } }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "node_modules/es-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", + "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", "dev": true }, - "node_modules/array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "node_modules/es-set-tostringtag": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "is-string": "^1.0.7" + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-slice": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "hasown": "^2.0.0" } }, - "node_modules/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", - "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.2.1" + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -3689,2619 +3581,2547 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": ">= 0.4" + "node": ">=12" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" } }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "node_modules/esbuild-android-64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz", + "integrity": "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", - "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", + "node_modules/esbuild-android-arm64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz", + "integrity": "sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.1.3" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", - "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", + "node_modules/esbuild-darwin-64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz", + "integrity": "sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, - "node_modules/asn1.js": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", - "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "node_modules/esbuild-darwin-arm64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz", + "integrity": "sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "safer-buffer": "^2.1.0" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" } }, - "node_modules/asn1.js/node_modules/bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - }, - "node_modules/assert": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", - "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "node_modules/esbuild-freebsd-64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz", + "integrity": "sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "object-assign": "^4.1.1", - "util": "0.10.3" + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" } }, - "node_modules/assert/node_modules/inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - }, - "node_modules/assert/node_modules/util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "node_modules/esbuild-freebsd-arm64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz", + "integrity": "sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "inherits": "2.0.1" + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" } }, - "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/esbuild-linux-32": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz", + "integrity": "sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==", + "cpu": [ + "ia32" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "*" + "node": ">=12" } }, - "node_modules/assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "node_modules/esbuild-linux-64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz", + "integrity": "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "node_modules/astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "node_modules/esbuild-linux-arm": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz", + "integrity": "sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==", + "cpu": [ + "arm" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=4" + "node": ">=12" } }, - "node_modules/async": { - "version": "1.5.2", - "resolved": "git+https://git@github.com/ably-forks/async.git#76306396b3d3a25316be0170ab6483ccbaaaa097", - "integrity": "sha512-G936e+Px0kczo3DHOhyHuRIjkv5HT92fWTOVzE6ysIKun1qhAqoJOsko0b+erkFPKCEDTiHMzp9w8zuuhmidQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", - "dev": true, - "optional": true - }, - "node_modules/asynciterator.prototype": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", - "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", + "node_modules/esbuild-linux-arm64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz", + "integrity": "sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "has-symbols": "^1.0.3" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "node_modules/esbuild-linux-mips64le": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz", + "integrity": "sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "node_modules/esbuild-linux-ppc64le": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz", + "integrity": "sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==", + "cpu": [ + "ppc64" + ], "dev": true, - "bin": { - "atob": "bin/atob.js" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 4.5.0" + "node": ">=12" } }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "node_modules/esbuild-linux-riscv64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz", + "integrity": "sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==", + "cpu": [ + "riscv64" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, - "node_modules/aws-sdk": { - "version": "2.1414.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1414.0.tgz", - "integrity": "sha512-WhqTWiTZRUxWITvUG5VMPYGdCLNAm4zOTDIiotbErR9x+uDExk2CAGbXE8HH11+tD8PhZVXyukymSiG+7rJMMg==", + "node_modules/esbuild-linux-s390x": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz", + "integrity": "sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==", + "cpu": [ + "s390x" + ], "dev": true, - "dependencies": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "util": "^0.12.4", - "uuid": "8.0.0", - "xml2js": "0.5.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 10.0.0" + "node": ">=12" } }, - "node_modules/aws-sdk/node_modules/events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "node_modules/esbuild-netbsd-64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz", + "integrity": "sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=0.4.x" + "node": ">=12" } }, - "node_modules/aws-sdk/node_modules/ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true + "node_modules/esbuild-openbsd-64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz", + "integrity": "sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } }, - "node_modules/aws-sdk/node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true + "node_modules/esbuild-plugin-umd-wrapper": { + "version": "1.0.7", + "resolved": "git+ssh://git@github.com/ably-forks/esbuild-plugin-umd-wrapper.git#6eef42a607a9192960706d31e444a2c79a2daeb0", + "dev": true, + "license": "MIT" }, - "node_modules/aws-sdk/node_modules/url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "node_modules/esbuild-runner": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/esbuild-runner/-/esbuild-runner-2.2.2.tgz", + "integrity": "sha512-fRFVXcmYVmSmtYm2mL8RlUASt2TDkGh3uRcvHFOKNr/T58VrfVeKD9uT9nlgxk96u0LS0ehS/GY7Da/bXWKkhw==", "dev": true, "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" + "source-map-support": "0.5.21", + "tslib": "2.4.0" + }, + "bin": { + "esr": "bin/esr.js" + }, + "peerDependencies": { + "esbuild": "*" } }, - "node_modules/aws-sdk/node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "node_modules/esbuild-runner/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/esbuild-sunos-64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz", + "integrity": "sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" } }, - "node_modules/babel-loader": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", - "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", + "node_modules/esbuild-windows-32": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz", + "integrity": "sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==", + "cpu": [ + "ia32" + ], "dev": true, - "dependencies": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^2.0.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 8.9" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "webpack": ">=2" + "node": ">=12" } }, - "node_modules/babel-loader/node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "node_modules/esbuild-windows-64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz", + "integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + "node": ">=12" } }, - "node_modules/babel-loader/node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/esbuild-windows-arm64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz", + "integrity": "sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==", + "cpu": [ + "arm64" + ], "dev": true, - "bin": { - "json5": "lib/cli.js" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=6" + "node": ">=12" } }, - "node_modules/babel-loader/node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, "engines": { - "node": ">=8.9.0" + "node": ">=6" } }, - "node_modules/babel-loader/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "node_modules/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, - "dependencies": { - "semver": "^6.0.0" - }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/babel-loader/node_modules/schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" }, "engines": { - "node": ">= 8.9.0" + "node": ">=6.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "optionalDependencies": { + "source-map": "~0.6.1" } }, - "node_modules/babel-loader/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "optional": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.7.tgz", - "integrity": "sha512-LidDk/tEGDfuHW2DWh/Hgo4rmnw3cduK6ZkOI1NPFceSK3n/yAGeOsNT7FLnSGHkXj3RHGSEVkN3FsCTY6w2CQ==", + "node_modules/eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.4.4", - "semver": "^6.3.1" + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "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": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "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, - "bin": { - "semver": "bin/semver.js" + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" } }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.8.7", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.7.tgz", - "integrity": "sha512-KyDvZYxAzkC0Aj2dAPyDzi2Ym15e5JKZSK+maI7NAwSqofvuFglbSsxE7wUOvTg9oFVnHMzVzBKcqEb4PJgtOA==", + "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": { - "@babel/helper-define-polyfill-provider": "^0.4.4", - "core-js-compat": "^3.33.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "ms": "^2.1.1" } }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.4.tgz", - "integrity": "sha512-S/x2iOCvDaCASLYsOOgWOq4bCfKYVqvO/uxjkaYyZ3rVsVE3CeAI/c84NpyuBBymEgNvHgjEot3a9/Z/kXvqsg==", + "node_modules/eslint-module-utils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.4" + "debug": "^3.2.7" }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "node_modules/base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "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, "dependencies": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" + "ms": "^2.1.1" } }, - "node_modules/base/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "node_modules/eslint-plugin-import": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", "dev": true, "dependencies": { - "is-descriptor": "^1.0.0" + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "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.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" } }, - "node_modules/base/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "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": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" + "ms": "^2.1.1" } }, - "node_modules/base/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "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": { - "kind-of": "^6.0.0" + "esutils": "^2.0.2" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/base/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "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-plugin-jsdoc": { + "version": "40.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-40.3.0.tgz", + "integrity": "sha512-EhCqpzRkxoT2DUB4AnrU0ggBYvTh3bWrLZzQTupq6vSVE6XzNwJVKsOHa41GCoevnsWMBNmoDVjXWGqckjuG1g==", "dev": true, "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "@es-joy/jsdoccomment": "~0.37.0", + "comment-parser": "1.3.1", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "esquery": "^1.5.0", + "semver": "^7.3.8", + "spdx-expression-parse": "^3.0.1" }, "engines": { - "node": ">=0.10.0" + "node": "^14 || ^16 || ^17 || ^18 || ^19" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" } }, - "node_modules/base64-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.0.2.tgz", - "integrity": "sha1-R0IRyV5s8qVH20YeT2d4tR0I+mU=", + "node_modules/eslint-plugin-react": { + "version": "7.33.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", + "integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "array.prototype.tosorted": "^1.1.1", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.0.12", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "object.hasown": "^1.1.2", + "object.values": "^1.1.6", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.4", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.8" + }, "engines": { - "node": ">= 0.4" + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" } }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", "dev": true, "engines": { - "node": "*" + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" } }, - "node_modules/binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, - "optional": true, + "dependencies": { + "esutils": "^2.0.2" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, - "optional": true, "dependencies": { - "file-uri-to-path": "1.0.0" + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true + "node_modules/eslint-plugin-react/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/bn.js": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", - "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==", - "dev": true + "node_modules/eslint-plugin-security": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-1.7.1.tgz", + "integrity": "sha512-sMStceig8AFglhhT2LqlU5r+/fn9OwsA72O5bBuQVTssPCdQAOQzL+oMn/ZcpeUY6KcNfLJArgcrsSULNjYYdQ==", + "dev": true, + "dependencies": { + "safe-regex": "^2.1.1" + } }, - "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "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": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">=8.0.0" } }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "dependencies": { - "ms": "2.0.0" + "engines": { + "node": ">=4.0" } }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/bops": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bops/-/bops-1.0.1.tgz", - "integrity": "sha512-qCMBuZKP36tELrrgXpAfM+gHzqa0nLsWZ+L37ncsb8txYlnAoxOPpVp+g7fK0sGkMXfA0wl8uQkESqw3v4HNag==", - "dependencies": { - "base64-js": "1.0.2", - "to-utf8": "0.0.1" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" } }, - "node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true, - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "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, - "dependencies": { - "is-extendable": "^0.1.0" - }, "engines": { - "node": ">=0.10.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "node_modules/eslint/node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", "dev": true, "dependencies": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "@babel/highlight": "^7.10.4" } }, - "node_modules/browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "node_modules/eslint/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==", "dev": true, - "dependencies": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" + "engines": { + "node": ">=10" } }, - "node_modules/browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/browserify-rsa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", - "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "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, - "dependencies": { - "bn.js": "^5.0.0", - "randombytes": "^2.0.1" + "engines": { + "node": ">= 4" } }, - "node_modules/browserify-sign": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", - "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "node_modules/eslint/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==", "dev": true, - "dependencies": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "elliptic": "^6.5.3", - "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/browserify-sign/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" }, "engines": { - "node": ">= 6" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true, - "dependencies": { - "pako": "~1.0.5" + "engines": { + "node": ">=4" } }, - "node_modules/browserslist": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", - "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001565", - "electron-to-chromium": "^1.4.601", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - }, "bin": { - "browserslist": "cli.js" + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" }, "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "node": ">=4" } }, - "node_modules/btoa": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", - "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, - "bin": { - "btoa": "bin/btoa.js" + "dependencies": { + "estraverse": "^5.1.0" }, "engines": { - "node": ">= 0.4.0" + "node": ">=0.10" } }, - "node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" } }, - "node_modules/buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "node_modules/buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "node_modules/buffer/node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "engines": { + "node": ">=4.0" + } }, - "node_modules/builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "dev": true }, - "node_modules/builtins": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", - "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "dependencies": { - "semver": "^7.0.0" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true, "engines": { - "node": ">= 0.8" + "node": ">= 0.6" } }, - "node_modules/cacache": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", - "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "node_modules/eventemitter2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "integrity": "sha512-K7J4xq5xAD5jHsGM5ReWXRTFa3JRGofHiMcVgQ8PRwgWxzjHpMWCIzsmyf60+mh8KLsqYPcjUMa0AC4hd6lPyQ==", + "dev": true + }, + "node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", "dev": true, - "dependencies": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" + "engines": { + "node": ">=0.4.x" } }, - "node_modules/cacache/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 0.8.0" } }, - "node_modules/cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", "dev": true, "dependencies": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" + "homedir-polyfill": "^1.0.1" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "engines": { - "node": ">=10.6.0" - } - }, - "node_modules/cacheable-request": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", - "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dev": true, "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" }, "engines": { - "node": ">=8" + "node": ">= 0.10.0" } }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "ms": "2.0.0" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, "engines": { - "node": ">=6" + "node": ">=8.6.0" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", "dev": true, "engines": { - "node": ">=6" + "node": ">= 4.9.1" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001570", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz", - "integrity": "sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw==", + "node_modules/fastq": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] + "dependencies": { + "reusify": "^1.0.4" + } }, - "node_modules/cardinal": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", - "integrity": "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==", + "node_modules/fflate": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz", + "integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==", + "dev": true + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "dependencies": { - "ansicolors": "~0.3.2", - "redeyed": "~2.1.0" + "flat-cache": "^3.0.4" }, - "bin": { - "cdl": "bin/cdl.js" + "engines": { + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/chai": { - "version": "4.3.8", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.8.tgz", - "integrity": "sha512-vX4YvVVtxlfSZ2VecZgFUTU5qPCYsobVI2O9FmwEXBhDigYGQA6jRXCycIs1yJnnWbZ6/+a2zNIF5DfVCcJBFQ==", + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", "dev": true, "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^4.1.2", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - }, - "engines": { - "node": ">=4" + "minimatch": "^5.0.1" } }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk/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/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "to-regex-range": "^5.0.1" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/chalk/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==", + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" }, "engines": { - "node": ">=7.0.0" + "node": ">= 0.8" } }, - "node_modules/chalk/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/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/chalk/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/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/chalk/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==", + "node_modules/findup-sync": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-5.0.0.tgz", + "integrity": "sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "detect-file": "^1.0.0", + "is-glob": "^4.0.3", + "micromatch": "^4.0.4", + "resolve-dir": "^1.0.1" }, "engines": { - "node": ">=8" + "node": ">= 10.13.0" } }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, + "node_modules/fined": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + }, "engines": { - "node": ">=10" + "node": ">= 0.10" } }, - "node_modules/charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "node_modules/flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", "dev": true, "engines": { - "node": "*" + "node": ">= 0.10" } }, - "node_modules/check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, - "engines": { - "node": "*" + "bin": { + "flat": "cli.js" } }, - "node_modules/chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, - "optional": true, "dependencies": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" }, - "optionalDependencies": { - "fsevents": "^1.2.7" + "engines": { + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, - "optional": true, "dependencies": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "is-callable": "^1.1.3" } }, - "node_modules/chokidar/node_modules/glob-parent/node_modules/is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", "dev": true, - "optional": true, - "dependencies": { - "is-extglob": "^2.1.0" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, - "node_modules/chrome-trace-event": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", - "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "node_modules/for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==", "dev": true, "dependencies": { - "tslib": "^1.9.0" + "for-in": "^1.0.1" }, "engines": { - "node": ">=6.0" + "node": ">=0.10.0" } }, - "node_modules/chrome-trace-event/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/cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "node_modules/form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", "dev": true, "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" } }, - "node_modules/class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "dev": true, - "dependencies": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/class-utils/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/cli-table3": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", - "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", + "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.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "dev": true, "dependencies": { - "string-width": "^4.2.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" }, "engines": { - "node": "10.* || >= 12.*" + "node": ">= 0.4" }, - "optionalDependencies": { - "@colors/colors": "1.5.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/cli-table3/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==", + "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": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", "dev": true }, - "node_modules/cli-table3/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/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, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/cli-table3/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "node_modules/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, - "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "engines": { + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, "engines": { - "node": ">=6" + "node": "*" } }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "node_modules/get-intrinsic": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dev": true, "dependencies": { - "ansi-regex": "^4.1.0" + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, - "engines": { - "node": ">=6" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, "engines": { - "node": ">=0.8" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/clone-buffer": { + "node_modules/get-symbol-description": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, "engines": { - "node": ">= 0.10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/clone-response": { + "node_modules/getobject": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "dependencies": { - "mimic-response": "^1.0.0" + "resolved": "https://registry.npmjs.org/getobject/-/getobject-1.0.2.tgz", + "integrity": "sha512-2zblDBaFcb3rB4rF77XVnuINOE2h2k/OnqXAiy0IrTxUfV1iFp3la33oAQVY9pCpWU268WFYVt2t71hlMuLsOg==", + "dev": true, + "engines": { + "node": ">=10" } }, - "node_modules/clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", - "dev": true - }, - "node_modules/cloneable-readable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", - "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "node_modules/glob": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.4.2.tgz", + "integrity": "sha512-uDfQml82dlkWsiYk+CdoOdJe09vfPUjTFBM23krHsaFuaC5/o4A5bLX95h7ccc/Fs/JjC5DuMwPgiIzTBV5WpA==", "dev": true, "dependencies": { - "inherits": "^2.0.1", - "process-nextick-args": "^2.0.0", - "readable-stream": "^2.3.5" + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^2.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": "*" } }, - "node_modules/collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" + "node": ">= 6" } }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "node_modules/colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true }, - "node_modules/colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/glob/node_modules/minimatch": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "integrity": "sha512-jQo6o1qSVLEWaw3l+bwYA2X0uLuK2KjNh2wjgO7Q/9UJnXr1Q3yQKR8BI0/Bt/rPg75e6SMW4hW/6cBHVTZUjA==", + "deprecated": "Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue", "dev": true, "dependencies": { - "delayed-stream": "~1.0.0" + "brace-expansion": "^1.0.0" }, "engines": { - "node": ">= 0.8" + "node": "*" } }, - "node_modules/command-line-usage": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.1.tgz", - "integrity": "sha512-F59pEuAR9o1SF/bD0dQBDluhpT4jJQNWUHEuVBqpDmCUo6gPjCi+m9fCWnWZVR/oG6cMTUms4h+3NPl74wGXvA==", + "node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, "dependencies": { - "array-back": "^4.0.1", - "chalk": "^2.4.2", - "table-layout": "^1.0.1", - "typical": "^5.2.0" + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" }, "engines": { - "node": ">=8.0.0" + "node": ">=0.10.0" } }, - "node_modules/command-line-usage/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", "dev": true, "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/comment-parser": { + "node_modules/global-prefix/node_modules/which": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", - "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, - "engines": { - "node": ">= 12.0.0" + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" } }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, - "engines": [ - "node >= 0.8" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" + "engines": { + "node": ">=4" } }, - "node_modules/console-browserify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", - "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", - "dev": true - }, - "node_modules/constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", "dev": true, "dependencies": { - "safe-buffer": "5.2.1" + "define-properties": "^1.1.3" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "node_modules/globby": { + "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.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, "engines": { - "node": ">= 0.6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, "dependencies": { - "safe-buffer": "~5.1.1" + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/convert-source-map/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==", - "dev": true - }, - "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "dev": true, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, "engines": { - "node": ">= 0.6" + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" } }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "dev": true, - "dependencies": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" - } + "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/copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "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": ">=0.10.0" + "node": ">=4.x" } }, - "node_modules/copy-webpack-plugin": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-6.4.1.tgz", - "integrity": "sha512-MXyPCjdPVx5iiWyl40Va3JGh27bKzOTNY3NjUTrosD2q7dR/cLD0013uqJ3BpFbUjyONINjb6qI7nDIJujrMbA==", + "node_modules/grunt": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.6.1.tgz", + "integrity": "sha512-/ABUy3gYWu5iBmrUSRBP97JLpQUm0GgVveDCp6t3yRNIoltIYw7rEj3g5y1o2PGPR2vfTRGa7WC/LZHLTXnEzA==", "dev": true, "dependencies": { - "cacache": "^15.0.5", - "fast-glob": "^3.2.4", - "find-cache-dir": "^3.3.1", - "glob-parent": "^5.1.1", - "globby": "^11.0.1", - "loader-utils": "^2.0.0", - "normalize-path": "^3.0.0", - "p-limit": "^3.0.2", - "schema-utils": "^3.0.0", - "serialize-javascript": "^5.0.1", - "webpack-sources": "^1.4.3" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "dateformat": "~4.6.2", + "eventemitter2": "~0.4.13", + "exit": "~0.1.2", + "findup-sync": "~5.0.0", + "glob": "~7.1.6", + "grunt-cli": "~1.4.3", + "grunt-known-options": "~2.0.0", + "grunt-legacy-log": "~3.0.0", + "grunt-legacy-util": "~2.0.1", + "iconv-lite": "~0.6.3", + "js-yaml": "~3.14.0", + "minimatch": "~3.0.4", + "nopt": "~3.0.6" }, - "peerDependencies": { - "webpack": "^4.37.0 || ^5.0.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/cacache": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.1.0.tgz", - "integrity": "sha512-mfx0C+mCfWjD1PnwQ9yaOrwG1ou9FkKnx0SvzUHWdFt7r7GaRtzT+9M8HAvLu62zIHtnpQ/1m93nWNDCckJGXQ==", - "dev": true, - "dependencies": { - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" + "bin": { + "grunt": "bin/grunt" }, "engines": { - "node": ">= 10" + "node": ">=16" } }, - "node_modules/copy-webpack-plugin/node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "node_modules/grunt-cli": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", + "integrity": "sha512-8oM6ZAe4yG8Y7co/Ejc9613AixyN+gdCADyAFvJ1BbHGvrNa0ltaqrEWXV9P/W0gbQbAh3C8swJIaDuAX7syiw==", "dev": true, + "dependencies": { + "findup-sync": "~0.3.0", + "grunt-known-options": "~1.1.0", + "nopt": "~3.0.6", + "resolve": "~1.1.0" + }, + "bin": { + "grunt": "bin/grunt" + }, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/copy-webpack-plugin/node_modules/find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "node_modules/grunt-cli/node_modules/findup-sync": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", + "integrity": "sha512-z8Nrwhi6wzxNMIbxlrTzuUW6KWuKkogZ/7OdDVq+0+kxn77KUH1nipx8iU6suqkHqc4y6n7a9A8IpmxY/pTjWg==", "dev": true, "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" + "glob": "~5.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + "node": ">= 0.6.0" } }, - "node_modules/copy-webpack-plugin/node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "node_modules/grunt-cli/node_modules/glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "2 || 3", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, "engines": { "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/copy-webpack-plugin/node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/grunt-cli/node_modules/grunt-known-options": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.1.tgz", + "integrity": "sha512-cHwsLqoighpu7TuYj5RonnEuxGVFnztcUqTqp5rXFGYL4OuPFofwC4Ycg7n9fYwvK6F5WbYgeVOwph9Crs2fsQ==", "dev": true, - "bin": { - "json5": "lib/cli.js" - }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/copy-webpack-plugin/node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "node_modules/grunt-cli/node_modules/resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", + "dev": true + }, + "node_modules/grunt-known-options": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-2.0.0.tgz", + "integrity": "sha512-GD7cTz0I4SAede1/+pAbmJRG44zFLPipVtdL9o3vqx9IEyb7b4/Y3s7r6ofI3CchR5GvYJ+8buCSioDv5dQLiA==", "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, "engines": { - "node": ">=8.9.0" + "node": ">=0.10.0" } }, - "node_modules/copy-webpack-plugin/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/grunt-legacy-log": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-3.0.0.tgz", + "integrity": "sha512-GHZQzZmhyq0u3hr7aHW4qUH0xDzwp2YXldLPZTCjlOeGscAOWWPftZG3XioW8MasGp+OBRIu39LFx14SLjXRcA==", "dev": true, "dependencies": { - "yallist": "^4.0.0" + "colors": "~1.1.2", + "grunt-legacy-log-utils": "~2.1.0", + "hooker": "~0.2.3", + "lodash": "~4.17.19" }, "engines": { - "node": ">=10" + "node": ">= 0.10.0" } }, - "node_modules/copy-webpack-plugin/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "node_modules/grunt-legacy-log-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.1.0.tgz", + "integrity": "sha512-lwquaPXJtKQk0rUM1IQAop5noEpwFqOXasVoedLeNzaibf/OPWjKYvvdqnEHNmU+0T0CaReAXIbGo747ZD+Aaw==", "dev": true, "dependencies": { - "semver": "^6.0.0" + "chalk": "~4.1.0", + "lodash": "~4.17.19" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=10" } }, - "node_modules/copy-webpack-plugin/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "node_modules/grunt-legacy-util": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-2.0.1.tgz", + "integrity": "sha512-2bQiD4fzXqX8rhNdXkAywCadeqiPiay0oQny77wA2F3WF4grPJXCvAcyoWUJV+po/b15glGkxuSiQCK299UC2w==", "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" + "dependencies": { + "async": "~3.2.0", + "exit": "~0.1.2", + "getobject": "~1.0.0", + "hooker": "~0.2.3", + "lodash": "~4.17.21", + "underscore.string": "~3.3.5", + "which": "~2.0.2" }, "engines": { "node": ">=10" } }, - "node_modules/copy-webpack-plugin/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/grunt-legacy-util/node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true + }, + "node_modules/grunt-shell": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/grunt-shell/-/grunt-shell-1.1.2.tgz", + "integrity": "sha512-gPYS+1WzGT9K5A4rFAz6I41Q8Dr3dgt464gNwLggwUP5bl2nzNipbn1QliN3/gFrBmed9BdQpeFKKS1+NTd/gA==", "dev": true, "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" + "chalk": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/copy-webpack-plugin/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "node_modules/grunt-shell/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=0.10.0" } }, - "node_modules/copy-webpack-plugin/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "node_modules/grunt-shell/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/copy-webpack-plugin/node_modules/ssri": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", - "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "node_modules/grunt-shell/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, "dependencies": { - "minipass": "^3.1.1" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" }, "engines": { - "node": ">= 8" + "node": ">=0.10.0" } }, - "node_modules/core-js-compat": { - "version": "3.34.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.34.0.tgz", - "integrity": "sha512-4ZIyeNbW/Cn1wkMMDy+mvrRUxrwFNjKwbhCfQpDd+eLgYipDqp8oGFGtLmhh18EDPKA0g3VUBYOxQGGwvWLVpA==", + "node_modules/grunt-shell/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, - "dependencies": { - "browserslist": "^4.22.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" + "engines": { + "node": ">=0.8.0" } }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "node_modules/grunt-shell/node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", "dev": true, "dependencies": { - "object-assign": "^4", - "vary": "^1" + "ansi-regex": "^2.0.0" }, "engines": { - "node": ">= 0.10" + "node": ">=0.10.0" } }, - "node_modules/create-ecdh": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", - "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "node_modules/grunt-shell/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "dependencies": { - "bn.js": "^4.1.0", - "elliptic": "^6.5.3" + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/create-ecdh/node_modules/bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - }, - "node_modules/create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "node_modules/grunt-shell/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true, - "dependencies": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" + "engines": { + "node": ">=0.8.0" } }, - "node_modules/create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "node_modules/grunt-webpack": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/grunt-webpack/-/grunt-webpack-5.0.0.tgz", + "integrity": "sha512-C7emzVIGJhZ5V5ZYjylr9sDD9PE9Dh/55NaSzP2P2dhif+m/1gHbjDZQ7PDzguyuzwBz4Qc8voQHR06FSp4CKg==", "dev": true, "dependencies": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "deep-for-each": "^3.0.0", + "lodash": "^4.17.19" + }, + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" } }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "node_modules/grunt/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "dev": true, "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">= 8" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "node_modules/grunt/node_modules/grunt-cli": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.4.3.tgz", + "integrity": "sha512-9Dtx/AhVeB4LYzsViCjUQkd0Kw0McN2gYpdmGYKtE2a5Yt7v1Q+HYZVWhqXc/kGnxlMtqKDxSwotiGeFmkrCoQ==", "dev": true, + "dependencies": { + "grunt-known-options": "~2.0.0", + "interpret": "~1.1.0", + "liftup": "~3.0.1", + "nopt": "~4.0.1", + "v8flags": "~3.2.0" + }, + "bin": { + "grunt": "bin/grunt" + }, "engines": { - "node": "*" + "node": ">=10" } }, - "node_modules/crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "node_modules/grunt/node_modules/grunt-cli/node_modules/nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", "dev": true, "dependencies": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" + "abbrev": "1", + "osenv": "^0.1.4" }, - "engines": { - "node": "*" + "bin": { + "nopt": "bin/nopt.js" } }, - "node_modules/crypto-js": { - "version": "4.0.0", - "resolved": "git+https://git@github.com/ably-forks/crypto-js.git#07e09b48fd8f850f71c10c9d9307ca4e6ac622a0", - "integrity": "sha512-3RE9Ztinx+QLpEDwcsPTVGMXIIqtS/r+YyD1E7BGXE+CRBcCwkyHWVyl1Aperg5QBOUkg2dtiC8p18a41IKltQ==", + "node_modules/grunt/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, - "license": "MIT" + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/cssom": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", - "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "node_modules/grunt/node_modules/interpret": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", + "integrity": "sha512-CLM8SNMDu7C5psFCn6Wg/tgpj/bKAg7hc2gWqcuR9OD5Ft9PhBpIu8PLicPeis+xDd6YX2ncI8MCA64I9tftIA==", "dev": true }, - "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "node_modules/grunt/node_modules/minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", "dev": true, "dependencies": { - "cssom": "~0.3.6" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=8" + "node": "*" } }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - }, - "node_modules/csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", - "dev": true - }, - "node_modules/cyclist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", - "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", - "dev": true - }, - "node_modules/data-urls": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", - "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", "dev": true, "dependencies": { - "abab": "^2.0.6", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0" + "duplexer": "^0.1.2" }, "engines": { - "node": ">=12" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/dateformat": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", - "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, - "engines": { - "node": "*" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "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, - "dependencies": { - "ms": "2.1.2" - }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=8" } }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "node_modules/has-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "get-intrinsic": "^1.2.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "dev": true - }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", "dev": true, "engines": { - "node": ">=0.10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dependencies": { - "mimic-response": "^3.1.0" - }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", "dev": true, "dependencies": { - "type-detect": "^4.0.0" + "function-bind": "^1.1.2" }, "engines": { - "node": ">=6" + "node": ">= 0.4" } }, - "node_modules/deep-equal": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.2.tgz", - "integrity": "sha512-xjVyBf0w5vH0I42jdAZzOKVldmPgSulmiyPRywoyq7HXC9qdgo17kxJE+rdnif5Tz6+pIrpJI8dCpMNLIGkUiA==", + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/hexy": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/hexy/-/hexy-0.2.11.tgz", + "integrity": "sha512-ciq6hFsSG/Bpt2DmrZJtv+56zpPdnq+NQ4ijEFrveKN0ZG1mhl/LdT1NQZ9se6ty1fACcI4d4vYqC9v8EYpH2A==", + "dev": true, + "bin": { + "hexy": "bin/hexy_cmd.js" + } + }, + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", "dev": true, "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.1", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.0", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" + "parse-passwd": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/deep-equal/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/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "node_modules/hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", + "integrity": "sha512-t+UerCsQviSymAInD01Pw+Dn/usmz1sRO+3Zk1+lx8eg+WKpD2ulcwWqHHL0+aseRBr+3+vIhiG1K1JTwaIcTA==", "dev": true, "engines": { - "node": ">=4.0.0" + "node": "*" } }, - "node_modules/deep-for-each": { + "node_modules/html-encoding-sniffer": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/deep-for-each/-/deep-for-each-3.0.0.tgz", - "integrity": "sha512-pPN+0f8jlnNP+z90qqOdxGghJU5XM6oBDhvAR+qdQzjCg5pk/7VPPvKK1GqoXEFkHza6ZS+Otzzvmr0g3VUaKw==", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", "dev": true, "dependencies": { - "lodash.isplainobject": "^4.0.6" - } - }, - "node_modules/deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "whatwg-encoding": "^2.0.0" + }, "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dev": true, "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8" } }, - "node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, - "node_modules/define-property/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", "dependencies": { - "kind-of": "^6.0.0" + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10.19.0" } }, - "node_modules/define-property/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, "dependencies": { - "kind-of": "^6.0.0" + "agent-base": "6", + "debug": "4" }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, - "node_modules/define-property/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true, - "engines": { - "node": ">=0.4.0" - } + "node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "node_modules/ignore": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", "dev": true, "engines": { - "node": ">= 0.8" + "node": ">= 4" } }, - "node_modules/des.js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", - "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "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==", "dev": true, "dependencies": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "engines": { - "node": ">=0.3.1" + "node": ">=0.8.19" } }, - "node_modules/diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "dependencies": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" + "once": "^1.3.0", + "wrappy": "1" } }, - "node_modules/diffie-hellman/node_modules/bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", + "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", "dev": true, "dependencies": { - "path-type": "^4.0.0" + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, "engines": { - "node": ">=6.0.0" + "node": ">= 0.10" } }, - "node_modules/dom-accessibility-api": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", - "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true - }, - "node_modules/domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true, "engines": { - "node": ">=0.4", - "npm": ">=1.2" + "node": ">= 0.10" } }, - "node_modules/domexception": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", - "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "node_modules/is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", "dev": true, "dependencies": { - "webidl-conversions": "^7.0.0" + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" }, "engines": { - "node": ">=12" - } - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, - "node_modules/duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" + "node": ">=0.10.0" } }, - "node_modules/ee-first": { + "node_modules/is-arguments": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true - }, - "node_modules/ejs": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", - "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "dev": true, "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/electron-to-chromium": { - "version": "1.4.611", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.611.tgz", - "integrity": "sha512-ZtRpDxrjHapOwxtv+nuth5ByB8clyn8crVynmRNGO3wG3LOp8RTcyZDqwaI6Ng6y8FCK2hVZmJoqwCskKbNMaw==", - "dev": true - }, - "node_modules/elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", "dev": true, "dependencies": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/elliptic/node_modules/bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/emojilib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", - "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", - "dev": true - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true, - "engines": { - "node": ">= 4" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "node_modules/is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dependencies": { - "once": "^1.4.0" + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/enhanced-resolve": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", - "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dev": true, "dependencies": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.5.0", - "tapable": "^1.0.0" + "has-bigints": "^1.0.1" }, - "engines": { - "node": ">=6.9.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/enhanced-resolve/node_modules/memory-fs": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", - "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "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": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" + "binary-extensions": "^2.0.0" }, "engines": { - "node": ">=4.3.0 <5.0.0 || >=5.10" + "node": ">=8" } }, - "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/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, "dependencies": { - "ansi-colors": "^4.1.1" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=8.6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "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.12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/envinfo": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.7.3.tgz", - "integrity": "sha512-46+j5QxbPWza0PB1i15nZx0xQ4I/EfQxg9J8Had3b408SV63nEtor2e+oiY63amTo9KTuh2a3XLObNwduxYwwA==", + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, - "bin": { - "envinfo": "dist/cli.js" + "dependencies": { + "hasown": "^2.0.0" }, - "engines": { - "node": ">=4" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, "dependencies": { - "prr": "~1.0.1" + "has-tostringtag": "^1.0.0" }, - "bin": { - "errno": "cli.js" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-abstract": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", - "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.1", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.1", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.0", - "safe-array-concat": "^1.0.0", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.10" + "bin": { + "is-docker": "cli.js" }, "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", + "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" + "call-bind": "^1.0.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-get-iterator/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/es-iterator-helpers": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.14.tgz", - "integrity": "sha512-JgtVnwiuoRuzLvqelrvN3Xu7H9bu2ap/kQ2CrM62iidP8SKuD99rWU3CJy++s7IVL2qb/AjXPGR/E7i9ngd/Cw==", + "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==", "dev": true, - "dependencies": { - "asynciterator.prototype": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-set-tostringtag": "^2.0.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.2.1", - "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "iterator.prototype": "^1.1.0", - "safe-array-concat": "^1.0.0" + "engines": { + "node": ">=8" } }, - "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "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==", "dev": true, "dependencies": { - "has": "^1.0.3" + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "node_modules/is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, "engines": { "node": ">= 0.4" }, @@ -6309,2029 +6129,1945 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esbuild": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz", - "integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==", + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.15.18", - "@esbuild/linux-loong64": "0.15.18", - "esbuild-android-64": "0.15.18", - "esbuild-android-arm64": "0.15.18", - "esbuild-darwin-64": "0.15.18", - "esbuild-darwin-arm64": "0.15.18", - "esbuild-freebsd-64": "0.15.18", - "esbuild-freebsd-arm64": "0.15.18", - "esbuild-linux-32": "0.15.18", - "esbuild-linux-64": "0.15.18", - "esbuild-linux-arm": "0.15.18", - "esbuild-linux-arm64": "0.15.18", - "esbuild-linux-mips64le": "0.15.18", - "esbuild-linux-ppc64le": "0.15.18", - "esbuild-linux-riscv64": "0.15.18", - "esbuild-linux-s390x": "0.15.18", - "esbuild-netbsd-64": "0.15.18", - "esbuild-openbsd-64": "0.15.18", - "esbuild-sunos-64": "0.15.18", - "esbuild-windows-32": "0.15.18", - "esbuild-windows-64": "0.15.18", - "esbuild-windows-arm64": "0.15.18" + "node": ">=0.12.0" } }, - "node_modules/esbuild-android-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz", - "integrity": "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==", - "cpu": [ - "x64" - ], + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "has-tostringtag": "^1.0.0" + }, "engines": { - "node": ">=12" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esbuild-android-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz", - "integrity": "sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==", - "cpu": [ - "arm64" - ], + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, - "optional": true, - "os": [ - "android" - ], "engines": { - "node": ">=12" + "node": ">=8" } }, - "node_modules/esbuild-darwin-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz", - "integrity": "sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==", - "cpu": [ - "x64" - ], + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "isobject": "^3.0.1" + }, "engines": { - "node": ">=12" + "node": ">=0.10.0" } }, - "node_modules/esbuild-darwin-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz", - "integrity": "sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==", - "cpu": [ - "arm64" - ], + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, "engines": { - "node": ">=12" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esbuild-freebsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz", - "integrity": "sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==", - "cpu": [ - "x64" - ], + "node_modules/is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], + "dependencies": { + "is-unc-path": "^1.0.0" + }, "engines": { - "node": ">=12" + "node": ">=0.10.0" } }, - "node_modules/esbuild-freebsd-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz", - "integrity": "sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==", - "cpu": [ - "arm64" - ], + "node_modules/is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esbuild-linux-32": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz", - "integrity": "sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==", - "cpu": [ - "ia32" - ], + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esbuild-linux-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz", - "integrity": "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==", - "cpu": [ - "x64" - ], + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "has-tostringtag": "^1.0.0" + }, "engines": { - "node": ">=12" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esbuild-linux-arm": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz", - "integrity": "sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==", - "cpu": [ - "arm" - ], + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "has-symbols": "^1.0.2" + }, "engines": { - "node": ">=12" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esbuild-linux-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz", - "integrity": "sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==", - "cpu": [ - "arm64" - ], + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "which-typed-array": "^1.1.11" + }, "engines": { - "node": ">=12" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esbuild-linux-mips64le": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz", - "integrity": "sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==", - "cpu": [ - "mips64el" - ], + "node_modules/is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "unc-path-regex": "^0.1.2" + }, "engines": { - "node": ">=12" + "node": ">=0.10.0" } }, - "node_modules/esbuild-linux-ppc64le": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz", - "integrity": "sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==", - "cpu": [ - "ppc64" - ], + "node_modules/is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esbuild-linux-riscv64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz", - "integrity": "sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==", - "cpu": [ - "riscv64" - ], + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esbuild-linux-s390x": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz", - "integrity": "sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==", - "cpu": [ - "s390x" - ], + "node_modules/is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esbuild-netbsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz", - "integrity": "sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==", - "cpu": [ - "x64" - ], + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true, - "optional": true, - "os": [ - "netbsd" - ], "engines": { - "node": ">=12" + "node": ">=0.10.0" } }, - "node_modules/esbuild-openbsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz", - "integrity": "sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-sunos-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz", - "integrity": "sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-windows-32": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz", - "integrity": "sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==", - "cpu": [ - "ia32" - ], + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "is-docker": "^2.0.0" + }, "engines": { - "node": ">=12" + "node": ">=8" } }, - "node_modules/esbuild-windows-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz", - "integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true }, - "node_modules/esbuild-windows-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz", - "integrity": "sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "node_modules/iterator.prototype": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", + "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", "dev": true, - "engines": { - "node": ">=0.8.0" + "dependencies": { + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" } }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "node_modules/jake": { + "version": "10.8.7", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", + "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", "dev": true, "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" }, "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" + "jake": "bin/cli.js" }, "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" + "node": ">=10" } }, - "node_modules/escodegen/node_modules/estraverse": { - "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" - } + "node_modules/jake/node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true }, - "node_modules/escodegen/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, - "optional": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 10.13.0" } }, - "node_modules/eslint": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.13.0.tgz", - "integrity": "sha512-uCORMuOO8tUzJmsdRtrvcGq5qposf7Rw0LwkTJkoDbOycVQtQjmnhZSuLQnozLE4TmAzlMVV45eCHmQ1OpDKUQ==", + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.2.1", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.0", - "esquery": "^1.2.0", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash": "^4.17.19", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^5.2.3", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" + "has-flag": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=10" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "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==", + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", "dev": true, - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" + "engines": { + "node": ">= 0.6.0" } }, - "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": { - "ms": "^2.1.1" - } + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true }, - "node_modules/eslint-import-resolver-node/node_modules/resolve": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", - "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "node_modules/jsdoc-type-pratt-parser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz", + "integrity": "sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==", "dev": true, - "dependencies": { - "debug": "^3.2.7" - }, "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } + "node": ">=12.0.0" } }, - "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==", + "node_modules/jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", "dev": true, "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.28.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz", - "integrity": "sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.findlastindex": "^1.2.2", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.8.0", - "has": "^1.0.3", - "is-core-module": "^2.13.0", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.6", - "object.groupby": "^1.0.0", - "object.values": "^1.1.6", - "semver": "^6.3.1", - "tsconfig-paths": "^3.14.2" + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=14" }, "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": 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==", + "node_modules/jsdom/node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, - "dependencies": { - "ms": "^2.1.1" + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" } }, - "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==", + "node_modules/jsdom/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dev": true, "dependencies": { - "esutils": "^2.0.2" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, - "node_modules/eslint-plugin-import/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" + "bin": { + "jsesc": "bin/jsesc" }, "engines": { - "node": "*" + "node": ">=4" } }, - "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==", + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "bin": { - "semver": "bin/semver.js" + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, - "node_modules/eslint-plugin-import/node_modules/tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true }, - "node_modules/eslint-plugin-jsdoc": { - "version": "40.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-40.0.0.tgz", - "integrity": "sha512-LOPyIu1vAVvGPkye3ci0moj0iNf3f8bmin6do2DYDj+77NRXWnkmhKRy8swWsatUs3mB5jYPWPUsFg9pyfEiyA==", + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, "dependencies": { - "@es-joy/jsdoccomment": "~0.36.1", - "comment-parser": "1.3.1", - "debug": "^4.3.4", - "escape-string-regexp": "^4.0.0", - "esquery": "^1.4.0", - "semver": "^7.3.8", - "spdx-expression-parse": "^3.0.1" + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" }, "engines": { - "node": "^14 || ^16 || ^17 || ^18 || ^19" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "node": ">=4.0" } }, - "node_modules/eslint-plugin-jsdoc/node_modules/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==", + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-react": { - "version": "7.33.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", - "integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==", + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "array.prototype.tosorted": "^1.1.1", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.12", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "object.hasown": "^1.1.2", - "object.values": "^1.1.6", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.4", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.8" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "node": ">= 0.8.0" } }, - "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "node_modules/liftup": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/liftup/-/liftup-3.0.1.tgz", + "integrity": "sha512-yRHaiQDizWSzoXk3APcA71eOI/UuhEkNN9DiW2Tt44mhYzX4joFoCZlxsSOF7RyeLlfqzFLQI1ngFq3ggMPhOw==", "dev": true, + "dependencies": { + "extend": "^3.0.2", + "findup-sync": "^4.0.0", + "fined": "^1.2.0", + "flagged-respawn": "^1.0.1", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.1", + "rechoir": "^0.7.0", + "resolve": "^1.19.0" + }, "engines": { "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" } }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "node_modules/liftup/node_modules/findup-sync": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz", + "integrity": "sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==", "dev": true, "dependencies": { - "esutils": "^2.0.2" + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^4.0.2", + "resolve-dir": "^1.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 8" } }, - "node_modules/eslint-plugin-react/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/liftup/node_modules/rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", "dev": true, + "dependencies": { + "resolve": "^1.9.0" + }, "engines": { - "node": ">=4.0" + "node": ">= 0.10" } }, - "node_modules/eslint-plugin-react/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, "engines": { - "node": "*" + "node": ">=6.11.5" } }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "node_modules/local-pkg": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", + "integrity": "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==", "dev": true, - "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" + "engines": { + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-plugin-react/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" + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/eslint-plugin-security": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-1.4.0.tgz", - "integrity": "sha512-xlS7P2PLMXeqfhyf3NpqbvbnW04kN8M9NtmhpR3XGyOvt/vNKS7XPXT5EDbwKW9vCjWH4PpfQvgD/+JgN0VJKA==", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "dependencies": { - "safe-regex": "^1.1.0" + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", "dev": true, "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "chalk": "^4.0.0" }, "engines": { - "node": ">=8.0.0" + "node": ">=10" } }, - "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": ">=6" + "js-tokens": "^3.0.0 || ^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" + "bin": { + "loose-envify": "cli.js" } }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", "dev": true, - "engines": { - "node": ">=4" + "dependencies": { + "get-func-name": "^2.0.1" } }, - "node_modules/eslint-visitor-keys": { + "node_modules/lowercase-keys": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", - "dev": true, + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/espree": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", - "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==", + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.3.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" + "yallist": "^3.0.2" } }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" + "lz-string": "bin/bin.js" } }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "node_modules/make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", "dev": true, "dependencies": { - "estraverse": "^5.1.0" + "kind-of": "^6.0.2" }, "engines": { - "node": ">=0.10" + "node": ">=0.10.0" } }, - "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==", + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", "dev": true, "engines": { - "node": ">=4.0" + "node": ">=0.10.0" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/marked": { + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz", + "integrity": "sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==", "dev": true, - "dependencies": { - "estraverse": "^5.2.0" + "bin": { + "marked": "bin/marked.js" }, "engines": { - "node": ">=4.0" + "node": ">= 16" } }, - "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==", + "node_modules/marked-terminal": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-6.2.0.tgz", + "integrity": "sha512-ubWhwcBFHnXsjYNsu+Wndpg0zhY4CahSpPlA70PlO0rR9r2sZpkyU+rkCsOWH+KMEkx847UpALON+HWgxowFtw==", "dev": true, + "dependencies": { + "ansi-escapes": "^6.2.0", + "cardinal": "^2.1.1", + "chalk": "^5.3.0", + "cli-table3": "^0.6.3", + "node-emoji": "^2.1.3", + "supports-hyperlinks": "^3.0.0" + }, "engines": { - "node": ">=4.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "marked": ">=1 <12" } }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "node_modules/marked-terminal/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, "engines": { - "node": ">=4.0" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" } }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true, "engines": { "node": ">= 0.6" } }, - "node_modules/eventemitter2": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", - "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", "dev": true }, - "node_modules/events": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", - "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, "engines": { - "node": ">=0.8.x" + "node": ">= 8" } }, - "node_modules/evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "dev": true, - "dependencies": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" + "engines": { + "node": ">= 0.6" } }, - "node_modules/execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "dependencies": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" + "braces": "^3.0.2", + "picomatch": "^2.3.1" }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true, "engines": { - "node": ">= 0.8.0" + "node": ">=8.6" } }, - "node_modules/expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, - "dependencies": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "bin": { + "mime": "cli.js" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" + "node": ">=4" } }, - "node_modules/expand-brackets/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/expand-brackets/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "dependencies": { - "is-extendable": "^0.1.0" + "mime-db": "1.52.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/expand-brackets/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "dev": true, - "dependencies": { - "homedir-polyfill": "^1.0.1" - }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">= 0.10.0" + "node": "*" } }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, - "dependencies": { - "ms": "2.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", "dev": true, - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" + "bin": { + "mkdirp": "dist/cjs/src/bin.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/extend-shallow/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "node_modules/mocha": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", + "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", "dev": true, "dependencies": { - "is-plain-object": "^2.0.4" + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.1", + "debug": "4.3.1", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.0.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.20", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.1.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" }, "engines": { - "node": ">=0.10.0" + "node": ">= 10.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" } }, - "node_modules/extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "node_modules/mocha-junit-reporter": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/mocha-junit-reporter/-/mocha-junit-reporter-2.2.1.tgz", + "integrity": "sha512-iDn2tlKHn8Vh8o4nCzcUVW4q7iXp7cC4EB78N0cDHIobLymyHNwe0XG8HEHHjc3hJlXm0Vy6zcrxaIhnI2fWmw==", "dev": true, "dependencies": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "debug": "^4.3.4", + "md5": "^2.3.0", + "mkdirp": "^3.0.0", + "strip-ansi": "^6.0.1", + "xml": "^1.0.1" }, - "engines": { - "node": ">=0.10.0" + "peerDependencies": { + "mocha": ">=2.2.5" } }, - "node_modules/extglob/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "node_modules/mocha/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==", "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/extglob/node_modules/extend-shallow": { + "node_modules/mocha/node_modules/argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/mocha/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "dependencies": { - "is-extendable": "^0.1.0" + "ms": "2.1.2" }, "engines": { - "node": ">=0.10.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/extglob/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "node_modules/mocha/node_modules/debug/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==", + "dev": true + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "dependencies": { - "kind-of": "^6.0.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/extglob/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "node_modules/mocha/node_modules/js-yaml": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", "dev": true, "dependencies": { - "kind-of": "^6.0.0" + "argparse": "^2.0.1" }, - "engines": { - "node": ">=0.10.0" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/extglob/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "node_modules/mocha/node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=0.10.0" + "node": "*" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "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/fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/fast-glob/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } + "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==", + "dev": true }, - "node_modules/fast-glob/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/nanoid": { + "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" + "bin": { + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": ">=8" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/fast-glob/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "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/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true, "engines": { - "node": ">=0.12.0" + "node": ">= 0.6" } }, - "node_modules/fast-glob/node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/node-emoji": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.3.tgz", + "integrity": "sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==", "dev": true, "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "@sindresorhus/is": "^4.6.0", + "char-regex": "^1.0.2", + "emojilib": "^2.4.0", + "skin-tone": "^2.0.0" }, "engines": { - "node": ">=8.6" + "node": ">=18" } }, - "node_modules/fast-glob/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", "dev": true, "dependencies": { - "is-number": "^7.0.0" + "abbrev": "1" }, - "engines": { - "node": ">=8.0" + "bin": { + "nopt": "bin/nopt.js" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "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=", - "dev": true - }, - "node_modules/fastq": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", - "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, - "dependencies": { - "reusify": "^1.0.4" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/fflate": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz", - "integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==", - "dev": true + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/figgy-pudding": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", - "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", + "node_modules/nwsapi": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", + "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", "dev": true }, - "node_modules/file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, - "dependencies": { - "flat-cache": "^2.0.1" - }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true, - "optional": true + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", "dev": true, "dependencies": { - "minimatch": "^5.0.1" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "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, - "dependencies": { - "balanced-match": "^1.0.0" + "engines": { + "node": ">= 0.4" } }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "node_modules/object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==", "dev": true, "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "node_modules/object.entries": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", + "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", "dev": true, "dependencies": { - "is-extendable": "^0.1.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "node_modules/object.fromentries": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", "dev": true, "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/object.groupby": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", + "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", "dev": true, "dependencies": { - "ms": "2.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1" } }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "node_modules/object.hasown": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz", + "integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==", "dev": true, "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, - "engines": { - "node": ">=6" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/find-cache-dir/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "node_modules/object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w==", "dev": true, "dependencies": { - "locate-path": "^3.0.0" + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/find-cache-dir/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", "dev": true, "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "isobject": "^3.0.1" }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/find-cache-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/object.values": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", "dev": true, "dependencies": { - "p-try": "^2.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/find-cache-dir/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, "dependencies": { - "p-limit": "^2.0.0" + "ee-first": "1.1.1" }, "engines": { - "node": ">=6" - } - }, - "node_modules/find-cache-dir/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true, - "engines": { - "node": ">=4" + "node": ">= 0.8" } }, - "node_modules/find-cache-dir/node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=6" + "wrappy": "1" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", "dev": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/findup-sync": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", - "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=", + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "dependencies": { - "glob": "~5.0.0" + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" }, "engines": { - "node": ">= 0.6.0" + "node": ">= 0.8.0" } }, - "node_modules/findup-sync/node_modules/glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "node_modules/os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", "dev": true, - "dependencies": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, "engines": { - "node": "*" + "node": ">=0.10.0" } }, - "node_modules/fined": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", - "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true, - "dependencies": { - "expand-tilde": "^2.0.2", - "is-plain-object": "^2.0.3", - "object.defaults": "^1.1.0", - "object.pick": "^1.2.0", - "parse-filepath": "^1.0.1" - }, "engines": { - "node": ">= 0.10" + "node": ">=0.10.0" } }, - "node_modules/flagged-respawn": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", - "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "node_modules/osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, + "dependencies": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", "engines": { - "node": ">= 0.10" + "node": ">=8" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "bin": { - "flat": "cli.js" + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "dependencies": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "dev": true - }, - "node_modules/flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" + "engines": { + "node": ">=6" } }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "dependencies": { - "is-callable": "^1.1.3" + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "node_modules/for-in": { + "node_modules/parse-filepath": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", "dev": true, + "dependencies": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=0.8" } }, - "node_modules/for-own": { + "node_modules/parse-passwd": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", "dev": true, - "dependencies": { - "for-in": "^1.0.1" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/form-data": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", "dev": true, "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "entities": "^4.4.0" }, - "engines": { - "node": ">= 0.12" + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, - "node_modules/fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "dependencies": { - "map-cache": "^0.2.2" - }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "engines": { - "node": ">= 0.6" + "node": ">=0.10.0" } }, - "node_modules/from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" + "engines": { + "node": ">=8" } }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "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-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", "dev": true, "dependencies": { - "minipass": "^3.0.0" + "path-root-regex": "^0.1.0" }, "engines": { - "node": ">= 8" + "node": ">=0.10.0" } }, - "node_modules/fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "node_modules/path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", "dev": true }, - "node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, "engines": { - "node": ">= 4.0" + "node": ">=8" } }, - "node_modules/function-bind": { + "node_modules/pathval": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "*" } }, - "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=", + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, - "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==", + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "engines": { + "node": ">=8.6" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, "engines": { - "node": ">=6.9.0" + "node": ">=8" } }, - "node_modules/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==", + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">=8" } }, - "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, "dependencies": { - "pump": "^3.0.0" + "p-limit": "^2.2.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "node_modules/playwright": { + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.0.tgz", + "integrity": "sha512-XOsfl5ZtAik/T9oek4V0jAypNlaCNzuKOwVhqhgYT3os6kH34PzbRb74F0VWcLYa5WFdnmxl7qyAHBXvPv7lqQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "playwright-core": "1.41.0" + }, + "bin": { + "playwright": "cli.js" }, "engines": { - "node": ">= 0.4" + "node": ">=16" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optionalDependencies": { + "fsevents": "2.3.2" } }, - "node_modules/get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "node_modules/playwright-core": { + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.0.tgz", + "integrity": "sha512-UGKASUhXmvqm2Lxa1fNr8sFwAtqjpgBRr9jQ7XBI8Rn5uFiEowGUGwrruUQsVPIom4bk7Lt+oLGpXobnXzrBIw==", "dev": true, + "bin": { + "playwright-core": "cli.js" + }, "engines": { - "node": ">=0.10.0" + "node": ">=16" } }, - "node_modules/getobject": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/getobject/-/getobject-1.0.0.tgz", - "integrity": "sha512-tbUz6AKKKr2YiMB+fLWIgq5ZeBOobop9YMMAU9dC54/ot2ksMXt3DOFyBuhZw6ptcVszEykgByK20j7W9jHFag==", + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=10" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/glob": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-4.4.2.tgz", - "integrity": "sha512-uDfQml82dlkWsiYk+CdoOdJe09vfPUjTFBM23krHsaFuaC5/o4A5bLX95h7ccc/Fs/JjC5DuMwPgiIzTBV5WpA==", + "node_modules/postcss": { + "version": "8.4.33", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", + "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^2.0.1", - "once": "^1.3.0" + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" }, "engines": { - "node": "*" + "node": "^10 || ^12 || >=14" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "dev": true, - "dependencies": { - "is-glob": "^4.0.1" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": ">= 6" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/glob/node_modules/minimatch": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", - "integrity": "sha512-jQo6o1qSVLEWaw3l+bwYA2X0uLuK2KjNh2wjgO7Q/9UJnXr1Q3yQKR8BI0/Bt/rPg75e6SMW4hW/6cBHVTZUjA==", - "deprecated": "Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "dependencies": { - "brace-expansion": "^1.0.0" - }, "engines": { - "node": "*" + "node": ">= 0.8.0" } }, - "node_modules/global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, - "dependencies": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" + "bin": { + "prettier": "bin-prettier.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "dependencies": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" }, "engines": { - "node": ">=0.10.0" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "dependencies": { - "isexe": "^2.0.0" + "engines": { + "node": ">=10" }, - "bin": { - "which": "bin/which" + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "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/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dev": true, "dependencies": { - "type-fest": "^0.8.1" + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.10" } }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, "dependencies": { - "define-properties": "^1.1.3" + "side-channel": "^1.0.4" }, "engines": { - "node": ">= 0.4" + "node": ">=0.6" }, "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==", + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", "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", - "slash": "^3.0.0" - }, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "engines": { "node": ">=10" }, @@ -8339,517 +8075,687 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby/node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "dev": true, "engines": { - "node": ">= 4" + "node": ">= 0.6" } }, - "node_modules/google-closure-compiler": { - "version": "20180610.0.2", - "resolved": "https://registry.npmjs.org/google-closure-compiler/-/google-closure-compiler-20180610.0.2.tgz", - "integrity": "sha1-Eggy9gzK2ZXo5HxedRIRLVTGro8=", + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dev": true, "dependencies": { - "chalk": "^1.0.0", - "vinyl": "^2.0.1", - "vinyl-sourcemaps-apply": "^0.2.0" - }, - "bin": { - "google-closure-compiler": "cli.js" + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" }, "engines": { - "node": ">=6" + "node": ">= 0.8" } }, - "node_modules/google-closure-compiler/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", "dev": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/google-closure-compiler/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dev": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "node_modules/react-refresh": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.13.0.tgz", + "integrity": "sha512-XP8A9BT0CpRBD+NYLLeIhld/RqG9+gktUjW1FkE+Vm7OCinbG1SshcK5tb9ls4kzvjZr9mOQc7HYgBngEyPAXg==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/google-closure-compiler/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, - "node_modules/google-closure-compiler/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "node_modules/readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, "dependencies": { - "ansi-regex": "^2.0.0" + "picomatch": "^2.2.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8.10.0" } }, - "node_modules/google-closure-compiler/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, "engines": { - "node": ">=0.8.0" + "node": ">= 0.10" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "node_modules/redeyed": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", + "integrity": "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "esprima": "~4.0.0" } }, - "node_modules/got": { - "version": "11.8.5", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.5.tgz", - "integrity": "sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==", + "node_modules/reflect.getprototypeof": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", + "integrity": "sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==", + "dev": true, "dependencies": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "globalthis": "^1.0.3", + "which-builtin-type": "^1.1.3" }, "engines": { - "node": ">=10.19.0" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "dev": true }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "node_modules/regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", "dev": true, - "engines": { - "node": ">=4.x" + "bin": { + "regexp-tree": "bin/regexp-tree" } }, - "node_modules/grunt": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.6.1.tgz", - "integrity": "sha512-/ABUy3gYWu5iBmrUSRBP97JLpQUm0GgVveDCp6t3yRNIoltIYw7rEj3g5y1o2PGPR2vfTRGa7WC/LZHLTXnEzA==", + "node_modules/regexp.prototype.flags": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", "dev": true, "dependencies": { - "dateformat": "~4.6.2", - "eventemitter2": "~0.4.13", - "exit": "~0.1.2", - "findup-sync": "~5.0.0", - "glob": "~7.1.6", - "grunt-cli": "~1.4.3", - "grunt-known-options": "~2.0.0", - "grunt-legacy-log": "~3.0.0", - "grunt-legacy-util": "~2.0.1", - "iconv-lite": "~0.6.3", - "js-yaml": "~3.14.0", - "minimatch": "~3.0.4", - "nopt": "~3.0.6" - }, - "bin": { - "grunt": "bin/grunt" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" }, "engines": { - "node": ">=16" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/grunt-bump": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/grunt-bump/-/grunt-bump-0.3.4.tgz", - "integrity": "sha1-dyUehv+L5NvSNK/NXugk5WOaR2c=", + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true, - "dependencies": { - "semver": "^4.3.3" - }, "engines": { - "node": ">= 0.8.0" + "node": ">=8" }, - "peerDependencies": { - "grunt": ">=0.4.0" + "funding": { + "url": "https://github.com/sponsors/mysticatea" } }, - "node_modules/grunt-bump/node_modules/semver": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", - "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, - "bin": { - "semver": "bin/semver" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/grunt-cli": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", - "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=", + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, - "dependencies": { - "findup-sync": "~0.3.0", - "grunt-known-options": "~1.1.0", - "nopt": "~3.0.6", - "resolve": "~1.1.0" - }, - "bin": { - "grunt": "bin/grunt" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/grunt-closure-tools": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/grunt-closure-tools/-/grunt-closure-tools-1.0.0.tgz", - "integrity": "sha1-+pdty8JrZSYgq1pYkYsZnxbpyZ0=", + "node_modules/requirejs": { + "version": "2.1.22", + "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.1.22.tgz", + "integrity": "sha512-AhZqN7UrWV8R2d1LfGfznskMJNF0Vb6yStwrCn52UbktJg6y5V1I7RoGRyJtACe86d50PyQn0Iw0jYDKMvc4iA==", "dev": true, - "dependencies": { - "grunt": "^1.0.1", - "task-closure-tools": "^0.1.10" + "bin": { + "r.js": "bin/r.js" }, "engines": { - "node": ">=0.8.0" + "node": ">=0.4.0" } }, - "node_modules/grunt-contrib-concat": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/grunt-contrib-concat/-/grunt-contrib-concat-0.5.1.tgz", - "integrity": "sha1-lTxu/f39LBB6uchQd/LUsk0xzUk=", + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { - "chalk": "^0.5.1", - "source-map": "^0.3.0" + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" }, - "engines": { - "node": ">=0.10.0" + "bin": { + "resolve": "bin/resolve" }, - "peerDependencies": { - "grunt": ">=0.4.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/grunt-contrib-concat/node_modules/ansi-regex": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", - "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=", + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/grunt-contrib-concat/node_modules/ansi-styles": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", - "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=", + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/grunt-contrib-concat/node_modules/chalk": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", - "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", + "node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", "dev": true, "dependencies": { - "ansi-styles": "^1.1.0", - "escape-string-regexp": "^1.0.0", - "has-ansi": "^0.1.0", - "strip-ansi": "^0.3.0", - "supports-color": "^0.2.0" + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/grunt-contrib-concat/node_modules/has-ansi": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", - "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "dependencies": { - "ansi-regex": "^0.2.0" - }, - "bin": { - "has-ansi": "cli.js" - }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/grunt-contrib-concat/node_modules/source-map": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.3.0.tgz", - "integrity": "sha1-hYb7mloAXltQHiHNGLbyG0V60fk=", - "dev": true, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", "dependencies": { - "amdefine": ">=0.0.4" + "lowercase-keys": "^2.0.0" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, "engines": { - "node": ">=0.8.0" + "iojs": ">=1.0.0", + "node": ">=0.10.0" } }, - "node_modules/grunt-contrib-concat/node_modules/strip-ansi": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", - "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "dependencies": { - "ansi-regex": "^0.2.1" + "glob": "^7.1.3" }, "bin": { - "strip-ansi": "cli.js" + "rimraf": "bin.js" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/grunt-contrib-concat/node_modules/supports-color": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", - "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=", + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, - "bin": { - "supports-color": "cli.js" + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/grunt-known-options": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.1.tgz", - "integrity": "sha512-cHwsLqoighpu7TuYj5RonnEuxGVFnztcUqTqp5rXFGYL4OuPFofwC4Ycg7n9fYwvK6F5WbYgeVOwph9Crs2fsQ==", + "node_modules/rollup": { + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, "engines": { - "node": ">=0.10.0" + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/grunt-legacy-log": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-3.0.0.tgz", - "integrity": "sha512-GHZQzZmhyq0u3hr7aHW4qUH0xDzwp2YXldLPZTCjlOeGscAOWWPftZG3XioW8MasGp+OBRIu39LFx14SLjXRcA==", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "dependencies": { - "colors": "~1.1.2", - "grunt-legacy-log-utils": "~2.1.0", - "hooker": "~0.2.3", - "lodash": "~4.17.19" - }, - "engines": { - "node": ">= 0.10.0" + "queue-microtask": "^1.2.2" } }, - "node_modules/grunt-legacy-log-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.1.0.tgz", - "integrity": "sha512-lwquaPXJtKQk0rUM1IQAop5noEpwFqOXasVoedLeNzaibf/OPWjKYvvdqnEHNmU+0T0CaReAXIbGo747ZD+Aaw==", + "node_modules/safe-array-concat": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", + "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", "dev": true, "dependencies": { - "chalk": "~4.1.0", - "lodash": "~4.17.19" + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" }, "engines": { - "node": ">=10" + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/grunt-legacy-util": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-2.0.1.tgz", - "integrity": "sha512-2bQiD4fzXqX8rhNdXkAywCadeqiPiay0oQny77wA2F3WF4grPJXCvAcyoWUJV+po/b15glGkxuSiQCK299UC2w==", + "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.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", + "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==", "dev": true, "dependencies": { - "async": "~3.2.0", - "exit": "~0.1.2", - "getobject": "~1.0.0", - "hooker": "~0.2.3", - "lodash": "~4.17.21", - "underscore.string": "~3.3.5", - "which": "~2.0.2" + "regexp-tree": "~0.1.1" + } + }, + "node_modules/safe-regex-test": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.2.tgz", + "integrity": "sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2", + "is-regex": "^1.1.4" }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/grunt-legacy-util/node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, - "node_modules/grunt-shell": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/grunt-shell/-/grunt-shell-1.1.2.tgz", - "integrity": "sha1-Rz5GUwHSnQtW3xb+MQeYznFNCVY=", + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==", + "dev": true + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "dev": true, "dependencies": { - "chalk": "^1.0.0" + "xmlchars": "^2.2.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=v12.22.7" } }, - "node_modules/grunt-shell/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "loose-envify": "^1.1.0" } }, - "node_modules/grunt-shell/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/grunt-shell/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "yallist": "^4.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/grunt-shell/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dev": true, "dependencies": { - "ansi-regex": "^2.0.0" + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8.0" } }, - "node_modules/grunt-shell/node_modules/supports-color": { + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/send/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/serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, "engines": { - "node": ">=0.8.0" + "node": ">= 0.8.0" } }, - "node_modules/grunt-webpack": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/grunt-webpack/-/grunt-webpack-4.0.2.tgz", - "integrity": "sha512-rrqb9SRlY69jEJuCglelB7IvGrI7lRpdfH2GXpFlIOGPRTTtlSxYMU4Fjg8FHaC6ilnMbW5jd55Ff1lR5OibCA==", + "node_modules/set-function-length": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", + "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", "dev": true, "dependencies": { - "deep-for-each": "^3.0.0", - "lodash": "^4.17.19" + "define-data-property": "^1.1.1", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.2", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" }, "engines": { - "node": ">=10.13.0" + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" }, - "peerDependencies": { - "webpack": "^4.0.0" + "engines": { + "node": ">= 0.4" } }, - "node_modules/grunt/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "kind-of": "^6.0.2" }, "engines": { "node": ">=8" } }, - "node_modules/grunt/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "dependencies": { - "to-regex-range": "^5.0.1" + "shebang-regex": "^3.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/grunt/node_modules/findup-sync": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-5.0.0.tgz", - "integrity": "sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", "dev": true, "dependencies": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.3", - "micromatch": "^4.0.4", - "resolve-dir": "^1.0.1" + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" }, "engines": { - "node": ">= 10.13.0" + "node": ">=4" } }, - "node_modules/grunt/node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "node_modules/shelljs/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -8860,885 +8766,855 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/grunt/node_modules/grunt-cli": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.4.3.tgz", - "integrity": "sha512-9Dtx/AhVeB4LYzsViCjUQkd0Kw0McN2gYpdmGYKtE2a5Yt7v1Q+HYZVWhqXc/kGnxlMtqKDxSwotiGeFmkrCoQ==", + "node_modules/shiki": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", + "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", "dev": true, "dependencies": { - "grunt-known-options": "~2.0.0", - "interpret": "~1.1.0", - "liftup": "~3.0.1", - "nopt": "~4.0.1", - "v8flags": "~3.2.0" - }, - "bin": { - "grunt": "bin/grunt" - }, - "engines": { - "node": ">=10" + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" } }, - "node_modules/grunt/node_modules/grunt-cli/node_modules/nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", "dev": true, "dependencies": { - "abbrev": "1", - "osenv": "^0.1.4" + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" }, - "bin": { - "nopt": "bin/nopt.js" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/grunt/node_modules/grunt-known-options": { + "node_modules/skin-tone": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-2.0.0.tgz", - "integrity": "sha512-GD7cTz0I4SAede1/+pAbmJRG44zFLPipVtdL9o3vqx9IEyb7b4/Y3s7r6ofI3CchR5GvYJ+8buCSioDv5dQLiA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/grunt/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", + "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", "dev": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "unicode-emoji-modifier-base": "^1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/grunt/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { - "node": ">=0.12.0" + "node": ">=8" } }, - "node_modules/grunt/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" }, "engines": { - "node": ">=8.6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/grunt/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/source-map-explorer": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/source-map-explorer/-/source-map-explorer-2.5.3.tgz", + "integrity": "sha512-qfUGs7UHsOBE5p/lGfQdaAj/5U/GWYBw2imEpD6UQNkqElYonkow8t+HBL1qqIl3CuGZx7n8/CQo4x1HwSHhsg==", "dev": true, "dependencies": { - "is-number": "^7.0.0" + "btoa": "^1.2.1", + "chalk": "^4.1.0", + "convert-source-map": "^1.7.0", + "ejs": "^3.1.5", + "escape-html": "^1.0.3", + "glob": "^7.1.6", + "gzip-size": "^6.0.0", + "lodash": "^4.17.20", + "open": "^7.3.1", + "source-map": "^0.7.4", + "temp": "^0.9.4", + "yargs": "^16.2.0" + }, + "bin": { + "sme": "bin/cli.js", + "source-map-explorer": "bin/cli.js" }, "engines": { - "node": ">=8.0" + "node": ">=12" } }, - "node_modules/gzip-size": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", - "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "node_modules/source-map-explorer/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/source-map-explorer/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { - "duplexer": "^0.1.2" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=10" + "node": "*" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "node_modules/source-map-explorer/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, "engines": { - "node": ">=0.10.0" + "node": ">= 8" } }, - "node_modules/has-ansi/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "node_modules/source-map-support": { + "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, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/spdx-license-ids": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", + "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", + "dev": true }, - "node_modules/has-symbols": { + "node_modules/sprintf-js": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8" } }, - "node_modules/has-tostringtag": { + "node_modules/stop-iteration-iterator": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" + "internal-slot": "^1.0.4" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "node_modules/stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", "dev": true, "dependencies": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" } }, - "node_modules/has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "dependencies": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" + "safe-buffer": "~5.2.0" } }, - "node_modules/has-values/node_modules/kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { - "is-buffer": "^1.1.5" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "node_modules/string.prototype.matchall": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", + "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", "dev": true, "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "regexp.prototype.flags": "^1.5.0", + "set-function-name": "^2.0.0", + "side-channel": "^1.0.4" }, - "engines": { - "node": ">=4" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hash-base/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "node_modules/string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { - "node": ">= 6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "node_modules/string.prototype.trimend": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "node_modules/string.prototype.trimstart": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", "dev": true, - "bin": { - "he": "bin/he" + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hexy": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/hexy/-/hexy-0.2.11.tgz", - "integrity": "sha512-ciq6hFsSG/Bpt2DmrZJtv+56zpPdnq+NQ4ijEFrveKN0ZG1mhl/LdT1NQZ9se6ty1fACcI4d4vYqC9v8EYpH2A==", + "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, - "bin": { - "hexy": "bin/hexy_cmd.js" + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "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, - "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" + "engines": { + "node": ">=4" } }, - "node_modules/homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, - "dependencies": { - "parse-passwd": "^1.0.0" - }, "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/hooker": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", - "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=", + "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, + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/html-encoding-sniffer": { + "node_modules/supports-hyperlinks": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz", + "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==", "dev": true, "dependencies": { - "whatwg-encoding": "^2.0.0" + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" }, "engines": { - "node": ">=12" + "node": ">=14.18" } }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "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, - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, "engines": { - "node": ">= 0.8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "node_modules/table": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", "dev": true, "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">= 6" + "node": ">=10.0.0" } }, - "node_modules/http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "node_modules/table/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">=10.19.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/https-browserify": { + "node_modules/table/node_modules/json-schema-traverse": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, "engines": { - "node": ">= 6" + "node": ">=6" } }, - "node_modules/human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "node_modules/temp": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", + "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", "dev": true, + "dependencies": { + "mkdirp": "^0.5.1", + "rimraf": "~2.6.2" + }, "engines": { - "node": ">=8.12.0" + "node": ">=6.0.0" } }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "node_modules/temp/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", - "dev": true - }, - "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/temp/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, - "engines": { - "node": ">= 4" + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" } }, - "node_modules/import-fresh": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", - "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", + "node_modules/temp/node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "dev": true, "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "glob": "^7.1.3" }, - "engines": { - "node": ">=6" + "bin": { + "rimraf": "bin.js" } }, - "node_modules/import-local": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", - "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "node_modules/terser": { + "version": "5.27.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz", + "integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==", "dev": true, "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" }, "bin": { - "import-local-fixture": "fixtures/cli.js" + "terser": "bin/terser" }, "engines": { - "node": ">=8" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true, - "engines": { - "node": ">=0.8.19" + "node": ">=10" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, "engines": { - "node": ">=8" - } - }, - "node_modules/infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" }, "engines": { - "node": ">= 0.4" + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", - "dev": true - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, - "engines": { - "node": ">= 0.10" + "dependencies": { + "randombytes": "^2.1.0" } }, - "node_modules/is-absolute": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "node_modules/terser/node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, - "dependencies": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.4.0" } }, - "node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/tinypool": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.2.4.tgz", + "integrity": "sha512-Vs3rhkUH6Qq1t5bqtb816oT+HeJTXfwt2cbPH17sWHIYKTotQIFPk3tf2fgqRrVyMDVOc1EnPgzIxfIulXVzwQ==", "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, "engines": { - "node": ">=0.10.0" + "node": ">=14.0.0" } }, - "node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "node_modules/tinyspy": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-1.1.1.tgz", + "integrity": "sha512-UVq5AXt/gQlti7oxoIg5oi/9r0WpF7DGEVwXgqWSMmyN16+e3tl5lIvTaOpJ3TAtu5xFzWccFRM4R5NaWHF+4g==", "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, "engines": { - "node": ">=0.10.0" + "node": ">=14.0.0" } }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" + "is-number": "^7.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8.0" } }, - "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "node_modules/to-utf8": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/to-utf8/-/to-utf8-0.0.1.tgz", + "integrity": "sha512-zks18/TWT1iHO3v0vFp5qLKOG27m67ycq/Y7a7cTiRuUNlc4gf3HGnkRgMv0NyhnfTamtkYBJl+YeD1/j07gBQ==" + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.6" } }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "node_modules/tough-cookie": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", "dev": true, "dependencies": { - "has-bigints": "^1.0.1" + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=6" } }, - "node_modules/is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", "dev": true, - "optional": true, "dependencies": { - "binary-extensions": "^1.0.0" + "punycode": "^2.1.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "node_modules/ts-expose-internals-conditionally": { + "version": "1.0.0-empty.0", + "resolved": "https://registry.npmjs.org/ts-expose-internals-conditionally/-/ts-expose-internals-conditionally-1.0.0-empty.0.tgz", + "integrity": "sha512-F8m9NOF6ZhdOClDVdlM8gj3fDCav4ZIFSs/EI3ksQbAAXVSCN/Jh5OCJDDZWBuBy9psFc6jULGDlPwjMYMhJDw==", + "dev": true + }, + "node_modules/ts-loader": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", + "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" }, "engines": { - "node": ">= 0.4" + "node": ">=12.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" } }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "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==", + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 8" } }, - "node_modules/is-core-module": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", - "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "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": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" } }, - "node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "node_modules/tsconfig-paths-webpack-plugin": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.1.0.tgz", + "integrity": "sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==", "dev": true, "dependencies": { - "kind-of": "^3.0.2" + "chalk": "^4.1.0", + "enhanced-resolve": "^5.7.0", + "tsconfig-paths": "^4.1.2" }, "engines": { - "node": ">=0.10.0" + "node": ">=10.13.0" } }, - "node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "node_modules/tsconfig-paths-webpack-plugin/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", "dev": true, "dependencies": { - "is-buffer": "^1.1.5" + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "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": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" + "minimist": "^1.2.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "json5": "lib/cli.js" } }, - "node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "dev": true, "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "tslib": "^1.8.1" }, "engines": { - "node": ">=0.10.0" + "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/is-descriptor/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "node_modules/tsutils/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/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "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, - "bin": { - "is-docker": "cli.js" + "dependencies": { + "prelude-ls": "^1.2.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.8.0" } }, - "node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "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": ">=0.10.0" + "node": ">=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/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", "dev": true, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" + "node": ">=14.16" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, "engines": { - "node": ">=4" + "node": ">= 0.6" } }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" }, "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==", + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", "dev": true, "dependencies": { - "is-extglob": "^2.1.1" + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -9746,8041 +9622,1604 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", "dev": true, "dependencies": { - "kind-of": "^3.0.2" + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "node_modules/typedoc": { + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.8.tgz", + "integrity": "sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==", "dev": true, "dependencies": { - "is-buffer": "^1.1.5" + "lunr": "^2.3.9", + "marked": "^4.3.0", + "minimatch": "^9.0.0", + "shiki": "^0.14.1" + }, + "bin": { + "typedoc": "bin/typedoc" }, "engines": { - "node": ">=0.10.0" + "node": ">= 14.14" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x" } }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "node_modules/typedoc/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "node_modules/typedoc/node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true, - "dependencies": { - "isobject": "^3.0.1" + "bin": { + "marked": "bin/marked.js" }, "engines": { - "node": ">=0.10.0" + "node": ">= 12" } }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "node_modules/typedoc/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/is-relative": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, - "dependencies": { - "is-unc-path": "^1.0.0" + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4.2.0" } }, - "node_modules/is-shared-array-buffer": { + "node_modules/unbox-primitive": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "node_modules/unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "node_modules/underscore.string": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.6.tgz", + "integrity": "sha512-VoC83HWXmCrF6rgkyxS9GHv8W9Q5nhMKho+OadDJGzL2oDYbYEppBaCMH6pFlwLeqj2QS+hhkw2kpXkSdD1JxQ==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "sprintf-js": "^1.1.1", + "util-deprecate": "^1.0.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "*" } }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "node_modules/underscore.string/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/unicode-emoji-modifier-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", + "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 4.0.0" } }, - "node_modules/is-unc-path": { + "node_modules/unpipe": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true, - "dependencies": { - "unc-path-regex": "^0.1.2" - }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8" } }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "call-bind": "^1.0.2" + "escalade": "^3.1.1", + "picocolors": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "punycode": "^2.1.0" } }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" } }, - "node_modules/is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", "dev": true, - "engines": { - "node": ">=4" + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", "dev": true }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" } }, - "node_modules/iterator.prototype": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.0.tgz", - "integrity": "sha512-rjuhAk1AJ1fssphHD0IFV6TWL40CwRZ53FrztKx43yk2v6rguBYsY4Bj1VU4HmoMmKwZUlx7mfnhDf9cOp4YTw==", + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "dev": true, - "dependencies": { - "define-properties": "^1.1.4", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "has-tostringtag": "^1.0.0", - "reflect.getprototypeof": "^1.0.3" + "engines": { + "node": ">= 0.4.0" } }, - "node_modules/jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", "dev": true, - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" - }, "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" + "uuid": "dist/bin/uuid" } }, - "node_modules/jake/node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "node_modules/v8-compile-cache": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", + "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", "dev": true }, - "node_modules/jmespath": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "node_modules/v8flags": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", + "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, "engines": { - "node": ">= 0.6.0" + "node": ">= 0.10" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "node_modules/validate-npm-package-name": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz", + "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==", "dev": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "builtins": "^5.0.0" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/jsdoc-type-pratt-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-3.1.0.tgz", - "integrity": "sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw==", + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true, "engines": { - "node": ">=12.0.0" + "node": ">= 0.8" } }, - "node_modules/jsdom": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", - "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "node_modules/vite": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz", + "integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==", "dev": true, "dependencies": { - "abab": "^2.0.6", - "acorn": "^8.8.1", - "acorn-globals": "^7.0.0", - "cssom": "^0.5.0", - "cssstyle": "^2.3.0", - "data-urls": "^3.0.2", - "decimal.js": "^10.4.2", - "domexception": "^4.0.0", - "escodegen": "^2.0.0", - "form-data": "^4.0.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.1", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.2", - "parse5": "^7.1.1", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.1.2", - "w3c-xmlserializer": "^4.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^2.0.0", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0", - "ws": "^8.11.0", - "xml-name-validator": "^4.0.0" + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" + }, + "bin": { + "vite": "bin/vite.js" }, "engines": { - "node": ">=14" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" }, "peerDependencies": { - "canvas": "^2.5.0" + "@types/node": ">= 14", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" }, "peerDependenciesMeta": { - "canvas": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { "optional": true } } }, - "node_modules/jsdom/node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/jsdom/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "node_modules/vitest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.18.1.tgz", + "integrity": "sha512-4F/1K/Vn4AvJwe7i2YblR02PT5vMKcw9KN4unDq2KD0YcSxX0B/6D6Qu9PJaXwVuxXMFTQ5ovd4+CQaW3bwofA==", "dev": true, "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "@types/chai": "^4.3.1", + "@types/chai-subset": "^1.3.3", + "@types/node": "*", + "chai": "^4.3.6", + "debug": "^4.3.4", + "local-pkg": "^0.4.2", + "tinypool": "^0.2.4", + "tinyspy": "^1.0.0", + "vite": "^2.9.12 || ^3.0.0-0" }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, "bin": { - "jsesc": "bin/jsesc" + "vitest": "vitest.mjs" }, "engines": { - "node": ">=4" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "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" + "node": ">=v14.16.0" }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" + "funding": { + "url": "https://github.com/sponsors/antfu" }, - "engines": { - "node": ">=4.0" + "peerDependencies": { + "@edge-runtime/vm": "*", + "@vitest/ui": "*", + "c8": "*", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "c8": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } } }, - "node_modules/kexec": { - "version": "3.0.0", - "resolved": "git+https://git@github.com/ably-forks/node-kexec.git#f29f54037c7db6ad29e1781463b182e5929215a0", - "integrity": "sha512-AF1dssPoaSWqFN8u6seVKCIBjavJ+wRQ5UBdUGMIldfKcJKipXanb43zFL8DWGYSHPR0nGOHsa72xmlQ81bKzw==", + "node_modules/vitest/node_modules/@esbuild/android-arm": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz", + "integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==", + "cpu": [ + "arm" + ], "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "nan": "^2.13.2" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=0.10" - } - }, - "node_modules/keyv": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz", - "integrity": "sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==", - "dependencies": { - "json-buffer": "3.0.1" + "node": ">=12" } }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "node_modules/vitest/node_modules/@esbuild/linux-loong64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz", + "integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==", + "cpu": [ + "loong64" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "node_modules/vitest/node_modules/esbuild": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz", + "integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==", "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, "engines": { - "node": ">=6" + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.15.18", + "@esbuild/linux-loong64": "0.15.18", + "esbuild-android-64": "0.15.18", + "esbuild-android-arm64": "0.15.18", + "esbuild-darwin-64": "0.15.18", + "esbuild-darwin-arm64": "0.15.18", + "esbuild-freebsd-64": "0.15.18", + "esbuild-freebsd-arm64": "0.15.18", + "esbuild-linux-32": "0.15.18", + "esbuild-linux-64": "0.15.18", + "esbuild-linux-arm": "0.15.18", + "esbuild-linux-arm64": "0.15.18", + "esbuild-linux-mips64le": "0.15.18", + "esbuild-linux-ppc64le": "0.15.18", + "esbuild-linux-riscv64": "0.15.18", + "esbuild-linux-s390x": "0.15.18", + "esbuild-netbsd-64": "0.15.18", + "esbuild-openbsd-64": "0.15.18", + "esbuild-sunos-64": "0.15.18", + "esbuild-windows-32": "0.15.18", + "esbuild-windows-64": "0.15.18", + "esbuild-windows-arm64": "0.15.18" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/vitest/node_modules/rollup": { + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "bin": { + "rollup": "dist/bin/rollup" }, "engines": { - "node": ">= 0.8.0" + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/liftup": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/liftup/-/liftup-3.0.1.tgz", - "integrity": "sha512-yRHaiQDizWSzoXk3APcA71eOI/UuhEkNN9DiW2Tt44mhYzX4joFoCZlxsSOF7RyeLlfqzFLQI1ngFq3ggMPhOw==", + "node_modules/vitest/node_modules/vite": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-3.2.8.tgz", + "integrity": "sha512-EtQU16PLIJpAZol2cTLttNP1mX6L0SyI0pgQB1VOoWeQnMSvtiwovV3D6NcjN8CZQWWyESD2v5NGnpz5RvgOZA==", "dev": true, "dependencies": { - "extend": "^3.0.2", - "findup-sync": "^4.0.0", - "fined": "^1.2.0", - "flagged-respawn": "^1.0.1", - "is-plain-object": "^2.0.4", - "object.map": "^1.0.1", - "rechoir": "^0.7.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/liftup/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/liftup/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/liftup/node_modules/findup-sync": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz", - "integrity": "sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==", - "dev": true, - "dependencies": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^4.0.2", - "resolve-dir": "^1.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/liftup/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/liftup/node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/liftup/node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/liftup/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", - "dev": true, - "engines": { - "node": ">=4.3.0 <5.0.0 || >=5.10" - } - }, - "node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/local-pkg": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", - "integrity": "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==", - "dev": true, - "engines": { - "node": ">=14" + "esbuild": "^0.15.9", + "postcss": "^8.4.18", + "resolve": "^1.22.1", + "rollup": "^2.79.1" }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" + "bin": { + "vite": "bin/vite.js" }, "engines": { - "node": ">=10" + "node": "^14.18.0 || >=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", - "dev": true - }, - "node_modules/log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0" + "optionalDependencies": { + "fsevents": "~2.3.2" }, - "engines": { - "node": ">=10" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/loupe": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", - "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", - "dev": true, - "dependencies": { - "get-func-name": "^2.0.0" - } - }, - "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } } }, - "node_modules/lru-cache/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", "dev": true }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "node_modules/vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", "dev": true }, - "node_modules/lz-string": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", - "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "dev": true, - "bin": { - "lz-string": "bin/bin.js" - } - }, - "node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/make-iterator": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", - "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "dependencies": { - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/marked": { - "version": "4.0.17", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.17.tgz", - "integrity": "sha512-Wfk0ATOK5iPxM4ptrORkFemqroz0ZDxp5MWfYA7H/F+wO17NRWV5Ypxi6p3g2Xmw2bKeiYOl6oVnLHKxBA0VhA==", - "dev": true, - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/marked-terminal": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-6.1.0.tgz", - "integrity": "sha512-QaCSF6NV82oo6K0szEnmc65ooDeW0T/Adcyf0fcW+Hto2GT1VADFg8dn1zaeHqzj65fqDH1hMNChGNRaC/lbkA==", - "dev": true, - "dependencies": { - "ansi-escapes": "^6.2.0", - "cardinal": "^2.1.1", - "chalk": "^5.3.0", - "cli-table3": "^0.6.3", - "node-emoji": "^2.1.0", - "supports-hyperlinks": "^3.0.0" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "marked": ">=1 <11" - } - }, - "node_modules/marked-terminal/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/md5": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", - "dev": true, - "dependencies": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" - } - }, - "node_modules/md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "dependencies": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", - "dev": true - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "dependencies": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - }, - "bin": { - "miller-rabin": "bin/miller-rabin" - } - }, - "node_modules/miller-rabin/node_modules/bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, - "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, - "node_modules/minipass": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", - "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", - "dev": true, - "dependencies": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "dependencies": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mixin-deep/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mocha": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz", - "integrity": "sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==", - "dev": true, - "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.4.3", - "debug": "4.2.0", - "diff": "4.0.2", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.1.6", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.14.0", - "log-symbols": "4.0.0", - "minimatch": "3.0.4", - "ms": "2.1.2", - "nanoid": "3.1.12", - "serialize-javascript": "5.0.1", - "strip-json-comments": "3.1.1", - "supports-color": "7.2.0", - "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.0.2", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, - "engines": { - "node": ">= 10.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/mocha-junit-reporter": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/mocha-junit-reporter/-/mocha-junit-reporter-2.2.1.tgz", - "integrity": "sha512-iDn2tlKHn8Vh8o4nCzcUVW4q7iXp7cC4EB78N0cDHIobLymyHNwe0XG8HEHHjc3hJlXm0Vy6zcrxaIhnI2fWmw==", - "dev": true, - "dependencies": { - "debug": "^4.3.4", - "md5": "^2.3.0", - "mkdirp": "^3.0.0", - "strip-ansi": "^6.0.1", - "xml": "^1.0.1" - }, - "peerDependencies": { - "mocha": ">=2.2.5" - } - }, - "node_modules/mocha-junit-reporter/node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mocha/node_modules/anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/mocha/node_modules/binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/chokidar": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", - "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", - "dev": true, - "dependencies": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.1.2" - } - }, - "node_modules/mocha/node_modules/debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "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", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "deprecated": "\"Please update to latest v2.3 or v2.2\"", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/mocha/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "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==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/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/mocha/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/mocha/node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/mocha/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, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", - "dev": true, - "dependencies": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - } - }, - "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==", - "dev": true - }, - "node_modules/nan": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", - "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", - "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || >=13.7" - } - }, - "node_modules/nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/node-emoji": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.1.tgz", - "integrity": "sha512-+fyi06+Z9LARCwnTmUF1sRPVQFhGlIpuye3zwlzMN8bIKou6l7k1rGV8WVOEu9EQnRLfoVOYj/p107u0CoQoKA==", - "dev": true, - "dependencies": { - "@sindresorhus/is": "^6.0.0", - "char-regex": "^1.0.2", - "emojilib": "^2.4.0", - "skin-tone": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/node-emoji/node_modules/@sindresorhus/is": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-6.1.0.tgz", - "integrity": "sha512-BuvU07zq3tQ/2SIgBsEuxKYDyDjC0n7Zir52bpHy2xnBbW81+po43aLFPLbeV3HRAheFbGud1qgcqSYfhtHMAg==", - "dev": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/node-libs-browser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", - "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", - "dev": true, - "dependencies": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.1", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "^1.0.1" - } - }, - "node_modules/node-libs-browser/node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true - }, - "node_modules/nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/null-loader": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/null-loader/-/null-loader-4.0.1.tgz", - "integrity": "sha512-pxqVbi4U6N26lq+LmgIbB5XATP0VdZKOG25DhHi8btMmJJefGArFyDg1yc4U3hWCJbMqSrw0qyrz1UQX+qYXqg==", - "dev": true, - "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/null-loader/node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/null-loader/node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/null-loader/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/nwsapi": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", - "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", - "dev": true - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "dependencies": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "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-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "dependencies": { - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.defaults": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", - "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", - "dev": true, - "dependencies": { - "array-each": "^1.0.1", - "array-slice": "^1.0.0", - "for-own": "^1.0.0", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.entries": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", - "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.groupby": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", - "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1" - } - }, - "node_modules/object.hasown": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz", - "integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", - "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", - "dev": true, - "dependencies": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", - "dev": true, - "dependencies": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open/node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, - "node_modules/os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dev": true, - "dependencies": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/p-limit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", - "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "node_modules/parallel-transform": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", - "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", - "dev": true, - "dependencies": { - "cyclist": "^1.0.1", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-asn1": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", - "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", - "dev": true, - "dependencies": { - "asn1.js": "^5.2.0", - "browserify-aes": "^1.0.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, - "node_modules/parse-filepath": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", - "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", - "dev": true, - "dependencies": { - "is-absolute": "^1.0.0", - "map-cache": "^0.2.0", - "path-root": "^0.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dev": true, - "dependencies": { - "entities": "^4.4.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", - "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", - "dev": true - }, - "node_modules/path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", - "dev": true, - "optional": true - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "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-root": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", - "dev": true, - "dependencies": { - "path-root-regex": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-root-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/pbkdf2": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", - "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", - "dev": true, - "dependencies": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/playwright": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz", - "integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==", - "dev": true, - "dependencies": { - "playwright-core": "1.39.0" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=16" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz", - "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==", - "dev": true, - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/playwright/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss": { - "version": "8.4.29", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", - "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss/node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", - "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true, - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "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/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "dev": true - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dev": true, - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "dev": true - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true - }, - "node_modules/public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, - "dependencies": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/public-encrypt/node_modules/bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "dependencies": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - } - }, - "node_modules/pumpify/node_modules/pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", - "dev": true, - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true, - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "dependencies": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dev": true, - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dev": true, - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "dev": true, - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "node_modules/react-refresh": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.13.0.tgz", - "integrity": "sha512-XP8A9BT0CpRBD+NYLLeIhld/RqG9+gktUjW1FkE+Vm7OCinbG1SshcK5tb9ls4kzvjZr9mOQc7HYgBngEyPAXg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readable-stream/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==", - "dev": true - }, - "node_modules/readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "optional": true, - "dependencies": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", - "dev": true, - "dependencies": { - "resolve": "^1.9.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/rechoir/node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/redeyed": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", - "integrity": "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==", - "dev": true, - "dependencies": { - "esprima": "~4.0.0" - } - }, - "node_modules/reduce-flatten": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", - "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.3.tgz", - "integrity": "sha512-TTAOZpkJ2YLxl7mVHWrNo3iDMEkYlva/kgFcXndqMgbo/AZUmmavEkdXV+hXtE4P8xdyEKRzalaFqZVuwIk/Nw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.1", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", - "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", - "dev": true, - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", - "dev": true - }, - "node_modules/regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, - "node_modules/regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "dependencies": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", - "dev": true, - "dependencies": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", - "dev": true, - "dependencies": { - "jsesc": "~0.5.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "node_modules/repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/replace-ext": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", - "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/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, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "node_modules/requirejs": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.1.22.tgz", - "integrity": "sha1-3Xj9LTQYDA1ixyS1uK68BmTgNm8=", - "dev": true, - "bin": { - "r.js": "bin/r.js" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, - "node_modules/resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", - "dev": true, - "dependencies": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "deprecated": "https://github.com/lydell/resolve-url#deprecated", - "dev": true - }, - "node_modules/responselike": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", - "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", - "dependencies": { - "lowercase-keys": "^2.0.0" - } - }, - "node_modules/ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "node_modules/rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", - "dev": true, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=10.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/rollup/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "dev": true, - "dependencies": { - "aproba": "^1.1.1" - } - }, - "node_modules/safe-array-concat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", - "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "has-symbols": "^1.0.3", - "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.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "dependencies": { - "ret": "~0.1.10" - } - }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.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", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "node_modules/sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==", - "dev": true - }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dev": true, - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=v12.22.7" - } - }, - "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dev": true, - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "dependencies": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/send/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/serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dev": true, - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "node_modules/set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/set-value/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true - }, - "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - }, - "bin": { - "sha.js": "bin.js" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "dev": true, - "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/shelljs/node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/shelljs/node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/shiki": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", - "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", - "dev": true, - "dependencies": { - "jsonc-parser": "^3.0.0", - "vscode-oniguruma": "^1.6.1", - "vscode-textmate": "5.2.0" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "node_modules/skin-tone": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", - "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", - "dev": true, - "dependencies": { - "unicode-emoji-modifier-base": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "dependencies": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "dependencies": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "dependencies": { - "kind-of": "^3.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-util/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/snapdragon/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "dev": true - }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-explorer": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/source-map-explorer/-/source-map-explorer-2.5.2.tgz", - "integrity": "sha512-gBwOyCcHPHcdLbgw6Y6kgoH1uLKL6hN3zz0xJcNI2lpnElZliIlmSYAjUVwAWnc7+HscoTyh1ScR7ITtFuEnxg==", - "dev": true, - "dependencies": { - "btoa": "^1.2.1", - "chalk": "^4.1.0", - "convert-source-map": "^1.7.0", - "ejs": "^3.1.5", - "escape-html": "^1.0.3", - "glob": "^7.1.6", - "gzip-size": "^6.0.0", - "lodash": "^4.17.20", - "open": "^7.3.1", - "source-map": "^0.7.3", - "temp": "^0.9.4", - "yargs": "^16.2.0" - }, - "bin": { - "sme": "bin/cli.js", - "source-map-explorer": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/source-map-explorer/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/source-map-explorer/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/source-map-explorer/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/source-map-explorer/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/source-map-explorer/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 - }, - "node_modules/source-map-explorer/node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/source-map-explorer/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==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map-explorer/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/source-map-explorer/node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map-explorer/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", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/source-map-explorer/node_modules/y18n": { - "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/source-map-explorer/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/source-map-explorer/node_modules/yargs-parser": { - "version": "20.2.7", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", - "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dev": true, - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "node_modules/source-map-support": { - "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", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", - "dev": true - }, - "node_modules/split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "dependencies": { - "extend-shallow": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "node_modules/ssri": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", - "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", - "dev": true, - "dependencies": { - "figgy-pudding": "^3.5.1" - } - }, - "node_modules/static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "dependencies": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/stop-iteration-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", - "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", - "dev": true, - "dependencies": { - "internal-slot": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/stream-browserify": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", - "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", - "dev": true, - "dependencies": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" - } - }, - "node_modules/stream-each": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", - "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "node_modules/stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", - "dev": true, - "dependencies": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" - } - }, - "node_modules/stream-shift": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", - "dev": true - }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string_decoder/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==", - "dev": true - }, - "node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.9.tgz", - "integrity": "sha512-6i5hL3MqG/K2G43mWXWgP+qizFW/QH/7kCNN13JrJS5q48FN5IKksLDscexKP3dnmB6cdm9jlNgAsWNLpSykmA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "regexp.prototype.flags": "^1.5.0", - "side-channel": "^1.0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.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" - }, - "engines": { - "node": ">=8" - } - }, - "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/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-hyperlinks": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz", - "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=14.18" - } - }, - "node_modules/supports-hyperlinks/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/supports-hyperlinks/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, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true - }, - "node_modules/table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", - "dev": true, - "dependencies": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/table-layout": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.1.tgz", - "integrity": "sha512-dEquqYNJiGwY7iPfZ3wbXDI944iqanTSchrACLL2nOB+1r+h1Nzu2eH+DuPPvWvm5Ry7iAPeFlgEtP5bIp5U7Q==", - "dev": true, - "dependencies": { - "array-back": "^4.0.1", - "deep-extend": "~0.6.0", - "typical": "^5.2.0", - "wordwrapjs": "^4.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/tar": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", - "dev": true, - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/tar/node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/task-closure-tools": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/task-closure-tools/-/task-closure-tools-0.1.10.tgz", - "integrity": "sha1-2bHs+A7jfi2tIkRbJiAseN0VU3s=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/temp": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", - "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", - "dev": true, - "dependencies": { - "mkdirp": "^0.5.1", - "rimraf": "~2.6.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/terser": { - "version": "5.19.3", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.3.tgz", - "integrity": "sha512-pQzJ9UJzM0IgmT4FAtYI6+VqFf0lj/to58AV0Xfgg0Up37RyPG7Al+1cepC6/BVuAxR9oNb41/DL4DEoHJvTdg==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", - "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", - "dev": true, - "dependencies": { - "cacache": "^12.0.2", - "find-cache-dir": "^2.1.0", - "is-wsl": "^1.1.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^4.0.0", - "source-map": "^0.6.1", - "terser": "^4.1.2", - "webpack-sources": "^1.4.0", - "worker-farm": "^1.7.0" - }, - "engines": { - "node": ">= 6.9.0" - }, - "peerDependencies": { - "webpack": "^4.0.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/terser": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", - "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==", - "dev": true, - "dependencies": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/terser/node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true, - "optional": true, - "peer": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/timers-browserify": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", - "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", - "dev": true, - "dependencies": { - "setimmediate": "^1.0.4" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/tinypool": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.2.4.tgz", - "integrity": "sha512-Vs3rhkUH6Qq1t5bqtb816oT+HeJTXfwt2cbPH17sWHIYKTotQIFPk3tf2fgqRrVyMDVOc1EnPgzIxfIulXVzwQ==", - "dev": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-1.1.1.tgz", - "integrity": "sha512-UVq5AXt/gQlti7oxoIg5oi/9r0WpF7DGEVwXgqWSMmyN16+e3tl5lIvTaOpJ3TAtu5xFzWccFRM4R5NaWHF+4g==", - "dev": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-object-path/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "dependencies": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-utf8": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/to-utf8/-/to-utf8-0.0.1.tgz", - "integrity": "sha1-0Xrqcv8vujm55DYBvns/9y4ImFI=" - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", - "dev": true, - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tr46": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", - "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", - "dev": true, - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/ts-loader": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.2.0.tgz", - "integrity": "sha512-ebXBFrNyMSmbWgjnb3WBloUBK+VSx1xckaXsMXxlZRDqce/OPdYBVN5efB0W3V0defq0Gcy4YuzvPGqRgjj85A==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "enhanced-resolve": "^4.0.0", - "loader-utils": "^2.0.0", - "micromatch": "^4.0.0", - "semver": "^7.3.4" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "typescript": "*", - "webpack": "*" - } - }, - "node_modules/ts-loader/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-loader/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-loader/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/ts-loader/node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/ts-loader/node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/ts-loader/node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/ts-loader/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "dev": true, - "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tsconfig-paths-webpack-plugin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.0.1.tgz", - "integrity": "sha512-m5//KzLoKmqu2MVix+dgLKq70MnFi8YL8sdzQZ6DblmCdfuq/y3OqvJd5vMndg2KEVCOeNz8Es4WVZhYInteLw==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.7.0", - "tsconfig-paths": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/tsconfig-paths-webpack-plugin/node_modules/enhanced-resolve": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz", - "integrity": "sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/tsconfig-paths-webpack-plugin/node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "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/tsutils/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/tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, - "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.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "node_modules/typedoc": { - "version": "0.23.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.8.tgz", - "integrity": "sha512-NLRTY/7XSrhiowR3xnH/nlfTnHk+dkzhHWAMT8guoZ6RHCQZIu3pJREMCqzdkWVCC5+dr9We7TtNeprR3Qy6Ag==", - "dev": true, - "dependencies": { - "lunr": "^2.3.9", - "marked": "^4.0.16", - "minimatch": "^5.1.0", - "shiki": "^0.10.1" - }, - "bin": { - "typedoc": "bin/typedoc" - }, - "engines": { - "node": ">= 14.14" - }, - "peerDependencies": { - "typescript": "4.6.x || 4.7.x" - } - }, - "node_modules/typedoc/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/typedoc/node_modules/minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/typescript": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", - "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/typical": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", - "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/underscore.string": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz", - "integrity": "sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==", - "dev": true, - "dependencies": { - "sprintf-js": "^1.0.3", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-emoji-modifier-base": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", - "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "dependencies": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "dev": true, - "dependencies": { - "unique-slug": "^2.0.0" - } - }, - "node_modules/unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4" - } - }, - "node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "dependencies": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "dependencies": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "dependencies": { - "isarray": "1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true, - "optional": true, - "engines": { - "node": ">=4", - "yarn": "*" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", - "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "deprecated": "Please see https://github.com/lydell/urix#deprecated", - "dev": true - }, - "node_modules/url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "node_modules/url/node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - }, - "node_modules/use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", - "dev": true, - "dependencies": { - "inherits": "2.0.3" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "node_modules/util/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-compile-cache": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", - "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", - "dev": true - }, - "node_modules/v8flags": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", - "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", - "dev": true, - "dependencies": { - "homedir-polyfill": "^1.0.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/validate-npm-package-name": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz", - "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==", - "dev": true, - "dependencies": { - "builtins": "^5.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vinyl": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", - "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", - "dev": true, - "dependencies": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/vinyl-sourcemaps-apply": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", - "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", - "dev": true, - "dependencies": { - "source-map": "^0.5.1" - } - }, - "node_modules/vite": { - "version": "4.4.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz", - "integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==", - "dev": true, - "dependencies": { - "esbuild": "^0.18.10", - "postcss": "^8.4.27", - "rollup": "^3.27.1" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - }, - "peerDependencies": { - "@types/node": ">= 14", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/esbuild": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", - "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.18.20", - "@esbuild/android-arm64": "0.18.20", - "@esbuild/android-x64": "0.18.20", - "@esbuild/darwin-arm64": "0.18.20", - "@esbuild/darwin-x64": "0.18.20", - "@esbuild/freebsd-arm64": "0.18.20", - "@esbuild/freebsd-x64": "0.18.20", - "@esbuild/linux-arm": "0.18.20", - "@esbuild/linux-arm64": "0.18.20", - "@esbuild/linux-ia32": "0.18.20", - "@esbuild/linux-loong64": "0.18.20", - "@esbuild/linux-mips64el": "0.18.20", - "@esbuild/linux-ppc64": "0.18.20", - "@esbuild/linux-riscv64": "0.18.20", - "@esbuild/linux-s390x": "0.18.20", - "@esbuild/linux-x64": "0.18.20", - "@esbuild/netbsd-x64": "0.18.20", - "@esbuild/openbsd-x64": "0.18.20", - "@esbuild/sunos-x64": "0.18.20", - "@esbuild/win32-arm64": "0.18.20", - "@esbuild/win32-ia32": "0.18.20", - "@esbuild/win32-x64": "0.18.20" - } - }, - "node_modules/vite/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/vite/node_modules/rollup": { - "version": "3.28.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.28.1.tgz", - "integrity": "sha512-R9OMQmIHJm9znrU3m3cpE8uhN0fGdXiawME7aZIpQqvpS/85+Vt1Hq1/yVIcYfOmaQiHjvXkQAoJukvLpau6Yw==", - "dev": true, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=14.18.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/vitest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.18.1.tgz", - "integrity": "sha512-4F/1K/Vn4AvJwe7i2YblR02PT5vMKcw9KN4unDq2KD0YcSxX0B/6D6Qu9PJaXwVuxXMFTQ5ovd4+CQaW3bwofA==", - "dev": true, - "dependencies": { - "@types/chai": "^4.3.1", - "@types/chai-subset": "^1.3.3", - "@types/node": "*", - "chai": "^4.3.6", - "debug": "^4.3.4", - "local-pkg": "^0.4.2", - "tinypool": "^0.2.4", - "tinyspy": "^1.0.0", - "vite": "^2.9.12 || ^3.0.0-0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": ">=v14.16.0" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@vitest/ui": "*", - "c8": "*", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "c8": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "node_modules/vitest/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/vitest/node_modules/resolve": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", - "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/vitest/node_modules/vite": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.2.7.tgz", - "integrity": "sha512-29pdXjk49xAP0QBr0xXqu2s5jiQIXNvE/xwd0vUizYT2Hzqe4BksNNoWllFVXJf4eLZ+UlVQmXfB4lWrc+t18g==", - "dev": true, - "dependencies": { - "esbuild": "^0.15.9", - "postcss": "^8.4.18", - "resolve": "^1.22.1", - "rollup": "^2.79.1" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - }, - "peerDependencies": { - "@types/node": ">= 14", - "less": "*", - "sass": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vm-browserify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", - "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", - "dev": true - }, - "node_modules/vscode-oniguruma": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz", - "integrity": "sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==", - "dev": true - }, - "node_modules/vscode-textmate": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", - "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", - "dev": true - }, - "node_modules/w3c-xmlserializer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", - "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", - "dev": true, - "dependencies": { - "xml-name-validator": "^4.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/watchpack": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", - "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" - }, - "optionalDependencies": { - "chokidar": "^3.4.1", - "watchpack-chokidar2": "^2.0.1" - } - }, - "node_modules/watchpack-chokidar2": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", - "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", - "dev": true, - "optional": true, - "dependencies": { - "chokidar": "^2.1.8" - } - }, - "node_modules/watchpack/node_modules/anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "optional": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/watchpack/node_modules/binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", - "dev": true, - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/watchpack/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "optional": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/watchpack/node_modules/chokidar": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", - "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", - "dev": true, - "optional": true, - "dependencies": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.1.2" - } - }, - "node_modules/watchpack/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "optional": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/watchpack/node_modules/fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "deprecated": "\"Please update to latest v2.3 or v2.2\"", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/watchpack/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, - "optional": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/watchpack/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/watchpack/node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "optional": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/watchpack/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "optional": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/webpack": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz", - "integrity": "sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-module-context": "1.9.0", - "@webassemblyjs/wasm-edit": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0", - "acorn": "^6.4.1", - "ajv": "^6.10.2", - "ajv-keywords": "^3.4.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^4.5.0", - "eslint-scope": "^4.0.3", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.4.0", - "loader-utils": "^1.2.3", - "memory-fs": "^0.4.1", - "micromatch": "^3.1.10", - "mkdirp": "^0.5.3", - "neo-async": "^2.6.1", - "node-libs-browser": "^2.2.1", - "schema-utils": "^1.0.0", - "tapable": "^1.1.3", - "terser-webpack-plugin": "^1.4.3", - "watchpack": "^1.7.4", - "webpack-sources": "^1.4.1" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=6.11.5" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - }, - "webpack-command": { - "optional": true - } - } - }, - "node_modules/webpack-cli": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.2.0.tgz", - "integrity": "sha512-EIl3k88vaF4fSxWSgtAQR+VwicfLMTZ9amQtqS4o+TDPW9HGaEpbFBbAZ4A3ZOT5SOnMxNOzROsSTPiE8tBJPA==", - "dev": true, - "dependencies": { - "@webpack-cli/info": "^1.1.0", - "@webpack-cli/serve": "^1.1.0", - "colorette": "^1.2.1", - "command-line-usage": "^6.1.0", - "commander": "^6.2.0", - "enquirer": "^2.3.6", - "execa": "^4.1.0", - "import-local": "^3.0.2", - "interpret": "^2.2.0", - "leven": "^3.1.0", - "rechoir": "^0.7.0", - "v8-compile-cache": "^2.2.0", - "webpack-merge": "^4.2.2" - }, - "bin": { - "webpack-cli": "bin/cli.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "peerDependencies": { - "webpack": "4.x.x || 5.x.x" - }, - "peerDependenciesMeta": { - "@webpack-cli/generate-loader": { - "optional": true - }, - "@webpack-cli/generate-plugin": { - "optional": true - }, - "@webpack-cli/init": { - "optional": true - }, - "@webpack-cli/migrate": { - "optional": true - }, - "webpack-bundle-analyzer": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", - "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/webpack-cli/node_modules/interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/webpack-merge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", - "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", - "dev": true, - "dependencies": { - "lodash": "^4.17.15" - } - }, - "node_modules/webpack-node-externals": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", - "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dev": true, - "dependencies": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, - "node_modules/webpack-sources/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack/node_modules/acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/webpack/node_modules/eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "dev": true, - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/whatwg-mimetype": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-url": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", - "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", - "dev": true, - "dependencies": { - "tr46": "^3.0.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", - "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", - "dev": true, - "dependencies": { - "function.prototype.name": "^1.1.5", - "has-tostringtag": "^1.0.0", - "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", - "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - }, - "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.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", - "dev": true, - "dependencies": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" - }, - "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.11", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", - "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/wide-align/node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrapjs": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.0.tgz", - "integrity": "sha512-Svqw723a3R34KvsMgpjFBYCgNOSdcW3mQFK4wIfhGQhtaFVOJmdYoXgi63ne3dTlWgatVcUc7t4HtQ/+bUVIzQ==", - "dev": true, - "dependencies": { - "reduce-flatten": "^2.0.0", - "typical": "^5.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/worker-farm": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", - "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", - "dev": true, - "dependencies": { - "errno": "~0.1.7" - } - }, - "node_modules/workerpool": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", - "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "node_modules/write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "dependencies": { - "mkdirp": "^0.5.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ws": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", - "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", - "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", - "dev": true - }, - "node_modules/xml-name-validator": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", - "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", - "dev": true, - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "dependencies": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - } - }, - "node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs-unparser/node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true, - "engines": { - "node": ">=4" - } - } - }, - "dependencies": { - "@ably/msgpack-js": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@ably/msgpack-js/-/msgpack-js-0.4.0.tgz", - "integrity": "sha512-IPt/BoiQwCWubqoNik1aw/6M/DleMdrxJOUpSja6xmMRbT2p1TA8oqKWgfZabqzrq8emRNeSl/+4XABPNnW5pQ==", - "requires": { - "bops": "^1.0.1" - } - }, - "@ably/vcdiff-decoder": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@ably/vcdiff-decoder/-/vcdiff-decoder-1.0.4.tgz", - "integrity": "sha512-D2j7j+keGWOI48ahTjtKmPEahSXtnm2PLVSp1fDCctibyGd/ywRWyJP2TzNtVMwL1IDD9PAaKPPK3+cTo0WP3g==", - "dev": true - }, - "@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@andrewbranch/untar.js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@andrewbranch/untar.js/-/untar.js-1.0.3.tgz", - "integrity": "sha512-Jh15/qVmrLGhkKJBdXlK1+9tY4lZruYjsgkDFj08ZmDiWVBLJcqkok7Z0/R0In+i1rScBpJlSvrTS2Lm41Pbnw==", - "dev": true - }, - "@arethetypeswrong/cli": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/@arethetypeswrong/cli/-/cli-0.13.1.tgz", - "integrity": "sha512-nmaSMnVb1iHvJIJ4UmrNMRwtxxjnuOL2IELTuYLP4QatnebrGmqeRWOzbsuwh9OT48LX4fxp+wnJMGdrUf/nvw==", - "dev": true, - "requires": { - "@arethetypeswrong/core": "0.13.0", - "chalk": "^4.1.2", - "cli-table3": "^0.6.3", - "commander": "^10.0.1", - "marked": "^9.1.2", - "marked-terminal": "^6.0.0", - "semver": "^7.5.4" - }, - "dependencies": { - "commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "dev": true - }, - "marked": { - "version": "9.1.6", - "resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz", - "integrity": "sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==", - "dev": true - } - } - }, - "@arethetypeswrong/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@arethetypeswrong/core/-/core-0.13.0.tgz", - "integrity": "sha512-dEDo+Vq/Kt12ZZJANuUDKWZzogswcmndcY293LVgswKv69YXq9jMR8HjVTDGhwnlGJ8fAiJC+XYhg/wfTwAFCA==", - "dev": true, - "requires": { - "@andrewbranch/untar.js": "^1.0.3", - "fflate": "^0.7.4", - "semver": "^7.5.4", - "typescript": "^5.2.2", - "validate-npm-package-name": "^5.0.0" - }, - "dependencies": { - "typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true - } - } - }, - "@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", - "dev": true, - "requires": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - } - } - }, - "@babel/compat-data": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", - "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", - "dev": true - }, - "@babel/core": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.11.tgz", - "integrity": "sha512-lh7RJrtPdhibbxndr6/xx0w8+CVlY5FJZiaSz908Fpy+G0xkBFTvwLcKJFF4PJxVfGhVWNebikpWGnOoC71juQ==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-compilation-targets": "^7.22.10", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.11", - "@babel/parser": "^7.22.11", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.11", - "@babel/types": "^7.22.11", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "dependencies": { - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", - "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", - "dev": true, - "requires": { - "@babel/types": "^7.22.10", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", - "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", - "dev": true, - "requires": { - "@babel/types": "^7.22.15" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", - "browserslist": "^4.22.2", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.6.tgz", - "integrity": "sha512-cBXU1vZni/CpGF29iTu4YRbOZt3Wat6zCoMDxRF1MayiEc4URxOj31tT65HUM0CRpMowA3HCJaAOVOUnMf96cw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-member-expression-to-functions": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", - "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "regexpu-core": "^5.3.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-define-polyfill-provider": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.4.tgz", - "integrity": "sha512-QcJMILQCu2jm5TFPGA3lCpJJTeEP+mqeXooG/NZbg/h5FTFi6V0+99ahlRsW8/kRLyb24LZVCCiclDedhLKcBA==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" - }, - "dependencies": { - "resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "requires": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "requires": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", - "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", - "dev": true, - "requires": { - "@babel/types": "^7.23.0" - } - }, - "@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dev": true, - "requires": { - "@babel/types": "^7.22.15" - } - }, - "@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", - "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-wrap-function": "^7.22.20" - } - }, - "@babel/helper-replace-supers": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", - "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.22.15", - "@babel/helper-optimise-call-expression": "^7.22.5" - } - }, - "@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", - "dev": true - }, - "@babel/helper-wrap-function": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", - "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.22.5", - "@babel/template": "^7.22.15", - "@babel/types": "^7.22.19" - } - }, - "@babel/helpers": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.11.tgz", - "integrity": "sha512-vyOXC8PBWaGc5h7GMsNx68OH33cypkEDJCHvYVVgVbbxJDROYVtexSk0gK5iCF1xNjRIN2s8ai7hwkWDq5szWg==", - "dev": true, - "requires": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.11", - "@babel/types": "^7.22.11" - } - }, - "@babel/highlight": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", - "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - } - } - }, - "@babel/parser": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", - "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", - "dev": true - }, - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz", - "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz", - "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.23.3" - } - }, - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.3.tgz", - "integrity": "sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "dev": true, - "requires": {} - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-import-assertions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz", - "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-syntax-import-attributes": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz", - "integrity": "sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==", + "node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" } }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" } }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "engines": { + "node": ">=12" } }, - "@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "node_modules/webpack": { + "version": "5.89.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", + "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.7", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } } }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "node_modules/webpack-cli": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", + "colorette": "^2.0.14", + "commander": "^10.0.1", + "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } } }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "node_modules/webpack-cli/node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "engines": { + "node": ">=10.13.0" } }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "node_modules/webpack-cli/node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" } }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "node_modules/webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" } }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "engines": { + "node": ">=10.13.0" } }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "node_modules/webpack/node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" } }, - "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "node_modules/webpack/node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" + "peerDependencies": { + "acorn": "^8" } }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "node_modules/webpack/node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" + "engines": { + "node": ">=0.8.x" } }, - "@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz", - "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==", + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" } }, - "@babel/plugin-transform-async-generator-functions": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.4.tgz", - "integrity": "sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw==", + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.20", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", - "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.20" + "engines": { + "node": ">=12" } }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz", - "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==", + "node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "@babel/plugin-transform-block-scoping": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz", - "integrity": "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==", + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "@babel/plugin-transform-class-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", - "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "@babel/plugin-transform-class-static-block": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz", - "integrity": "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==", + "node_modules/which-builtin-type": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", + "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-class-static-block": "^7.14.5" + "dependencies": { + "function.prototype.name": "^1.1.5", + "has-tostringtag": "^1.0.0", + "is-async-function": "^2.0.0", + "is-date-object": "^1.0.5", + "is-finalizationregistry": "^1.0.2", + "is-generator-function": "^1.0.10", + "is-regex": "^1.1.4", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "@babel/plugin-transform-classes": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.5.tgz", - "integrity": "sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg==", + "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.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", - "@babel/helper-split-export-declaration": "^7.22.6", - "globals": "^11.1.0" - }, "dependencies": { - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - } + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "@babel/plugin-transform-computed-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz", - "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==", + "node_modules/which-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/template": "^7.22.15" + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.4", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "@babel/plugin-transform-destructuring": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz", - "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==", + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "dependencies": { + "string-width": "^1.0.2 || 2" } }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz", - "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==", + "node_modules/wide-align/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "engines": { + "node": ">=4" } }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz", - "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==", + "node_modules/wide-align/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "engines": { + "node": ">=4" } }, - "@babel/plugin-transform-dynamic-import": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz", - "integrity": "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==", + "node_modules/wide-align/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" } }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz", - "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==", + "node_modules/wide-align/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "@babel/plugin-transform-export-namespace-from": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", - "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true }, - "@babel/plugin-transform-for-of": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz", - "integrity": "sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" - } + "node_modules/workerpool": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", + "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", + "dev": true }, - "@babel/plugin-transform-function-name": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz", - "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==", + "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, - "requires": { - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5" + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "@babel/plugin-transform-json-strings": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz", - "integrity": "sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-json-strings": "^7.8.3" - } + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, - "@babel/plugin-transform-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz", - "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "@babel/plugin-transform-logical-assignment-operators": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", - "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", + "node_modules/xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", + "dev": true + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + "engines": { + "node": ">=12" } }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz", - "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==", + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" } }, - "@babel/plugin-transform-modules-amd": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz", - "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==", + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5" + "engines": { + "node": ">=4.0" } }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", - "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" + "engines": { + "node": ">=10" } }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz", - "integrity": "sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==", + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "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, - "requires": { - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20" + "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" } }, - "@babel/plugin-transform-modules-umd": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz", - "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==", + "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==", "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5" + "engines": { + "node": ">=10" } }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", - "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" } }, - "@babel/plugin-transform-new-target": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz", - "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==", + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } + } + }, + "dependencies": { + "@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true }, - "@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", - "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", - "dev": true, + "@ably/msgpack-js": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@ably/msgpack-js/-/msgpack-js-0.4.0.tgz", + "integrity": "sha512-IPt/BoiQwCWubqoNik1aw/6M/DleMdrxJOUpSja6xmMRbT2p1TA8oqKWgfZabqzrq8emRNeSl/+4XABPNnW5pQ==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + "bops": "^1.0.1" } }, - "@babel/plugin-transform-numeric-separator": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz", - "integrity": "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==", + "@ably/vcdiff-decoder": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@ably/vcdiff-decoder/-/vcdiff-decoder-1.0.6.tgz", + "integrity": "sha512-VdA8vat5GIaG7gQaUL4ZpF5x3iHH9IolfcztEtQXJa7OSw++G/x9UrNQFXsdv4ZyiMAti6bRMR70G1zNkL67/g==", + "dev": true + }, + "@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" } }, - "@babel/plugin-transform-object-rest-spread": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz", - "integrity": "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==", + "@andrewbranch/untar.js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@andrewbranch/untar.js/-/untar.js-1.0.3.tgz", + "integrity": "sha512-Jh15/qVmrLGhkKJBdXlK1+9tY4lZruYjsgkDFj08ZmDiWVBLJcqkok7Z0/R0In+i1rScBpJlSvrTS2Lm41Pbnw==", + "dev": true + }, + "@arethetypeswrong/cli": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/@arethetypeswrong/cli/-/cli-0.13.5.tgz", + "integrity": "sha512-S5vPcInsCwt6QJlPi1CxxkVfemVrapeeySv5a1jeodwUzfkG/rLjZTYd1uhwgqDMCgQK4sR8z+W9GZITdqLIew==", "dev": true, "requires": { - "@babel/compat-data": "^7.23.3", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.23.3" + "@arethetypeswrong/core": "0.13.5", + "chalk": "^4.1.2", + "cli-table3": "^0.6.3", + "commander": "^10.0.1", + "marked": "^9.1.2", + "marked-terminal": "^6.0.0", + "semver": "^7.5.4" } }, - "@babel/plugin-transform-object-super": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz", - "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==", + "@arethetypeswrong/core": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/@arethetypeswrong/core/-/core-0.13.5.tgz", + "integrity": "sha512-Ahk+vBUK9RBBx4zle2Y1imrbl68GCtLEl0/UBxsJeDd2j5HkbWcgGtgE2tEPMy4qAgWxj2JEptDPl6Jucbov2w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20" + "@andrewbranch/untar.js": "^1.0.3", + "fflate": "^0.7.4", + "semver": "^7.5.4", + "ts-expose-internals-conditionally": "1.0.0-empty.0", + "typescript": "5.3.3", + "validate-npm-package-name": "^5.0.0" + }, + "dependencies": { + "typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true + } } }, - "@babel/plugin-transform-optional-catch-binding": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz", - "integrity": "sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==", + "@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, - "@babel/plugin-transform-optional-chaining": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", - "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", + "@babel/compat-data": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "dev": true + }, + "@babel/core": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz", + "integrity": "sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.7", + "@babel/parser": "^7.23.6", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } } }, - "@babel/plugin-transform-parameters": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz", - "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==", + "@babel/generator": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/types": "^7.23.6", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" } }, - "@babel/plugin-transform-private-methods": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", - "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", + "@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/types": "^7.22.5" } }, - "@babel/plugin-transform-private-property-in-object": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", - "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", + "@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } } }, - "@babel/plugin-transform-property-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz", - "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==", + "@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true + }, + "@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, - "@babel/plugin-transform-react-jsx": { + "@babel/helper-hoist-variables": { "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.5.tgz", - "integrity": "sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA==", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.22.5", "@babel/types": "^7.22.5" } }, - "@babel/plugin-transform-react-jsx-development": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", - "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", + "@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dev": true, "requires": { - "@babel/plugin-transform-react-jsx": "^7.22.5" + "@babel/types": "^7.22.15" } }, - "@babel/plugin-transform-react-jsx-self": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.22.5.tgz", - "integrity": "sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==", + "@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" } }, - "@babel/plugin-transform-react-jsx-source": { + "@babel/helper-plugin-utils": { "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.22.5.tgz", - "integrity": "sha512-yIiRO6yobeEIaI0RTbIr8iAK9FcBHLtZq0S89ZPjDLQXBA4xvghaKqI0etp/tF3htTM0sazJKKLz9oEiGRtu7w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true }, - "@babel/plugin-transform-regenerator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz", - "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==", + "@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "regenerator-transform": "^0.15.2" + "@babel/types": "^7.22.5" } }, - "@babel/plugin-transform-reserved-words": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz", - "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==", + "@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/types": "^7.22.5" } }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz", - "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } + "@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "dev": true }, - "@babel/plugin-transform-spread": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz", - "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==", + "@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "dev": true + }, + "@babel/helpers": { + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.8.tgz", + "integrity": "sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6" } }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz", - "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==", + "@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, - "@babel/plugin-transform-template-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz", - "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } + "@babel/parser": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", + "dev": true }, - "@babel/plugin-transform-typeof-symbol": { + "@babel/plugin-syntax-jsx": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz", - "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz", - "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==", + "@babel/plugin-transform-react-jsx": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz", + "integrity": "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/types": "^7.23.4" } }, - "@babel/plugin-transform-unicode-property-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz", - "integrity": "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==", + "@babel/plugin-transform-react-jsx-development": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", + "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/plugin-transform-react-jsx": "^7.22.5" } }, - "@babel/plugin-transform-unicode-regex": { + "@babel/plugin-transform-react-jsx-self": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz", - "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz", + "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" } }, - "@babel/plugin-transform-unicode-sets-regex": { + "@babel/plugin-transform-react-jsx-source": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz", - "integrity": "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz", + "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" } }, - "@babel/preset-env": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.6.tgz", - "integrity": "sha512-2XPn/BqKkZCpzYhUUNZ1ssXw7DcXfKQEjv/uXZUXgaebCMYmkEsfZ2yY+vv+xtXv50WmL5SGhyB6/xsWxIvvOQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.3", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.23.3", - "@babel/plugin-syntax-import-attributes": "^7.23.3", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.23.3", - "@babel/plugin-transform-async-generator-functions": "^7.23.4", - "@babel/plugin-transform-async-to-generator": "^7.23.3", - "@babel/plugin-transform-block-scoped-functions": "^7.23.3", - "@babel/plugin-transform-block-scoping": "^7.23.4", - "@babel/plugin-transform-class-properties": "^7.23.3", - "@babel/plugin-transform-class-static-block": "^7.23.4", - "@babel/plugin-transform-classes": "^7.23.5", - "@babel/plugin-transform-computed-properties": "^7.23.3", - "@babel/plugin-transform-destructuring": "^7.23.3", - "@babel/plugin-transform-dotall-regex": "^7.23.3", - "@babel/plugin-transform-duplicate-keys": "^7.23.3", - "@babel/plugin-transform-dynamic-import": "^7.23.4", - "@babel/plugin-transform-exponentiation-operator": "^7.23.3", - "@babel/plugin-transform-export-namespace-from": "^7.23.4", - "@babel/plugin-transform-for-of": "^7.23.6", - "@babel/plugin-transform-function-name": "^7.23.3", - "@babel/plugin-transform-json-strings": "^7.23.4", - "@babel/plugin-transform-literals": "^7.23.3", - "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", - "@babel/plugin-transform-member-expression-literals": "^7.23.3", - "@babel/plugin-transform-modules-amd": "^7.23.3", - "@babel/plugin-transform-modules-commonjs": "^7.23.3", - "@babel/plugin-transform-modules-systemjs": "^7.23.3", - "@babel/plugin-transform-modules-umd": "^7.23.3", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.23.3", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", - "@babel/plugin-transform-numeric-separator": "^7.23.4", - "@babel/plugin-transform-object-rest-spread": "^7.23.4", - "@babel/plugin-transform-object-super": "^7.23.3", - "@babel/plugin-transform-optional-catch-binding": "^7.23.4", - "@babel/plugin-transform-optional-chaining": "^7.23.4", - "@babel/plugin-transform-parameters": "^7.23.3", - "@babel/plugin-transform-private-methods": "^7.23.3", - "@babel/plugin-transform-private-property-in-object": "^7.23.4", - "@babel/plugin-transform-property-literals": "^7.23.3", - "@babel/plugin-transform-regenerator": "^7.23.3", - "@babel/plugin-transform-reserved-words": "^7.23.3", - "@babel/plugin-transform-shorthand-properties": "^7.23.3", - "@babel/plugin-transform-spread": "^7.23.3", - "@babel/plugin-transform-sticky-regex": "^7.23.3", - "@babel/plugin-transform-template-literals": "^7.23.3", - "@babel/plugin-transform-typeof-symbol": "^7.23.3", - "@babel/plugin-transform-unicode-escapes": "^7.23.3", - "@babel/plugin-transform-unicode-property-regex": "^7.23.3", - "@babel/plugin-transform-unicode-regex": "^7.23.3", - "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.6", - "babel-plugin-polyfill-corejs3": "^0.8.5", - "babel-plugin-polyfill-regenerator": "^0.5.3", - "core-js-compat": "^3.31.0", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", - "dev": true - }, "@babel/runtime": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz", - "integrity": "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==", + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.8.tgz", + "integrity": "sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==", "dev": true, "requires": { "regenerator-runtime": "^0.14.0" @@ -17798,29 +11237,21 @@ } }, "@babel/traverse": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.11.tgz", - "integrity": "sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz", + "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==", "dev": true, "requires": { - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.11", - "@babel/types": "^7.22.11", - "debug": "^4.1.0", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", "globals": "^11.1.0" - }, - "dependencies": { - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - } } }, "@babel/types": { @@ -17841,21 +11272,27 @@ "dev": true, "optional": true }, + "@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true + }, "@es-joy/jsdoccomment": { - "version": "0.36.1", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.36.1.tgz", - "integrity": "sha512-922xqFsTpHs6D0BUiG4toiyPOMc8/jafnWKxz1KWgS4XzKPy2qXf1Pe6UFuNSCQqt6tOuhAWXBNuuyUhJmw9Vg==", + "version": "0.37.1", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.37.1.tgz", + "integrity": "sha512-5vxWJ1gEkEF0yRd0O+uK6dHJf7adrxwQSX8PuRiPfFSAbNLnY0ZJfXaZucoz14Jj2N11xn2DnlEPwWRpYpvRjg==", "dev": true, "requires": { "comment-parser": "1.3.1", - "esquery": "^1.4.0", - "jsdoc-type-pratt-parser": "~3.1.0" + "esquery": "^1.5.0", + "jsdoc-type-pratt-parser": "~4.0.0" } }, "@esbuild/android-arm": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz", - "integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", "dev": true, "optional": true }, @@ -17923,9 +11360,9 @@ "optional": true }, "@esbuild/linux-loong64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz", - "integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", "dev": true, "optional": true }, @@ -18006,24 +11443,78 @@ "dev": true, "optional": true }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true + }, "@eslint/eslintrc": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.1.tgz", - "integrity": "sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.1.1", "espree": "^7.3.0", - "globals": "^12.1.0", + "globals": "^13.9.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", - "lodash": "^4.17.19", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "globals": { + "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" + } + }, + "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 + }, + "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 + } + } + }, + "@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" } }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, "@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -18052,8 +11543,6 @@ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "dev": true, - "optional": true, - "peer": true, "requires": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -18066,9 +11555,9 @@ "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.21.tgz", + "integrity": "sha512-SRfKmRe1KvYnxjEMtxEr+J4HIeMX5YBg/qhRHpxEIGjhX1rshcHlnFUE9K0GazhVKWM7B+nARSkV8LuvJdJ5/g==", "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.1.0", @@ -18076,72 +11565,31 @@ } }, "@nodelib/fs.scandir": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", - "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "requires": { - "@nodelib/fs.stat": "2.0.4", + "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "@nodelib/fs.stat": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", - "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true }, "@nodelib/fs.walk": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", - "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "requires": { - "@nodelib/fs.scandir": "2.1.4", + "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, - "@npmcli/move-file": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", - "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", - "dev": true, - "requires": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "dependencies": { - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, "@rollup/pluginutils": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", @@ -18153,9 +11601,9 @@ } }, "@sindresorhus/is": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.4.0.tgz", - "integrity": "sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ==" + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==" }, "@szmarczak/http-timer": { "version": "4.0.6", @@ -18199,53 +11647,79 @@ "dev": true }, "@types/aria-query": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", - "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true }, "@types/cacheable-request": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", - "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", "requires": { "@types/http-cache-semantics": "*", - "@types/keyv": "*", + "@types/keyv": "^3.1.4", "@types/node": "*", - "@types/responselike": "*" + "@types/responselike": "^1.0.0" } }, "@types/caseless": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", - "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", "dev": true }, "@types/chai": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", - "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==", + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.11.tgz", + "integrity": "sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ==", "dev": true }, "@types/chai-subset": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.3.tgz", - "integrity": "sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.5.tgz", + "integrity": "sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==", "dev": true, "requires": { "@types/chai": "*" } }, - "@types/crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==", + "@types/cli-table": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@types/cli-table/-/cli-table-0.3.4.tgz", + "integrity": "sha512-GsALrTL69mlwbAw/MHF1IPTadSLZQnsxe7a80G8l4inN/iEXCOcVeT/S7aRc6hbhqzL9qZ314kHPDQnQ3ev+HA==", + "dev": true + }, + "@types/eslint": { + "version": "8.56.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.2.tgz", + "integrity": "sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, "@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" }, "@types/jmespath": { "version": "0.15.2", @@ -18254,9 +11728,9 @@ "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": { @@ -18266,28 +11740,31 @@ "dev": true }, "@types/keyv": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", - "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", "requires": { "@types/node": "*" } }, "@types/node": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.0.tgz", - "integrity": "sha512-YN1d+ae2MCb4U0mMa+Zlb5lWTdpFShbAj5nmte6lel27waMMBfivrm0prC16p/Di3DyTrmerrYUT8/145HXxVw==" + "version": "18.19.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.8.tgz", + "integrity": "sha512-g1pZtPhsvGVTwmeVoexWZLTQaOvXwoSq//pTL0DHeNzUDrFnir4fgETdhjhIxjVnN+hKOuh98+E1eMLnUXstFg==", + "requires": { + "undici-types": "~5.26.4" + } }, "@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "version": "15.7.11", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", "dev": true }, "@types/react": { - "version": "18.2.21", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.21.tgz", - "integrity": "sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA==", + "version": "18.2.48", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.48.tgz", + "integrity": "sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==", "dev": true, "requires": { "@types/prop-types": "*", @@ -18296,18 +11773,18 @@ } }, "@types/react-dom": { - "version": "18.2.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", - "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", + "version": "18.2.18", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.18.tgz", + "integrity": "sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==", "dev": true, "requires": { "@types/react": "*" } }, "@types/request": { - "version": "2.48.8", - "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.8.tgz", - "integrity": "sha512-whjk1EDJPcAR2kYHRbFl/lKeeKYTi05A15K9bnLInCVroNDCtXce57xKdI0/rQaA3K+6q0eFyUBPmqfSndUZdQ==", + "version": "2.48.12", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", + "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", "dev": true, "requires": { "@types/caseless": "*", @@ -18317,154 +11794,137 @@ } }, "@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", "requires": { "@types/node": "*" } }, "@types/scheduler": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", + "dev": true + }, + "@types/semver": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", "dev": true }, "@types/tough-cookie": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz", - "integrity": "sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", "dev": true }, "@types/ws": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.0.tgz", - "integrity": "sha512-mTClfhq5cuGyW4jthaFuig6Q8OVfB3IRyZfN/9SCyJtiM5H0SubwM89cHoT9UngO6HyUFic88HvT1zSNLNyxWA==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", "dev": true, "requires": { "@types/node": "*" } }, "@typescript-eslint/eslint-plugin": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.14.0.tgz", - "integrity": "sha512-ir0wYI4FfFUDfLcuwKzIH7sMVA+db7WYen47iRSaCGl+HMAZI9fpBwfDo45ZALD3A45ZGyHWDNLhbg8tZrMX4w==", + "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": { - "@typescript-eslint/scope-manager": "5.14.0", - "@typescript-eslint/type-utils": "5.14.0", - "@typescript-eslint/utils": "5.14.0", - "debug": "^4.3.2", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.2.0", - "semver": "^7.3.5", + "@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" - }, - "dependencies": { - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true - } } }, "@typescript-eslint/parser": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.14.0.tgz", - "integrity": "sha512-aHJN8/FuIy1Zvqk4U/gcO/fxeMKyoSv/rS46UXMXOJKVsLQ+iYPuXNbpbH7cBLcpSbmyyFbwrniLx5+kutu1pw==", + "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": { - "@typescript-eslint/scope-manager": "5.14.0", - "@typescript-eslint/types": "5.14.0", - "@typescript-eslint/typescript-estree": "5.14.0", - "debug": "^4.3.2" + "@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/scope-manager": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.14.0.tgz", - "integrity": "sha512-LazdcMlGnv+xUc5R4qIlqH0OWARyl2kaP8pVCS39qSL3Pd1F7mI10DbdXeARcE62sVQE4fHNvEqMWsypWO+yEw==", + "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/types": "5.14.0", - "@typescript-eslint/visitor-keys": "5.14.0" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" } }, "@typescript-eslint/type-utils": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.14.0.tgz", - "integrity": "sha512-d4PTJxsqaUpv8iERTDSQBKUCV7Q5yyXjqXUl3XF7Sd9ogNLuKLkxz82qxokqQ4jXdTPZudWpmNtr/JjbbvUixw==", + "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/utils": "5.14.0", - "debug": "^4.3.2", + "@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.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.14.0.tgz", - "integrity": "sha512-BR6Y9eE9360LNnW3eEUqAg6HxS9Q35kSIs4rp4vNHRdfg0s+/PgHgskvu5DFTM7G5VKAVjuyaN476LCPrdA7Mw==", + "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.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.14.0.tgz", - "integrity": "sha512-QGnxvROrCVtLQ1724GLTHBTR0lZVu13izOp9njRvMkCBgWX26PKvmMP8k82nmXBRD3DQcFFq2oj3cKDwr0FaUA==", + "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.14.0", - "@typescript-eslint/visitor-keys": "5.14.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.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.14.0.tgz", - "integrity": "sha512-EHwlII5mvUA0UsKYnVzySb/5EE/t03duUTweVy8Zqt3UQXBrpEVY144OTceFKaOe4xQXZJrkptCf7PjEBeGK4w==", + "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", - "@typescript-eslint/scope-manager": "5.14.0", - "@typescript-eslint/types": "5.14.0", - "@typescript-eslint/typescript-estree": "5.14.0", + "@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", - "eslint-utils": "^3.0.0" - }, - "dependencies": { - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - } - } + "semver": "^7.3.7" } }, "@typescript-eslint/visitor-keys": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.14.0.tgz", - "integrity": "sha512-yL0XxfzR94UEkjBqyymMLgCBdojzEuy/eim7N9/RIcTNxpJudAcqsU8eRyfzBbcEzGoPWfdM3AGak3cN08WOIw==", + "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.14.0", - "eslint-visitor-keys": "^3.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true - } + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" } }, "@ungap/promise-all-settled": { @@ -18487,209 +11947,172 @@ "@rollup/pluginutils": "^4.2.1", "react-refresh": "^0.13.0", "resolve": "^1.22.0" - }, - "dependencies": { - "resolve": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", - "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", - "dev": true, - "requires": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - } } }, "@webassemblyjs/ast": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", - "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", "dev": true, "requires": { - "@webassemblyjs/helper-module-context": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0" + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, "@webassemblyjs/floating-point-hex-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", - "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", "dev": true }, "@webassemblyjs/helper-api-error": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", - "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", "dev": true }, "@webassemblyjs/helper-buffer": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", - "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", - "dev": true - }, - "@webassemblyjs/helper-code-frame": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", - "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", - "dev": true, - "requires": { - "@webassemblyjs/wast-printer": "1.9.0" - } - }, - "@webassemblyjs/helper-fsm": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", - "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", "dev": true }, - "@webassemblyjs/helper-module-context": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", - "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.9.0" + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" } }, "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", - "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", "dev": true }, "@webassemblyjs/helper-wasm-section": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", - "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" } }, "@webassemblyjs/ieee754": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", - "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "dev": true, "requires": { "@xtuc/ieee754": "^1.2.0" } }, "@webassemblyjs/leb128": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", - "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "dev": true, "requires": { "@xtuc/long": "4.2.2" } }, "@webassemblyjs/utf8": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", - "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", "dev": true }, "@webassemblyjs/wasm-edit": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", - "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/helper-wasm-section": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0", - "@webassemblyjs/wasm-opt": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0", - "@webassemblyjs/wast-printer": "1.9.0" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" } }, "@webassemblyjs/wasm-gen": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", - "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/ieee754": "1.9.0", - "@webassemblyjs/leb128": "1.9.0", - "@webassemblyjs/utf8": "1.9.0" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "@webassemblyjs/wasm-opt": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", - "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" } }, "@webassemblyjs/wasm-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", - "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-api-error": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/ieee754": "1.9.0", - "@webassemblyjs/leb128": "1.9.0", - "@webassemblyjs/utf8": "1.9.0" - } - }, - "@webassemblyjs/wast-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", - "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/floating-point-hex-parser": "1.9.0", - "@webassemblyjs/helper-api-error": "1.9.0", - "@webassemblyjs/helper-code-frame": "1.9.0", - "@webassemblyjs/helper-fsm": "1.9.0", - "@xtuc/long": "4.2.2" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "@webassemblyjs/wast-printer": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", - "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0", + "@webassemblyjs/ast": "1.11.6", "@xtuc/long": "4.2.2" } }, + "@webpack-cli/configtest": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", + "dev": true, + "requires": {} + }, "@webpack-cli/info": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.1.0.tgz", - "integrity": "sha512-uNWSdaYHc+f3LdIZNwhdhkjjLDDl3jP2+XBqAq9H8DjrJUvlOKdP8TNruy1yEaDfgpAIgbSAN7pye4FEHg9tYQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", "dev": true, - "requires": { - "envinfo": "^7.7.3" - } + "requires": {} }, "@webpack-cli/serve": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.1.0.tgz", - "integrity": "sha512-7RfnMXCpJ/NThrhq4gYQYILB18xWyoQcBey81oIyVbmgbc6m5ZHHyFK+DyH7pLHJf0p14MxL4mTsoPAgBSTpIg==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", "dev": true, "requires": {} }, @@ -18744,24 +12167,24 @@ }, "dependencies": { "acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true } } }, "acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "requires": {} }, "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", "dev": true }, "agent-base": { @@ -18773,16 +12196,6 @@ "debug": "4" } }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -18795,13 +12208,6 @@ "uri-js": "^4.2.2" } }, - "ajv-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "dev": true, - "requires": {} - }, "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -18809,16 +12215,10 @@ "dev": true, "requires": {} }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true - }, "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-escapes": { @@ -18828,14 +12228,6 @@ "dev": true, "requires": { "type-fest": "^3.0.0" - }, - "dependencies": { - "type-fest": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", - "dev": true - } } }, "ansi-regex": { @@ -18844,13 +12236,19 @@ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, + "ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", + "dev": true + }, "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "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": "^1.9.0" + "color-convert": "^2.0.1" } }, "ansicolors": { @@ -18860,34 +12258,15 @@ "dev": true }, "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, - "optional": true, "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", - "dev": true, - "optional": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" } }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -18906,30 +12285,6 @@ "deep-equal": "^2.0.5" } }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-back": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.1.tgz", - "integrity": "sha512-Z/JnaVEXv+A9xabHzN43FiiiWEE7gPCRXMrVmRm00tWbjZRul1iHm7ECzlyNq1p4a4ATXz+G9FJ3GqGOkOV3fg==", - "dev": true - }, "array-buffer-byte-length": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", @@ -18943,25 +12298,25 @@ "array-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", - "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", + "integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==", "dev": true }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "dev": true }, "array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", "is-string": "^1.0.7" } }, @@ -18977,12 +12332,6 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, "array.prototype.findlastindex": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", @@ -18997,427 +12346,128 @@ } }, "array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-shim-unscopables": "^1.0.0" } }, "array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.tosorted": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", - "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.1.3" - } - }, - "arraybuffer.prototype.slice": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", - "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", "dev": true, "requires": { - "array-buffer-byte-length": "^1.0.0", "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" - } - }, - "asn1.js": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", - "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "safer-buffer": "^2.1.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } - } - }, - "assert": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", - "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", - "dev": true, - "requires": { - "object-assign": "^4.1.1", - "util": "0.10.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - } - } - } - }, - "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 - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true - }, - "async": { - "version": "git+https://git@github.com/ably-forks/async.git#76306396b3d3a25316be0170ab6483ccbaaaa097", - "integrity": "sha512-G936e+Px0kczo3DHOhyHuRIjkv5HT92fWTOVzE6ysIKun1qhAqoJOsko0b+erkFPKCEDTiHMzp9w8zuuhmidQA==", - "dev": true, - "from": "async@ably-forks/async#requirejs" - }, - "async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", - "dev": true, - "optional": true - }, - "asynciterator.prototype": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", - "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.3" - } - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, - "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true - }, - "aws-sdk": { - "version": "2.1414.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1414.0.tgz", - "integrity": "sha512-WhqTWiTZRUxWITvUG5VMPYGdCLNAm4zOTDIiotbErR9x+uDExk2CAGbXE8HH11+tD8PhZVXyukymSiG+7rJMMg==", - "dev": true, - "requires": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "util": "^0.12.4", - "uuid": "8.0.0", - "xml2js": "0.5.0" - }, - "dependencies": { - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", - "dev": true - }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true - }, - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - }, - "url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - } - } - }, - "babel-loader": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", - "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", - "dev": true, - "requires": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^2.0.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - }, - "dependencies": { - "find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - } - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.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 - } - } - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.7.tgz", - "integrity": "sha512-LidDk/tEGDfuHW2DWh/Hgo4rmnw3cduK6ZkOI1NPFceSK3n/yAGeOsNT7FLnSGHkXj3RHGSEVkN3FsCTY6w2CQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.4.4", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" } }, - "babel-plugin-polyfill-corejs3": { - "version": "0.8.7", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.7.tgz", - "integrity": "sha512-KyDvZYxAzkC0Aj2dAPyDzi2Ym15e5JKZSK+maI7NAwSqofvuFglbSsxE7wUOvTg9oFVnHMzVzBKcqEb4PJgtOA==", + "array.prototype.tosorted": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz", + "integrity": "sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==", "dev": true, "requires": { - "@babel/helper-define-polyfill-provider": "^0.4.4", - "core-js-compat": "^3.33.1" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" } }, - "babel-plugin-polyfill-regenerator": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.4.tgz", - "integrity": "sha512-S/x2iOCvDaCASLYsOOgWOq4bCfKYVqvO/uxjkaYyZ3rVsVE3CeAI/c84NpyuBBymEgNvHgjEot3a9/Z/kXvqsg==", + "arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", "dev": true, "requires": { - "@babel/helper-define-polyfill-provider": "^0.4.4" + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" } }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "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 + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "async": { + "version": "git+ssh://git@github.com/ably-forks/async.git#76306396b3d3a25316be0170ab6483ccbaaaa097", + "dev": true, + "from": "async@ably-forks/async#requirejs" + }, + "asynciterator.prototype": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", + "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", "dev": true, "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } + "has-symbols": "^1.0.3" } }, - "base64-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.0.2.tgz", - "integrity": "sha1-R0IRyV5s8qVH20YeT2d4tR0I+mU=" - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true, - "optional": true + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "aws-sdk": { + "version": "2.1539.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1539.0.tgz", + "integrity": "sha512-gqQgZPFWSfYFkkTDI0JEF9vs8ASYFm8z1+hH933sXMhnTqUB5jgaujcicpe3PJ3cy/SLAvNSzc6MhKoZiIU9+g==", "dev": true, - "optional": true, "requires": { - "file-uri-to-path": "1.0.0" + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.5.0" } }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "bn.js": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", - "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==", + "base64-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.0.2.tgz", + "integrity": "sha512-ZXBDPMt/v/8fsIqn+Z5VwrhdR6jVka0bYobHdGia0Nxi7BJ9i/Uvml3AocHIBtIIBhZjBw5MR0aR4ROs/8+SNg==" + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, "body-parser": { @@ -19477,132 +12527,20 @@ } }, "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "fill-range": "^7.0.1" } }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, "browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "browserify-rsa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", - "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", - "dev": true, - "requires": { - "bn.js": "^5.0.0", - "randombytes": "^2.0.1" - } - }, - "browserify-sign": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", - "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", - "dev": true, - "requires": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "elliptic": "^6.5.3", - "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "requires": { - "pako": "~1.0.5" - } - }, "browserslist": { "version": "4.22.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", @@ -19630,32 +12568,12 @@ "base64-js": "^1.0.2", "ieee754": "^1.1.4", "isarray": "^1.0.0" - }, - "dependencies": { - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true - } } }, "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, "builtins": { @@ -19673,71 +12591,15 @@ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true }, - "cacache": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", - "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", - "dev": true, - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, "cacheable-lookup": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==" }, "cacheable-request": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", - "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", "requires": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", @@ -19749,13 +12611,14 @@ } }, "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" } }, "callsites": { @@ -19765,15 +12628,15 @@ "dev": true }, "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, "caniuse-lite": { - "version": "1.0.30001570", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz", - "integrity": "sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw==", + "version": "1.0.30001579", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz", + "integrity": "sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==", "dev": true }, "cardinal": { @@ -19787,18 +12650,18 @@ } }, "chai": { - "version": "4.3.8", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.8.tgz", - "integrity": "sha512-vX4YvVVtxlfSZ2VecZgFUTU5qPCYsobVI2O9FmwEXBhDigYGQA6jRXCycIs1yJnnWbZ6/+a2zNIF5DfVCcJBFQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", "dev": true, "requires": { "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^4.1.2", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", "pathval": "^1.1.1", - "type-detect": "^4.0.5" + "type-detect": "^4.0.8" } }, "chalk": { @@ -19809,175 +12672,68 @@ "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.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==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "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==", - "dev": true - }, - "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 - }, - "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, - "requires": { - "has-flag": "^4.0.0" - } - } } }, "char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, - "charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", - "dev": true - }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": true, - "optional": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - }, - "dependencies": { - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", - "dev": true, - "optional": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", - "dev": true, - "optional": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - } - } + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", "dev": true }, - "chrome-trace-event": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", - "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", "dev": true, "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } + "get-func-name": "^2.0.2" } }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", "dev": true, "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" } }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true + }, + "cli-table": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.11.tgz", + "integrity": "sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==", "dev": true, "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" + "colors": "1.0.3" }, "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", + "dev": true } } }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, "cli-table3": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", @@ -19986,133 +12742,63 @@ "requires": { "@colors/colors": "1.5.0", "string-width": "^4.2.0" - }, - "dependencies": { - "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 - }, - "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 - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - } } }, "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "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": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true - }, - "clone-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", - "dev": true - }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "requires": { - "mimic-response": "^1.0.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", - "dev": true - }, - "cloneable-readable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", - "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, "requires": { - "inherits": "^2.0.1", - "process-nextick-args": "^2.0.0", - "readable-stream": "^2.3.5" + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" } }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, + "clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "mimic-response": "^1.0.0" } }, "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "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.3" + "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "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 }, "colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true }, "colors": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "integrity": "sha512-ENwblkFQpqqia6b++zLD/KUWafYlVY/UNnAp7oz7LY7E924wmpye416wBOmvv/HMWzl8gL1kJlfvId/1Dg176w==", "dev": true }, "combined-stream": { @@ -20124,35 +12810,10 @@ "delayed-stream": "~1.0.0" } }, - "command-line-usage": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.1.tgz", - "integrity": "sha512-F59pEuAR9o1SF/bD0dQBDluhpT4jJQNWUHEuVBqpDmCUo6gPjCi+m9fCWnWZVR/oG6cMTUms4h+3NPl74wGXvA==", - "dev": true, - "requires": { - "array-back": "^4.0.1", - "chalk": "^2.4.2", - "table-layout": "^1.0.1", - "typical": "^5.2.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - } - } - }, "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true }, "comment-parser": { @@ -20161,46 +12822,10 @@ "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", "dev": true }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "console-browserify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", - "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", - "dev": true - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "content-disposition": { @@ -20219,220 +12844,21 @@ "dev": true }, "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - }, - "dependencies": { - "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==", - "dev": true - } - } - }, - "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "dev": true - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", - "dev": true - }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, - "copy-webpack-plugin": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-6.4.1.tgz", - "integrity": "sha512-MXyPCjdPVx5iiWyl40Va3JGh27bKzOTNY3NjUTrosD2q7dR/cLD0013uqJ3BpFbUjyONINjb6qI7nDIJujrMbA==", - "dev": true, - "requires": { - "cacache": "^15.0.5", - "fast-glob": "^3.2.4", - "find-cache-dir": "^3.3.1", - "glob-parent": "^5.1.1", - "globby": "^11.0.1", - "loader-utils": "^2.0.0", - "normalize-path": "^3.0.0", - "p-limit": "^3.0.2", - "schema-utils": "^3.0.0", - "serialize-javascript": "^5.0.1", - "webpack-sources": "^1.4.3" - }, - "dependencies": { - "cacache": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.1.0.tgz", - "integrity": "sha512-mfx0C+mCfWjD1PnwQ9yaOrwG1ou9FkKnx0SvzUHWdFt7r7GaRtzT+9M8HAvLu62zIHtnpQ/1m93nWNDCckJGXQ==", - "dev": true, - "requires": { - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" - } - }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true - }, - "find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - } - }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "ssri": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", - "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", - "dev": true, - "requires": { - "minipass": "^3.1.1" - } - } - } - }, - "core-js-compat": { - "version": "3.34.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.34.0.tgz", - "integrity": "sha512-4ZIyeNbW/Cn1wkMMDy+mvrRUxrwFNjKwbhCfQpDd+eLgYipDqp8oGFGtLmhh18EDPKA0g3VUBYOxQGGwvWLVpA==", - "dev": true, - "requires": { - "browserslist": "^4.22.2" - } + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "dev": true }, "cors": { @@ -20445,51 +12871,6 @@ "vary": "^1" } }, - "create-ecdh": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", - "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.5.3" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } - } - }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -20507,31 +12888,6 @@ "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", "dev": true }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - } - }, - "crypto-js": { - "version": "git+https://git@github.com/ably-forks/crypto-js.git#07e09b48fd8f850f71c10c9d9307ca4e6ac622a0", - "integrity": "sha512-3RE9Ztinx+QLpEDwcsPTVGMXIIqtS/r+YyD1E7BGXE+CRBcCwkyHWVyl1Aperg5QBOUkg2dtiC8p18a41IKltQ==", - "dev": true, - "from": "crypto-js@ably-forks/crypto-js#crypto-lite" - }, "cssom": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", @@ -20556,15 +12912,9 @@ } }, "csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", - "dev": true - }, - "cyclist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", - "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "dev": true }, "data-urls": { @@ -20594,9 +12944,9 @@ } }, "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true }, "decimal.js": { @@ -20605,12 +12955,6 @@ "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", "dev": true }, - "decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "dev": true - }, "decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -20636,15 +12980,15 @@ } }, "deep-equal": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.2.tgz", - "integrity": "sha512-xjVyBf0w5vH0I42jdAZzOKVldmPgSulmiyPRywoyq7HXC9qdgo17kxJE+rdnif5Tz6+pIrpJI8dCpMNLIGkUiA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", "dev": true, "requires": { "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", + "call-bind": "^1.0.5", "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.1", + "get-intrinsic": "^1.2.2", "is-arguments": "^1.1.1", "is-array-buffer": "^3.0.2", "is-date-object": "^1.0.5", @@ -20654,11 +12998,11 @@ "object-is": "^1.1.5", "object-keys": "^1.1.1", "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.0", + "regexp.prototype.flags": "^1.5.1", "side-channel": "^1.0.4", "which-boxed-primitive": "^1.0.2", "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" + "which-typed-array": "^1.1.13" }, "dependencies": { "isarray": { @@ -20669,12 +13013,6 @@ } } }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, "deep-for-each": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/deep-for-each/-/deep-for-each-3.0.0.tgz", @@ -20685,9 +13023,9 @@ } }, "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, "defer-to-connect": { @@ -20695,61 +13033,32 @@ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" }, - "define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", "dev": true, "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" } }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "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": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } + "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=", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true }, "depd": { @@ -20758,16 +13067,6 @@ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true }, - "des.js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", - "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, "destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -20777,34 +13076,15 @@ "detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", "dev": true }, "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } - } - }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -20829,12 +13109,6 @@ "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", - "dev": true - }, "domexception": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", @@ -20850,18 +13124,6 @@ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "dev": true }, - "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "dev": true, - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -20869,47 +13131,24 @@ "dev": true }, "ejs": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", - "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", + "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", "dev": true, "requires": { "jake": "^10.8.5" } }, "electron-to-chromium": { - "version": "1.4.611", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.611.tgz", - "integrity": "sha512-ZtRpDxrjHapOwxtv+nuth5ByB8clyn8crVynmRNGO3wG3LOp8RTcyZDqwaI6Ng6y8FCK2hVZmJoqwCskKbNMaw==", + "version": "1.4.639", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.639.tgz", + "integrity": "sha512-CkKf3ZUVZchr+zDpAlNLEEy2NJJ9T64ULWaDgy3THXXlPVPkLu3VOs9Bac44nebVtdwl2geSj6AxTtGDOxoXhg==", "dev": true }, - "elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", - "dev": true, - "requires": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } - } - }, "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "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 }, "emojilib": { @@ -20918,12 +13157,6 @@ "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", "dev": true }, - "emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true - }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -20939,35 +13172,23 @@ } }, "enhanced-resolve": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", - "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.5.0", - "tapable": "^1.0.0" - }, - "dependencies": { - "memory-fs": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", - "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - } + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" } }, "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "dev": true, "requires": { - "ansi-colors": "^4.1.1" + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" } }, "entities": { @@ -20977,41 +13198,32 @@ "dev": true }, "envinfo": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.7.3.tgz", - "integrity": "sha512-46+j5QxbPWza0PB1i15nZx0xQ4I/EfQxg9J8Had3b408SV63nEtor2e+oiY63amTo9KTuh2a3XLObNwduxYwwA==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.11.0.tgz", + "integrity": "sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg==", "dev": true }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dev": true, - "requires": { - "prr": "~1.0.1" - } - }, "es-abstract": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", - "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", "dev": true, "requires": { "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.2", "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.5", "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.2", "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has": "^1.0.3", "has-property-descriptors": "^1.0.0", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", + "hasown": "^2.0.0", "internal-slot": "^1.0.5", "is-array-buffer": "^3.0.2", "is-callable": "^1.2.7", @@ -21019,23 +13231,23 @@ "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", + "is-typed-array": "^1.1.12", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", + "object-inspect": "^1.13.1", "object-keys": "^1.1.1", "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.0", - "safe-array-concat": "^1.0.0", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", "typed-array-buffer": "^1.0.0", "typed-array-byte-length": "^1.0.0", "typed-array-byte-offset": "^1.0.0", "typed-array-length": "^1.0.4", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.10" + "which-typed-array": "^1.1.13" } }, "es-get-iterator": { @@ -21064,14 +13276,14 @@ } }, "es-iterator-helpers": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.14.tgz", - "integrity": "sha512-JgtVnwiuoRuzLvqelrvN3Xu7H9bu2ap/kQ2CrM62iidP8SKuD99rWU3CJy++s7IVL2qb/AjXPGR/E7i9ngd/Cw==", + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", + "integrity": "sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==", "dev": true, "requires": { "asynciterator.prototype": "^1.0.0", "call-bind": "^1.0.2", - "define-properties": "^1.2.0", + "define-properties": "^1.2.1", "es-abstract": "^1.22.1", "es-set-tostringtag": "^2.0.1", "function-bind": "^1.1.1", @@ -21081,28 +13293,34 @@ "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "internal-slot": "^1.0.5", - "iterator.prototype": "^1.1.0", - "safe-array-concat": "^1.0.0" + "iterator.prototype": "^1.1.2", + "safe-array-concat": "^1.0.1" } }, + "es-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", + "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", + "dev": true + }, "es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", "dev": true, "requires": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" } }, "es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "dev": true, "requires": { - "has": "^1.0.3" + "hasown": "^2.0.0" } }, "es-to-primitive": { @@ -21117,33 +13335,33 @@ } }, "esbuild": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz", - "integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", "dev": true, "requires": { - "@esbuild/android-arm": "0.15.18", - "@esbuild/linux-loong64": "0.15.18", - "esbuild-android-64": "0.15.18", - "esbuild-android-arm64": "0.15.18", - "esbuild-darwin-64": "0.15.18", - "esbuild-darwin-arm64": "0.15.18", - "esbuild-freebsd-64": "0.15.18", - "esbuild-freebsd-arm64": "0.15.18", - "esbuild-linux-32": "0.15.18", - "esbuild-linux-64": "0.15.18", - "esbuild-linux-arm": "0.15.18", - "esbuild-linux-arm64": "0.15.18", - "esbuild-linux-mips64le": "0.15.18", - "esbuild-linux-ppc64le": "0.15.18", - "esbuild-linux-riscv64": "0.15.18", - "esbuild-linux-s390x": "0.15.18", - "esbuild-netbsd-64": "0.15.18", - "esbuild-openbsd-64": "0.15.18", - "esbuild-sunos-64": "0.15.18", - "esbuild-windows-32": "0.15.18", - "esbuild-windows-64": "0.15.18", - "esbuild-windows-arm64": "0.15.18" + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" } }, "esbuild-android-64": { @@ -21258,6 +13476,29 @@ "dev": true, "optional": true }, + "esbuild-plugin-umd-wrapper": { + "version": "git+ssh://git@github.com/ably-forks/esbuild-plugin-umd-wrapper.git#6eef42a607a9192960706d31e444a2c79a2daeb0", + "dev": true, + "from": "esbuild-plugin-umd-wrapper@ably-forks/esbuild-plugin-umd-wrapper#1.0.7-optional-amd-named-module" + }, + "esbuild-runner": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/esbuild-runner/-/esbuild-runner-2.2.2.tgz", + "integrity": "sha512-fRFVXcmYVmSmtYm2mL8RlUASt2TDkGh3uRcvHFOKNr/T58VrfVeKD9uT9nlgxk96u0LS0ehS/GY7Da/bXWKkhw==", + "dev": true, + "requires": { + "source-map-support": "0.5.21", + "tslib": "2.4.0" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + } + } + }, "esbuild-sunos-64": { "version": "0.15.18", "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz", @@ -21295,13 +13536,13 @@ "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "dev": true }, "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "escodegen": { @@ -21316,12 +13557,6 @@ "source-map": "~0.6.1" }, "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -21332,29 +13567,32 @@ } }, "eslint": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.13.0.tgz", - "integrity": "sha512-uCORMuOO8tUzJmsdRtrvcGq5qposf7Rw0LwkTJkoDbOycVQtQjmnhZSuLQnozLE4TmAzlMVV45eCHmQ1OpDKUQ==", + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.2.1", + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", "eslint-scope": "^5.1.1", "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.0", - "esquery": "^1.2.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -21362,7 +13600,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.19", + "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -21371,9 +13609,47 @@ "semver": "^7.2.1", "strip-ansi": "^6.0.0", "strip-json-comments": "^3.1.0", - "table": "^5.2.3", + "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "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==", + "dev": true + }, + "globals": { + "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" + } + }, + "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 + }, + "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 + } } }, "eslint-import-resolver-node": { @@ -21393,18 +13669,7 @@ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { - "ms": "^2.1.1" - } - }, - "resolve": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", - "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", - "dev": true, - "requires": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "ms": "^2.1.1" } } } @@ -21430,28 +13695,28 @@ } }, "eslint-plugin-import": { - "version": "2.28.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz", - "integrity": "sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==", + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", "dev": true, "requires": { - "array-includes": "^3.1.6", - "array.prototype.findlastindex": "^1.2.2", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "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.7", + "eslint-import-resolver-node": "^0.3.9", "eslint-module-utils": "^2.8.0", - "has": "^1.0.3", - "is-core-module": "^2.13.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.fromentries": "^2.0.6", - "object.groupby": "^1.0.0", - "object.values": "^1.1.6", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", "semver": "^6.3.1", - "tsconfig-paths": "^3.14.2" + "tsconfig-paths": "^3.15.0" }, "dependencies": { "debug": { @@ -21472,56 +13737,27 @@ "esutils": "^2.0.2" } }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, "semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true - }, - "tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } } } }, "eslint-plugin-jsdoc": { - "version": "40.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-40.0.0.tgz", - "integrity": "sha512-LOPyIu1vAVvGPkye3ci0moj0iNf3f8bmin6do2DYDj+77NRXWnkmhKRy8swWsatUs3mB5jYPWPUsFg9pyfEiyA==", + "version": "40.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-40.3.0.tgz", + "integrity": "sha512-EhCqpzRkxoT2DUB4AnrU0ggBYvTh3bWrLZzQTupq6vSVE6XzNwJVKsOHa41GCoevnsWMBNmoDVjXWGqckjuG1g==", "dev": true, "requires": { - "@es-joy/jsdoccomment": "~0.36.1", + "@es-joy/jsdoccomment": "~0.37.0", "comment-parser": "1.3.1", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", - "esquery": "^1.4.0", + "esquery": "^1.5.0", "semver": "^7.3.8", "spdx-expression-parse": "^3.0.1" - }, - "dependencies": { - "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 - } } }, "eslint-plugin-react": { @@ -21557,28 +13793,13 @@ "esutils": "^2.0.2" } }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, "resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, "requires": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -21599,12 +13820,12 @@ "requires": {} }, "eslint-plugin-security": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-1.4.0.tgz", - "integrity": "sha512-xlS7P2PLMXeqfhyf3NpqbvbnW04kN8M9NtmhpR3XGyOvt/vNKS7XPXT5EDbwKW9vCjWH4PpfQvgD/+JgN0VJKA==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-1.7.1.tgz", + "integrity": "sha512-sMStceig8AFglhhT2LqlU5r+/fn9OwsA72O5bBuQVTssPCdQAOQzL+oMn/ZcpeUY6KcNfLJArgcrsSULNjYYdQ==", "dev": true, "requires": { - "safe-regex": "^1.1.0" + "safe-regex": "^2.1.1" } }, "eslint-scope": { @@ -21615,6 +13836,14 @@ "requires": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" + }, + "dependencies": { + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + } } }, "eslint-utils": { @@ -21635,19 +13864,19 @@ } }, "eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "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": "7.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", - "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", "dev": true, "requires": { "acorn": "^7.4.0", - "acorn-jsx": "^5.2.0", + "acorn-jsx": "^5.3.1", "eslint-visitor-keys": "^1.3.0" }, "dependencies": { @@ -21666,20 +13895,12 @@ "dev": true }, "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "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==", - "dev": true - } } }, "esrecurse": { @@ -21689,20 +13910,12 @@ "dev": true, "requires": { "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } } }, "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, "estree-walker": { @@ -21726,102 +13939,25 @@ "eventemitter2": { "version": "0.4.14", "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", - "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", + "integrity": "sha512-K7J4xq5xAD5jHsGM5ReWXRTFa3JRGofHiMcVgQ8PRwgWxzjHpMWCIzsmyf60+mh8KLsqYPcjUMa0AC4hd6lPyQ==", "dev": true }, "events": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", - "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", "dev": true }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, "exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, "expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", "dev": true, "requires": { "homedir-polyfill": "^1.0.1" @@ -21878,7 +14014,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true } } @@ -21889,92 +14025,6 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -21982,62 +14032,16 @@ "dev": true }, "fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", + "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - }, - "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } + "micromatch": "^4.0.4" } }, "fast-json-stable-stringify": { @@ -22049,13 +14053,19 @@ "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 + }, + "fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", "dev": true }, "fastq": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", - "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -22067,28 +14077,15 @@ "integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==", "dev": true }, - "figgy-pudding": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", - "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", - "dev": true - }, "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "requires": { - "flat-cache": "^2.0.1" + "flat-cache": "^3.0.4" } }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "optional": true - }, "filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -22108,9 +14105,9 @@ } }, "minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -22119,26 +14116,12 @@ } }, "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "to-regex-range": "^5.0.1" } }, "finalhandler": { @@ -22157,84 +14140,19 @@ }, "dependencies": { "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } - }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { - "find-up": "^3.0.0" + "ms": "2.0.0" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true } } }, @@ -22249,27 +14167,15 @@ } }, "findup-sync": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", - "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-5.0.0.tgz", + "integrity": "sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==", "dev": true, "requires": { - "glob": "~5.0.0" - }, - "dependencies": { - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dev": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } + "detect-file": "^1.0.0", + "is-glob": "^4.0.3", + "micromatch": "^4.0.4", + "resolve-dir": "^1.0.1" } }, "fined": { @@ -22298,32 +14204,22 @@ "dev": true }, "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" } }, "flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, - "flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" - } - }, "for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -22336,13 +14232,13 @@ "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", "dev": true }, "for-own": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==", "dev": true, "requires": { "for-in": "^1.0.1" @@ -22365,73 +14261,29 @@ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "dev": true }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "dev": true }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, - "optional": true, - "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - } + "optional": true }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "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": { @@ -22449,7 +14301,7 @@ "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=", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", "dev": true }, "functions-have-names": { @@ -22471,21 +14323,21 @@ "dev": true }, "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true }, "get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", + "function-bind": "^1.1.2", "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" } }, "get-stream": { @@ -22506,16 +14358,10 @@ "get-intrinsic": "^1.1.1" } }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, "getobject": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/getobject/-/getobject-1.0.0.tgz", - "integrity": "sha512-tbUz6AKKKr2YiMB+fLWIgq5ZeBOobop9YMMAU9dC54/ot2ksMXt3DOFyBuhZw6ptcVszEykgByK20j7W9jHFag==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/getobject/-/getobject-1.0.2.tgz", + "integrity": "sha512-2zblDBaFcb3rB4rF77XVnuINOE2h2k/OnqXAiy0IrTxUfV1iFp3la33oAQVY9pCpWU268WFYVt2t71hlMuLsOg==", "dev": true }, "glob": { @@ -22550,6 +14396,12 @@ "is-glob": "^4.0.1" } }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, "global-modules": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", @@ -22564,7 +14416,7 @@ "global-prefix": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", "dev": true, "requires": { "expand-tilde": "^2.0.2", @@ -22586,13 +14438,10 @@ } }, "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true }, "globalthis": { "version": "1.0.3", @@ -22604,78 +14453,17 @@ } }, "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" - }, - "dependencies": { - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - } - } - }, - "google-closure-compiler": { - "version": "20180610.0.2", - "resolved": "https://registry.npmjs.org/google-closure-compiler/-/google-closure-compiler-20180610.0.2.tgz", - "integrity": "sha1-Eggy9gzK2ZXo5HxedRIRLVTGro8=", - "dev": true, - "requires": { - "chalk": "^1.0.0", - "vinyl": "^2.0.1", - "vinyl-sourcemaps-apply": "^0.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } } }, "gopd": { @@ -22688,9 +14476,9 @@ } }, "got": { - "version": "11.8.5", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.5.tgz", - "integrity": "sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==", + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", "requires": { "@sindresorhus/is": "^4.0.0", "@szmarczak/http-timer": "^4.0.5", @@ -22706,9 +14494,15 @@ } }, "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, "growl": { @@ -22738,36 +14532,6 @@ "nopt": "~3.0.6" }, "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "findup-sync": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-5.0.0.tgz", - "integrity": "sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==", - "dev": true, - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.3", - "micromatch": "^4.0.4", - "resolve-dir": "^1.0.1" - } - }, "glob": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", @@ -22807,12 +14571,6 @@ } } }, - "grunt-known-options": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-2.0.0.tgz", - "integrity": "sha512-GD7cTz0I4SAede1/+pAbmJRG44zFLPipVtdL9o3vqx9IEyb7b4/Y3s7r6ofI3CchR5GvYJ+8buCSioDv5dQLiA==", - "dev": true - }, "iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -22822,146 +14580,75 @@ "safer-buffer": ">= 2.1.2 < 3.0.0" } }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "interpret": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", + "integrity": "sha512-CLM8SNMDu7C5psFCn6Wg/tgpj/bKAg7hc2gWqcuR9OD5Ft9PhBpIu8PLicPeis+xDd6YX2ncI8MCA64I9tftIA==", "dev": true }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", "dev": true, "requires": { - "is-number": "^7.0.0" + "brace-expansion": "^1.1.7" } } } }, - "grunt-bump": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/grunt-bump/-/grunt-bump-0.3.4.tgz", - "integrity": "sha1-dyUehv+L5NvSNK/NXugk5WOaR2c=", - "dev": true, - "requires": { - "semver": "^4.3.3" - }, - "dependencies": { - "semver": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", - "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", - "dev": true - } - } - }, "grunt-cli": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", - "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=", + "integrity": "sha512-8oM6ZAe4yG8Y7co/Ejc9613AixyN+gdCADyAFvJ1BbHGvrNa0ltaqrEWXV9P/W0gbQbAh3C8swJIaDuAX7syiw==", "dev": true, "requires": { "findup-sync": "~0.3.0", "grunt-known-options": "~1.1.0", "nopt": "~3.0.6", "resolve": "~1.1.0" - } - }, - "grunt-closure-tools": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/grunt-closure-tools/-/grunt-closure-tools-1.0.0.tgz", - "integrity": "sha1-+pdty8JrZSYgq1pYkYsZnxbpyZ0=", - "dev": true, - "requires": { - "grunt": "^1.0.1", - "task-closure-tools": "^0.1.10" - } - }, - "grunt-contrib-concat": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/grunt-contrib-concat/-/grunt-contrib-concat-0.5.1.tgz", - "integrity": "sha1-lTxu/f39LBB6uchQd/LUsk0xzUk=", - "dev": true, - "requires": { - "chalk": "^0.5.1", - "source-map": "^0.3.0" }, "dependencies": { - "ansi-regex": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", - "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=", - "dev": true - }, - "ansi-styles": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", - "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=", - "dev": true - }, - "chalk": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", - "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", - "dev": true, - "requires": { - "ansi-styles": "^1.1.0", - "escape-string-regexp": "^1.0.0", - "has-ansi": "^0.1.0", - "strip-ansi": "^0.3.0", - "supports-color": "^0.2.0" - } - }, - "has-ansi": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", - "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", - "dev": true, - "requires": { - "ansi-regex": "^0.2.0" - } - }, - "source-map": { + "findup-sync": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.3.0.tgz", - "integrity": "sha1-hYb7mloAXltQHiHNGLbyG0V60fk=", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", + "integrity": "sha512-z8Nrwhi6wzxNMIbxlrTzuUW6KWuKkogZ/7OdDVq+0+kxn77KUH1nipx8iU6suqkHqc4y6n7a9A8IpmxY/pTjWg==", "dev": true, "requires": { - "amdefine": ">=0.0.4" + "glob": "~5.0.0" } }, - "strip-ansi": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", - "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", "dev": true, "requires": { - "ansi-regex": "^0.2.1" + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, - "supports-color": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", - "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=", + "grunt-known-options": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.1.tgz", + "integrity": "sha512-cHwsLqoighpu7TuYj5RonnEuxGVFnztcUqTqp5rXFGYL4OuPFofwC4Ycg7n9fYwvK6F5WbYgeVOwph9Crs2fsQ==", + "dev": true + }, + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", "dev": true } } }, "grunt-known-options": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.1.tgz", - "integrity": "sha512-cHwsLqoighpu7TuYj5RonnEuxGVFnztcUqTqp5rXFGYL4OuPFofwC4Ycg7n9fYwvK6F5WbYgeVOwph9Crs2fsQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-2.0.0.tgz", + "integrity": "sha512-GD7cTz0I4SAede1/+pAbmJRG44zFLPipVtdL9o3vqx9IEyb7b4/Y3s7r6ofI3CchR5GvYJ+8buCSioDv5dQLiA==", "dev": true }, "grunt-legacy-log": { @@ -23002,9 +14689,9 @@ }, "dependencies": { "async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", "dev": true } } @@ -23012,7 +14699,7 @@ "grunt-shell": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/grunt-shell/-/grunt-shell-1.1.2.tgz", - "integrity": "sha1-Rz5GUwHSnQtW3xb+MQeYznFNCVY=", + "integrity": "sha512-gPYS+1WzGT9K5A4rFAz6I41Q8Dr3dgt464gNwLggwUP5bl2nzNipbn1QliN3/gFrBmed9BdQpeFKKS1+NTd/gA==", "dev": true, "requires": { "chalk": "^1.0.0" @@ -23021,19 +14708,19 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, "requires": { "ansi-styles": "^2.2.1", @@ -23043,10 +14730,25 @@ "supports-color": "^2.0.0" } }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -23055,15 +14757,15 @@ "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true } } }, "grunt-webpack": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/grunt-webpack/-/grunt-webpack-4.0.2.tgz", - "integrity": "sha512-rrqb9SRlY69jEJuCglelB7IvGrI7lRpdfH2GXpFlIOGPRTTtlSxYMU4Fjg8FHaC6ilnMbW5jd55Ff1lR5OibCA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/grunt-webpack/-/grunt-webpack-5.0.0.tgz", + "integrity": "sha512-C7emzVIGJhZ5V5ZYjylr9sDD9PE9Dh/55NaSzP2P2dhif+m/1gHbjDZQ7PDzguyuzwBz4Qc8voQHR06FSp4CKg==", "dev": true, "requires": { "deep-for-each": "^3.0.0", @@ -23076,33 +14778,7 @@ "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", "dev": true, "requires": { - "duplexer": "^0.1.2" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - } + "duplexer": "^0.1.2" } }, "has-bigints": { @@ -23112,18 +14788,18 @@ "dev": true }, "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", "dev": true, "requires": { - "get-intrinsic": "^1.1.1" + "get-intrinsic": "^1.2.2" } }, "has-proto": { @@ -23147,70 +14823,13 @@ "has-symbols": "^1.0.2" } }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dev": true, - "requires": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", "dev": true, "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" + "function-bind": "^1.1.2" } }, "he": { @@ -23225,17 +14844,6 @@ "integrity": "sha512-ciq6hFsSG/Bpt2DmrZJtv+56zpPdnq+NQ4ijEFrveKN0ZG1mhl/LdT1NQZ9se6ty1fACcI4d4vYqC9v8EYpH2A==", "dev": true }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, "homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -23248,7 +14856,7 @@ "hooker": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", - "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=", + "integrity": "sha512-t+UerCsQviSymAInD01Pw+Dn/usmz1sRO+3Zk1+lx8eg+WKpD2ulcwWqHHL0+aseRBr+3+vIhiG1K1JTwaIcTA==", "dev": true }, "html-encoding-sniffer": { @@ -23298,12 +14906,6 @@ "resolve-alpn": "^1.0.0" } }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true - }, "https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -23314,12 +14916,6 @@ "debug": "4" } }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true - }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -23330,27 +14926,21 @@ } }, "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true - }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", "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==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", "dev": true }, "import-fresh": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", - "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "requires": { "parent-module": "^1.0.0", @@ -23358,9 +14948,9 @@ } }, "import-local": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", - "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", "dev": true, "requires": { "pkg-dir": "^4.2.0", @@ -23370,25 +14960,13 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "requires": { "once": "^1.3.0", @@ -23408,20 +14986,20 @@ "dev": true }, "internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", + "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", "dev": true, "requires": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", "side-channel": "^1.0.4" } }, "interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "dev": true }, "ipaddr.js": { @@ -23440,26 +15018,6 @@ "is-windows": "^1.0.1" } }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -23500,13 +15058,12 @@ } }, "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "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, - "optional": true, "requires": { - "binary-extensions": "^1.0.0" + "binary-extensions": "^2.0.0" } }, "is-boolean-object": { @@ -23532,32 +15089,12 @@ "dev": true }, "is-core-module": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", - "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "hasown": "^2.0.0" } }, "is-date-object": { @@ -23569,41 +15106,16 @@ "has-tostringtag": "^1.0.0" } }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, "is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-finalizationregistry": { @@ -23616,9 +15128,9 @@ } }, "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "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 }, "is-generator-function": { @@ -23652,24 +15164,10 @@ "dev": true }, "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true }, "is-number-object": { "version": "1.0.7", @@ -23735,12 +15233,6 @@ "call-bind": "^1.0.2" } }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - }, "is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -23760,16 +15252,12 @@ } }, "is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", "dev": true, "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "which-typed-array": "^1.1.11" } }, "is-unc-path": { @@ -23813,62 +15301,87 @@ "dev": true }, "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true }, "iterator.prototype": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.0.tgz", - "integrity": "sha512-rjuhAk1AJ1fssphHD0IFV6TWL40CwRZ53FrztKx43yk2v6rguBYsY4Bj1VU4HmoMmKwZUlx7mfnhDf9cOp4YTw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", + "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", "dev": true, "requires": { - "define-properties": "^1.1.4", - "get-intrinsic": "^1.1.3", + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", "has-symbols": "^1.0.3", - "has-tostringtag": "^1.0.0", - "reflect.getprototypeof": "^1.0.3" + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" } }, "jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "version": "10.8.7", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", + "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", "dev": true, "requires": { "async": "^3.2.3", "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" + "filelist": "^1.0.4", + "minimatch": "^3.1.2" }, "dependencies": { "async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", "dev": true } } }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "jmespath": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", @@ -23882,9 +15395,9 @@ "dev": true }, "js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -23892,9 +15405,9 @@ } }, "jsdoc-type-pratt-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-3.1.0.tgz", - "integrity": "sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz", + "integrity": "sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==", "dev": true }, "jsdom": { @@ -23932,9 +15445,9 @@ }, "dependencies": { "acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true }, "form-data": { @@ -23961,10 +15474,10 @@ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, "json-schema-traverse": { @@ -23976,17 +15489,14 @@ "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "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" - } + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true }, "jsonc-parser": { "version": "3.2.0", @@ -24006,19 +15516,10 @@ "object.values": "^1.1.6" } }, - "kexec": { - "version": "git+https://git@github.com/ably-forks/node-kexec.git#f29f54037c7db6ad29e1781463b182e5929215a0", - "integrity": "sha512-AF1dssPoaSWqFN8u6seVKCIBjavJ+wRQ5UBdUGMIldfKcJKipXanb43zFL8DWGYSHPR0nGOHsa72xmlQ81bKzw==", - "dev": true, - "from": "kexec@ably-forks/node-kexec#update-for-node-12", - "requires": { - "nan": "^2.13.2" - } - }, "keyv": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz", - "integrity": "sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "requires": { "json-buffer": "3.0.1" } @@ -24029,12 +15530,6 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -24061,24 +15556,6 @@ "resolve": "^1.19.0" }, "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, "findup-sync": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz", @@ -24091,60 +15568,23 @@ "resolve-dir": "^1.0.1" } }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", "dev": true, "requires": { - "is-number": "^7.0.0" + "resolve": "^1.9.0" } } } }, "loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true }, - "loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - }, "local-pkg": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", @@ -24166,16 +15606,22 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true - }, "lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", "dev": true }, "log-symbols": { @@ -24197,12 +15643,12 @@ } }, "loupe": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", - "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", "dev": true, "requires": { - "get-func-name": "^2.0.0" + "get-func-name": "^2.0.1" } }, "lowercase-keys": { @@ -24217,14 +15663,6 @@ "dev": true, "requires": { "yallist": "^3.0.2" - }, - "dependencies": { - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } } }, "lunr": { @@ -24237,25 +15675,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "dev": true - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } + "dev": true }, "make-iterator": { "version": "1.0.1", @@ -24269,35 +15689,26 @@ "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", "dev": true }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, "marked": { - "version": "4.0.17", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.17.tgz", - "integrity": "sha512-Wfk0ATOK5iPxM4ptrORkFemqroz0ZDxp5MWfYA7H/F+wO17NRWV5Ypxi6p3g2Xmw2bKeiYOl6oVnLHKxBA0VhA==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz", + "integrity": "sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==", "dev": true }, "marked-terminal": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-6.1.0.tgz", - "integrity": "sha512-QaCSF6NV82oo6K0szEnmc65ooDeW0T/Adcyf0fcW+Hto2GT1VADFg8dn1zaeHqzj65fqDH1hMNChGNRaC/lbkA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-6.2.0.tgz", + "integrity": "sha512-ubWhwcBFHnXsjYNsu+Wndpg0zhY4CahSpPlA70PlO0rR9r2sZpkyU+rkCsOWH+KMEkx847UpALON+HWgxowFtw==", "dev": true, "requires": { "ansi-escapes": "^6.2.0", "cardinal": "^2.1.1", "chalk": "^5.3.0", "cli-table3": "^0.6.3", - "node-emoji": "^2.1.0", + "node-emoji": "^2.1.3", "supports-hyperlinks": "^3.0.0" }, "dependencies": { @@ -24320,37 +15731,16 @@ "is-buffer": "~1.1.6" } }, - "md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", "dev": true }, "merge-stream": { @@ -24368,46 +15758,17 @@ "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "dev": true }, "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } + "braces": "^3.0.2", + "picomatch": "^2.3.1" } }, "mime": { @@ -24431,243 +15792,94 @@ "mime-db": "1.52.0" } }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, "mimic-response": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true }, - "minipass": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", - "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - } - }, - "mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true }, "mocha": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz", - "integrity": "sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", + "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.4.3", - "debug": "4.2.0", - "diff": "4.0.2", + "chokidar": "3.5.1", + "debug": "4.3.1", + "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", "glob": "7.1.6", "growl": "1.10.5", "he": "1.2.0", - "js-yaml": "3.14.0", + "js-yaml": "4.0.0", "log-symbols": "4.0.0", "minimatch": "3.0.4", - "ms": "2.1.2", - "nanoid": "3.1.12", + "ms": "2.1.3", + "nanoid": "3.1.20", "serialize-javascript": "5.0.1", "strip-json-comments": "3.1.1", - "supports-color": "7.2.0", + "supports-color": "8.1.1", "which": "2.0.2", "wide-align": "1.1.3", - "workerpool": "6.0.2", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", + "workerpool": "6.1.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" }, "dependencies": { - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", + "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==", "dev": true }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chokidar": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", - "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - } + "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 }, "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { "ms": "2.1.2" + }, + "dependencies": { + "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 + } } }, - "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 - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true - }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -24682,53 +15894,38 @@ "path-is-absolute": "^1.0.0" } }, - "has-flag": { + "js-yaml": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "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==", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", "dev": true, "requires": { - "binary-extensions": "^2.0.0" + "argparse": "^2.0.1" } }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "picomatch": "^2.2.1" + "brace-expansion": "^1.1.7" } }, + "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": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "requires": { "has-flag": "^4.0.0" } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } } } }, @@ -24743,28 +15940,6 @@ "mkdirp": "^3.0.0", "strip-ansi": "^6.0.1", "xml": "^1.0.1" - }, - "dependencies": { - "mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true - } - } - }, - "move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" } }, "ms": { @@ -24773,41 +15948,22 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "nan": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", - "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", - "dev": true - }, "nanoid": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", - "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", + "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", "dev": true }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "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 }, "negotiator": { @@ -24823,62 +15979,15 @@ "dev": true }, "node-emoji": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.1.tgz", - "integrity": "sha512-+fyi06+Z9LARCwnTmUF1sRPVQFhGlIpuye3zwlzMN8bIKou6l7k1rGV8WVOEu9EQnRLfoVOYj/p107u0CoQoKA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.3.tgz", + "integrity": "sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==", "dev": true, "requires": { - "@sindresorhus/is": "^6.0.0", + "@sindresorhus/is": "^4.6.0", "char-regex": "^1.0.2", "emojilib": "^2.4.0", "skin-tone": "^2.0.0" - }, - "dependencies": { - "@sindresorhus/is": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-6.1.0.tgz", - "integrity": "sha512-BuvU07zq3tQ/2SIgBsEuxKYDyDjC0n7Zir52bpHy2xnBbW81+po43aLFPLbeV3HRAheFbGud1qgcqSYfhtHMAg==", - "dev": true - } - } - }, - "node-libs-browser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", - "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", - "dev": true, - "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.1", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "^1.0.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - } } }, "node-releases": { @@ -24890,7 +15999,7 @@ "nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", "dev": true, "requires": { "abbrev": "1" @@ -24907,55 +16016,6 @@ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "null-loader": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/null-loader/-/null-loader-4.0.1.tgz", - "integrity": "sha512-pxqVbi4U6N26lq+LmgIbB5XATP0VdZKOG25DhHi8btMmJJefGArFyDg1yc4U3hWCJbMqSrw0qyrz1UQX+qYXqg==", - "dev": true, - "requires": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "dependencies": { - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, - "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } - } - }, "nwsapi": { "version": "2.2.7", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", @@ -24968,41 +16028,10 @@ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true }, "object-is": { @@ -25021,23 +16050,14 @@ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" - } - }, "object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" } @@ -25045,7 +16065,7 @@ "object.defaults": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", - "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==", "dev": true, "requires": { "array-each": "^1.0.1", @@ -25101,7 +16121,7 @@ "object.map": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", - "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "integrity": "sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w==", "dev": true, "requires": { "for-own": "^1.0.0", @@ -25111,7 +16131,7 @@ "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", "dev": true, "requires": { "isobject": "^3.0.1" @@ -25140,71 +16160,45 @@ "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "requires": { "wrappy": "1" } }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, "open": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", - "dev": true, - "requires": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - }, - "dependencies": { - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "requires": { - "is-docker": "^2.0.0" - } - } + "dev": true, + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" } }, "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "requires": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" } }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", "dev": true }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true }, "osenv": { @@ -25223,12 +16217,12 @@ "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==" }, "p-limit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", - "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" } }, "p-locate": { @@ -25240,38 +16234,12 @@ "p-limit": "^3.0.2" } }, - "p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "parallel-transform": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", - "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", - "dev": true, - "requires": { - "cyclist": "^1.0.1", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - } - }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -25281,23 +16249,10 @@ "callsites": "^3.0.0" } }, - "parse-asn1": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", - "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", - "dev": true, - "requires": { - "asn1.js": "^5.2.0", - "browserify-aes": "^1.0.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, "parse-filepath": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", - "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", "dev": true, "requires": { "is-absolute": "^1.0.0", @@ -25308,7 +16263,7 @@ "parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", "dev": true }, "parse5": { @@ -25326,25 +16281,12 @@ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, "path-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", - "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", "dev": true }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", - "dev": true, - "optional": true - }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -25354,7 +16296,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true }, "path-key": { @@ -25372,7 +16314,7 @@ "path-root": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", "dev": true, "requires": { "path-root-regex": "^0.1.0" @@ -25381,13 +16323,13 @@ "path-root-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", + "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", "dev": true }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", "dev": true }, "path-type": { @@ -25402,19 +16344,6 @@ "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true }, - "pbkdf2": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", - "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", - "dev": true, - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -25427,12 +16356,6 @@ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, "pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -25482,13 +16405,13 @@ } }, "playwright": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz", - "integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==", + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.0.tgz", + "integrity": "sha512-XOsfl5ZtAik/T9oek4V0jAypNlaCNzuKOwVhqhgYT3os6kH34PzbRb74F0VWcLYa5WFdnmxl7qyAHBXvPv7lqQ==", "dev": true, "requires": { "fsevents": "2.3.2", - "playwright-core": "1.39.0" + "playwright-core": "1.41.0" }, "dependencies": { "fsevents": { @@ -25501,32 +16424,26 @@ } }, "playwright-core": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz", - "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==", - "dev": true - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.0.tgz", + "integrity": "sha512-UGKASUhXmvqm2Lxa1fNr8sFwAtqjpgBRr9jQ7XBI8Rn5uFiEowGUGwrruUQsVPIom4bk7Lt+oLGpXobnXzrBIw==", "dev": true }, "postcss": { - "version": "8.4.29", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", - "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", + "version": "8.4.33", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", + "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", "dev": true, "requires": { - "nanoid": "^3.3.6", + "nanoid": "^3.3.7", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, "dependencies": { "nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "dev": true } } @@ -25538,9 +16455,9 @@ "dev": true }, "prettier": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", - "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true }, "pretty-format": { @@ -25565,13 +16482,7 @@ "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "dev": true }, "progress": { @@ -25580,12 +16491,6 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, - "promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "dev": true - }, "prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -25615,40 +16520,12 @@ "ipaddr.js": "1.9.1" } }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "dev": true - }, "psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, - "public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } - } - }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -25658,33 +16535,10 @@ "once": "^1.3.1" } }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - }, - "dependencies": { - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true }, "qs": { @@ -25699,13 +16553,7 @@ "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", "dev": true }, "querystringify": { @@ -25734,16 +16582,6 @@ "safe-buffer": "^5.1.0" } }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -25794,59 +16632,32 @@ "dev": true }, "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "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==", - "dev": true - } + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" } }, "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, - "optional": true, "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "picomatch": "^2.2.1" } }, "rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", "dev": true, "requires": { - "resolve": "^1.9.0" - }, - "dependencies": { - "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - } + "resolve": "^1.1.6" } }, "redeyed": { @@ -25858,75 +16669,41 @@ "esprima": "~4.0.0" } }, - "reduce-flatten": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", - "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==", - "dev": true - }, "reflect.getprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.3.tgz", - "integrity": "sha512-TTAOZpkJ2YLxl7mVHWrNo3iDMEkYlva/kgFcXndqMgbo/AZUmmavEkdXV+hXtE4P8xdyEKRzalaFqZVuwIk/Nw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", + "integrity": "sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.1", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", "globalthis": "^1.0.3", "which-builtin-type": "^1.1.3" } }, - "regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", - "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", - "dev": true, - "requires": { - "regenerate": "^1.4.2" - } - }, "regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "dev": true }, - "regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.8.4" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } + "regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "dev": true }, "regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" + "set-function-name": "^2.0.0" } }, "regexpp": { @@ -25935,77 +16712,22 @@ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, - "regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", - "dev": true, - "requires": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - } - }, - "regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "dev": true - } - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "replace-ext": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", - "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", - "dev": true - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true }, "requirejs": { "version": "2.1.22", "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.1.22.tgz", - "integrity": "sha1-3Xj9LTQYDA1ixyS1uK68BmTgNm8=", + "integrity": "sha512-AhZqN7UrWV8R2d1LfGfznskMJNF0Vb6yStwrCn52UbktJg6y5V1I7RoGRyJtACe86d50PyQn0Iw0jYDKMvc4iA==", "dev": true }, "requires-port": { @@ -26015,10 +16737,15 @@ "dev": true }, "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "requires": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } }, "resolve-alpn": { "version": "1.2.1", @@ -26045,7 +16772,7 @@ "resolve-dir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", "dev": true, "requires": { "expand-tilde": "^2.0.0", @@ -26058,26 +16785,14 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, "responselike": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", - "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", "requires": { "lowercase-keys": "^2.0.0" } }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -26085,56 +16800,37 @@ "dev": true }, "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" }, "dependencies": { "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } } } }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, "rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", "dev": true, "requires": { "fsevents": "~2.3.2" - }, - "dependencies": { - "fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "optional": true - } } }, "run-parallel": { @@ -26146,23 +16842,14 @@ "queue-microtask": "^1.2.2" } }, - "run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "dev": true, - "requires": { - "aproba": "^1.1.1" - } - }, "safe-array-concat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", - "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", + "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2", "has-symbols": "^1.0.3", "isarray": "^2.0.5" }, @@ -26182,22 +16869,22 @@ "dev": true }, "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", + "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==", "dev": true, "requires": { - "ret": "~0.1.10" + "regexp-tree": "~0.1.1" } }, "safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.2.tgz", + "integrity": "sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2", "is-regex": "^1.1.4" } }, @@ -26231,17 +16918,6 @@ "loose-envify": "^1.1.0" } }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - }, "semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -26259,6 +16935,12 @@ "requires": { "yallist": "^4.0.0" } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } }, @@ -26329,40 +17011,29 @@ "send": "0.18.0" } }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "set-function-length": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", + "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "define-data-property": "^1.1.1", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.2", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" } }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true + "set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "requires": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + } }, "setprototypeof": { "version": "1.2.0", @@ -26370,14 +17041,13 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "kind-of": "^6.0.2" } }, "shebang-command": { @@ -26407,39 +17077,31 @@ }, "dependencies": { "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "requires": { - "resolve": "^1.1.6" - } } } }, "shiki": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", - "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", + "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", "dev": true, "requires": { - "jsonc-parser": "^3.0.0", - "vscode-oniguruma": "^1.6.1", - "vscode-textmate": "5.2.0" + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" } }, "side-channel": { @@ -26453,12 +17115,6 @@ "object-inspect": "^1.9.0" } }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, "skin-tone": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", @@ -26475,154 +17131,20 @@ "dev": true }, "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - } - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" } }, - "source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, "source-map-explorer": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/source-map-explorer/-/source-map-explorer-2.5.2.tgz", - "integrity": "sha512-gBwOyCcHPHcdLbgw6Y6kgoH1uLKL6hN3zz0xJcNI2lpnElZliIlmSYAjUVwAWnc7+HscoTyh1ScR7ITtFuEnxg==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/source-map-explorer/-/source-map-explorer-2.5.3.tgz", + "integrity": "sha512-qfUGs7UHsOBE5p/lGfQdaAj/5U/GWYBw2imEpD6UQNkqElYonkow8t+HBL1qqIl3CuGZx7n8/CQo4x1HwSHhsg==", "dev": true, "requires": { "btoa": "^1.2.1", @@ -26634,125 +17156,35 @@ "gzip-size": "^6.0.0", "lodash": "^4.17.20", "open": "^7.3.1", - "source-map": "^0.7.3", + "source-map": "^0.7.4", "temp": "^0.9.4", "yargs": "^16.2.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==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "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" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "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==", - "dev": true - }, - "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==", + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, - "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 - }, "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "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", - "strip-ansi": "^6.0.0" - } - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "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" - } - }, - "yargs-parser": { - "version": "20.2.7", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", - "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true } } @@ -26763,19 +17195,6 @@ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "dev": true }, - "source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dev": true, - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, "source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -26794,12 +17213,6 @@ } } }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, "spdx-exceptions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", @@ -26817,56 +17230,17 @@ } }, "spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", + "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", "dev": true }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "ssri": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", - "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -26883,93 +17257,39 @@ } }, "stream-browserify": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", - "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", - "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" - } - }, - "stream-each": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", - "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", "dev": true, "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" } }, - "stream-shift": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", - "dev": true - }, "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "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==", - "dev": true - } + "safe-buffer": "~5.2.0" } }, "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "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": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } }, "string.prototype.matchall": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.9.tgz", - "integrity": "sha512-6i5hL3MqG/K2G43mWXWgP+qizFW/QH/7kCNN13JrJS5q48FN5IKksLDscexKP3dnmB6cdm9jlNgAsWNLpSykmA==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", + "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -26979,40 +17299,41 @@ "has-symbols": "^1.0.3", "internal-slot": "^1.0.5", "regexp.prototype.flags": "^1.5.0", + "set-function-name": "^2.0.0", "side-channel": "^1.0.4" } }, "string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" } }, "string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" } }, "string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" } }, "strip-ansi": { @@ -27030,12 +17351,6 @@ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -27043,12 +17358,12 @@ "dev": true }, "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "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, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } }, "supports-hyperlinks": { @@ -27059,99 +17374,57 @@ "requires": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" - }, - "dependencies": { - "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 - }, - "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, - "requires": { - "has-flag": "^4.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 - }, - "symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true - }, - "table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", - "dev": true, - "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" - } - }, - "table-layout": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.1.tgz", - "integrity": "sha512-dEquqYNJiGwY7iPfZ3wbXDI944iqanTSchrACLL2nOB+1r+h1Nzu2eH+DuPPvWvm5Ry7iAPeFlgEtP5bIp5U7Q==", - "dev": true, - "requires": { - "array-back": "^4.0.1", - "deep-extend": "~0.6.0", - "typical": "^5.2.0", - "wordwrapjs": "^4.0.0" - } + "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 }, - "tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, - "tar": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "table": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", "dev": true, "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" }, "dependencies": { - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true } } }, - "task-closure-tools": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/task-closure-tools/-/task-closure-tools-0.1.10.tgz", - "integrity": "sha1-2bHs+A7jfi2tIkRbJiAseN0VU3s=", + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true }, "temp": { @@ -27162,15 +17435,47 @@ "requires": { "mkdirp": "^0.5.1", "rimraf": "~2.6.2" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "terser": { - "version": "5.19.3", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.3.tgz", - "integrity": "sha512-pQzJ9UJzM0IgmT4FAtYI6+VqFf0lj/to58AV0Xfgg0Up37RyPG7Al+1cepC6/BVuAxR9oNb41/DL4DEoHJvTdg==", + "version": "5.27.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz", + "integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==", "dev": true, - "optional": true, - "peer": true, "requires": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -27179,56 +17484,50 @@ }, "dependencies": { "acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true, - "optional": true, - "peer": true + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true } } }, "terser-webpack-plugin": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", - "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", "dev": true, "requires": { - "cacache": "^12.0.2", - "find-cache-dir": "^2.1.0", - "is-wsl": "^1.1.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^4.0.0", - "source-map": "^0.6.1", - "terser": "^4.1.2", - "webpack-sources": "^1.4.0", - "worker-farm": "^1.7.0" + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" }, "dependencies": { - "serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, "requires": { - "randombytes": "^2.1.0" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "terser": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", - "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==", + "serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "requires": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" + "randombytes": "^2.1.0" } } } @@ -27236,28 +17535,9 @@ "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "timers-browserify": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", - "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", - "dev": true, - "requires": { - "setimmediate": "^1.0.4" - } - }, "tinypool": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.2.4.tgz", @@ -27270,64 +17550,25 @@ "integrity": "sha512-UVq5AXt/gQlti7oxoIg5oi/9r0WpF7DGEVwXgqWSMmyN16+e3tl5lIvTaOpJ3TAtu5xFzWccFRM4R5NaWHF+4g==", "dev": true }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "^7.0.0" } }, "to-utf8": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/to-utf8/-/to-utf8-0.0.1.tgz", - "integrity": "sha1-0Xrqcv8vujm55DYBvns/9y4ImFI=" + "integrity": "sha512-zks18/TWT1iHO3v0vFp5qLKOG27m67ycq/Y7a7cTiRuUNlc4gf3HGnkRgMv0NyhnfTamtkYBJl+YeD1/j07gBQ==" }, "toidentifier": { "version": "1.0.1", @@ -27356,104 +17597,60 @@ "punycode": "^2.1.1" } }, + "ts-expose-internals-conditionally": { + "version": "1.0.0-empty.0", + "resolved": "https://registry.npmjs.org/ts-expose-internals-conditionally/-/ts-expose-internals-conditionally-1.0.0-empty.0.tgz", + "integrity": "sha512-F8m9NOF6ZhdOClDVdlM8gj3fDCav4ZIFSs/EI3ksQbAAXVSCN/Jh5OCJDDZWBuBy9psFc6jULGDlPwjMYMhJDw==", + "dev": true + }, "ts-loader": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.2.0.tgz", - "integrity": "sha512-ebXBFrNyMSmbWgjnb3WBloUBK+VSx1xckaXsMXxlZRDqce/OPdYBVN5efB0W3V0defq0Gcy4YuzvPGqRgjj85A==", + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", + "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", "dev": true, "requires": { "chalk": "^4.1.0", - "enhanced-resolve": "^4.0.0", - "loader-utils": "^2.0.0", + "enhanced-resolve": "^5.0.0", "micromatch": "^4.0.0", - "semver": "^7.3.4" + "semver": "^7.3.4", + "source-map": "^0.7.4" }, "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true - }, - "loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } } } }, "tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "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": { - "json5": "^2.2.2", + "@types/json5": "^0.0.29", + "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" }, "dependencies": { "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true + "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" + } } } }, "tsconfig-paths-webpack-plugin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.0.1.tgz", - "integrity": "sha512-m5//KzLoKmqu2MVix+dgLKq70MnFi8YL8sdzQZ6DblmCdfuq/y3OqvJd5vMndg2KEVCOeNz8Es4WVZhYInteLw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.1.0.tgz", + "integrity": "sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==", "dev": true, "requires": { "chalk": "^4.1.0", @@ -27461,28 +17658,23 @@ "tsconfig-paths": "^4.1.2" }, "dependencies": { - "enhanced-resolve": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz", - "integrity": "sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==", + "tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", "dev": true, "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" } - }, - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true } } }, "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true }, "tsutils": { @@ -27502,12 +17694,6 @@ } } }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -27524,9 +17710,9 @@ "dev": true }, "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", "dev": true }, "type-is": { @@ -27586,22 +17772,16 @@ "is-typed-array": "^1.1.9" } }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, "typedoc": { - "version": "0.23.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.8.tgz", - "integrity": "sha512-NLRTY/7XSrhiowR3xnH/nlfTnHk+dkzhHWAMT8guoZ6RHCQZIu3pJREMCqzdkWVCC5+dr9We7TtNeprR3Qy6Ag==", + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.8.tgz", + "integrity": "sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==", "dev": true, "requires": { "lunr": "^2.3.9", - "marked": "^4.0.16", - "minimatch": "^5.1.0", - "shiki": "^0.10.1" + "marked": "^4.3.0", + "minimatch": "^9.0.0", + "shiki": "^0.14.1" }, "dependencies": { "brace-expansion": { @@ -27613,10 +17793,16 @@ "balanced-match": "^1.0.0" } }, + "marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true + }, "minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -27625,15 +17811,9 @@ } }, "typescript": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", - "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", - "dev": true - }, - "typical": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", - "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true }, "unbox-primitive": { @@ -27651,24 +17831,31 @@ "unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", + "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", "dev": true }, "underscore.string": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz", - "integrity": "sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.6.tgz", + "integrity": "sha512-VoC83HWXmCrF6rgkyxS9GHv8W9Q5nhMKho+OadDJGzL2oDYbYEppBaCMH6pFlwLeqj2QS+hhkw2kpXkSdD1JxQ==", "dev": true, "requires": { - "sprintf-js": "^1.0.3", + "sprintf-js": "^1.1.1", "util-deprecate": "^1.0.2" + }, + "dependencies": { + "sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true + } } }, - "unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "dev": true + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "unicode-emoji-modifier-base": { "version": "1.0.0", @@ -27676,58 +17863,6 @@ "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", "dev": true }, - "unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, - "requires": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", - "dev": true - }, - "unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "dev": true - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "dev": true, - "requires": { - "unique-slug": "^2.0.0" - } - }, - "unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4" - } - }, "universalify": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", @@ -27740,53 +17875,6 @@ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - } - } - }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true, - "optional": true - }, "update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", @@ -27798,24 +17886,18 @@ } }, "uri-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", - "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "requires": { "punycode": "^2.1.0" } }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", "dev": true, "requires": { "punycode": "1.3.2", @@ -27825,7 +17907,7 @@ "punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", "dev": true } } @@ -27840,39 +17922,29 @@ "requires-port": "^1.0.0" } }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, "util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", "dev": true, "requires": { - "inherits": "2.0.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - } + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" } }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "dev": true }, "uuid": { @@ -27882,9 +17954,9 @@ "dev": true }, "v8-compile-cache": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", - "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", + "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", "dev": true }, "v8flags": { @@ -27908,104 +17980,19 @@ "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true - }, - "vinyl": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", - "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", - "dev": true, - "requires": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" - } - }, - "vinyl-sourcemaps-apply": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", - "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", - "dev": true, - "requires": { - "source-map": "^0.5.1" - } + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true }, "vite": { - "version": "4.4.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz", - "integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz", + "integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==", "dev": true, "requires": { "esbuild": "^0.18.10", "fsevents": "~2.3.2", "postcss": "^8.4.27", "rollup": "^3.27.1" - }, - "dependencies": { - "@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", - "dev": true, - "optional": true - }, - "@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", - "dev": true, - "optional": true - }, - "esbuild": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", - "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", - "dev": true, - "requires": { - "@esbuild/android-arm": "0.18.20", - "@esbuild/android-arm64": "0.18.20", - "@esbuild/android-x64": "0.18.20", - "@esbuild/darwin-arm64": "0.18.20", - "@esbuild/darwin-x64": "0.18.20", - "@esbuild/freebsd-arm64": "0.18.20", - "@esbuild/freebsd-x64": "0.18.20", - "@esbuild/linux-arm": "0.18.20", - "@esbuild/linux-arm64": "0.18.20", - "@esbuild/linux-ia32": "0.18.20", - "@esbuild/linux-loong64": "0.18.20", - "@esbuild/linux-mips64el": "0.18.20", - "@esbuild/linux-ppc64": "0.18.20", - "@esbuild/linux-riscv64": "0.18.20", - "@esbuild/linux-s390x": "0.18.20", - "@esbuild/linux-x64": "0.18.20", - "@esbuild/netbsd-x64": "0.18.20", - "@esbuild/openbsd-x64": "0.18.20", - "@esbuild/sunos-x64": "0.18.20", - "@esbuild/win32-arm64": "0.18.20", - "@esbuild/win32-ia32": "0.18.20", - "@esbuild/win32-x64": "0.18.20" - } - }, - "fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "optional": true - }, - "rollup": { - "version": "3.28.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.28.1.tgz", - "integrity": "sha512-R9OMQmIHJm9znrU3m3cpE8uhN0fGdXiawME7aZIpQqvpS/85+Vt1Hq1/yVIcYfOmaQiHjvXkQAoJukvLpau6Yw==", - "dev": true, - "requires": { - "fsevents": "~2.3.2" - } - } } }, "vitest": { @@ -28025,28 +18012,63 @@ "vite": "^2.9.12 || ^3.0.0-0" }, "dependencies": { - "fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "@esbuild/android-arm": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz", + "integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==", "dev": true, "optional": true }, - "resolve": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", - "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "@esbuild/linux-loong64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz", + "integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==", + "dev": true, + "optional": true + }, + "esbuild": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz", + "integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==", "dev": true, "requires": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "@esbuild/android-arm": "0.15.18", + "@esbuild/linux-loong64": "0.15.18", + "esbuild-android-64": "0.15.18", + "esbuild-android-arm64": "0.15.18", + "esbuild-darwin-64": "0.15.18", + "esbuild-darwin-arm64": "0.15.18", + "esbuild-freebsd-64": "0.15.18", + "esbuild-freebsd-arm64": "0.15.18", + "esbuild-linux-32": "0.15.18", + "esbuild-linux-64": "0.15.18", + "esbuild-linux-arm": "0.15.18", + "esbuild-linux-arm64": "0.15.18", + "esbuild-linux-mips64le": "0.15.18", + "esbuild-linux-ppc64le": "0.15.18", + "esbuild-linux-riscv64": "0.15.18", + "esbuild-linux-s390x": "0.15.18", + "esbuild-netbsd-64": "0.15.18", + "esbuild-openbsd-64": "0.15.18", + "esbuild-sunos-64": "0.15.18", + "esbuild-windows-32": "0.15.18", + "esbuild-windows-64": "0.15.18", + "esbuild-windows-arm64": "0.15.18" + } + }, + "rollup": { + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "dev": true, + "requires": { + "fsevents": "~2.3.2" } }, "vite": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.2.7.tgz", - "integrity": "sha512-29pdXjk49xAP0QBr0xXqu2s5jiQIXNvE/xwd0vUizYT2Hzqe4BksNNoWllFVXJf4eLZ+UlVQmXfB4lWrc+t18g==", + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-3.2.8.tgz", + "integrity": "sha512-EtQU16PLIJpAZol2cTLttNP1mX6L0SyI0pgQB1VOoWeQnMSvtiwovV3D6NcjN8CZQWWyESD2v5NGnpz5RvgOZA==", "dev": true, "requires": { "esbuild": "^0.15.9", @@ -28058,22 +18080,16 @@ } } }, - "vm-browserify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", - "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", - "dev": true - }, "vscode-oniguruma": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz", - "integrity": "sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", "dev": true }, "vscode-textmate": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", - "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", "dev": true }, "w3c-xmlserializer": { @@ -28086,126 +18102,13 @@ } }, "watchpack": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", - "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", - "dev": true, - "requires": { - "chokidar": "^3.4.1", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0", - "watchpack-chokidar2": "^2.0.1" - }, - "dependencies": { - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "optional": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", - "dev": true, - "optional": true - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "optional": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chokidar": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", - "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", - "dev": true, - "optional": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "optional": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true - }, - "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, - "optional": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "optional": true - }, - "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "optional": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "optional": true, - "requires": { - "is-number": "^7.0.0" - } - } - } - }, - "watchpack-chokidar2": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", - "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "dev": true, - "optional": true, "requires": { - "chokidar": "^2.1.8" + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" } }, "webidl-conversions": { @@ -28215,121 +18118,123 @@ "dev": true }, "webpack": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz", - "integrity": "sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-module-context": "1.9.0", - "@webassemblyjs/wasm-edit": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0", - "acorn": "^6.4.1", - "ajv": "^6.10.2", - "ajv-keywords": "^3.4.1", + "version": "5.89.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", + "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^4.5.0", - "eslint-scope": "^4.0.3", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.4.0", - "loader-utils": "^1.2.3", - "memory-fs": "^0.4.1", - "micromatch": "^3.1.10", - "mkdirp": "^0.5.3", - "neo-async": "^2.6.1", - "node-libs-browser": "^2.2.1", - "schema-utils": "^1.0.0", - "tapable": "^1.1.3", - "terser-webpack-plugin": "^1.4.3", - "watchpack": "^1.7.4", - "webpack-sources": "^1.4.1" + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.7", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" }, "dependencies": { "acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true + }, + "acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "dev": true, + "requires": {} + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" } } } }, "webpack-cli": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.2.0.tgz", - "integrity": "sha512-EIl3k88vaF4fSxWSgtAQR+VwicfLMTZ9amQtqS4o+TDPW9HGaEpbFBbAZ4A3ZOT5SOnMxNOzROsSTPiE8tBJPA==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, "requires": { - "@webpack-cli/info": "^1.1.0", - "@webpack-cli/serve": "^1.1.0", - "colorette": "^1.2.1", - "command-line-usage": "^6.1.0", - "commander": "^6.2.0", - "enquirer": "^2.3.6", - "execa": "^4.1.0", + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", + "colorette": "^2.0.14", + "commander": "^10.0.1", + "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", + "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", - "interpret": "^2.2.0", - "leven": "^3.1.0", - "rechoir": "^0.7.0", - "v8-compile-cache": "^2.2.0", - "webpack-merge": "^4.2.2" + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^5.7.3" }, "dependencies": { - "commander": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", - "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", - "dev": true - }, "interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", "dev": true + }, + "rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "requires": { + "resolve": "^1.20.0" + } } } }, "webpack-merge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", - "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", "dev": true, "requires": { - "lodash": "^4.17.15" + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" } }, - "webpack-node-externals": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", - "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", - "dev": true - }, "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true }, "whatwg-encoding": { "version": "2.0.0", @@ -28429,20 +18334,14 @@ "is-weakset": "^2.0.1" } }, - "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.11", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", - "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", "dev": true, "requires": { "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.4", "for-each": "^0.3.3", "gopd": "^1.0.1", "has-tostringtag": "^1.0.0" @@ -28463,6 +18362,12 @@ "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", "dev": true }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -28476,7 +18381,7 @@ "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, "requires": { "ansi-regex": "^3.0.0" @@ -28484,83 +18389,38 @@ } } }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", "dev": true }, - "wordwrapjs": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.0.tgz", - "integrity": "sha512-Svqw723a3R34KvsMgpjFBYCgNOSdcW3mQFK4wIfhGQhtaFVOJmdYoXgi63ne3dTlWgatVcUc7t4HtQ/+bUVIzQ==", - "dev": true, - "requires": { - "reduce-flatten": "^2.0.0", - "typical": "^5.0.0" - } - }, - "worker-farm": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", - "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", - "dev": true, - "requires": { - "errno": "~0.1.7" - } - }, "workerpool": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", - "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", + "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", "dev": true }, "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "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": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "ws": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", - "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "requires": {} }, "xml": { @@ -28597,96 +18457,38 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true - }, "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" } }, "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true }, "yargs-unparser": { "version": "2.0.0", @@ -28698,21 +18500,13 @@ "decamelize": "^4.0.0", "flat": "^5.0.2", "is-plain-obj": "^2.1.0" - }, - "dependencies": { - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - } } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true } } } diff --git a/package.json b/package.json index f1a48ec275..b6430e5827 100644 --- a/package.json +++ b/package.json @@ -1,27 +1,33 @@ { "name": "ably", "description": "Realtime client library for Ably, the realtime messaging service", - "version": "1.2.50", + "version": "2.0.0", "license": "Apache-2.0", "bugs": { "url": "https://github.com/ably/ably-js/issues", "email": "support@ably.com" }, - "main": "./build/ably-node.js", - "typings": "./ably.d.ts", - "react-native": { - "./build/ably-node.js": "./build/ably-reactnative.js" - }, - "browser": { - "./build/ably-node.js": "./build/ably-commonjs.js" + "exports": { + ".": { + "types": "./ably.d.ts", + "node": "./build/ably-node.js", + "react-native": "./build/ably-reactnative.js", + "default": "./build/ably.js" + }, + "./modular": { + "types": "./modular.d.ts", + "import": "./build/modular/index.mjs" + }, + "./react": { + "require": "./react/cjs/index.js", + "import": "./react/mjs/index.js" + } }, + "typings": "./ably.d.ts", "files": [ "build/**", "ably.d.ts", - "callbacks.d.ts", - "callbacks.js", - "promises.d.ts", - "promises.js", + "modular.d.ts", "resources/**", "src/**", "react/**" @@ -44,25 +50,28 @@ } }, "devDependencies": { - "@ably/vcdiff-decoder": "1.0.4", + "@ably/vcdiff-decoder": "1.0.6", "@arethetypeswrong/cli": "^0.13.1", - "@babel/preset-env": "^7.23.6", + "@babel/generator": "^7.23.6", + "@babel/parser": "^7.23.6", + "@babel/traverse": "^7.23.7", "@testing-library/react": "^13.3.0", - "@types/crypto-js": "^4.0.1", + "@types/cli-table": "^0.3.4", "@types/jmespath": "^0.15.2", - "@types/node": "^15.0.0", + "@types/node": "^18.0.0", "@types/request": "^2.48.7", "@types/ws": "^8.2.0", - "@typescript-eslint/eslint-plugin": "^5.14.0", - "@typescript-eslint/parser": "^5.14.0", + "@typescript-eslint/eslint-plugin": "^5.59.6", + "@typescript-eslint/parser": "^5.59.6", "@vitejs/plugin-react": "^1.3.2", "async": "ably-forks/async#requirejs", "aws-sdk": "^2.1413.0", - "babel-loader": "^8.3.0", "chai": "^4.2.0", - "copy-webpack-plugin": "^6.4.1", + "cli-table": "^0.3.11", "cors": "^2.8.5", - "crypto-js": "ably-forks/crypto-js#crypto-lite", + "esbuild": "^0.18.10", + "esbuild-plugin-umd-wrapper": "ably-forks/esbuild-plugin-umd-wrapper#1.0.7-optional-amd-named-module", + "esbuild-runner": "^2.2.2", "eslint": "^7.13.0", "eslint-plugin-import": "^2.28.0", "eslint-plugin-jsdoc": "^40.0.0", @@ -71,42 +80,39 @@ "eslint-plugin-security": "^1.4.0", "express": "^4.17.1", "glob": "~4.4", - "google-closure-compiler": "^20180610.0.1", "grunt": "^1.6.1", - "grunt-bump": "^0.3.1", "grunt-cli": "~1.2.0", - "grunt-closure-tools": "^1.0.0", - "grunt-contrib-concat": "~0.5", "grunt-shell": "~1.1", - "grunt-webpack": "^4.0.2", + "grunt-webpack": "^5.0.0", "hexy": "~0.2", "jmespath": "^0.16.0", "jsdom": "^20.0.0", - "kexec": "ably-forks/node-kexec#update-for-node-12", "minimist": "^1.2.5", "mocha": "^8.1.3", "mocha-junit-reporter": "^2.2.1", - "null-loader": "^4.0.1", + "path-browserify": "^1.0.1", "playwright": "^1.39.0", - "prettier": "^2.5.1", + "prettier": "^2.8.8", + "process": "^0.11.10", "react": ">=18.1.0", "react-dom": ">=18.1.0", "requirejs": "~2.1", "shelljs": "~0.8", "source-map-explorer": "^2.5.2", - "ts-loader": "^8.2.0", + "source-map-support": "^0.5.21", + "stream-browserify": "^3.0.0", + "ts-loader": "^9.4.2", "tsconfig-paths-webpack-plugin": "^4.0.1", "tslib": "^2.3.1", - "typedoc": "^0.23.8", - "typescript": "^4.6.4", + "typedoc": "^0.24.7", + "typescript": "^4.9.5", "vite": "^4.4.9", "vitest": "^0.18.0", - "webpack": "^4.44.2", - "webpack-cli": "^4.2.0", - "webpack-node-externals": "^3.0.0" + "webpack": "^5.79.0", + "webpack-cli": "^5.0.1" }, "engines": { - "node": ">=5.10.x" + "node": ">=16" }, "repository": "ably/ably-js", "jspm": { @@ -119,12 +125,13 @@ "scripts": { "start:react": "npx vite serve", "grunt": "grunt", - "test": "grunt test", - "test:node": "grunt test:node", - "test:node:skip-build": "grunt mocha", + "test": "npm run test:node", + "test:node": "npm run build:node && mocha", + "test:node:skip-build": "mocha", "test:webserver": "grunt test:webserver", "test:playwright": "node test/support/runPlaywrightTests.js", "test:react": "vitest run", + "test:package": "grunt test:package", "concat": "grunt concat", "build": "grunt build:all && npm run build:react", "build:node": "grunt build:node", @@ -135,12 +142,11 @@ "requirejs": "grunt requirejs", "lint": "eslint .", "lint:fix": "eslint --fix .", - "check-closure-compiler": "grunt check-closure-compiler", "prepare": "npm run build", - "format": "prettier --write --ignore-path .gitignore --ignore-path .prettierignore src test ably.d.ts webpack.config.js Gruntfile.js scripts/cdn_deploy.js docs/chrome-mv3.md", - "format:check": "prettier --check --ignore-path .gitignore --ignore-path .prettierignore src test ably.d.ts webpack.config.js Gruntfile.js scripts/cdn_deploy.js", + "format": "prettier --write --ignore-path .gitignore --ignore-path .prettierignore src test ably.d.ts modular.d.ts webpack.config.js Gruntfile.js scripts/*.[jt]s docs/**/*.md grunt", + "format:check": "prettier --check --ignore-path .gitignore --ignore-path .prettierignore src test ably.d.ts modular.d.ts webpack.config.js Gruntfile.js scripts/*.[jt]s docs/**/*.md grunt", "sourcemap": "source-map-explorer build/ably.min.js", - "sourcemap:noencryption": "source-map-explorer build/ably.noencryption.min.js", - "docs": "typedoc --entryPoints ably.d.ts --out docs/generated/default --readme docs/landing-pages/default.md && typedoc --entryPoints promises.d.ts --out docs/generated/promises --name \"ably (Promise-based)\" --readme docs/landing-pages/promises.md && cp docs/landing-pages/choose-library.html docs/generated/index.html" + "modulereport": "tsc --noEmit --esModuleInterop scripts/moduleReport.ts && esr scripts/moduleReport.ts", + "docs": "typedoc" } } diff --git a/promises.d.ts b/promises.d.ts deleted file mode 100644 index fcbb9ac461..0000000000 --- a/promises.d.ts +++ /dev/null @@ -1,18 +0,0 @@ -import Ably = require('./ably'); - -/** - * The `Rest` object offers a simple stateless API to interact directly with Ably's REST API. - */ -export declare class Rest extends Ably.Rest.Promise {} -/** - * A client that extends the functionality of {@link Rest} and provides additional realtime-specific features. - */ -export declare class Realtime extends Ably.Realtime.Promise {} -/** - * A generic Ably error object that contains an Ably-specific status code, and a generic status code. Errors returned from the Ably server are compatible with the `ErrorInfo` structure and should result in errors that inherit from `ErrorInfo`. - */ -export declare class ErrorInfo extends Types.ErrorInfo {} - -// Re-export the Types namespace. -import Types = Ably.Types; -export { Types }; diff --git a/promises.js b/promises.js deleted file mode 100644 index efb1645a40..0000000000 --- a/promises.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict'; -function promisifyOptions(options) { - if (typeof options == 'string') { - options = options.indexOf(':') == -1 ? { token: options } : { key: options }; - } - options.promises = true; - return options; -} - -/* Please note that the file imported below is only generated after running - * the build task. */ -// eslint-disable-next-line @typescript-eslint/no-var-requires -var Ably = require('./build/ably-node'); - -var ErrorInfo = function(message, code, statusCode, cause) { - return new Ably.ErrorInfo(message, code, statusCode, cause); -} - -var RestPromise = function (options) { - return new Ably.Rest(promisifyOptions(options)); -}; -Object.assign(RestPromise, Ably.Rest); - -var RealtimePromise = function (options) { - return new Ably.Realtime(promisifyOptions(options)); -}; -Object.assign(RealtimePromise, Ably.Realtime); - -module.exports = { - ErrorInfo: ErrorInfo, - Rest: RestPromise, - Realtime: RealtimePromise, -}; diff --git a/scripts/cdn_deploy.js b/scripts/cdn_deploy.js index a8f5f944b2..d660ba53ef 100755 --- a/scripts/cdn_deploy.js +++ b/scripts/cdn_deploy.js @@ -21,7 +21,7 @@ async function run() { // Comma separated directories (relative to `path`) to exclude from upload excludeDirs: 'node_modules,.git', // Regex to match files against for upload - fileRegex: '^ably(\\.noencryption)?(\\.min)?\\.js$', + fileRegex: '^ably?(\\.min)?\\.js$', ...argv, }; @@ -63,7 +63,7 @@ async function run() { for (let version of versions) { const relativePath = path.relative( config.includeDirs.find((d) => file.startsWith(d)), - file + file, ); const split = relativePath.split('.js'); const newPath = `${split[0]}-${version}.js`; diff --git a/scripts/moduleReport.ts b/scripts/moduleReport.ts new file mode 100644 index 0000000000..c933800afe --- /dev/null +++ b/scripts/moduleReport.ts @@ -0,0 +1,332 @@ +import * as esbuild from 'esbuild'; +import * as path from 'path'; +import { explore } from 'source-map-explorer'; +import { promisify } from 'util'; +import { gzip } from 'zlib'; +import Table from 'cli-table'; + +// The maximum size we allow for a minimal useful Realtime bundle (i.e. one that can subscribe to a channel) +const minimalUsefulRealtimeBundleSizeThresholdsKiB = { raw: 95, gzip: 29 }; + +const baseClientNames = ['BaseRest', 'BaseRealtime']; + +// List of all plugins accepted in ModularPlugins +const pluginNames = [ + 'Rest', + 'Crypto', + 'MsgPack', + 'RealtimePresence', + 'XHRPolling', + 'WebSocketTransport', + 'XHRRequest', + 'FetchRequest', + 'MessageInteractions', +]; + +// List of all free-standing functions exported by the library along with the +// ModularPlugins entries that we expect them to transitively import +const functions = [ + { name: 'generateRandomKey', transitiveImports: ['Crypto'] }, + { name: 'getDefaultCryptoParams', transitiveImports: ['Crypto'] }, + { name: 'decodeMessage', transitiveImports: [] }, + { name: 'decodeEncryptedMessage', transitiveImports: ['Crypto'] }, + { name: 'decodeMessages', transitiveImports: [] }, + { name: 'decodeEncryptedMessages', transitiveImports: ['Crypto'] }, + { name: 'decodePresenceMessage', transitiveImports: [] }, + { name: 'decodePresenceMessages', transitiveImports: [] }, + { name: 'constructPresenceMessage', transitiveImports: [] }, +]; + +function formatBytes(bytes: number) { + const kibibytes = bytes / 1024; + const formatted = kibibytes.toFixed(2); + return `${formatted} KiB`; +} + +interface BundleInfo { + byteSize: number; + code: Uint8Array; + sourceMap: Uint8Array; +} + +interface ByteSizes { + rawByteSize: number; + gzipEncodedByteSize: number; +} + +interface TableRow { + description: string; + sizes: ByteSizes; +} + +interface Output { + tableRows: TableRow[]; + errors: Error[]; +} + +// Uses esbuild to create a bundle containing the named exports from 'ably/modular' +function getBundleInfo(exports: string[]): BundleInfo { + const outfile = exports.join(''); + const result = esbuild.buildSync({ + stdin: { + contents: `export { ${exports.join(', ')} } from './build/modular/index.mjs'`, + resolveDir: '.', + }, + metafile: true, + minify: true, + bundle: true, + outfile, + write: false, + sourcemap: 'external', + }); + + const pathHasBase = (component: string) => { + return (outputFile: esbuild.OutputFile) => { + return path.parse(outputFile.path).base === component; + }; + }; + + const codeOutputFile = result.outputFiles.find(pathHasBase(outfile))!; + const sourceMapOutputFile = result.outputFiles.find(pathHasBase(`${outfile}.map`))!; + + return { + byteSize: result.metafile.outputs[outfile].bytes, + code: codeOutputFile.contents, + sourceMap: sourceMapOutputFile.contents, + }; +} + +// Gets the bundled size in bytes of an array of named exports from 'ably/modular' +async function getImportSizes(exports: string[]): Promise { + const bundleInfo = getBundleInfo(exports); + + return { + rawByteSize: bundleInfo.byteSize, + // I’m trusting that the default settings of the `gzip` function (e.g. compression level) are somewhat representative of how gzip compression is normally used when serving files in the real world + gzipEncodedByteSize: (await promisify(gzip)(bundleInfo.code)).byteLength, + }; +} + +async function runSourceMapExplorer(bundleInfo: BundleInfo) { + return explore({ + code: Buffer.from(bundleInfo.code), + map: Buffer.from(bundleInfo.sourceMap), + }); +} + +async function calculateAndCheckExportSizes(): Promise { + const output: Output = { tableRows: [], errors: [] }; + + for (const baseClient of baseClientNames) { + const baseClientSizes = await getImportSizes([baseClient]); + + // First output the size of the base client + output.tableRows.push({ description: baseClient, sizes: baseClientSizes }); + + // Then output the size of each export together with the base client + for (const exportName of [...pluginNames, ...functions.map((functionData) => functionData.name)]) { + const sizes = await getImportSizes([baseClient, exportName]); + output.tableRows.push({ description: `${baseClient} + ${exportName}`, sizes }); + + if (!(baseClientSizes.rawByteSize < sizes.rawByteSize) && !(baseClient === 'BaseRest' && exportName === 'Rest')) { + // Emit an error if adding the export does not increase the bundle size + // (this means that the export is not being tree-shaken correctly). + output.errors.push(new Error(`Adding ${exportName} to ${baseClient} does not increase the bundle size.`)); + } + } + } + + return output; +} + +async function calculateAndCheckFunctionSizes(): Promise { + const output: Output = { tableRows: [], errors: [] }; + + for (const functionData of functions) { + const { name: functionName, transitiveImports } = functionData; + + // First output the size of the function + const standaloneSizes = await getImportSizes([functionName]); + output.tableRows.push({ description: functionName, sizes: standaloneSizes }); + + // Then output the size of the function together with the plugin we expect + // it to transitively import + if (transitiveImports.length > 0) { + const withTransitiveImportsSizes = await getImportSizes([functionName, ...transitiveImports]); + output.tableRows.push({ + description: `${functionName} + ${transitiveImports.join(' + ')}`, + sizes: withTransitiveImportsSizes, + }); + + if (withTransitiveImportsSizes.rawByteSize > standaloneSizes.rawByteSize) { + // Emit an error if the bundle size is increased by adding the plugins + // that we expect this function to have transitively imported anyway. + // This seemed like a useful sense check, but it might need tweaking in + // the future if we make future optimisations that mean that the + // standalone functions don’t necessarily import the whole plugin. + output.errors.push( + new Error( + `Adding ${transitiveImports.join(' + ')} to ${functionName} unexpectedly increases the bundle size.`, + ), + ); + } + } + } + + return output; +} + +async function calculateAndCheckMinimalUsefulRealtimeBundleSize(): Promise { + const output: Output = { tableRows: [], errors: [] }; + + const exports = ['BaseRealtime', 'FetchRequest', 'WebSocketTransport']; + const sizes = await getImportSizes(exports); + + output.tableRows.push({ description: `Minimal useful Realtime (${exports.join(' + ')})`, sizes }); + + if (sizes.rawByteSize > minimalUsefulRealtimeBundleSizeThresholdsKiB.raw * 1024) { + output.errors.push( + new Error( + `Minimal raw useful Realtime bundle is ${formatBytes( + sizes.rawByteSize, + )}, which is greater than allowed maximum of ${minimalUsefulRealtimeBundleSizeThresholdsKiB.raw} KiB.`, + ), + ); + } + + if (sizes.gzipEncodedByteSize > minimalUsefulRealtimeBundleSizeThresholdsKiB.gzip * 1024) { + output.errors.push( + new Error( + `Minimal gzipped useful Realtime bundle is ${formatBytes( + sizes.gzipEncodedByteSize, + )}, which is greater than allowed maximum of ${minimalUsefulRealtimeBundleSizeThresholdsKiB.gzip} KiB.`, + ), + ); + } + + return output; +} + +async function calculateAllExportsBundleSize(): Promise { + const exports = [...baseClientNames, ...pluginNames, ...functions.map((val) => val.name)]; + const sizes = await getImportSizes(exports); + + return { tableRows: [{ description: 'All exports', sizes }], errors: [] }; +} + +// Performs a sense check that there are no unexpected files making a large contribution to the BaseRealtime bundle size. +async function checkBaseRealtimeFiles() { + const baseRealtimeBundleInfo = getBundleInfo(['BaseRealtime']); + const exploreResult = await runSourceMapExplorer(baseRealtimeBundleInfo); + + const files = exploreResult.bundles[0].files; + delete files['[sourceMappingURL]']; + delete files['[unmapped]']; + delete files['[EOLs]']; + + const thresholdBytes = 100; + const filesAboveThreshold = Object.entries(files).filter((file) => file[1].size >= thresholdBytes); + + // These are the files that are allowed to contribute >= `threshold` bytes to the BaseRealtime bundle. + // + // The threshold is chosen pretty arbitrarily. There are some files (e.g. presencemessage.ts) whose bulk should not be included in the BaseRealtime bundle, but which make a small contribution to the bundle (probably because we make use of one exported constant or something; I haven’t looked into it). + const allowedFiles = new Set([ + 'src/common/constants/HttpStatusCodes.ts', + 'src/common/constants/XHRStates.ts', + 'src/common/lib/client/auth.ts', + 'src/common/lib/client/baseclient.ts', + 'src/common/lib/client/baserealtime.ts', + 'src/common/lib/client/channelstatechange.ts', + 'src/common/lib/client/connection.ts', + 'src/common/lib/client/connectionstatechange.ts', + 'src/common/lib/client/realtimechannel.ts', + 'src/common/lib/transport/connectionerrors.ts', + 'src/common/lib/transport/connectionmanager.ts', + 'src/common/lib/transport/messagequeue.ts', + 'src/common/lib/transport/protocol.ts', + 'src/common/lib/transport/transport.ts', + 'src/common/lib/types/errorinfo.ts', + 'src/common/lib/types/message.ts', + 'src/common/lib/types/protocolmessage.ts', + 'src/common/lib/types/pushchannelsubscription.ts', // TODO why? https://github.com/ably/ably-js/issues/1506 + 'src/common/lib/util/defaults.ts', + 'src/common/lib/util/eventemitter.ts', + 'src/common/lib/util/logger.ts', + 'src/common/lib/util/multicaster.ts', + 'src/common/lib/util/utils.ts', + 'src/common/types/http.ts', + 'src/platform/web/config.ts', + 'src/platform/web/lib/http/http.ts', + 'src/platform/web/lib/util/bufferutils.ts', + 'src/platform/web/lib/util/defaults.ts', + 'src/platform/web/lib/util/hmac-sha256.ts', + 'src/platform/web/lib/util/webstorage.ts', + 'src/platform/web/modular.ts', + ]); + + const errors: Error[] = []; + + // Check that no files other than those allowed above make a large contribution to the bundle size + for (const file of filesAboveThreshold) { + if (!allowedFiles.has(file[0])) { + errors.push( + new Error( + `Unexpected file ${file[0]}, contributes ${file[1].size}B to BaseRealtime, more than allowed ${thresholdBytes}B`, + ), + ); + } + } + + // Check that there are no stale entries in the allowed list + for (const allowedFile of Array.from(allowedFiles)) { + const file = files[allowedFile]; + + if (file) { + if (file.size < thresholdBytes) { + errors.push( + new Error( + `File ${allowedFile} contributes ${file.size}B, which is less than the expected minimum of ${thresholdBytes}B. Remove it from the \`allowedFiles\` list.`, + ), + ); + } + } else { + errors.push(new Error(`File ${allowedFile} is referenced in \`allowedFiles\` but does not contribute to bundle`)); + } + } + + return errors; +} + +(async function run() { + const output = ( + await Promise.all([ + calculateAndCheckMinimalUsefulRealtimeBundleSize(), + calculateAllExportsBundleSize(), + calculateAndCheckExportSizes(), + calculateAndCheckFunctionSizes(), + ]) + ).reduce((accum, current) => ({ + tableRows: [...accum.tableRows, ...current.tableRows], + errors: [...accum.errors, ...current.errors], + })); + + output.errors.push(...(await checkBaseRealtimeFiles())); + + const table = new Table({ + style: { head: ['green'] }, + head: ['Exports', 'Size (raw, KiB)', 'Size (gzipped, KiB)'], + rows: output.tableRows.map((row) => [ + row.description, + formatBytes(row.sizes.rawByteSize), + formatBytes(row.sizes.gzipEncodedByteSize), + ]), + }); + console.log(table.toString()); + + if (output.errors.length > 0) { + for (const error of output.errors) { + console.log(error.message); + } + process.exit(1); + } +})(); diff --git a/src/common/constants/TransportName.ts b/src/common/constants/TransportName.ts new file mode 100644 index 0000000000..eda3ba2789 --- /dev/null +++ b/src/common/constants/TransportName.ts @@ -0,0 +1,9 @@ +export namespace TransportNames { + export const WebSocket = 'web_socket' as const; + export const Comet = 'comet' as const; + export const XhrPolling = 'xhr_polling' as const; +} + +type TransportName = typeof TransportNames.WebSocket | typeof TransportNames.Comet | typeof TransportNames.XhrPolling; + +export default TransportName; diff --git a/src/common/constants/TransportNames.ts b/src/common/constants/TransportNames.ts deleted file mode 100644 index c3e0ada287..0000000000 --- a/src/common/constants/TransportNames.ts +++ /dev/null @@ -1,9 +0,0 @@ -enum TransportNames { - WebSocket = 'web_socket', - Comet = 'comet', - XhrStreaming = 'xhr_streaming', - XhrPolling = 'xhr_polling', - JsonP = 'jsonp', -} - -export default TransportNames; diff --git a/src/common/lib/client/auth.ts b/src/common/lib/client/auth.ts index feef6678b5..80b4ed2533 100644 --- a/src/common/lib/client/auth.ts +++ b/src/common/lib/client/auth.ts @@ -1,36 +1,31 @@ import Logger from '../util/logger'; import * as Utils from '../util/utils'; -import Multicaster from '../util/multicaster'; +import Multicaster, { MulticasterInstance } from '../util/multicaster'; import ErrorInfo, { IPartialErrorInfo } from '../types/errorinfo'; -import HmacSHA256 from 'crypto-js/build/hmac-sha256'; -import { stringify as stringifyBase64 } from 'crypto-js/build/enc-base64'; -import { createHmac } from 'crypto'; -import { ErrnoException, RequestCallback, RequestParams } from '../../types/http'; +import { RequestResultError, RequestParams, RequestResult } from '../../types/http'; import * as API from '../../../../ably'; -import { StandardCallback } from '../../types/utils'; -import Rest from './rest'; -import Realtime from './realtime'; +import BaseClient from './baseclient'; +import BaseRealtime from './baserealtime'; import ClientOptions from '../../types/ClientOptions'; import HttpMethods from '../../constants/HttpMethods'; import HttpStatusCodes from 'common/constants/HttpStatusCodes'; -import Platform from '../../platform'; -import Resource from './resource'; - -type BatchResult = API.Types.BatchResult; -type TokenRevocationTargetSpecifier = API.Types.TokenRevocationTargetSpecifier; -type TokenRevocationOptions = API.Types.TokenRevocationOptions; -type TokenRevocationSuccessResult = API.Types.TokenRevocationSuccessResult; -type TokenRevocationFailureResult = API.Types.TokenRevocationFailureResult; +import Platform, { Bufferlike } from '../../platform'; +import Defaults from '../util/defaults'; + +type BatchResult = API.BatchResult; +type TokenRevocationTargetSpecifier = API.TokenRevocationTargetSpecifier; +type TokenRevocationOptions = API.TokenRevocationOptions; +type TokenRevocationSuccessResult = API.TokenRevocationSuccessResult; +type TokenRevocationFailureResult = API.TokenRevocationFailureResult; type TokenRevocationResult = BatchResult; const MAX_TOKEN_LENGTH = Math.pow(2, 17); -function noop() {} function random() { return ('000000' + Math.floor(Math.random() * 1e16)).slice(-16); } -function isRealtime(client: Rest | Realtime): client is Realtime { - return !!(client as Realtime).connection; +function isRealtime(client: BaseClient): client is BaseRealtime { + return !!(client as BaseRealtime).connection; } /* A client auth callback may give errors in any number of formats; normalise to an ErrorInfo or PartialErrorInfo */ @@ -52,12 +47,14 @@ function normaliseAuthcallbackError(err: any) { } let hmac = (text: string, key: string): string => { - if (Platform.Config.createHmac) { - const inst = (Platform.Config.createHmac as typeof createHmac)('SHA256', key); - inst.update(text); - return inst.digest('base64'); - } - return stringifyBase64(HmacSHA256(text, key)); + const bufferUtils = Platform.BufferUtils; + + const textBuffer = bufferUtils.utf8Encode(text); + const keyBuffer = bufferUtils.utf8Encode(key); + + const digest = bufferUtils.hmacSha256(textBuffer, keyBuffer); + + return bufferUtils.base64Encode(digest); }; function c14n(capability?: string | Record>) { @@ -75,7 +72,7 @@ function c14n(capability?: string | Record>) { return JSON.stringify(c14nCapability); } -function logAndValidateTokenAuthMethod(authOptions: API.Types.AuthOptions) { +function logAndValidateTokenAuthMethod(authOptions: AuthOptions) { if (authOptions.authCallback) { Logger.logAction(Logger.LOG_MINOR, 'Auth()', 'using token auth with authCallback'); } else if (authOptions.authUrl) { @@ -96,7 +93,7 @@ function basicAuthForced(options: ClientOptions) { } /* RSA4 */ -function useTokenAuth(options: ClientOptions) { +export function useTokenAuth(options: ClientOptions) { return ( options.useTokenAuth || (!basicAuthForced(options) && (options.authCallback || options.authUrl || options.token || options.tokenDetails)) @@ -113,20 +110,30 @@ function getTokenRequestId() { return trId++; } +/** + * Auth options used only for testing. + */ +type PrivateAuthOptions = { + requestHeaders?: Record; + suppressMaxLengthCheck?: boolean; +}; + +type AuthOptions = API.AuthOptions & PrivateAuthOptions; + class Auth { - client: Rest | Realtime; - tokenParams: API.Types.TokenParams; + client: BaseClient; + tokenParams: API.TokenParams; currentTokenRequestId: number | null; - waitingForTokenRequest: ReturnType | null; + waitingForTokenRequest: MulticasterInstance | null; // This initialization is always overwritten and only used to prevent a TypeScript compiler error - authOptions: API.Types.AuthOptions = {} as API.Types.AuthOptions; - tokenDetails?: API.Types.TokenDetails | null; + authOptions: AuthOptions = {} as AuthOptions; + tokenDetails?: API.TokenDetails | null; method?: string; key?: string; basicKey?: string; clientId?: string | null; - constructor(client: Rest | Realtime, options: ClientOptions) { + constructor(client: BaseClient, options: ClientOptions) { this.client = client; this.tokenParams = options.defaultTokenParams || {}; /* The id of the current token request if one is in progress, else null */ @@ -135,19 +142,14 @@ class Auth { if (useTokenAuth(options)) { /* Token auth */ - if (options.key && !hmac) { - const msg = 'client-side token request signing not supported'; - Logger.logAction(Logger.LOG_ERROR, 'Auth()', msg); - throw new Error(msg); - } if (noWayToRenew(options)) { Logger.logAction( Logger.LOG_ERROR, 'Auth()', - 'Warning: library initialized with a token literal without any way to renew the token when it expires (no authUrl, authCallback, or key). See https://help.ably.io/error/40171 for help' + 'Warning: library initialized with a token literal without any way to renew the token when it expires (no authUrl, authCallback, or key). See https://help.ably.io/error/40171 for help', ); } - this._saveTokenOptions(options.defaultTokenParams as API.Types.TokenDetails, options); + this._saveTokenOptions(options.defaultTokenParams as API.TokenDetails, options); logAndValidateTokenAuthMethod(this.authOptions); } else { /* Basic auth */ @@ -166,10 +168,8 @@ class Auth { * Instructs the library to get a token immediately and ensures Token Auth * is used for all future requests, storing the tokenParams and authOptions * given as the new defaults for subsequent use. - * - * @param callback (err, tokenDetails) */ - authorize(callback: Function): void; + async authorize(): Promise; /** * Instructs the library to get a token immediately and ensures Token Auth @@ -192,10 +192,8 @@ class Auth { * * - timestamp: (optional) the time in ms since the epoch. If none is specified, * the system will be queried for a time value to use. - * - * @param callback (err, tokenDetails) */ - authorize(tokenParams: API.Types.TokenParams | null, callback: Function): void; + async authorize(tokenParams: API.TokenParams | null): Promise; /** * Instructs the library to get a token immediately and ensures Token Auth @@ -248,97 +246,55 @@ class Auth { * * - requestHeaders (optional, unsupported, for testing only) extra headers to add to the * requestToken request - * - * @param callback (err, tokenDetails) */ - authorize( - tokenParams: API.Types.TokenParams | null, - authOptions: API.Types.AuthOptions | null, - callback: Function - ): void; - - authorize( - tokenParams: Record | Function | null, - authOptions?: API.Types.AuthOptions | null | Function, - callback?: Function - ): void | Promise { - let _authOptions: API.Types.AuthOptions | null; - /* shuffle and normalise arguments as necessary */ - if (typeof tokenParams == 'function' && !callback) { - callback = tokenParams; - _authOptions = tokenParams = null; - } else if (typeof authOptions == 'function' && !callback) { - callback = authOptions; - _authOptions = null; - } else { - _authOptions = authOptions as API.Types.AuthOptions; - } - if (!callback) { - if (this.client.options.promises) { - return Utils.promisify(this, 'authorize', arguments); - } - } + async authorize(tokenParams: API.TokenParams | null, authOptions: AuthOptions | null): Promise; + async authorize( + tokenParams?: Record | null, + authOptions?: AuthOptions | null, + ): Promise { /* RSA10a: authorize() call implies token auth. If a key is passed it, we * just check if it doesn't clash and assume we're generating a token from it */ - if (_authOptions && _authOptions.key && this.authOptions.key !== _authOptions.key) { + if (authOptions && authOptions.key && this.authOptions.key !== authOptions.key) { throw new ErrorInfo('Unable to update auth options with incompatible key', 40102, 401); } - if (_authOptions && 'force' in _authOptions) { - Logger.deprecated( - 'The `force` auth option', - 'If you’re using this option to force `authorize()` to fetch a new token even if the current token has not expired, this is no longer necessary, as `authorize()` now always fetches a new token. Update your code to no longer pass the `force` auth option. Note that, in general, passing an auth options argument to `authorize()` will overwrite the library’s stored auth options, which may not be what you want. The library currently contains a special case behavior where passing an auth options object which only contains `{ force: true }` will _not_ overwrite the stored options. This special case behavior will be removed alongside support for the `force` option, so if you’re currently passing `authorize()` an auth options object which only contains `{ force: true }`, you should stop passing it an auth options object entirely.' - ); - /* Emulate the old behaviour: if 'force' was the only member of authOptions, - * set it to null so it doesn't overwrite stored. TODO: remove in version 1.0 */ - if (Utils.isOnlyPropIn(_authOptions, 'force')) { - _authOptions = null; + try { + let tokenDetails = await this._forceNewToken(tokenParams ?? null, authOptions ?? null); + + /* RTC8 + * - When authorize called by an end user and have a realtime connection, + * don't call back till new token has taken effect. + * - Use this.client.connection as a proxy for (this.client instanceof BaseRealtime), + * which doesn't work in node as BaseRealtime isn't part of the vm context for Rest clients */ + if (isRealtime(this.client)) { + return new Promise((resolve, reject) => { + (this.client as BaseRealtime).connection.connectionManager.onAuthUpdated( + tokenDetails, + (err: unknown, tokenDetails?: API.TokenDetails) => (err ? reject(err) : resolve(tokenDetails!)), + ); + }); + } else { + return tokenDetails; } - } - - this._forceNewToken( - tokenParams as API.Types.TokenParams, - _authOptions, - (err: ErrorInfo, tokenDetails: API.Types.TokenDetails) => { - if (err) { - if ((this.client as Realtime).connection && err.statusCode === HttpStatusCodes.Forbidden) { - /* Per RSA4d & RSA4d1, if the auth server explicitly repudiates our right to - * stay connecticed by returning a 403, we actively disconnect the connection - * even though we may well still have time left in the old token. */ - (this.client as Realtime).connection.connectionManager.actOnErrorFromAuthorize(err); - } - callback?.(err); - return; - } - - /* RTC8 - * - When authorize called by an end user and have a realtime connection, - * don't call back till new token has taken effect. - * - Use this.client.connection as a proxy for (this.client instanceof Realtime), - * which doesn't work in node as Realtime isn't part of the vm context for Rest clients */ - if (isRealtime(this.client)) { - this.client.connection.connectionManager.onAuthUpdated(tokenDetails, callback || noop); - } else { - callback?.(null, tokenDetails); - } + } catch (err) { + if ((this.client as BaseRealtime).connection && (err as ErrorInfo).statusCode === HttpStatusCodes.Forbidden) { + /* Per RSA4d & RSA4d1, if the auth server explicitly repudiates our right to + * stay connecticed by returning a 403, we actively disconnect the connection + * even though we may well still have time left in the old token. */ + (this.client as BaseRealtime).connection.connectionManager.actOnErrorFromAuthorize(err as ErrorInfo); } - ); - } - - authorise(tokenParams: API.Types.TokenParams | null, authOptions: API.Types.AuthOptions, callback: Function): void { - Logger.renamedMethod('Auth', 'authorise', 'authorize'); - this.authorize(tokenParams, authOptions, callback); + throw err; + } } /* For internal use, eg by connectionManager - useful when want to call back * as soon as we have the new token, rather than waiting for it to take * effect on the connection as #authorize does */ - _forceNewToken( - tokenParams: API.Types.TokenParams | null, - authOptions: API.Types.AuthOptions | null, - callback: Function - ) { + async _forceNewToken( + tokenParams: API.TokenParams | null, + authOptions: AuthOptions | null, + ): Promise { /* get rid of current token even if still valid */ this.tokenDetails = null; @@ -349,19 +305,19 @@ class Auth { logAndValidateTokenAuthMethod(this.authOptions); - this._ensureValidAuthCredentials(true, (err: ErrorInfo | null, tokenDetails?: API.Types.TokenDetails) => { + try { + return this._ensureValidAuthCredentials(true); + } finally { /* RSA10g */ delete this.tokenParams.timestamp; delete this.authOptions.queryTime; - callback(err, tokenDetails); - }); + } } /** * Request an access token - * @param callback (err, tokenDetails) */ - requestToken(callback: StandardCallback): void; + async requestToken(): Promise; /** * Request an access token @@ -380,10 +336,8 @@ class Auth { * * - timestamp: (optional) the time in ms since the epoch. If none is specified, * the system will be queried for a time value to use. - * - * @param callback (err, tokenDetails) */ - requestToken(tokenParams: API.Types.TokenParams | null, callback: StandardCallback): void; + async requestToken(tokenParams: API.TokenParams | null): Promise; /** * Request an access token @@ -428,91 +382,88 @@ class Auth { * * - requestHeaders (optional, unsupported, for testing only) extra headers to add to the * requestToken request - * - * @param callback (err, tokenDetails) */ - requestToken( - tokenParams: API.Types.TokenParams | null, - authOptions: API.Types.AuthOptions, - callback: StandardCallback - ): void; - - requestToken( - tokenParams: API.Types.TokenParams | StandardCallback | null, - authOptions?: any | StandardCallback, - callback?: StandardCallback - ): void | Promise { - /* shuffle and normalise arguments as necessary */ - if (typeof tokenParams == 'function' && !callback) { - callback = tokenParams; - authOptions = tokenParams = null; - } else if (typeof authOptions == 'function' && !callback) { - callback = authOptions; - authOptions = null; - } - if (!callback && this.client.options.promises) { - return Utils.promisify(this, 'requestToken', arguments); - } + async requestToken(tokenParams: API.TokenParams | null, authOptions: AuthOptions): Promise; + async requestToken(tokenParams?: API.TokenParams | null, authOptions?: AuthOptions): Promise { /* RSA8e: if authOptions passed in, they're used instead of stored, don't merge them */ - authOptions = authOptions || this.authOptions; - tokenParams = tokenParams || Utils.copy(this.tokenParams); - const _callback = callback || noop; + const resolvedAuthOptions = authOptions || this.authOptions; + const resolvedTokenParams = tokenParams || Utils.copy(this.tokenParams); /* first set up whatever callback will be used to get signed * token requests */ - let tokenRequestCallback, + let tokenRequestCallback: ( + data: API.TokenParams, + callback: ( + error: API.ErrorInfo | RequestResultError | string | null, + tokenRequestOrDetails: API.TokenDetails | API.TokenRequest | string | null, + contentType?: string, + ) => void, + ) => void, client = this.client; - if (authOptions.authCallback) { + if (resolvedAuthOptions.authCallback) { Logger.logAction(Logger.LOG_MINOR, 'Auth.requestToken()', 'using token auth with authCallback'); - tokenRequestCallback = authOptions.authCallback; - } else if (authOptions.authUrl) { + tokenRequestCallback = resolvedAuthOptions.authCallback; + } else if (resolvedAuthOptions.authUrl) { Logger.logAction(Logger.LOG_MINOR, 'Auth.requestToken()', 'using token auth with authUrl'); - tokenRequestCallback = (params: Record, cb: Function) => { - const authHeaders = Utils.mixin({ accept: 'application/json, text/plain' }, authOptions.authHeaders) as Record< - string, - string - >; - const usePost = authOptions.authMethod && authOptions.authMethod.toLowerCase() === 'post'; + tokenRequestCallback = (params, cb) => { + const authHeaders = Utils.mixin( + { accept: 'application/json, text/plain' }, + resolvedAuthOptions.authHeaders, + ) as Record; + const usePost = resolvedAuthOptions.authMethod && resolvedAuthOptions.authMethod.toLowerCase() === 'post'; let providedQsParams; /* Combine authParams with any qs params given in the authUrl */ - const queryIdx = authOptions.authUrl.indexOf('?'); + const queryIdx = resolvedAuthOptions.authUrl!.indexOf('?'); if (queryIdx > -1) { - providedQsParams = Utils.parseQueryString(authOptions.authUrl.slice(queryIdx)); - authOptions.authUrl = authOptions.authUrl.slice(0, queryIdx); + providedQsParams = Utils.parseQueryString(resolvedAuthOptions.authUrl!.slice(queryIdx)); + resolvedAuthOptions.authUrl = resolvedAuthOptions.authUrl!.slice(0, queryIdx); if (!usePost) { /* In case of conflict, authParams take precedence over qs params in the authUrl */ - authOptions.authParams = Utils.mixin(providedQsParams, authOptions.authParams); + resolvedAuthOptions.authParams = Utils.mixin( + providedQsParams, + resolvedAuthOptions.authParams, + ) as typeof resolvedAuthOptions.authParams; } } /* RSA8c2 */ - const authParams = Utils.mixin({}, authOptions.authParams || {}, params) as RequestParams; - const authUrlRequestCallback = function ( - err: ErrorInfo, - body: string, - headers: Record, - unpacked: any - ) { - let contentType; - if (err) { + const authParams = Utils.mixin({}, resolvedAuthOptions.authParams || {}, params) as RequestParams; + const authUrlRequestCallback = function (result: RequestResult) { + let body = (result.body ?? null) as string | Bufferlike | API.TokenDetails | API.TokenRequest | null; + + let contentType: string | null = null; + if (result.error) { Logger.logAction( Logger.LOG_MICRO, 'Auth.requestToken().tokenRequestCallback', - 'Received Error: ' + Utils.inspectError(err) + 'Received Error: ' + Utils.inspectError(result.error), ); } else { - contentType = headers['content-type']; + const contentTypeHeaderOrHeaders = result.headers!['content-type'] ?? null; + if (Array.isArray(contentTypeHeaderOrHeaders)) { + // Combine multiple header values into a comma-separated list per https://datatracker.ietf.org/doc/html/rfc9110#section-5.2; see https://github.com/ably/ably-js/issues/1616 for doing this consistently across the codebase. + contentType = contentTypeHeaderOrHeaders.join(', '); + } else { + contentType = contentTypeHeaderOrHeaders; + } Logger.logAction( Logger.LOG_MICRO, 'Auth.requestToken().tokenRequestCallback', - 'Received; content-type: ' + contentType + '; body: ' + Utils.inspectBody(body) + 'Received; content-type: ' + contentType + '; body: ' + Utils.inspectBody(body), ); } - if (err || unpacked) return cb(err, body); + if (result.error) { + cb(result.error, null); + return; + } + if (result.unpacked) { + cb(null, body as Exclude); + return; + } if (Platform.BufferUtils.isBuffer(body)) body = body.toString(); if (!contentType) { - cb(new ErrorInfo('authUrl response is missing a content-type header', 40170, 401)); + cb(new ErrorInfo('authUrl response is missing a content-type header', 40170, 401), null); return; } const json = contentType.indexOf('application/json') > -1, @@ -524,67 +475,77 @@ class Auth { contentType + ', should be either text/plain, application/jwt or application/json', 40170, - 401 - ) + 401, + ), + null, ); return; } if (json) { - if (body.length > MAX_TOKEN_LENGTH) { - cb(new ErrorInfo('authUrl response exceeded max permitted length', 40170, 401)); + if ((body as string).length > MAX_TOKEN_LENGTH) { + cb(new ErrorInfo('authUrl response exceeded max permitted length', 40170, 401), null); return; } try { - body = JSON.parse(body); + body = JSON.parse(body as string); } catch (e) { cb( - new ErrorInfo('Unexpected error processing authURL response; err = ' + (e as Error).message, 40170, 401) + new ErrorInfo( + 'Unexpected error processing authURL response; err = ' + (e as Error).message, + 40170, + 401, + ), + null, ); return; } } - cb(null, body, contentType); + cb(null, body as Exclude, contentType); }; Logger.logAction( Logger.LOG_MICRO, 'Auth.requestToken().tokenRequestCallback', 'Requesting token from ' + - authOptions.authUrl + + resolvedAuthOptions.authUrl + '; Params: ' + JSON.stringify(authParams) + '; method: ' + - (usePost ? 'POST' : 'GET') + (usePost ? 'POST' : 'GET'), ); if (usePost) { /* send body form-encoded */ const headers = authHeaders || {}; headers['content-type'] = 'application/x-www-form-urlencoded'; const body = Utils.toQueryString(authParams).slice(1); /* slice is to remove the initial '?' */ - this.client.http.doUri( - HttpMethods.Post, - client, - authOptions.authUrl, - headers, - body, - providedQsParams as Record, - authUrlRequestCallback as RequestCallback + Utils.whenPromiseSettles( + this.client.http.doUri( + HttpMethods.Post, + resolvedAuthOptions.authUrl!, + headers, + body, + providedQsParams as Record, + ), + (err: any, result) => + err + ? authUrlRequestCallback(err) // doUri isn’t meant to throw an error, but handle any just in case + : authUrlRequestCallback(result!), ); } else { - this.client.http.doUri( - HttpMethods.Get, - client, - authOptions.authUrl, - authHeaders || {}, - null, - authParams, - authUrlRequestCallback as RequestCallback + Utils.whenPromiseSettles( + this.client.http.doUri(HttpMethods.Get, resolvedAuthOptions.authUrl!, authHeaders || {}, null, authParams), + (err: any, result) => + err + ? authUrlRequestCallback(err) // doUri isn’t meant to throw an error, but handle any just in case + : authUrlRequestCallback(result!), ); } }; - } else if (authOptions.key) { + } else if (resolvedAuthOptions.key) { Logger.logAction(Logger.LOG_MINOR, 'Auth.requestToken()', 'using token auth with client-side signing'); - tokenRequestCallback = (params: any, cb: Function) => { - this.createTokenRequest(params, authOptions, cb); + tokenRequestCallback = (params, cb) => { + Utils.whenPromiseSettles(this.createTokenRequest(params, resolvedAuthOptions), (err, result) => + cb(err as string | ErrorInfo | null, result ?? null), + ); }; } else { const msg = @@ -592,145 +553,144 @@ class Auth { Logger.logAction( Logger.LOG_ERROR, 'Auth()', - 'library initialized with a token literal without any way to renew the token when it expires (no authUrl, authCallback, or key). See https://help.ably.io/error/40171 for help' + 'library initialized with a token literal without any way to renew the token when it expires (no authUrl, authCallback, or key). See https://help.ably.io/error/40171 for help', ); - _callback(new ErrorInfo(msg, 40171, 403)); - return; + throw new ErrorInfo(msg, 40171, 403); } /* normalise token params */ - if ('capability' in (tokenParams as Record)) - (tokenParams as Record).capability = c14n((tokenParams as Record).capability); + if ('capability' in (resolvedTokenParams as Record)) + (resolvedTokenParams as Record).capability = c14n( + (resolvedTokenParams as Record).capability, + ); - const tokenRequest = (signedTokenParams: Record, tokenCb: Function) => { + const tokenRequest = ( + signedTokenParams: Record, + tokenCb: (err: RequestResultError | null, tokenResponse?: API.TokenDetails | string, unpacked?: boolean) => void, + ) => { const keyName = signedTokenParams.keyName, path = '/keys/' + keyName + '/requestToken', tokenUri = function (host: string) { return client.baseUri(host) + path; }; - const requestHeaders = Utils.defaultPostHeaders(this.client.options); - if (authOptions.requestHeaders) Utils.mixin(requestHeaders, authOptions.requestHeaders); + const requestHeaders = Defaults.defaultPostHeaders(this.client.options); + if (resolvedAuthOptions.requestHeaders) Utils.mixin(requestHeaders, resolvedAuthOptions.requestHeaders); Logger.logAction( Logger.LOG_MICRO, 'Auth.requestToken().requestToken', - 'Sending POST to ' + path + '; Token params: ' + JSON.stringify(signedTokenParams) + 'Sending POST to ' + path + '; Token params: ' + JSON.stringify(signedTokenParams), ); - this.client.http.do( - HttpMethods.Post, - client, - tokenUri, - requestHeaders, - JSON.stringify(signedTokenParams), - null, - tokenCb as RequestCallback + Utils.whenPromiseSettles( + this.client.http.do(HttpMethods.Post, tokenUri, requestHeaders, JSON.stringify(signedTokenParams), null), + (err: any, result) => + err + ? tokenCb(err) // doUri isn’t meant to throw an error, but handle any just in case + : tokenCb(result!.error, result!.body as API.TokenDetails | string | undefined, result!.unpacked), ); }; - let tokenRequestCallbackTimeoutExpired = false, - timeoutLength = this.client.options.timeouts.realtimeRequestTimeout, - tokenRequestCallbackTimeout = setTimeout(function () { - tokenRequestCallbackTimeoutExpired = true; - const msg = 'Token request callback timed out after ' + timeoutLength / 1000 + ' seconds'; - Logger.logAction(Logger.LOG_ERROR, 'Auth.requestToken()', msg); - _callback(new ErrorInfo(msg, 40170, 401)); - }, timeoutLength); + return new Promise((resolve, reject) => { + let tokenRequestCallbackTimeoutExpired = false, + timeoutLength = this.client.options.timeouts.realtimeRequestTimeout, + tokenRequestCallbackTimeout = setTimeout(function () { + tokenRequestCallbackTimeoutExpired = true; + const msg = 'Token request callback timed out after ' + timeoutLength / 1000 + ' seconds'; + Logger.logAction(Logger.LOG_ERROR, 'Auth.requestToken()', msg); + reject(new ErrorInfo(msg, 40170, 401)); + }, timeoutLength); - tokenRequestCallback(tokenParams, function (err: ErrorInfo, tokenRequestOrDetails: any, contentType: string) { - if (tokenRequestCallbackTimeoutExpired) return; - clearTimeout(tokenRequestCallbackTimeout); + tokenRequestCallback!(resolvedTokenParams, function (err, tokenRequestOrDetails, contentType) { + if (tokenRequestCallbackTimeoutExpired) return; + clearTimeout(tokenRequestCallbackTimeout); - if (err) { - Logger.logAction( - Logger.LOG_ERROR, - 'Auth.requestToken()', - 'token request signing call returned error; err = ' + Utils.inspectError(err) - ); - _callback(normaliseAuthcallbackError(err)); - return; - } - /* the response from the callback might be a token string, a signed request or a token details */ - if (typeof tokenRequestOrDetails === 'string') { - if (tokenRequestOrDetails.length === 0) { - _callback(new ErrorInfo('Token string is empty', 40170, 401)); - } else if (tokenRequestOrDetails.length > MAX_TOKEN_LENGTH) { - _callback( - new ErrorInfo( - 'Token string exceeded max permitted length (was ' + tokenRequestOrDetails.length + ' bytes)', - 40170, - 401 - ) + if (err) { + Logger.logAction( + Logger.LOG_ERROR, + 'Auth.requestToken()', + 'token request signing call returned error; err = ' + Utils.inspectError(err), ); - } else if (tokenRequestOrDetails === 'undefined' || tokenRequestOrDetails === 'null') { - /* common failure mode with poorly-implemented authCallbacks */ - _callback(new ErrorInfo('Token string was literal null/undefined', 40170, 401)); - } else if (tokenRequestOrDetails[0] === '{' && !(contentType && contentType.indexOf('application/jwt') > -1)) { - _callback( + reject(normaliseAuthcallbackError(err)); + return; + } + /* the response from the callback might be a token string, a signed request or a token details */ + if (typeof tokenRequestOrDetails === 'string') { + if (tokenRequestOrDetails.length === 0) { + reject(new ErrorInfo('Token string is empty', 40170, 401)); + } else if (tokenRequestOrDetails.length > MAX_TOKEN_LENGTH) { + reject( + new ErrorInfo( + 'Token string exceeded max permitted length (was ' + tokenRequestOrDetails.length + ' bytes)', + 40170, + 401, + ), + ); + } else if (tokenRequestOrDetails === 'undefined' || tokenRequestOrDetails === 'null') { + /* common failure mode with poorly-implemented authCallbacks */ + reject(new ErrorInfo('Token string was literal null/undefined', 40170, 401)); + } else if ( + tokenRequestOrDetails[0] === '{' && + !(contentType && contentType.indexOf('application/jwt') > -1) + ) { + reject( + new ErrorInfo( + "Token was double-encoded; make sure you're not JSON-encoding an already encoded token request or details", + 40170, + 401, + ), + ); + } else { + resolve({ token: tokenRequestOrDetails } as API.TokenDetails); + } + return; + } + if (typeof tokenRequestOrDetails !== 'object' || tokenRequestOrDetails === null) { + const msg = + 'Expected token request callback to call back with a token string or token request/details object, but got a ' + + typeof tokenRequestOrDetails; + Logger.logAction(Logger.LOG_ERROR, 'Auth.requestToken()', msg); + reject(new ErrorInfo(msg, 40170, 401)); + return; + } + const objectSize = JSON.stringify(tokenRequestOrDetails).length; + if (objectSize > MAX_TOKEN_LENGTH && !resolvedAuthOptions.suppressMaxLengthCheck) { + reject( new ErrorInfo( - "Token was double-encoded; make sure you're not JSON-encoding an already encoded token request or details", + 'Token request/details object exceeded max permitted stringified size (was ' + objectSize + ' bytes)', 40170, - 401 - ) + 401, + ), ); - } else { - _callback(null, { token: tokenRequestOrDetails } as API.Types.TokenDetails); + return; } - return; - } - if (typeof tokenRequestOrDetails !== 'object') { - const msg = - 'Expected token request callback to call back with a token string or token request/details object, but got a ' + - typeof tokenRequestOrDetails; - Logger.logAction(Logger.LOG_ERROR, 'Auth.requestToken()', msg); - _callback(new ErrorInfo(msg, 40170, 401)); - return; - } - const objectSize = JSON.stringify(tokenRequestOrDetails).length; - if (objectSize > MAX_TOKEN_LENGTH && !authOptions.suppressMaxLengthCheck) { - _callback( - new ErrorInfo( - 'Token request/details object exceeded max permitted stringified size (was ' + objectSize + ' bytes)', - 40170, - 401 - ) - ); - return; - } - if ('issued' in tokenRequestOrDetails) { - /* a tokenDetails object */ - _callback(null, tokenRequestOrDetails); - return; - } - if (!('keyName' in tokenRequestOrDetails)) { - const msg = - 'Expected token request callback to call back with a token string, token request object, or token details object'; - Logger.logAction(Logger.LOG_ERROR, 'Auth.requestToken()', msg); - _callback(new ErrorInfo(msg, 40170, 401)); - return; - } - /* it's a token request, so make the request */ - tokenRequest( - tokenRequestOrDetails, - function ( - err?: ErrorInfo | ErrnoException | null, - tokenResponse?: API.Types.TokenDetails | string, - headers?: Record, - unpacked?: boolean - ) { + if ('issued' in tokenRequestOrDetails) { + /* a tokenDetails object */ + resolve(tokenRequestOrDetails); + return; + } + if (!('keyName' in tokenRequestOrDetails)) { + const msg = + 'Expected token request callback to call back with a token string, token request object, or token details object'; + Logger.logAction(Logger.LOG_ERROR, 'Auth.requestToken()', msg); + reject(new ErrorInfo(msg, 40170, 401)); + return; + } + /* it's a token request, so make the request */ + tokenRequest(tokenRequestOrDetails, function (err, tokenResponse, unpacked) { if (err) { Logger.logAction( Logger.LOG_ERROR, 'Auth.requestToken()', - 'token request API call returned error; err = ' + Utils.inspectError(err) + 'token request API call returned error; err = ' + Utils.inspectError(err), ); - _callback(normaliseAuthcallbackError(err)); + reject(normaliseAuthcallbackError(err)); return; } if (!unpacked) tokenResponse = JSON.parse(tokenResponse as string); Logger.logAction(Logger.LOG_MINOR, 'Auth.getToken()', 'token received'); - _callback(null, tokenResponse as API.Types.TokenDetails); - } - ); + resolve(tokenResponse as API.TokenDetails); + }); + }); }); } @@ -766,128 +726,92 @@ class Auth { * * - timestamp: (optional) the time in ms since the epoch. If none is specified, * the system will be queried for a time value to use. - * - * @param callback */ - createTokenRequest(tokenParams: API.Types.TokenParams | null, authOptions: any, callback: Function) { - /* shuffle and normalise arguments as necessary */ - if (typeof tokenParams == 'function' && !callback) { - callback = tokenParams; - authOptions = tokenParams = null; - } else if (typeof authOptions == 'function' && !callback) { - callback = authOptions; - authOptions = null; - } - if (!callback && this.client.options.promises) { - return Utils.promisify(this, 'createTokenRequest', arguments); - } - + async createTokenRequest(tokenParams: API.TokenParams | null, authOptions: any): Promise { /* RSA9h: if authOptions passed in, they're used instead of stored, don't merge them */ authOptions = authOptions || this.authOptions; - tokenParams = tokenParams || Utils.copy(this.tokenParams); + tokenParams = tokenParams || Utils.copy(this.tokenParams); const key = authOptions.key; if (!key) { - callback(new ErrorInfo('No key specified', 40101, 403)); - return; + throw new ErrorInfo('No key specified', 40101, 403); } const keyParts = key.split(':'), keyName = keyParts[0], keySecret = keyParts[1]; if (!keySecret) { - callback(new ErrorInfo('Invalid key specified', 40101, 403)); - return; + throw new ErrorInfo('Invalid key specified', 40101, 403); } if (tokenParams.clientId === '') { - callback(new ErrorInfo('clientId can’t be an empty string', 40012, 400)); - return; + throw new ErrorInfo('clientId can’t be an empty string', 40012, 400); } if ('capability' in tokenParams) { tokenParams.capability = c14n(tokenParams.capability); } - const request = Utils.mixin({ keyName: keyName }, tokenParams), + const request: Partial = Utils.mixin({ keyName: keyName }, tokenParams), clientId = tokenParams.clientId || '', ttl = tokenParams.ttl || '', capability = tokenParams.capability || ''; - ((authoriseCb) => { - if (request.timestamp) { - authoriseCb(); - return; - } - this.getTimestamp(authOptions && authOptions.queryTime, function (err?: ErrorInfo | null, time?: number) { - if (err) { - callback(err); - return; - } - request.timestamp = time; - authoriseCb(); - }); - })(function () { - /* nonce */ - /* NOTE: there is no expectation that the client - * specifies the nonce; this is done by the library - * However, this can be overridden by the client - * simply for testing purposes. */ - const nonce = request.nonce || (request.nonce = random()), - timestamp = request.timestamp; - - const signText = - request.keyName + '\n' + ttl + '\n' + capability + '\n' + clientId + '\n' + timestamp + '\n' + nonce + '\n'; - - /* mac */ - /* NOTE: there is no expectation that the client - * specifies the mac; this is done by the library - * However, this can be overridden by the client - * simply for testing purposes. */ - request.mac = request.mac || hmac(signText, keySecret); - - Logger.logAction(Logger.LOG_MINOR, 'Auth.getTokenRequest()', 'generated signed request'); - callback(null, request); - }); + if (!request.timestamp) { + request.timestamp = await this.getTimestamp(authOptions && authOptions.queryTime); + } + + /* nonce */ + /* NOTE: there is no expectation that the client + * specifies the nonce; this is done by the library + * However, this can be overridden by the client + * simply for testing purposes. */ + const nonce = request.nonce || (request.nonce = random()), + timestamp = request.timestamp; + + const signText = + request.keyName + '\n' + ttl + '\n' + capability + '\n' + clientId + '\n' + timestamp + '\n' + nonce + '\n'; + + /* mac */ + /* NOTE: there is no expectation that the client + * specifies the mac; this is done by the library + * However, this can be overridden by the client + * simply for testing purposes. */ + request.mac = request.mac || hmac(signText, keySecret); + + Logger.logAction(Logger.LOG_MINOR, 'Auth.getTokenRequest()', 'generated signed request'); + + return request as API.TokenRequest; } /** * Get the auth query params to use for a websocket connection, * based on the current auth parameters */ - getAuthParams(callback: Function) { - if (this.method == 'basic') callback(null, { key: this.key }); - else - this._ensureValidAuthCredentials(false, function (err: ErrorInfo | null, tokenDetails?: API.Types.TokenDetails) { - if (err) { - callback(err); - return; - } - if (!tokenDetails) { - throw new Error('Auth.getAuthParams(): _ensureValidAuthCredentials returned no error or tokenDetails'); - } - callback(null, { access_token: tokenDetails.token }); - }); + async getAuthParams(): Promise> { + if (this.method == 'basic') return { key: this.key! }; + else { + let tokenDetails = await this._ensureValidAuthCredentials(false); + if (!tokenDetails) { + throw new Error('Auth.getAuthParams(): _ensureValidAuthCredentials returned no error or tokenDetails'); + } + return { access_token: tokenDetails.token }; + } } /** * Get the authorization header to use for a REST or comet request, * based on the current auth parameters */ - getAuthHeaders(callback: Function) { + async getAuthHeaders(): Promise> { if (this.method == 'basic') { - callback(null, { authorization: 'Basic ' + this.basicKey }); + return { authorization: 'Basic ' + this.basicKey }; } else { - this._ensureValidAuthCredentials(false, function (err: ErrorInfo | null, tokenDetails?: API.Types.TokenDetails) { - if (err) { - callback(err); - return; - } - if (!tokenDetails) { - throw new Error('Auth.getAuthParams(): _ensureValidAuthCredentials returned no error or tokenDetails'); - } - callback(null, { authorization: 'Bearer ' + Utils.toBase64(tokenDetails.token) }); - }); + const tokenDetails = await this._ensureValidAuthCredentials(false); + if (!tokenDetails) { + throw new Error('Auth.getAuthParams(): _ensureValidAuthCredentials returned no error or tokenDetails'); + } + return { authorization: 'Bearer ' + Utils.toBase64(tokenDetails.token) }; } } @@ -897,23 +821,23 @@ class Auth { * The server time offset from the local time is stored so that * only one request to the server to get the time is ever needed */ - getTimestamp(queryTime: boolean, callback: StandardCallback): void { + async getTimestamp(queryTime: boolean): Promise { if (!this.isTimeOffsetSet() && (queryTime || this.authOptions.queryTime)) { - this.client.time(callback); + return this.client.time(); } else { - callback(null, this.getTimestampUsingOffset()); + return this.getTimestampUsingOffset(); } } getTimestampUsingOffset() { - return Utils.now() + (this.client.serverTimeOffset || 0); + return Date.now() + (this.client.serverTimeOffset || 0); } isTimeOffsetSet() { return this.client.serverTimeOffset !== null; } - _saveBasicOptions(authOptions: API.Types.AuthOptions) { + _saveBasicOptions(authOptions: AuthOptions) { this.method = 'basic'; this.key = authOptions.key; this.basicKey = Utils.toBase64(authOptions.key as string); @@ -923,7 +847,7 @@ class Auth { } } - _saveTokenOptions(tokenParams: API.Types.TokenParams | null, authOptions: API.Types.AuthOptions | null) { + _saveTokenOptions(tokenParams: API.TokenParams | null, authOptions: AuthOptions | null) { this.method = 'token'; if (tokenParams) { @@ -939,7 +863,7 @@ class Auth { /* options.token may contain a token string or, for convenience, a TokenDetails */ authOptions.tokenDetails = typeof authOptions.token === 'string' - ? ({ token: authOptions.token } as API.Types.TokenDetails) + ? ({ token: authOptions.token } as API.TokenDetails) : authOptions.token; } @@ -957,62 +881,67 @@ class Auth { /* @param forceSupersede: force a new token request even if there's one in * progress, making all pending callbacks wait for the new one */ - _ensureValidAuthCredentials( - forceSupersede: boolean, - callback: (err: ErrorInfo | null, token?: API.Types.TokenDetails) => void - ) { + async _ensureValidAuthCredentials(forceSupersede: boolean): Promise { const token = this.tokenDetails; if (token) { if (this._tokenClientIdMismatch(token.clientId)) { /* 403 to trigger a permanently failed client - RSA15c */ - callback( - new ErrorInfo( - 'Mismatch between clientId in token (' + token.clientId + ') and current clientId (' + this.clientId + ')', - 40102, - 403 - ) + throw new ErrorInfo( + 'Mismatch between clientId in token (' + token.clientId + ') and current clientId (' + this.clientId + ')', + 40102, + 403, ); - return; } /* RSA4b1 -- if we have a server time offset set already, we can * automatically remove expired tokens. Else just use the cached token. If it is * expired Ably will tell us and we'll discard it then. */ if (!this.isTimeOffsetSet() || !token.expires || token.expires >= this.getTimestampUsingOffset()) { Logger.logAction(Logger.LOG_MINOR, 'Auth.getToken()', 'using cached token; expires = ' + token.expires); - callback(null, token); - return; + return token; } /* expired, so remove and fallthrough to getting a new one */ Logger.logAction(Logger.LOG_MINOR, 'Auth.getToken()', 'deleting expired token'); this.tokenDetails = null; } - (this.waitingForTokenRequest || (this.waitingForTokenRequest = Multicaster.create())).push(callback); + const promise = ( + this.waitingForTokenRequest || (this.waitingForTokenRequest = Multicaster.create()) + ).createPromise(); if (this.currentTokenRequestId !== null && !forceSupersede) { - return; + return promise; } /* Request a new token */ const tokenRequestId = (this.currentTokenRequestId = getTokenRequestId()); - this.requestToken(this.tokenParams, this.authOptions, (err: Function, tokenResponse?: API.Types.TokenDetails) => { - if ((this.currentTokenRequestId as number) > tokenRequestId) { - Logger.logAction( - Logger.LOG_MINOR, - 'Auth._ensureValidAuthCredentials()', - 'Discarding token request response; overtaken by newer one' - ); - return; - } - this.currentTokenRequestId = null; - const callbacks = this.waitingForTokenRequest || noop; - this.waitingForTokenRequest = null; - if (err) { - callbacks(err); - return; - } - callbacks(null, (this.tokenDetails = tokenResponse)); - }); + + let tokenResponse: API.TokenDetails, + caughtError: ErrorInfo | null = null; + try { + tokenResponse = await this.requestToken(this.tokenParams, this.authOptions); + } catch (err) { + caughtError = err as ErrorInfo; + } + + if ((this.currentTokenRequestId as number) > tokenRequestId) { + Logger.logAction( + Logger.LOG_MINOR, + 'Auth._ensureValidAuthCredentials()', + 'Discarding token request response; overtaken by newer one', + ); + return promise; + } + + this.currentTokenRequestId = null; + const multicaster = this.waitingForTokenRequest; + this.waitingForTokenRequest = null; + if (caughtError) { + multicaster?.rejectAll(caughtError); + return promise; + } + multicaster?.resolveAll((this.tokenDetails = tokenResponse!)); + + return promise; } /* User-set: check types, '*' is disallowed, throw any errors */ @@ -1023,7 +952,7 @@ class Auth { throw new ErrorInfo( 'Can’t use "*" as a clientId as that string is reserved. (To change the default token request behaviour to use a wildcard clientId, instantiate the library with {defaultTokenParams: {clientId: "*"}}), or if calling authorize(), pass it in as a tokenParam: authorize({clientId: "*"}, authOptions)', 40012, - 400 + 400, ); } else { const err = this._uncheckedSetClientId(clientId); @@ -1065,71 +994,8 @@ class Auth { revokeTokens( specifiers: TokenRevocationTargetSpecifier[], options?: TokenRevocationOptions, - callback?: API.Types.StandardCallback - ): void; - revokeTokens( - specifiers: TokenRevocationTargetSpecifier[], - options?: TokenRevocationOptions - ): Promise; - revokeTokens( - specifiers: TokenRevocationTargetSpecifier[], - optionsOrCallbackArg?: TokenRevocationOptions | API.Types.StandardCallback, - callbackArg?: API.Types.StandardCallback - ): void | Promise { - if (useTokenAuth(this.client.options)) { - throw new ErrorInfo('Cannot revoke tokens when using token auth', 40162, 401); - } - - const keyName = this.client.options.keyName!; - - let resolvedOptions: TokenRevocationOptions; - - if (typeof optionsOrCallbackArg === 'function') { - callbackArg = optionsOrCallbackArg; - resolvedOptions = {}; - } else { - resolvedOptions = optionsOrCallbackArg ?? {}; - } - - if (callbackArg === undefined) { - if (this.client.options.promises) { - return Utils.promisify(this, 'revokeTokens', [specifiers, resolvedOptions]); - } - callbackArg = noop; - } - - const callback = callbackArg; - - const requestBodyDTO = { - targets: specifiers.map((specifier) => `${specifier.type}:${specifier.value}`), - ...resolvedOptions, - }; - - const format = this.client.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, - headers = Utils.defaultPostHeaders(this.client.options, format); - - if (this.client.options.headers) Utils.mixin(headers, this.client.options.headers); - - const requestBody = Utils.encodeBody(requestBodyDTO, format); - Resource.post( - this.client, - `/keys/${keyName}/revokeTokens`, - requestBody, - headers, - { newBatchResponse: 'true' }, - null, - (err, body, headers, unpacked) => { - if (err) { - // TODO remove this type assertion after fixing https://github.com/ably/ably-js/issues/1405 - callback(err as API.Types.ErrorInfo); - return; - } - - const batchResult = (unpacked ? body : Utils.decodeBody(body, format)) as TokenRevocationResult; - - callback(null, batchResult); - } - ); + ): Promise { + return this.client.rest.revokeTokens(specifiers, options); } } diff --git a/src/common/lib/client/baseclient.ts b/src/common/lib/client/baseclient.ts new file mode 100644 index 0000000000..f4351902a2 --- /dev/null +++ b/src/common/lib/client/baseclient.ts @@ -0,0 +1,159 @@ +import Logger, { LoggerOptions } from '../util/logger'; +import Defaults from '../util/defaults'; +import Auth from './auth'; +import { HttpPaginatedResponse, PaginatedResult } from './paginatedresource'; +import ErrorInfo from '../types/errorinfo'; +import Stats from '../types/stats'; +import { Http, RequestParams } from '../../types/http'; +import ClientOptions, { NormalisedClientOptions } from '../../types/ClientOptions'; +import * as API from '../../../../ably'; + +import Platform from '../../platform'; +import { Rest } from './rest'; +import { IUntypedCryptoStatic } from 'common/types/ICryptoStatic'; +import { throwMissingPluginError } from '../util/utils'; +import { MsgPack } from 'common/types/msgpack'; +import { HTTPRequestImplementations } from 'platform/web/lib/http/http'; +import { FilteredSubscriptions } from './filteredsubscriptions'; + +type BatchResult = API.BatchResult; +type BatchPublishSpec = API.BatchPublishSpec; +type BatchPublishSuccessResult = API.BatchPublishSuccessResult; +type BatchPublishFailureResult = API.BatchPublishFailureResult; +type BatchPublishResult = BatchResult; +type BatchPresenceSuccessResult = API.BatchPresenceSuccessResult; +type BatchPresenceFailureResult = API.BatchPresenceFailureResult; +type BatchPresenceResult = BatchResult; + +/** + `BaseClient` acts as the base class for all of the client classes exported by the SDK. It is an implementation detail and this class is not advertised publicly. + */ +class BaseClient { + options: NormalisedClientOptions; + _currentFallback: null | { + host: string; + validUntil: number; + }; + serverTimeOffset: number | null; + http: Http; + auth: Auth; + + private readonly _rest: Rest | null; + readonly _Crypto: IUntypedCryptoStatic | null; + readonly _MsgPack: MsgPack | null; + // Extra HTTP request implementations available to this client, in addition to those in web’s Http.bundledRequestImplementations + readonly _additionalHTTPRequestImplementations: HTTPRequestImplementations | null; + private readonly __FilteredSubscriptions: typeof FilteredSubscriptions | null; + + constructor(options: ClientOptions) { + this._additionalHTTPRequestImplementations = options.plugins ?? null; + + Logger.setLog(options.logLevel, options.logHandler); + Logger.logAction( + Logger.LOG_MICRO, + 'BaseClient()', + 'initialized with clientOptions ' + Platform.Config.inspect(options), + ); + + this._MsgPack = options.plugins?.MsgPack ?? null; + const normalOptions = (this.options = Defaults.normaliseOptions(options, this._MsgPack)); + + /* process options */ + if (normalOptions.key) { + const keyMatch = normalOptions.key.match(/^([^:\s]+):([^:.\s]+)$/); + if (!keyMatch) { + const msg = 'invalid key parameter'; + Logger.logAction(Logger.LOG_ERROR, 'BaseClient()', msg); + throw new ErrorInfo(msg, 40400, 404); + } + normalOptions.keyName = keyMatch[1]; + normalOptions.keySecret = keyMatch[2]; + } + + if ('clientId' in normalOptions) { + if (!(typeof normalOptions.clientId === 'string' || normalOptions.clientId === null)) + throw new ErrorInfo('clientId must be either a string or null', 40012, 400); + else if (normalOptions.clientId === '*') + throw new ErrorInfo( + 'Can’t use "*" as a clientId as that string is reserved. (To change the default token request behaviour to use a wildcard clientId, use {defaultTokenParams: {clientId: "*"}})', + 40012, + 400, + ); + } + + Logger.logAction(Logger.LOG_MINOR, 'BaseClient()', 'started; version = ' + Defaults.version); + + this._currentFallback = null; + + this.serverTimeOffset = null; + this.http = new Http(this); + this.auth = new Auth(this, normalOptions); + + this._rest = options.plugins?.Rest ? new options.plugins.Rest(this) : null; + this._Crypto = options.plugins?.Crypto ?? null; + this.__FilteredSubscriptions = options.plugins?.MessageInteractions ?? null; + } + + get rest(): Rest { + if (!this._rest) { + throwMissingPluginError('Rest'); + } + return this._rest; + } + + get _FilteredSubscriptions(): typeof FilteredSubscriptions { + if (!this.__FilteredSubscriptions) { + throwMissingPluginError('MessageInteractions'); + } + return this.__FilteredSubscriptions; + } + + get channels() { + return this.rest.channels; + } + + get push() { + return this.rest.push; + } + + baseUri(host: string) { + return Defaults.getHttpScheme(this.options) + host + ':' + Defaults.getPort(this.options, false); + } + + async stats(params: RequestParams): Promise> { + return this.rest.stats(params); + } + + async time(params?: RequestParams): Promise { + return this.rest.time(params); + } + + async request( + method: string, + path: string, + version: number, + params: RequestParams, + body: unknown, + customHeaders: Record, + ): Promise> { + return this.rest.request(method, path, version, params, body, customHeaders); + } + + batchPublish( + specOrSpecs: T, + ): Promise { + return this.rest.batchPublish(specOrSpecs); + } + + batchPresence(channels: string[]): Promise { + return this.rest.batchPresence(channels); + } + + setLog(logOptions: LoggerOptions): void { + Logger.setLog(logOptions.level, logOptions.handler); + } + + static Platform = Platform; +} + +export default BaseClient; diff --git a/src/common/lib/client/realtime.ts b/src/common/lib/client/baserealtime.ts similarity index 64% rename from src/common/lib/client/realtime.ts rename to src/common/lib/client/baserealtime.ts index a5dc82a916..f41ed2d879 100644 --- a/src/common/lib/client/realtime.ts +++ b/src/common/lib/client/baserealtime.ts @@ -1,29 +1,62 @@ import * as Utils from '../util/utils'; -import Rest from './rest'; +import BaseClient from './baseclient'; import EventEmitter from '../util/eventemitter'; import Logger from '../util/logger'; import Connection from './connection'; import RealtimeChannel from './realtimechannel'; -import Defaults from '../util/defaults'; import ErrorInfo from '../types/errorinfo'; import ProtocolMessage from '../types/protocolmessage'; import { ChannelOptions } from '../../types/channel'; -import ClientOptions, { DeprecatedClientOptions } from '../../types/ClientOptions'; +import ClientOptions from '../../types/ClientOptions'; import * as API from '../../../../ably'; -import ConnectionManager from '../transport/connectionmanager'; -import Platform from 'common/platform'; -import Message from '../types/message'; +import { ModularPlugins, RealtimePresencePlugin } from './modularplugins'; +import { TransportNames } from 'common/constants/TransportName'; +import { TransportImplementations } from 'common/platform'; +import Defaults from '../util/defaults'; -class Realtime extends Rest { - channels: any; +/** + `BaseRealtime` is an export of the tree-shakable version of the SDK, and acts as the base class for the `DefaultRealtime` class exported by the non tree-shakable version. + */ +class BaseRealtime extends BaseClient { + readonly _RealtimePresence: RealtimePresencePlugin | null; + // Extra transport implementations available to this client, in addition to those in Platform.Transports.bundledImplementations + readonly _additionalTransportImplementations: TransportImplementations; + _channels: any; connection: Connection; - constructor(options: ClientOptions) { - super(options); + /* + * The public typings declare that this only accepts an object, but since we want to emit a good error message in the case where a non-TypeScript user does one of these things: + * + * 1. passes a string (which is quite likely if they’re e.g. migrating from the default variant to the modular variant) + * 2. passes no argument at all + * + * tell the compiler that these cases are possible so that it forces us to handle them. + */ + constructor(options?: ClientOptions | string) { + super(Defaults.objectifyOptions(options, false, 'BaseRealtime')); Logger.logAction(Logger.LOG_MINOR, 'Realtime()', ''); + this._additionalTransportImplementations = BaseRealtime.transportImplementationsFromPlugins(this.options.plugins); + this._RealtimePresence = this.options.plugins?.RealtimePresence ?? null; this.connection = new Connection(this, this.options); - this.channels = new Channels(this); - if (options.autoConnect !== false) this.connect(); + this._channels = new Channels(this); + if (this.options.autoConnect !== false) this.connect(); + } + + private static transportImplementationsFromPlugins(plugins?: ModularPlugins) { + const transports: TransportImplementations = {}; + + if (plugins?.WebSocketTransport) { + transports[TransportNames.WebSocket] = plugins.WebSocketTransport; + } + if (plugins?.XHRPolling) { + transports[TransportNames.XhrPolling] = plugins.XHRPolling; + } + + return transports; + } + + get channels() { + return this._channels; } connect(): void { @@ -35,27 +68,13 @@ class Realtime extends Rest { Logger.logAction(Logger.LOG_MINOR, 'Realtime.close()', ''); this.connection.close(); } - - static Promise = function (options: DeprecatedClientOptions): Realtime { - options = Defaults.objectifyOptions(options); - options.promises = true; - return new Realtime(options); - }; - - static Callbacks = Realtime; - static Utils = Utils; - static ConnectionManager = ConnectionManager; - static Platform = Platform; - static ProtocolMessage = ProtocolMessage; - static Message = Message; - static Crypto?: typeof Platform.Crypto; } class Channels extends EventEmitter { - realtime: Realtime; + realtime: BaseRealtime; all: Record; - constructor(realtime: Realtime) { + constructor(realtime: BaseRealtime) { super(); this.realtime = realtime; this.all = Object.create(null); @@ -83,13 +102,14 @@ class Channels extends EventEmitter { } } - onChannelMessage(msg: ProtocolMessage) { + // Access to this method is synchronised by ConnectionManager#processChannelMessage. + async processChannelMessage(msg: ProtocolMessage) { const channelName = msg.channel; if (channelName === undefined) { Logger.logAction( Logger.LOG_ERROR, - 'Channels.onChannelMessage()', - 'received event unspecified channel, action = ' + msg.action + 'Channels.processChannelMessage()', + 'received event unspecified channel, action = ' + msg.action, ); return; } @@ -97,12 +117,12 @@ class Channels extends EventEmitter { if (!channel) { Logger.logAction( Logger.LOG_ERROR, - 'Channels.onChannelMessage()', - 'received event for non-existent channel: ' + channelName + 'Channels.processChannelMessage()', + 'received event for non-existent channel: ' + channelName, ); return; } - channel.onMessage(msg); + await channel.processMessage(msg); } /* called when a transport becomes connected; reattempt attach/detach @@ -126,7 +146,7 @@ class Channels extends EventEmitter { * events) imply connection state changes for any channel which is either * attached, pending, or will attempt to become attached in the future */ propogateConnectionInterruption(connectionState: string, reason: ErrorInfo) { - const connectionStateToChannelState: Record = { + const connectionStateToChannelState: Record = { closing: 'detached', closed: 'detached', failed: 'failed', @@ -137,7 +157,7 @@ class Channels extends EventEmitter { for (const channelId in this.all) { const channel = this.all[channelId]; - if (Utils.arrIn(fromChannelStates, channel.state)) { + if (fromChannelStates.includes(channel.state)) { channel.notifyState(toChannelState, reason); } } @@ -153,7 +173,7 @@ class Channels extends EventEmitter { throw new ErrorInfo( 'Channels.get() cannot be used to set channel options that would cause the channel to reattach. Please, use RealtimeChannel.setOptions() instead.', 40000, - 400 + 400, ); } channel.setOptions(channelOptions); @@ -161,7 +181,7 @@ class Channels extends EventEmitter { return channel; } - getDerived(name: string, deriveOptions: API.Types.DeriveOptions, channelOptions?: ChannelOptions) { + getDerived(name: string, deriveOptions: API.DeriveOptions, channelOptions?: ChannelOptions) { if (deriveOptions.filter) { const filter = Utils.toBase64(deriveOptions.filter); const match = Utils.matchDerivedChannel(name); @@ -186,4 +206,4 @@ class Channels extends EventEmitter { } } -export default Realtime; +export default BaseRealtime; diff --git a/src/common/lib/client/baserest.ts b/src/common/lib/client/baserest.ts new file mode 100644 index 0000000000..46137a08cb --- /dev/null +++ b/src/common/lib/client/baserest.ts @@ -0,0 +1,23 @@ +import BaseClient from './baseclient'; +import ClientOptions from '../../types/ClientOptions'; +import { Rest } from './rest'; +import Defaults from '../util/defaults'; + +/** + `BaseRest` is an export of the tree-shakable version of the SDK, and acts as the base class for the `DefaultRest` class exported by the non tree-shakable version. + + It always includes the `Rest` plugin. + */ +export class BaseRest extends BaseClient { + /* + * The public typings declare that this only accepts an object, but since we want to emit a good error message in the case where a non-TypeScript user does one of these things: + * + * 1. passes a string (which is quite likely if they’re e.g. migrating from the default variant to the modular variant) + * 2. passes no argument at all + * + * tell the compiler that these cases are possible so that it forces us to handle them. + */ + constructor(options?: ClientOptions | string) { + super(Defaults.objectifyOptions(options, false, 'BaseRest', { Rest })); + } +} diff --git a/src/common/lib/client/channel.ts b/src/common/lib/client/channel.ts deleted file mode 100644 index 6350ef011d..0000000000 --- a/src/common/lib/client/channel.ts +++ /dev/null @@ -1,206 +0,0 @@ -import * as Utils from '../util/utils'; -import EventEmitter from '../util/eventemitter'; -import Logger from '../util/logger'; -import Presence from './presence'; -import Message, { CipherOptions } from '../types/message'; -import ErrorInfo from '../types/errorinfo'; -import PaginatedResource, { PaginatedResult } from './paginatedresource'; -import Resource, { ResourceCallback } from './resource'; -import { ChannelOptions } from '../../types/channel'; -import { PaginatedResultCallback, StandardCallback } from '../../types/utils'; -import Rest from './rest'; -import Realtime from './realtime'; -import * as API from '../../../../ably'; -import Platform from 'common/platform'; - -interface RestHistoryParams { - start?: number; - end?: number; - direction?: string; - limit?: number; -} - -function noop() {} - -const MSG_ID_ENTROPY_BYTES = 9; - -function allEmptyIds(messages: Array) { - return Utils.arrEvery(messages, function (message: Message) { - return !message.id; - }); -} - -function normaliseChannelOptions(options?: ChannelOptions) { - const channelOptions = options || {}; - if (channelOptions.cipher) { - if (!Platform.Crypto) throw new Error('Encryption not enabled; use ably.encryption.js instead'); - const cipher = Platform.Crypto.getCipher(channelOptions.cipher); - channelOptions.cipher = cipher.cipherParams; - channelOptions.channelCipher = cipher.cipher; - } else if ('cipher' in channelOptions) { - /* Don't deactivate an existing cipher unless options - * has a 'cipher' key that's falsey */ - channelOptions.cipher = undefined; - channelOptions.channelCipher = null; - } - return channelOptions; -} - -class Channel extends EventEmitter { - rest: Rest | Realtime; - name: string; - basePath: string; - presence: Presence; - channelOptions: ChannelOptions; - - constructor(rest: Rest | Realtime, name: string, channelOptions?: ChannelOptions) { - super(); - Logger.logAction(Logger.LOG_MINOR, 'Channel()', 'started; name = ' + name); - this.rest = rest; - this.name = name; - this.basePath = '/channels/' + encodeURIComponent(name); - this.presence = new Presence(this); - this.channelOptions = normaliseChannelOptions(channelOptions); - } - - setOptions(options?: ChannelOptions): void { - this.channelOptions = normaliseChannelOptions(options); - } - - history( - params: RestHistoryParams | null, - callback: PaginatedResultCallback - ): Promise> | void { - Logger.logAction(Logger.LOG_MICRO, 'Channel.history()', 'channel = ' + this.name); - /* params and callback are optional; see if params contains the callback */ - if (callback === undefined) { - if (typeof params == 'function') { - callback = params; - params = null; - } else { - if (this.rest.options.promises) { - return Utils.promisify(this, 'history', arguments); - } - callback = noop; - } - } - - this._history(params, callback); - } - - _history(params: RestHistoryParams | null, callback: PaginatedResultCallback): void { - const rest = this.rest, - format = rest.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, - envelope = this.rest.http.supportsLinkHeaders ? undefined : format, - headers = Utils.defaultGetHeaders(rest.options, format); - - if (rest.options.headers) Utils.mixin(headers, rest.options.headers); - - const options = this.channelOptions; - new PaginatedResource(rest, this.basePath + '/messages', headers, envelope, function ( - body: any, - headers: Record, - unpacked?: boolean - ) { - return Message.fromResponseBody(body, options, unpacked ? undefined : format); - }).get(params as Record, callback); - } - - publish(): void | Promise { - const argCount = arguments.length, - first = arguments[0], - second = arguments[1]; - let callback = arguments[argCount - 1]; - let messages: Array; - let params: any; - - if (typeof callback !== 'function') { - if (this.rest.options.promises) { - return Utils.promisify(this, 'publish', arguments); - } - callback = noop; - } - - if (typeof first === 'string' || first === null) { - /* (name, data, ...) */ - messages = [Message.fromValues({ name: first, data: second })]; - params = arguments[2]; - } else if (Utils.isObject(first)) { - messages = [Message.fromValues(first)]; - params = arguments[1]; - } else if (Utils.isArray(first)) { - messages = Message.fromValuesArray(first); - params = arguments[1]; - } else { - throw new ErrorInfo( - 'The single-argument form of publish() expects a message object or an array of message objects', - 40013, - 400 - ); - } - - if (typeof params !== 'object' || !params) { - /* No params supplied (so after-message argument is just the callback or undefined) */ - params = {}; - } - - const rest = this.rest, - options = rest.options, - format = options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, - idempotentRestPublishing = rest.options.idempotentRestPublishing, - headers = Utils.defaultPostHeaders(rest.options, format); - - if (options.headers) Utils.mixin(headers, options.headers); - - if (idempotentRestPublishing && allEmptyIds(messages)) { - const msgIdBase = Utils.randomString(MSG_ID_ENTROPY_BYTES); - Utils.arrForEach(messages, function (message, index) { - message.id = msgIdBase + ':' + index.toString(); - }); - } - - Message.encodeArray(messages, this.channelOptions as CipherOptions, (err: Error) => { - if (err) { - callback(err); - return; - } - - /* RSL1i */ - const size = Message.getMessagesSize(messages), - maxMessageSize = options.maxMessageSize; - if (size > maxMessageSize) { - callback( - new ErrorInfo( - 'Maximum size of messages that can be published at once exceeded ( was ' + - size + - ' bytes; limit is ' + - maxMessageSize + - ' bytes)', - 40009, - 400 - ) - ); - return; - } - - this._publish(Message.serialize(messages, format), headers, params, callback); - }); - } - - _publish(requestBody: unknown, headers: Record, params: any, callback: ResourceCallback): void { - Resource.post(this.rest, this.basePath + '/messages', requestBody, headers, params, null, callback); - } - - status(callback?: StandardCallback): void | Promise { - if (typeof callback !== 'function' && this.rest.options.promises) { - return Utils.promisify(this, 'status', []); - } - - const format = this.rest.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json; - const headers = Utils.defaultPostHeaders(this.rest.options, format); - - Resource.get(this.rest, this.basePath, headers, {}, format, callback || noop); - } -} - -export default Channel; diff --git a/src/common/lib/client/channelstatechange.ts b/src/common/lib/client/channelstatechange.ts index 09cf0a2c6f..1dea8c6e0a 100644 --- a/src/common/lib/client/channelstatechange.ts +++ b/src/common/lib/client/channelstatechange.ts @@ -12,7 +12,7 @@ class ChannelStateChange { current: string, resumed?: boolean, hasBacklog?: boolean, - reason?: string | Error | ErrorInfo | null + reason?: string | Error | ErrorInfo | null, ) { this.previous = previous; this.current = current; diff --git a/src/common/lib/client/connection.ts b/src/common/lib/client/connection.ts index a1f823710d..f8f62ad082 100644 --- a/src/common/lib/client/connection.ts +++ b/src/common/lib/client/connection.ts @@ -1,24 +1,21 @@ -import * as Utils from '../util/utils'; import EventEmitter from '../util/eventemitter'; import ConnectionManager from '../transport/connectionmanager'; import Logger from '../util/logger'; import ConnectionStateChange from './connectionstatechange'; import ErrorInfo from '../types/errorinfo'; import { NormalisedClientOptions } from '../../types/ClientOptions'; -import Realtime from './realtime'; +import BaseRealtime from './baserealtime'; import Platform from 'common/platform'; -function noop() {} - class Connection extends EventEmitter { - ably: Realtime; + ably: BaseRealtime; connectionManager: ConnectionManager; state: string; key?: string; id?: string; errorReason: ErrorInfo | null; - constructor(ably: Realtime, options: NormalisedClientOptions) { + constructor(ably: BaseRealtime, options: NormalisedClientOptions) { super(); this.ably = ably; this.connectionManager = new ConnectionManager(ably, options); @@ -40,14 +37,8 @@ class Connection extends EventEmitter { }); } - whenState = ((state: string, listener: Function) => { - return EventEmitter.prototype.whenState.call( - this, - state, - this.state, - listener, - new ConnectionStateChange(undefined, state) - ); + whenState = ((state: string) => { + return EventEmitter.prototype.whenState.call(this, state, this.state); }) as any; connect(): void { @@ -55,15 +46,11 @@ class Connection extends EventEmitter { this.connectionManager.requestState({ state: 'connecting' }); } - ping(callback: Function): Promise | void { + async ping(): Promise { Logger.logAction(Logger.LOG_MINOR, 'Connection.ping()', ''); - if (!callback) { - if (this.ably.options.promises) { - return Utils.promisify(this, 'ping', arguments); - } - callback = noop; - } - this.connectionManager.ping(null, callback); + return new Promise((resolve, reject) => { + this.connectionManager.ping(null, (err: unknown, result: number) => (err ? reject(err) : resolve(result))); + }); } close(): void { @@ -72,6 +59,9 @@ class Connection extends EventEmitter { } get recoveryKey(): string | null { + Logger.deprecationWarning( + 'The `Connection.recoveryKey` attribute has been replaced by the `Connection.createRecoveryKey()` method. Replace your usage of `recoveryKey` with the return value of `createRecoveryKey()`. `recoveryKey` will be removed in a future version.', + ); return this.createRecoveryKey(); } diff --git a/src/common/lib/client/defaultrealtime.ts b/src/common/lib/client/defaultrealtime.ts new file mode 100644 index 0000000000..866a963028 --- /dev/null +++ b/src/common/lib/client/defaultrealtime.ts @@ -0,0 +1,71 @@ +import BaseRealtime from './baserealtime'; +import ClientOptions from '../../types/ClientOptions'; +import { allCommonModularPlugins } from './modularplugins'; +import * as Utils from '../util/utils'; +import ConnectionManager from '../transport/connectionmanager'; +import ProtocolMessage from '../types/protocolmessage'; +import Platform from 'common/platform'; +import { DefaultMessage } from '../types/defaultmessage'; +import { MsgPack } from 'common/types/msgpack'; +import RealtimePresence from './realtimepresence'; +import { DefaultPresenceMessage } from '../types/defaultpresencemessage'; +import WebSocketTransport from '../transport/websockettransport'; +import { FilteredSubscriptions } from './filteredsubscriptions'; +import { + fromValues as presenceMessageFromValues, + fromValuesArray as presenceMessagesFromValuesArray, +} from '../types/presencemessage'; +import { Http } from 'common/types/http'; +import Defaults from '../util/defaults'; + +/** + `DefaultRealtime` is the class that the non tree-shakable version of the SDK exports as `Realtime`. It ensures that this version of the SDK includes all of the functionality which is optionally available in the tree-shakable version. + */ +export class DefaultRealtime extends BaseRealtime { + // The public typings declare that this requires an argument to be passed, but since we want to emit a good error message in the case where a non-TypeScript user does not pass an argument, tell the compiler that this is possible so that it forces us to handle it. + constructor(options?: ClientOptions | string) { + const MsgPack = DefaultRealtime._MsgPack; + if (!MsgPack) { + throw new Error('Expected DefaultRealtime._MsgPack to have been set'); + } + + super( + Defaults.objectifyOptions(options, true, 'Realtime', { + ...allCommonModularPlugins, + Crypto: DefaultRealtime.Crypto ?? undefined, + MsgPack, + RealtimePresence: { + RealtimePresence, + presenceMessageFromValues, + presenceMessagesFromValuesArray, + }, + WebSocketTransport, + MessageInteractions: FilteredSubscriptions, + }), + ); + } + + static Utils = Utils; + static ConnectionManager = ConnectionManager; + static ProtocolMessage = ProtocolMessage; + + private static _Crypto: typeof Platform.Crypto = null; + static get Crypto() { + if (this._Crypto === null) { + throw new Error('Encryption not enabled; use ably.encryption.js instead'); + } + + return this._Crypto; + } + static set Crypto(newValue: typeof Platform.Crypto) { + this._Crypto = newValue; + } + + static Message = DefaultMessage; + static PresenceMessage = DefaultPresenceMessage; + + static _MsgPack: MsgPack | null = null; + + // Used by tests + static _Http = Http; +} diff --git a/src/common/lib/client/defaultrest.ts b/src/common/lib/client/defaultrest.ts new file mode 100644 index 0000000000..110aef6fa3 --- /dev/null +++ b/src/common/lib/client/defaultrest.ts @@ -0,0 +1,50 @@ +import { BaseRest } from './baserest'; +import ClientOptions from '../../types/ClientOptions'; +import { allCommonModularPlugins } from './modularplugins'; +import Platform from 'common/platform'; +import { DefaultMessage } from '../types/defaultmessage'; +import { MsgPack } from 'common/types/msgpack'; +import { DefaultPresenceMessage } from '../types/defaultpresencemessage'; +import { Http } from 'common/types/http'; +import Defaults from '../util/defaults'; + +/** + `DefaultRest` is the class that the non tree-shakable version of the SDK exports as `Rest`. It ensures that this version of the SDK includes all of the functionality which is optionally available in the tree-shakable version. + */ +export class DefaultRest extends BaseRest { + // The public typings declare that this requires an argument to be passed, but since we want to emit a good error message in the case where a non-TypeScript user does not pass an argument, tell the compiler that this is possible so that it forces us to handle it. + constructor(options?: ClientOptions | string) { + const MsgPack = DefaultRest._MsgPack; + if (!MsgPack) { + throw new Error('Expected DefaultRest._MsgPack to have been set'); + } + + super( + Defaults.objectifyOptions(options, true, 'Rest', { + ...allCommonModularPlugins, + Crypto: DefaultRest.Crypto ?? undefined, + MsgPack: DefaultRest._MsgPack ?? undefined, + }), + ); + } + + private static _Crypto: typeof Platform.Crypto = null; + static get Crypto() { + if (this._Crypto === null) { + throw new Error('Encryption not enabled; use ably.encryption.js instead'); + } + + return this._Crypto; + } + static set Crypto(newValue: typeof Platform.Crypto) { + this._Crypto = newValue; + } + + static Message = DefaultMessage; + static PresenceMessage = DefaultPresenceMessage; + + static _MsgPack: MsgPack | null = null; + + // Used by tests + static _Http = Http; +} diff --git a/src/common/lib/client/filteredsubscriptions.ts b/src/common/lib/client/filteredsubscriptions.ts new file mode 100644 index 0000000000..6c824e5693 --- /dev/null +++ b/src/common/lib/client/filteredsubscriptions.ts @@ -0,0 +1,108 @@ +import * as API from '../../../../ably'; +import RealtimeChannel from './realtimechannel'; +import Message from '../types/message'; + +export class FilteredSubscriptions { + static subscribeFilter(channel: RealtimeChannel, filter: API.MessageFilter, listener: API.messageCallback) { + const filteredListener = (m: Message) => { + const mapping: { [key in keyof API.MessageFilter]: any } = { + name: m.name, + refTimeserial: m.extras?.ref?.timeserial, + refType: m.extras?.ref?.type, + isRef: !!m.extras?.ref?.timeserial, + clientId: m.clientId, + }; + // Check if any values are defined in the filter and if they match the value in the message object + if ( + Object.entries(filter).find(([key, value]) => + value !== undefined ? mapping[key as keyof API.MessageFilter] !== value : false, + ) + ) { + return; + } + listener(m); + }; + this.addFilteredSubscription(channel, filter, listener, filteredListener); + channel.subscriptions.on(filteredListener); + } + + // Adds a new filtered subscription + static addFilteredSubscription( + channel: RealtimeChannel, + filter: API.MessageFilter, + realListener: API.messageCallback, + filteredListener: API.messageCallback, + ) { + if (!channel.filteredSubscriptions) { + channel.filteredSubscriptions = new Map< + API.messageCallback, + Map[]> + >(); + } + if (channel.filteredSubscriptions.has(realListener)) { + const realListenerMap = channel.filteredSubscriptions.get(realListener) as Map< + API.MessageFilter, + API.messageCallback[] + >; + // Add the filtered listener to the map, or append to the array if this filter has already been used + realListenerMap.set(filter, realListenerMap?.get(filter)?.concat(filteredListener) || [filteredListener]); + } else { + channel.filteredSubscriptions.set( + realListener, + new Map[]>([[filter, [filteredListener]]]), + ); + } + } + + static getAndDeleteFilteredSubscriptions( + channel: RealtimeChannel, + filter: API.MessageFilter | undefined, + realListener: API.messageCallback | undefined, + ): API.messageCallback[] { + // No filtered subscriptions map means there has been no filtered subscriptions yet, so return nothing + if (!channel.filteredSubscriptions) { + return []; + } + // Only a filter is passed in with no specific listener + if (!realListener && filter) { + // Return each listener which is attached to the specified filter object + return Array.from(channel.filteredSubscriptions.entries()) + .map(([key, filterMaps]) => { + // Get (then delete) the maps matching this filter + let listenerMaps = filterMaps.get(filter); + filterMaps.delete(filter); + // Clear the parent if nothing is left + if (filterMaps.size === 0) { + channel.filteredSubscriptions?.delete(key); + } + return listenerMaps; + }) + .reduce( + (prev, cur) => (cur ? (prev as API.messageCallback[]).concat(...cur) : prev), + [], + ) as API.messageCallback[]; + } + + // No subscriptions for this listener + if (!realListener || !channel.filteredSubscriptions.has(realListener)) { + return []; + } + const realListenerMap = channel.filteredSubscriptions.get(realListener) as Map< + API.MessageFilter, + API.messageCallback[] + >; + // If no filter is specified return all listeners using that function + if (!filter) { + // array.flat is not available unless we support es2019 or higher + const listeners = Array.from(realListenerMap.values()).reduce((prev, cur) => prev.concat(...cur), []); + // remove the listener from the map + channel.filteredSubscriptions.delete(realListener); + return listeners; + } + + let listeners = realListenerMap.get(filter); + realListenerMap.delete(filter); + + return listeners || []; + } +} diff --git a/src/common/lib/client/modularplugins.ts b/src/common/lib/client/modularplugins.ts new file mode 100644 index 0000000000..28fd50b898 --- /dev/null +++ b/src/common/lib/client/modularplugins.ts @@ -0,0 +1,35 @@ +import { Rest } from './rest'; +import { IUntypedCryptoStatic } from '../../types/ICryptoStatic'; +import { MsgPack } from 'common/types/msgpack'; +import RealtimePresence from './realtimepresence'; +import XHRRequest from 'platform/web/lib/http/request/xhrrequest'; +import fetchRequest from 'platform/web/lib/http/request/fetchrequest'; +import { FilteredSubscriptions } from './filteredsubscriptions'; +import { + fromValues as presenceMessageFromValues, + fromValuesArray as presenceMessagesFromValuesArray, +} from '../types/presencemessage'; +import { TransportCtor } from '../transport/transport'; + +export interface PresenceMessagePlugin { + presenceMessageFromValues: typeof presenceMessageFromValues; + presenceMessagesFromValuesArray: typeof presenceMessagesFromValuesArray; +} + +export type RealtimePresencePlugin = PresenceMessagePlugin & { + RealtimePresence: typeof RealtimePresence; +}; + +export interface ModularPlugins { + Rest?: typeof Rest; + Crypto?: IUntypedCryptoStatic; + MsgPack?: MsgPack; + RealtimePresence?: RealtimePresencePlugin; + WebSocketTransport?: TransportCtor; + XHRPolling?: TransportCtor; + XHRRequest?: typeof XHRRequest; + FetchRequest?: typeof fetchRequest; + MessageInteractions?: typeof FilteredSubscriptions; +} + +export const allCommonModularPlugins: ModularPlugins = { Rest }; diff --git a/src/common/lib/client/paginatedresource.ts b/src/common/lib/client/paginatedresource.ts index 3af91d2d6b..8c2353009f 100644 --- a/src/common/lib/client/paginatedresource.ts +++ b/src/common/lib/client/paginatedresource.ts @@ -1,12 +1,12 @@ import * as Utils from '../util/utils'; import Logger from '../util/logger'; -import Resource from './resource'; +import Resource, { ResourceResult } from './resource'; import { IPartialErrorInfo } from '../types/errorinfo'; -import { PaginatedResultCallback } from '../../types/utils'; -import Rest from './rest'; +import BaseClient from './baseclient'; +import { RequestBody, ResponseHeaders } from 'common/types/http'; import HttpStatusCodes from '../../constants/HttpStatusCodes'; -export type BodyHandler = (body: unknown, headers: Record, unpacked?: boolean) => any; +export type BodyHandler = (body: unknown, headers: ResponseHeaders, unpacked?: boolean) => Promise; function getRelParams(linkUrl: string) { const urlMatch = linkUrl.match(/^\.\/(\w+)\?(.*)$/); @@ -29,14 +29,14 @@ function parseRelLinks(linkHeader: string | Array) { function returnErrOnly(err: IPartialErrorInfo, body: unknown, useHPR?: boolean) { /* If using httpPaginatedResponse, errors from Ably are returned as part of - * the HPR, only do callback(err) for network errors etc. which don't + * the HPR, only throw `err` for network errors etc. which don't * return a body and/or have no ably-originated error code (non-numeric * error codes originate from node) */ return !(useHPR && (body || typeof err.code === 'number')); } class PaginatedResource { - rest: Rest; + client: BaseClient; path: string; headers: Record; envelope: Utils.Format | null; @@ -44,14 +44,14 @@ class PaginatedResource { useHttpPaginatedResponse: boolean; constructor( - rest: Rest, + client: BaseClient, path: string, headers: Record, envelope: Utils.Format | undefined, bodyHandler: BodyHandler, - useHttpPaginatedResponse?: boolean + useHttpPaginatedResponse?: boolean, ) { - this.rest = rest; + this.client = client; this.path = path; this.headers = headers; this.envelope = envelope ?? null; @@ -59,115 +59,69 @@ class PaginatedResource { this.useHttpPaginatedResponse = useHttpPaginatedResponse || false; } - get(params: Record, callback: PaginatedResultCallback): void { - Resource.get( - this.rest, - this.path, - this.headers, - params, - this.envelope, - (err, body, headers, unpacked, statusCode) => { - this.handlePage(err, body, headers, unpacked, statusCode, callback); - } - ); + async get(params: Record): Promise> { + const result = await Resource.get(this.client, this.path, this.headers, params, this.envelope, false); + return this.handlePage(result); } - delete(params: Record, callback: PaginatedResultCallback): void { - Resource.delete( - this.rest, - this.path, - this.headers, - params, - this.envelope, - (err, body, headers, unpacked, statusCode) => { - this.handlePage(err, body, headers, unpacked, statusCode, callback); - } - ); + async delete(params: Record): Promise> { + const result = await Resource.delete(this.client, this.path, this.headers, params, this.envelope, false); + return this.handlePage(result); } - post(params: Record, body: unknown, callback: PaginatedResultCallback): void { - Resource.post( - this.rest, - this.path, - body, - this.headers, - params, - this.envelope, - (err, responseBody, headers, unpacked, statusCode) => { - if (callback) { - this.handlePage(err, responseBody, headers, unpacked, statusCode, callback); - } - } - ); + async post(params: Record, body: RequestBody | null): Promise> { + const result = await Resource.post(this.client, this.path, body, this.headers, params, this.envelope, false); + return this.handlePage(result); } - put(params: Record, body: unknown, callback: PaginatedResultCallback): void { - Resource.put( - this.rest, - this.path, - body, - this.headers, - params, - this.envelope, - (err, responseBody, headers, unpacked, statusCode) => { - if (callback) { - this.handlePage(err, responseBody, headers, unpacked, statusCode, callback); - } - } - ); + async put(params: Record, body: RequestBody | null): Promise> { + const result = await Resource.put(this.client, this.path, body, this.headers, params, this.envelope, false); + return this.handlePage(result); } - patch(params: Record, body: unknown, callback: PaginatedResultCallback): void { - Resource.patch( - this.rest, - this.path, - body, - this.headers, - params, - this.envelope, - (err, responseBody, headers, unpacked, statusCode) => { - if (callback) { - this.handlePage(err, responseBody, headers, unpacked, statusCode, callback); - } - } - ); + async patch(params: Record, body: RequestBody | null): Promise> { + const result = await Resource.patch(this.client, this.path, body, this.headers, params, this.envelope, false); + return this.handlePage(result); } - handlePage( - err: IPartialErrorInfo | null, - body: unknown, - headers: Record | undefined, - unpacked: boolean | undefined, - statusCode: number | undefined, - callback: PaginatedResultCallback - ): void { - if (err && returnErrOnly(err, body, this.useHttpPaginatedResponse)) { + async handlePage(result: ResourceResult): Promise> { + if (result.err && returnErrOnly(result.err, result.body, this.useHttpPaginatedResponse)) { Logger.logAction( Logger.LOG_ERROR, 'PaginatedResource.handlePage()', - 'Unexpected error getting resource: err = ' + Utils.inspectError(err) + 'Unexpected error getting resource: err = ' + Utils.inspectError(result.err), ); - callback?.(err); - return; + throw result.err; } + let items, linkHeader, relParams; + try { - items = statusCode == HttpStatusCodes.NoContent ? [] : this.bodyHandler(body, headers || {}, unpacked); + items = + result.statusCode == HttpStatusCodes.NoContent + ? [] + : await this.bodyHandler(result.body, result.headers || {}, result.unpacked); } catch (e) { /* If we got an error, the failure to parse the body is almost certainly - * due to that, so callback with that in preference over the parse error */ - callback?.(err || e); - return; + * due to that, so throw that in preference over the parse error */ + throw result.err || e; } - if (headers && (linkHeader = headers['Link'] || headers['link'])) { + if (result.headers && (linkHeader = result.headers['Link'] || result.headers['link'])) { relParams = parseRelLinks(linkHeader); } if (this.useHttpPaginatedResponse) { - callback(null, new HttpPaginatedResponse(this, items, headers || {}, statusCode as number, relParams, err)); + return new HttpPaginatedResponse( + this, + items, + result.headers || {}, + result.statusCode as number, + relParams, + result.err, + ); } else { - callback(null, new PaginatedResult(this, items, relParams)); + return new PaginatedResult(this, items, relParams); } } } @@ -175,9 +129,9 @@ class PaginatedResource { export class PaginatedResult { resource: PaginatedResource; items: T[]; - first?: (results: PaginatedResultCallback) => void; - next?: (results: PaginatedResultCallback) => void; - current?: (results: PaginatedResultCallback) => void; + first?: () => Promise>; + next?: () => Promise | null>; + current?: () => Promise>; hasNext?: () => boolean; isLast?: () => boolean; @@ -188,29 +142,20 @@ export class PaginatedResult { const self = this; if (relParams) { if ('first' in relParams) { - this.first = function (callback) { - if (!callback && self.resource.rest.options.promises) { - return Utils.promisify(self, 'first', []); - } - self.get(relParams.first, callback); + this.first = async function () { + return self.get(relParams.first); }; } if ('current' in relParams) { - this.current = function (callback) { - if (!callback && self.resource.rest.options.promises) { - return Utils.promisify(self, 'current', []); - } - self.get(relParams.current, callback); + this.current = async function () { + return self.get(relParams.current); }; } - this.next = function (callback) { - if (!callback && self.resource.rest.options.promises) { - return Utils.promisify(self, 'next', []); - } + this.next = async function () { if ('next' in relParams) { - self.get(relParams.next, callback); + return self.get(relParams.next); } else { - callback(null, null); + return null; } }; @@ -225,35 +170,27 @@ export class PaginatedResult { /* We assume that only the initial request can be a POST, and that accessing * the rest of a multipage set of results can always be done with GET */ - get(params: any, callback: PaginatedResultCallback): void { + async get(params: any): Promise> { const res = this.resource; - Resource.get( - res.rest, - res.path, - res.headers, - params, - res.envelope, - function (err, body, headers, unpacked, statusCode) { - res.handlePage(err, body, headers, unpacked, statusCode, callback); - } - ); + const result = await Resource.get(res.client, res.path, res.headers, params, res.envelope, false); + return res.handlePage(result); } } export class HttpPaginatedResponse extends PaginatedResult { statusCode: number; success: boolean; - headers: Record; + headers: ResponseHeaders; errorCode?: number | null; errorMessage?: string | null; constructor( resource: PaginatedResource, items: T[], - headers: Record, + headers: ResponseHeaders, statusCode: number, relParams: any, - err: IPartialErrorInfo | null + err: IPartialErrorInfo | null, ) { super(resource, items, relParams); this.statusCode = statusCode; diff --git a/src/common/lib/client/presence.ts b/src/common/lib/client/presence.ts deleted file mode 100644 index d17a68bc85..0000000000 --- a/src/common/lib/client/presence.ts +++ /dev/null @@ -1,97 +0,0 @@ -import * as Utils from '../util/utils'; -import EventEmitter from '../util/eventemitter'; -import Logger from '../util/logger'; -import PaginatedResource, { PaginatedResult } from './paginatedresource'; -import PresenceMessage from '../types/presencemessage'; -import { CipherOptions } from '../types/message'; -import { PaginatedResultCallback } from '../../types/utils'; -import Channel from './channel'; -import RealtimeChannel from './realtimechannel'; - -function noop() {} - -class Presence extends EventEmitter { - channel: RealtimeChannel | Channel; - basePath: string; - - constructor(channel: RealtimeChannel | Channel) { - super(); - this.channel = channel; - this.basePath = channel.basePath + '/presence'; - } - - get(params: any, callback: PaginatedResultCallback): void | Promise { - Logger.logAction(Logger.LOG_MICRO, 'Presence.get()', 'channel = ' + this.channel.name); - /* params and callback are optional; see if params contains the callback */ - if (callback === undefined) { - if (typeof params == 'function') { - callback = params; - params = null; - } else { - if (this.channel.rest.options.promises) { - return Utils.promisify(this, 'get', arguments); - } - callback = noop; - } - } - const rest = this.channel.rest, - format = rest.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, - envelope = this.channel.rest.http.supportsLinkHeaders ? undefined : format, - headers = Utils.defaultGetHeaders(rest.options, format); - - if (rest.options.headers) Utils.mixin(headers, rest.options.headers); - - const options = this.channel.channelOptions; - new PaginatedResource(rest, this.basePath, headers, envelope, function ( - body: any, - headers: Record, - unpacked?: boolean - ) { - return PresenceMessage.fromResponseBody(body, options as CipherOptions, unpacked ? undefined : format); - }).get(params, callback); - } - - history( - params: any, - callback: PaginatedResultCallback - ): void | Promise> { - Logger.logAction(Logger.LOG_MICRO, 'Presence.history()', 'channel = ' + this.channel.name); - return this._history(params, callback); - } - - _history( - params: any, - callback: PaginatedResultCallback - ): void | Promise> { - /* params and callback are optional; see if params contains the callback */ - if (callback === undefined) { - if (typeof params == 'function') { - callback = params; - params = null; - } else { - if (this.channel.rest.options.promises) { - return Utils.promisify(this, '_history', [params]); - } - callback = noop; - } - } - - const rest = this.channel.rest, - format = rest.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, - envelope = this.channel.rest.http.supportsLinkHeaders ? undefined : format, - headers = Utils.defaultGetHeaders(rest.options, format); - - if (rest.options.headers) Utils.mixin(headers, rest.options.headers); - - const options = this.channel.channelOptions; - new PaginatedResource(rest, this.basePath + '/history', headers, envelope, function ( - body: any, - headers: Record, - unpacked?: boolean - ) { - return PresenceMessage.fromResponseBody(body, options as CipherOptions, unpacked ? undefined : format); - }).get(params, callback); - } -} - -export default Presence; diff --git a/src/common/lib/client/push.ts b/src/common/lib/client/push.ts index 0b1a9c4fa7..65c5f68cc3 100644 --- a/src/common/lib/client/push.ts +++ b/src/common/lib/client/push.ts @@ -1,351 +1,272 @@ import * as Utils from '../util/utils'; import DeviceDetails from '../types/devicedetails'; import Resource from './resource'; -import PaginatedResource from './paginatedresource'; +import PaginatedResource, { PaginatedResult } from './paginatedresource'; import ErrorInfo from '../types/errorinfo'; import PushChannelSubscription from '../types/pushchannelsubscription'; -import { ErrCallback, PaginatedResultCallback, StandardCallback } from '../../types/utils'; -import Rest from './rest'; - -const noop = function () {}; +import BaseClient from './baseclient'; +import Defaults from '../util/defaults'; class Push { - rest: Rest; + client: BaseClient; admin: Admin; - constructor(rest: Rest) { - this.rest = rest; - this.admin = new Admin(rest); + constructor(client: BaseClient) { + this.client = client; + this.admin = new Admin(client); } } class Admin { - rest: Rest; + client: BaseClient; deviceRegistrations: DeviceRegistrations; channelSubscriptions: ChannelSubscriptions; - constructor(rest: Rest) { - this.rest = rest; - this.deviceRegistrations = new DeviceRegistrations(rest); - this.channelSubscriptions = new ChannelSubscriptions(rest); + constructor(client: BaseClient) { + this.client = client; + this.deviceRegistrations = new DeviceRegistrations(client); + this.channelSubscriptions = new ChannelSubscriptions(client); } - publish(recipient: any, payload: any, callback: ErrCallback) { - const rest = this.rest; - const format = rest.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, - headers = Utils.defaultPostHeaders(rest.options, format), + async publish(recipient: any, payload: any): Promise { + const client = this.client; + const format = client.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, + headers = Defaults.defaultPostHeaders(client.options, { format }), params = {}; const body = Utils.mixin({ recipient: recipient }, payload); - if (typeof callback !== 'function') { - if (this.rest.options.promises) { - return Utils.promisify(this, 'publish', arguments); - } - callback = noop; - } - - if (rest.options.headers) Utils.mixin(headers, rest.options.headers); + Utils.mixin(headers, client.options.headers); - if (rest.options.pushFullWait) Utils.mixin(params, { fullWait: 'true' }); + if (client.options.pushFullWait) Utils.mixin(params, { fullWait: 'true' }); - const requestBody = Utils.encodeBody(body, format); - Resource.post(rest, '/push/publish', requestBody, headers, params, null, (err) => callback(err)); + const requestBody = Utils.encodeBody(body, client._MsgPack, format); + await Resource.post(client, '/push/publish', requestBody, headers, params, null, true); } } class DeviceRegistrations { - rest: Rest; + client: BaseClient; - constructor(rest: Rest) { - this.rest = rest; + constructor(client: BaseClient) { + this.client = client; } - save(device: any, callback: StandardCallback) { - const rest = this.rest; + async save(device: any): Promise { + const client = this.client; const body = DeviceDetails.fromValues(device); - const format = rest.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, - headers = Utils.defaultPostHeaders(rest.options, format), + const format = client.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, + headers = Defaults.defaultPostHeaders(client.options, { format }), params = {}; - if (typeof callback !== 'function') { - if (this.rest.options.promises) { - return Utils.promisify(this, 'save', arguments); - } - callback = noop; - } + Utils.mixin(headers, client.options.headers); - if (rest.options.headers) Utils.mixin(headers, rest.options.headers); + if (client.options.pushFullWait) Utils.mixin(params, { fullWait: 'true' }); - if (rest.options.pushFullWait) Utils.mixin(params, { fullWait: 'true' }); - - const requestBody = Utils.encodeBody(body, format); - Resource.put( - rest, + const requestBody = Utils.encodeBody(body, client._MsgPack, format); + const response = await Resource.put( + client, '/push/deviceRegistrations/' + encodeURIComponent(device.id), requestBody, headers, params, null, - (err, body, headers, unpacked) => { - callback( - err, - !err - ? (DeviceDetails.fromResponseBody( - body as Record, - unpacked ? undefined : format - ) as DeviceDetails) - : undefined - ); - } + true, ); + + return DeviceDetails.fromResponseBody( + response.body as Record, + client._MsgPack, + response.unpacked ? undefined : format, + ) as DeviceDetails; } - get(deviceIdOrDetails: any, callback: StandardCallback) { - const rest = this.rest, - format = rest.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, - headers = Utils.defaultGetHeaders(rest.options, format), + async get(deviceIdOrDetails: any): Promise { + const client = this.client, + format = client.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, + headers = Defaults.defaultGetHeaders(client.options, { format }), deviceId = deviceIdOrDetails.id || deviceIdOrDetails; - if (typeof callback !== 'function') { - if (this.rest.options.promises) { - return Utils.promisify(this, 'get', arguments); - } - callback = noop; - } - if (typeof deviceId !== 'string' || !deviceId.length) { - callback( - new ErrorInfo( - 'First argument to DeviceRegistrations#get must be a deviceId string or DeviceDetails', - 40000, - 400 - ) + throw new ErrorInfo( + 'First argument to DeviceRegistrations#get must be a deviceId string or DeviceDetails', + 40000, + 400, ); - return; } - if (rest.options.headers) Utils.mixin(headers, rest.options.headers); + Utils.mixin(headers, client.options.headers); - Resource.get( - rest, + const response = await Resource.get( + client, '/push/deviceRegistrations/' + encodeURIComponent(deviceId), headers, {}, null, - function (err, body, headers, unpacked) { - callback( - err, - !err - ? (DeviceDetails.fromResponseBody( - body as Record, - unpacked ? undefined : format - ) as DeviceDetails) - : undefined - ); - } + true, ); - } - list(params: any, callback: PaginatedResultCallback) { - const rest = this.rest, - format = rest.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, - envelope = this.rest.http.supportsLinkHeaders ? undefined : format, - headers = Utils.defaultGetHeaders(rest.options, format); + return DeviceDetails.fromResponseBody( + response.body as Record, + client._MsgPack, + response.unpacked ? undefined : format, + ) as DeviceDetails; + } - if (typeof callback !== 'function') { - if (this.rest.options.promises) { - return Utils.promisify(this, 'list', arguments); - } - callback = noop; - } + async list(params: any): Promise> { + const client = this.client, + format = client.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, + envelope = this.client.http.supportsLinkHeaders ? undefined : format, + headers = Defaults.defaultGetHeaders(client.options, { format }); - if (rest.options.headers) Utils.mixin(headers, rest.options.headers); + Utils.mixin(headers, client.options.headers); - new PaginatedResource(rest, '/push/deviceRegistrations', headers, envelope, function ( - body: any, - headers: Record, - unpacked?: boolean + return new PaginatedResource(client, '/push/deviceRegistrations', headers, envelope, async function ( + body, + headers, + unpacked, ) { - return DeviceDetails.fromResponseBody(body, unpacked ? undefined : format); - }).get(params, callback); + return DeviceDetails.fromResponseBody( + body as Record[], + client._MsgPack, + unpacked ? undefined : format, + ); + }).get(params); } - remove(deviceIdOrDetails: any, callback: ErrCallback) { - const rest = this.rest, - format = rest.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, - headers = Utils.defaultGetHeaders(rest.options, format), + async remove(deviceIdOrDetails: any): Promise { + const client = this.client, + format = client.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, + headers = Defaults.defaultGetHeaders(client.options, { format }), params = {}, deviceId = deviceIdOrDetails.id || deviceIdOrDetails; - if (typeof callback !== 'function') { - if (this.rest.options.promises) { - return Utils.promisify(this, 'remove', arguments); - } - callback = noop; - } - if (typeof deviceId !== 'string' || !deviceId.length) { - callback( - new ErrorInfo( - 'First argument to DeviceRegistrations#remove must be a deviceId string or DeviceDetails', - 40000, - 400 - ) + throw new ErrorInfo( + 'First argument to DeviceRegistrations#remove must be a deviceId string or DeviceDetails', + 40000, + 400, ); - return; } - if (rest.options.headers) Utils.mixin(headers, rest.options.headers); + Utils.mixin(headers, client.options.headers); - if (rest.options.pushFullWait) Utils.mixin(params, { fullWait: 'true' }); + if (client.options.pushFullWait) Utils.mixin(params, { fullWait: 'true' }); - Resource['delete']( - rest, + await Resource['delete']( + client, '/push/deviceRegistrations/' + encodeURIComponent(deviceId), headers, params, null, - (err) => callback(err) + true, ); } - removeWhere(params: any, callback: ErrCallback) { - const rest = this.rest, - format = rest.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, - headers = Utils.defaultGetHeaders(rest.options, format); - - if (typeof callback !== 'function') { - if (this.rest.options.promises) { - return Utils.promisify(this, 'removeWhere', arguments); - } - callback = noop; - } + async removeWhere(params: any): Promise { + const client = this.client, + format = client.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, + headers = Defaults.defaultGetHeaders(client.options, { format }); - if (rest.options.headers) Utils.mixin(headers, rest.options.headers); + Utils.mixin(headers, client.options.headers); - if (rest.options.pushFullWait) Utils.mixin(params, { fullWait: 'true' }); + if (client.options.pushFullWait) Utils.mixin(params, { fullWait: 'true' }); - Resource['delete'](rest, '/push/deviceRegistrations', headers, params, null, (err) => callback(err)); + await Resource['delete'](client, '/push/deviceRegistrations', headers, params, null, true); } } class ChannelSubscriptions { - rest: Rest; + client: BaseClient; - constructor(rest: Rest) { - this.rest = rest; + constructor(client: BaseClient) { + this.client = client; } - save(subscription: Record, callback: PaginatedResultCallback) { - const rest = this.rest; + async save(subscription: Record): Promise { + const client = this.client; const body = PushChannelSubscription.fromValues(subscription); - const format = rest.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, - headers = Utils.defaultPostHeaders(rest.options, format), + const format = client.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, + headers = Defaults.defaultPostHeaders(client.options, { format }), params = {}; - if (typeof callback !== 'function') { - if (this.rest.options.promises) { - return Utils.promisify(this, 'save', arguments); - } - callback = noop; - } + Utils.mixin(headers, client.options.headers); - if (rest.options.headers) Utils.mixin(headers, rest.options.headers); + if (client.options.pushFullWait) Utils.mixin(params, { fullWait: 'true' }); - if (rest.options.pushFullWait) Utils.mixin(params, { fullWait: 'true' }); - - const requestBody = Utils.encodeBody(body, format); - Resource.post( - rest, + const requestBody = Utils.encodeBody(body, client._MsgPack, format); + const response = await Resource.post( + client, '/push/channelSubscriptions', requestBody, headers, params, null, - function (err, body, headers, unpacked) { - callback( - err, - !err && PushChannelSubscription.fromResponseBody(body as Record, unpacked ? undefined : format) - ); - } + true, ); + + return PushChannelSubscription.fromResponseBody( + response.body as Record, + client._MsgPack, + response.unpacked ? undefined : format, + ) as PushChannelSubscription; } - list(params: any, callback: PaginatedResultCallback) { - const rest = this.rest, - format = rest.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, - envelope = this.rest.http.supportsLinkHeaders ? undefined : format, - headers = Utils.defaultGetHeaders(rest.options, format); + async list(params: any): Promise> { + const client = this.client, + format = client.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, + envelope = this.client.http.supportsLinkHeaders ? undefined : format, + headers = Defaults.defaultGetHeaders(client.options, { format }); - if (typeof callback !== 'function') { - if (this.rest.options.promises) { - return Utils.promisify(this, 'list', arguments); - } - callback = noop; - } + Utils.mixin(headers, client.options.headers); - if (rest.options.headers) Utils.mixin(headers, rest.options.headers); - - new PaginatedResource(rest, '/push/channelSubscriptions', headers, envelope, function ( - body: any, - headers: Record, - unpacked?: boolean + return new PaginatedResource(client, '/push/channelSubscriptions', headers, envelope, async function ( + body, + headers, + unpacked, ) { - return PushChannelSubscription.fromResponseBody(body, unpacked ? undefined : format); - }).get(params, callback); + return PushChannelSubscription.fromResponseBody( + body as Record[], + client._MsgPack, + unpacked ? undefined : format, + ); + }).get(params); } - removeWhere(params: any, callback: PaginatedResultCallback) { - const rest = this.rest, - format = rest.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, - headers = Utils.defaultGetHeaders(rest.options, format); - - if (typeof callback !== 'function') { - if (this.rest.options.promises) { - return Utils.promisify(this, 'removeWhere', arguments); - } - callback = noop; - } + async removeWhere(params: any): Promise { + const client = this.client, + format = client.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, + headers = Defaults.defaultGetHeaders(client.options, { format }); - if (rest.options.headers) Utils.mixin(headers, rest.options.headers); + Utils.mixin(headers, client.options.headers); - if (rest.options.pushFullWait) Utils.mixin(params, { fullWait: 'true' }); + if (client.options.pushFullWait) Utils.mixin(params, { fullWait: 'true' }); - Resource['delete'](rest, '/push/channelSubscriptions', headers, params, null, (err) => callback(err)); + await Resource['delete'](client, '/push/channelSubscriptions', headers, params, null, true); } /* ChannelSubscriptions have no unique id; removing one is equivalent to removeWhere by its properties */ remove = ChannelSubscriptions.prototype.removeWhere; - listChannels(params: any, callback: PaginatedResultCallback) { - const rest = this.rest, - format = rest.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, - envelope = this.rest.http.supportsLinkHeaders ? undefined : format, - headers = Utils.defaultGetHeaders(rest.options, format); + async listChannels(params: any): Promise> { + const client = this.client, + format = client.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, + envelope = this.client.http.supportsLinkHeaders ? undefined : format, + headers = Defaults.defaultGetHeaders(client.options, { format }); - if (typeof callback !== 'function') { - if (this.rest.options.promises) { - return Utils.promisify(this, 'listChannels', arguments); - } - callback = noop; - } + Utils.mixin(headers, client.options.headers); - if (rest.options.headers) Utils.mixin(headers, rest.options.headers); + if (client.options.pushFullWait) Utils.mixin(params, { fullWait: 'true' }); - if (rest.options.pushFullWait) Utils.mixin(params, { fullWait: 'true' }); - - new PaginatedResource(rest, '/push/channels', headers, envelope, function ( - body: unknown, - headers: Record, - unpacked?: boolean - ) { - const parsedBody = (!unpacked && format ? Utils.decodeBody(body, format) : body) as Array; + return new PaginatedResource(client, '/push/channels', headers, envelope, async function (body, headers, unpacked) { + const parsedBody = ( + !unpacked && format ? Utils.decodeBody(body, client._MsgPack, format) : body + ) as Array; for (let i = 0; i < parsedBody.length; i++) { parsedBody[i] = String(parsedBody[i]); } return parsedBody; - }).get(params, callback); + }).get(params); } } diff --git a/src/common/lib/client/realtimechannel.ts b/src/common/lib/client/realtimechannel.ts index c2b1db6f39..637c1c508e 100644 --- a/src/common/lib/client/realtimechannel.ts +++ b/src/common/lib/client/realtimechannel.ts @@ -1,19 +1,33 @@ -import ProtocolMessage from '../types/protocolmessage'; +import ProtocolMessage, { + actions, + channelModes, + fromValues as protocolMessageFromValues, +} from '../types/protocolmessage'; import EventEmitter from '../util/eventemitter'; import * as Utils from '../util/utils'; -import Channel from './channel'; import Logger from '../util/logger'; import RealtimePresence from './realtimepresence'; -import Message, { CipherOptions } from '../types/message'; +import Message, { + fromValues as messageFromValues, + fromValuesArray as messagesFromValuesArray, + encodeArray as encodeMessagesArray, + decode as decodeMessage, + getMessagesSize, + CipherOptions, + EncodingDecodingContext, +} from '../types/message'; import ChannelStateChange from './channelstatechange'; -import ErrorInfo, { IPartialErrorInfo, PartialErrorInfo } from '../types/errorinfo'; -import PresenceMessage from '../types/presencemessage'; +import ErrorInfo, { PartialErrorInfo } from '../types/errorinfo'; +import PresenceMessage, { decode as decodePresenceMessage } from '../types/presencemessage'; import ConnectionErrors from '../transport/connectionerrors'; import * as API from '../../../../ably'; import ConnectionManager from '../transport/connectionmanager'; import ConnectionStateChange from './connectionstatechange'; -import { ErrCallback, PaginatedResultCallback, StandardCallback } from '../../types/utils'; -import Realtime from './realtime'; +import { ErrCallback, StandardCallback } from '../../types/utils'; +import BaseRealtime from './baserealtime'; +import { ChannelOptions } from '../../types/channel'; +import { normaliseChannelOptions } from '../util/defaults'; +import { PaginatedResult } from './paginatedresource'; interface RealtimeHistoryParams { start?: number; @@ -24,15 +38,14 @@ interface RealtimeHistoryParams { from_serial?: string; } -const actions = ProtocolMessage.Action; const noop = function () {}; -function validateChannelOptions(options?: API.Types.ChannelOptions) { +function validateChannelOptions(options?: API.ChannelOptions) { if (options && 'params' in options && !Utils.isObject(options.params)) { return new ErrorInfo('options.params must be an object', 40000, 400); } if (options && 'modes' in options) { - if (!Utils.isArray(options.modes)) { + if (!Array.isArray(options.modes)) { return new ErrorInfo('options.modes must be an array', 40000, 400); } for (let i = 0; i < options.modes.length; i++) { @@ -40,7 +53,7 @@ function validateChannelOptions(options?: API.Types.ChannelOptions) { if ( !currentMode || typeof currentMode !== 'string' || - !Utils.arrIn(ProtocolMessage.channelModes, String.prototype.toUpperCase.call(currentMode)) + !channelModes.includes(String.prototype.toUpperCase.call(currentMode)) ) { return new ErrorInfo('Invalid channel mode: ' + currentMode, 40000, 400); } @@ -48,26 +61,31 @@ function validateChannelOptions(options?: API.Types.ChannelOptions) { } } -class RealtimeChannel extends Channel { - realtime: Realtime; - presence: RealtimePresence; +class RealtimeChannel extends EventEmitter { + name: string; + channelOptions: ChannelOptions; + client: BaseRealtime; + private _presence: RealtimePresence | null; + get presence(): RealtimePresence { + if (!this._presence) { + Utils.throwMissingPluginError('RealtimePresence'); + } + return this._presence; + } connectionManager: ConnectionManager; - state: API.Types.ChannelState; + state: API.ChannelState; subscriptions: EventEmitter; - filteredSubscriptions?: Map< - API.Types.messageCallback, - Map[]> - >; + filteredSubscriptions?: Map, Map[]>>; syncChannelSerial?: string | null; properties: { attachSerial: string | null | undefined; channelSerial: string | null | undefined; }; errorReason: ErrorInfo | string | null; - _requestedFlags: Array | null; + _requestedFlags: Array | null; _mode?: null | number; _attachResume: boolean; - _decodingContext: { channelOptions: API.Types.ChannelOptions; plugins: any; baseEncodedPreviousPayload: undefined }; + _decodingContext: EncodingDecodingContext; _lastPayload: { messageId?: string | null; protocolMessageChannelSerial?: string | null; @@ -80,12 +98,14 @@ class RealtimeChannel extends Channel { retryTimer?: number | NodeJS.Timeout | null; retryCount: number = 0; - constructor(realtime: Realtime, name: string, options?: API.Types.ChannelOptions) { - super(realtime, name, options); + constructor(client: BaseRealtime, name: string, options?: API.ChannelOptions) { + super(); Logger.logAction(Logger.LOG_MINOR, 'RealtimeChannel()', 'started; name = ' + name); - this.realtime = realtime; - this.presence = new RealtimePresence(this); - this.connectionManager = realtime.connection.connectionManager; + this.name = name; + this.channelOptions = normaliseChannelOptions(client._Crypto ?? null, options); + this.client = client; + this._presence = client._RealtimePresence ? new client._RealtimePresence.RealtimePresence(this) : null; + this.connectionManager = client.connection.connectionManager; this.state = 'initialized'; this.subscriptions = new EventEmitter(); this.syncChannelSerial = undefined; @@ -100,7 +120,7 @@ class RealtimeChannel extends Channel { this._attachResume = false; this._decodingContext = { channelOptions: this.channelOptions, - plugins: realtime.options.plugins || {}, + plugins: client.options.plugins || {}, baseEncodedPreviousPayload: undefined, }; this._lastPayload = { @@ -118,42 +138,26 @@ class RealtimeChannel extends Channel { 'Channel operation failed as channel state is ' + this.state, 90001, 400, - this.errorReason || undefined + this.errorReason || undefined, ); } static processListenerArgs(args: unknown[]): any[] { - /* [event], listener, [callback] */ + /* [event], listener */ args = Array.prototype.slice.call(args); if (typeof args[0] === 'function') { args.unshift(null); } - if (args[args.length - 1] == undefined) { - args.pop(); - } return args; } - setOptions(options?: API.Types.ChannelOptions, callback?: ErrCallback): void | Promise { + async setOptions(options?: API.ChannelOptions): Promise { const previousChannelOptions = this.channelOptions; - if (!callback) { - if (this.rest.options.promises) { - return Utils.promisify(this, 'setOptions', arguments); - } - } - const _callback = - callback || - function (err?: IPartialErrorInfo | null) { - if (err) { - Logger.logAction(Logger.LOG_ERROR, 'RealtimeChannel.setOptions()', 'Set options failed: ' + err.toString()); - } - }; const err = validateChannelOptions(options); if (err) { - _callback(err); - return; + throw err; } - Channel.prototype.setOptions.call(this, options); + this.channelOptions = normaliseChannelOptions(this.client._Crypto ?? null, options); if (this._decodingContext) this._decodingContext.channelOptions = this.channelOptions; if (this._shouldReattachToSetOptions(options, previousChannelOptions)) { /* This does not just do _attach(true, null, callback) because that would put us @@ -163,29 +167,28 @@ class RealtimeChannel extends Channel { * rejecting messages until we have confirmation that the options have changed, * which would unnecessarily lose message continuity. */ this.attachImpl(); - // Ignore 'attaching' -- could be just due to to a resume & reattach, should not - // call back setOptions until we're definitely attached with the new options (or - // else in a terminal state) - this._allChannelChanges.once( - ['attached', 'update', 'detached', 'failed'], - function (this: { event: string }, stateChange: ConnectionStateChange) { - switch (this.event) { - case 'update': - case 'attached': - _callback?.(null); - return; - default: - _callback?.(stateChange.reason); - return; - } - } - ); - } else { - _callback(); + return new Promise((resolve, reject) => { + // Ignore 'attaching' -- could be just due to to a resume & reattach, should not + // call back setOptions until we're definitely attached with the new options (or + // else in a terminal state) + this._allChannelChanges.once( + ['attached', 'update', 'detached', 'failed'], + function (this: { event: string }, stateChange: ConnectionStateChange) { + switch (this.event) { + case 'update': + case 'attached': + resolve(); + break; + default: + reject(stateChange.reason); + } + }, + ); + }); } } - _shouldReattachToSetOptions(options: API.Types.ChannelOptions | undefined, prevOptions: API.Types.ChannelOptions) { + _shouldReattachToSetOptions(options: API.ChannelOptions | undefined, prevOptions: API.ChannelOptions) { if (!(this.state === 'attached' || this.state === 'attaching')) { return false; } @@ -210,62 +213,46 @@ class RealtimeChannel extends Channel { return false; } - publish(...args: any[]): void | Promise { + async publish(...args: any[]): Promise { let messages = args[0]; let argCount = args.length; - let callback = args[argCount - 1]; - if (typeof callback !== 'function') { - if (this.realtime.options.promises) { - return Utils.promisify(this, 'publish', arguments); - } - callback = noop; - ++argCount; - } if (!this.connectionManager.activeState()) { - callback(this.connectionManager.getError()); - return; + throw this.connectionManager.getError(); } - if (argCount == 2) { - if (Utils.isObject(messages)) messages = [Message.fromValues(messages)]; - else if (Utils.isArray(messages)) messages = Message.fromValuesArray(messages); + if (argCount == 1) { + if (Utils.isObject(messages)) messages = [messageFromValues(messages)]; + else if (Array.isArray(messages)) messages = messagesFromValuesArray(messages); else throw new ErrorInfo( 'The single-argument form of publish() expects a message object or an array of message objects', 40013, - 400 + 400, ); } else { - messages = [Message.fromValues({ name: args[0], data: args[1] })]; + messages = [messageFromValues({ name: args[0], data: args[1] })]; + } + const maxMessageSize = this.client.options.maxMessageSize; + await encodeMessagesArray(messages, this.channelOptions as CipherOptions); + /* RSL1i */ + const size = getMessagesSize(messages); + if (size > maxMessageSize) { + throw new ErrorInfo( + 'Maximum size of messages that can be published at once exceeded ( was ' + + size + + ' bytes; limit is ' + + maxMessageSize + + ' bytes)', + 40009, + 400, + ); } - const maxMessageSize = this.realtime.options.maxMessageSize; - Message.encodeArray(messages, this.channelOptions as CipherOptions, (err: Error | null) => { - if (err) { - callback(err); - return; - } - /* RSL1i */ - const size = Message.getMessagesSize(messages); - if (size > maxMessageSize) { - callback( - new ErrorInfo( - 'Maximum size of messages that can be published at once exceeded ( was ' + - size + - ' bytes; limit is ' + - maxMessageSize + - ' bytes)', - 40009, - 400 - ) - ); - return; - } - this.__publish(messages, callback); + return new Promise((resolve, reject) => { + this._publish(messages, (err) => (err ? reject(err) : resolve())); }); } - // Double underscore used to prevent type conflict with underlying Channel._publish method - __publish(messages: Array, callback: ErrCallback) { + _publish(messages: Array, callback: ErrCallback) { Logger.logAction(Logger.LOG_MICRO, 'RealtimeChannel.publish()', 'message count = ' + messages.length); const state = this.state; switch (state) { @@ -294,47 +281,20 @@ class RealtimeChannel extends Channel { } } - attach( - flags?: API.Types.ChannelMode[] | ErrCallback, - callback?: StandardCallback - ): void | Promise { - let _flags: API.Types.ChannelMode[] | null | undefined; - if (typeof flags === 'function') { - callback = flags; - _flags = null; - } else { - _flags = flags; - } - if (!callback) { - if (this.realtime.options.promises) { - return Utils.promisify(this, 'attach', arguments); - } - callback = function (err?: ErrorInfo | null) { - if (err) { - Logger.logAction(Logger.LOG_MAJOR, 'RealtimeChannel.attach()', 'Channel attach failed: ' + err.toString()); - } - }; - } - if (_flags) { - Logger.deprecated( - 'The ability to pass an array of channel mode flags as the first argument of `RealtimeChannel.attach()`', - 'To set channel mode flags, populate the `modes` property of the channel options object that you pass to `Channels.get()` or `RealtimeChannel.setOptions()`.' - ); - /* If flags requested, always do a re-attach. TODO only do this if - * current mode differs from requested mode */ - this._requestedFlags = _flags as API.Types.ChannelMode[]; - } else if (this.state === 'attached') { - callback(null, null); - return; + async attach(): Promise { + if (this.state === 'attached') { + return null; } - this._attach(false, null, callback); + return new Promise((resolve, reject) => { + this._attach(false, null, (err, result) => (err ? reject(err) : resolve(result!))); + }); } _attach( forceReattach: boolean, attachReason: ErrorInfo | null, - callback?: StandardCallback + callback?: StandardCallback, ): void { if (!callback) { callback = function (err?: ErrorInfo | null) { @@ -365,7 +325,7 @@ class RealtimeChannel extends Channel { callback?.( stateChange.reason || connectionManager.getError() || - new ErrorInfo('Unable to attach; reason unknown; state = ' + this.event, 90000, 500) + new ErrorInfo('Unable to attach; reason unknown; state = ' + this.event, 90000, 500), ); break; case 'detaching': @@ -377,7 +337,7 @@ class RealtimeChannel extends Channel { attachImpl(): void { Logger.logAction(Logger.LOG_MICRO, 'RealtimeChannel.attachImpl()', 'sending ATTACH message'); - const attachMsg = ProtocolMessage.fromValues({ + const attachMsg = protocolMessageFromValues({ action: actions.ATTACH, channel: this.name, params: this.channelOptions.params, @@ -388,7 +348,7 @@ class RealtimeChannel extends Channel { if (this._requestedFlags) { attachMsg.encodeModesToFlags(this._requestedFlags); } else if (this.channelOptions.modes) { - attachMsg.encodeModesToFlags(Utils.allToUpperCase(this.channelOptions.modes) as API.Types.ChannelMode[]); + attachMsg.encodeModesToFlags(Utils.allToUpperCase(this.channelOptions.modes) as API.ChannelMode[]); } if (this._attachResume) { attachMsg.setFlag('ATTACH_RESUME'); @@ -399,182 +359,68 @@ class RealtimeChannel extends Channel { this.sendMessage(attachMsg, noop); } - detach(callback: ErrCallback): void | Promise { - if (!callback) { - if (this.realtime.options.promises) { - return Utils.promisify(this, 'detach', arguments); - } - callback = noop; - } + async detach(): Promise { const connectionManager = this.connectionManager; if (!connectionManager.activeState()) { - callback(connectionManager.getError()); - return; + throw connectionManager.getError(); } switch (this.state) { case 'suspended': this.notifyState('detached'); - callback(); - break; + return; case 'detached': - callback(); - break; + return; case 'failed': - callback(new ErrorInfo('Unable to detach; channel state = failed', 90001, 400)); - break; + throw new ErrorInfo('Unable to detach; channel state = failed', 90001, 400); default: this.requestState('detaching'); // eslint-disable-next-line no-fallthrough case 'detaching': - this.once(function (this: { event: string }, stateChange: ChannelStateChange) { - switch (this.event) { - case 'detached': - callback(); - break; - case 'attached': - case 'suspended': - case 'failed': - callback( - stateChange.reason || - connectionManager.getError() || - new ErrorInfo('Unable to detach; reason unknown; state = ' + this.event, 90000, 500) - ); - break; - case 'attaching': - callback(new ErrorInfo('Detach request superseded by a subsequent attach request', 90000, 409)); - break; - } + return new Promise((resolve, reject) => { + this.once(function (this: { event: string }, stateChange: ChannelStateChange) { + switch (this.event) { + case 'detached': + resolve(); + break; + case 'attached': + case 'suspended': + case 'failed': + reject( + stateChange.reason || + connectionManager.getError() || + new ErrorInfo('Unable to detach; reason unknown; state = ' + this.event, 90000, 500), + ); + break; + case 'attaching': + reject(new ErrorInfo('Detach request superseded by a subsequent attach request', 90000, 409)); + break; + } + }); }); } } detachImpl(callback?: ErrCallback): void { Logger.logAction(Logger.LOG_MICRO, 'RealtimeChannel.detach()', 'sending DETACH message'); - const msg = ProtocolMessage.fromValues({ action: actions.DETACH, channel: this.name }); + const msg = protocolMessageFromValues({ action: actions.DETACH, channel: this.name }); this.sendMessage(msg, callback || noop); } - subscribe(...args: unknown[] /* [event], listener, [callback] */): void | Promise { - const [event, listener, callback] = RealtimeChannel.processListenerArgs(args); - - if (!callback && this.realtime.options.promises) { - return Utils.promisify(this, 'subscribe', [event, listener]); - } + async subscribe(...args: unknown[] /* [event], listener */): Promise { + const [event, listener] = RealtimeChannel.processListenerArgs(args); if (this.state === 'failed') { - callback?.(ErrorInfo.fromValues(this.invalidStateError())); - return; + throw ErrorInfo.fromValues(this.invalidStateError()); } // Filtered if (event && typeof event === 'object' && !Array.isArray(event)) { - this._subscribeFilter(event, listener); + this.client._FilteredSubscriptions.subscribeFilter(this, event, listener); } else { this.subscriptions.on(event, listener); } - return this.attach(callback || noop); - } - - _subscribeFilter(filter: API.Types.MessageFilter, listener: API.Types.messageCallback) { - const filteredListener = (m: Message) => { - const mapping: { [key in keyof API.Types.MessageFilter]: any } = { - name: m.name, - refTimeserial: m.extras?.ref?.timeserial, - refType: m.extras?.ref?.type, - isRef: !!m.extras?.ref?.timeserial, - clientId: m.clientId, - }; - // Check if any values are defined in the filter and if they match the value in the message object - if ( - Object.entries(filter).find(([key, value]) => - value !== undefined ? mapping[key as keyof API.Types.MessageFilter] !== value : false - ) - ) { - return; - } - listener(m); - }; - this._addFilteredSubscription(filter, listener, filteredListener); - this.subscriptions.on(filteredListener); - } - - // Adds a new filtered subscription - _addFilteredSubscription( - filter: API.Types.MessageFilter, - realListener: API.Types.messageCallback, - filteredListener: API.Types.messageCallback - ) { - if (!this.filteredSubscriptions) { - this.filteredSubscriptions = new Map< - API.Types.messageCallback, - Map[]> - >(); - } - if (this.filteredSubscriptions.has(realListener)) { - const realListenerMap = this.filteredSubscriptions.get(realListener) as Map< - API.Types.MessageFilter, - API.Types.messageCallback[] - >; - // Add the filtered listener to the map, or append to the array if this filter has already been used - realListenerMap.set(filter, realListenerMap?.get(filter)?.concat(filteredListener) || [filteredListener]); - } else { - this.filteredSubscriptions.set( - realListener, - new Map[]>([[filter, [filteredListener]]]) - ); - } - } - - _getAndDeleteFilteredSubscriptions( - filter: API.Types.MessageFilter | undefined, - realListener: API.Types.messageCallback | undefined - ): API.Types.messageCallback[] { - // No filtered subscriptions map means there has been no filtered subscriptions yet, so return nothing - if (!this.filteredSubscriptions) { - return []; - } - // Only a filter is passed in with no specific listener - if (!realListener && filter) { - // Return each listener which is attached to the specified filter object - return Array.from(this.filteredSubscriptions.entries()) - .map(([key, filterMaps]) => { - // Get (then delete) the maps matching this filter - let listenerMaps = filterMaps.get(filter); - filterMaps.delete(filter); - // Clear the parent if nothing is left - if (filterMaps.size === 0) { - this.filteredSubscriptions?.delete(key); - } - return listenerMaps; - }) - .reduce( - (prev, cur) => (cur ? (prev as API.Types.messageCallback[]).concat(...cur) : prev), - [] - ) as API.Types.messageCallback[]; - } - - // No subscriptions for this listener - if (!realListener || !this.filteredSubscriptions.has(realListener)) { - return []; - } - const realListenerMap = this.filteredSubscriptions.get(realListener) as Map< - API.Types.MessageFilter, - API.Types.messageCallback[] - >; - // If no filter is specified return all listeners using that function - if (!filter) { - // array.flat is not available unless we support es2019 or higher - const listeners = Array.from(realListenerMap.values()).reduce((prev, cur) => prev.concat(...cur), []); - // remove the listener from the map - this.filteredSubscriptions.delete(realListener); - return listeners; - } - - let listeners = realListenerMap.get(filter); - realListenerMap.delete(filter); - - return listeners || []; + return this.attach(); } unsubscribe(...args: unknown[] /* [event], listener */): void { @@ -582,7 +428,9 @@ class RealtimeChannel extends Channel { // If we either have a filtered listener, a filter or both we need to do additional processing to find the original function(s) if ((typeof event === 'object' && !listener) || this.filteredSubscriptions?.has(listener)) { - this._getAndDeleteFilteredSubscriptions(event, listener).forEach((l) => this.subscriptions.off(l)); + this.client._FilteredSubscriptions + .getAndDeleteFilteredSubscriptions(this, event, listener) + .forEach((l) => this.subscriptions.off(l)); return; } @@ -604,7 +452,7 @@ class RealtimeChannel extends Channel { } /* send sync request */ - const syncMessage = ProtocolMessage.fromValues({ action: actions.SYNC, channel: this.name }); + const syncMessage = protocolMessageFromValues({ action: actions.SYNC, channel: this.name }); if (this.syncChannelSerial) { syncMessage.channelSerial = this.syncChannelSerial; } @@ -612,21 +460,22 @@ class RealtimeChannel extends Channel { } sendMessage(msg: ProtocolMessage, callback?: ErrCallback): void { - this.connectionManager.send(msg, this.realtime.options.queueMessages, callback); + this.connectionManager.send(msg, this.client.options.queueMessages, callback); } sendPresence(presence: PresenceMessage | PresenceMessage[], callback?: ErrCallback): void { - const msg = ProtocolMessage.fromValues({ + const msg = protocolMessageFromValues({ action: actions.PRESENCE, channel: this.name, - presence: Utils.isArray(presence) - ? PresenceMessage.fromValuesArray(presence) - : [PresenceMessage.fromValues(presence)], + presence: Array.isArray(presence) + ? this.client._RealtimePresence!.presenceMessagesFromValuesArray(presence) + : [this.client._RealtimePresence!.presenceMessageFromValues(presence)], }); this.sendMessage(msg, callback); } - onMessage(message: ProtocolMessage): void { + // Access to this method is synchronised by ConnectionManager#processChannelMessage, in order to synchronise access to the state stored in _decodingContext. + async processMessage(message: ProtocolMessage): Promise { if ( message.action === actions.ATTACHED || message.action === actions.MESSAGE || @@ -651,7 +500,9 @@ class RealtimeChannel extends Channel { if (this.state === 'attached') { if (!resumed) { /* On a loss of continuity, the presence set needs to be re-synced */ - this.presence.onAttached(hasPresence); + if (this._presence) { + this._presence.onAttached(hasPresence); + } } const change = new ChannelStateChange(this.state, this.state, resumed, hasBacklog, message.error); this._allChannelChanges.emit('update', change); @@ -693,7 +544,12 @@ class RealtimeChannel extends Channel { if (!message.presence) break; // eslint-disable-next-line no-fallthrough case actions.PRESENCE: { - const presence = message.presence as Array; + const presence = message.presence; + + if (!presence) { + break; + } + const { id, connectionId, timestamp } = message; const options = this.channelOptions; @@ -701,15 +557,17 @@ class RealtimeChannel extends Channel { for (let i = 0; i < presence.length; i++) { try { presenceMsg = presence[i]; - PresenceMessage.decode(presenceMsg, options); + await decodePresenceMessage(presenceMsg, options); if (!presenceMsg.connectionId) presenceMsg.connectionId = connectionId; if (!presenceMsg.timestamp) presenceMsg.timestamp = timestamp; if (!presenceMsg.id) presenceMsg.id = id + ':' + i; } catch (e) { - Logger.logAction(Logger.LOG_ERROR, 'RealtimeChannel.onMessage()', (e as Error).toString()); + Logger.logAction(Logger.LOG_ERROR, 'RealtimeChannel.processMessage()', (e as Error).toString()); } } - this.presence.setPresence(presence, isSync, syncChannelSerial as any); + if (this._presence) { + this._presence.setPresence(presence, isSync, syncChannelSerial as any); + } break; } case actions.MESSAGE: { @@ -717,14 +575,14 @@ class RealtimeChannel extends Channel { if (this.state !== 'attached') { Logger.logAction( Logger.LOG_MAJOR, - 'RealtimeChannel.onMessage()', + 'RealtimeChannel.processMessage()', 'Message "' + message.id + '" skipped as this channel "' + this.name + '" state is not "attached" (state is "' + this.state + - '").' + '").', ); return; } @@ -747,7 +605,7 @@ class RealtimeChannel extends Channel { '" on this channel "' + this.name + '".'; - Logger.logAction(Logger.LOG_ERROR, 'RealtimeChannel.onMessage()', msg); + Logger.logAction(Logger.LOG_ERROR, 'RealtimeChannel.processMessage()', msg); this._startDecodeFailureRecovery(new ErrorInfo(msg, 40018, 400)); break; } @@ -755,10 +613,10 @@ class RealtimeChannel extends Channel { for (let i = 0; i < messages.length; i++) { const msg = messages[i]; try { - Message.decode(msg, this._decodingContext); + await decodeMessage(msg, this._decodingContext); } catch (e) { /* decrypt failed .. the most likely cause is that we have the wrong key */ - Logger.logAction(Logger.LOG_ERROR, 'RealtimeChannel.onMessage()', (e as Error).toString()); + Logger.logAction(Logger.LOG_ERROR, 'RealtimeChannel.processMessage()', (e as Error).toString()); switch ((e as ErrorInfo).code) { case 40018: /* decode failure */ @@ -798,8 +656,8 @@ class RealtimeChannel extends Channel { default: Logger.logAction( Logger.LOG_ERROR, - 'RealtimeChannel.onMessage()', - 'Fatal protocol error: unrecognised action (' + message.action + ')' + 'RealtimeChannel.processMessage()', + 'Fatal protocol error: unrecognised action (' + message.action + ')', ); this.connectionManager.abort(ConnectionErrors.unknownChannelErr()); } @@ -807,7 +665,11 @@ class RealtimeChannel extends Channel { _startDecodeFailureRecovery(reason: ErrorInfo): void { if (!this._lastPayload.decodeFailureRecoveryInProgress) { - Logger.logAction(Logger.LOG_MAJOR, 'RealtimeChannel.onMessage()', 'Starting decode failure recovery process.'); + Logger.logAction( + Logger.LOG_MAJOR, + 'RealtimeChannel.processMessage()', + 'Starting decode failure recovery process.', + ); this._lastPayload.decodeFailureRecoveryInProgress = true; this._attach(true, reason, () => { this._lastPayload.decodeFailureRecoveryInProgress = false; @@ -820,28 +682,30 @@ class RealtimeChannel extends Channel { } notifyState( - state: API.Types.ChannelState, + state: API.ChannelState, reason?: ErrorInfo | null, resumed?: boolean, hasPresence?: boolean, - hasBacklog?: boolean + hasBacklog?: boolean, ): void { Logger.logAction( Logger.LOG_MICRO, 'RealtimeChannel.notifyState', - 'name = ' + this.name + ', current state = ' + this.state + ', notifying state ' + state + 'name = ' + this.name + ', current state = ' + this.state + ', notifying state ' + state, ); this.clearStateTimer(); // RTP5a1 - if (Utils.arrIn(['detached', 'suspended', 'failed'], state)) { + if (['detached', 'suspended', 'failed'].includes(state)) { this.properties.channelSerial = null; } if (state === this.state) { return; } - this.presence.actOnChannelState(state, hasPresence, reason); + if (this._presence) { + this._presence.actOnChannelState(state, hasPresence, reason); + } if (state === 'suspended' && this.connectionManager.state.sendEvents) { this.startRetryTimer(); } else { @@ -851,12 +715,13 @@ class RealtimeChannel extends Channel { this.errorReason = reason; } const change = new ChannelStateChange(this.state, state, resumed, hasBacklog, reason); - const logLevel = state === 'failed' ? Logger.LOG_ERROR : Logger.LOG_MAJOR; - Logger.logAction( - logLevel, - 'Channel state for channel "' + this.name + '"', - state + (reason ? '; reason: ' + reason : '') - ); + const action = 'Channel state for channel "' + this.name + '"'; + const message = state + (reason ? '; reason: ' + reason : ''); + if (state === 'failed') { + Logger.logAction(Logger.LOG_ERROR, action, message); + } else { + Logger.logAction(Logger.LOG_MAJOR, action, message); + } if (state !== 'attaching' && state !== 'suspended') { this.retryCount = 0; @@ -878,7 +743,7 @@ class RealtimeChannel extends Channel { this.emit(state, change); } - requestState(state: API.Types.ChannelState, reason?: ErrorInfo | null): void { + requestState(state: API.ChannelState, reason?: ErrorInfo | null): void { Logger.logAction(Logger.LOG_MINOR, 'RealtimeChannel.requestState', 'name = ' + this.name + ', state = ' + state); this.notifyState(state, reason); /* send the event and await response */ @@ -888,13 +753,11 @@ class RealtimeChannel extends Channel { checkPendingState(): void { /* if can't send events, do nothing */ const cmState = this.connectionManager.state; - /* Allow attach messages to queue up when synchronizing, since this will be - * the state we'll be in when upgrade transport.active triggers a checkpendingstate */ - if (!(cmState.sendEvents || cmState.forceQueueEvents)) { + if (!cmState.sendEvents) { Logger.logAction( Logger.LOG_MINOR, 'RealtimeChannel.checkPendingState', - 'sendEvents is false; state is ' + this.connectionManager.state.state + 'sendEvents is false; state is ' + this.connectionManager.state.state, ); return; } @@ -902,7 +765,7 @@ class RealtimeChannel extends Channel { Logger.logAction( Logger.LOG_MINOR, 'RealtimeChannel.checkPendingState', - 'name = ' + this.name + ', state = ' + this.state + 'name = ' + this.name + ', state = ' + this.state, ); /* Only start the state timer running when actually sending the event */ switch (this.state) { @@ -947,7 +810,7 @@ class RealtimeChannel extends Channel { Logger.logAction(Logger.LOG_MINOR, 'RealtimeChannel.startStateTimerIfNotRunning', 'timer expired'); this.stateTimer = null; this.timeoutPendingState(); - }, this.realtime.options.timeouts.realtimeRequestTimeout); + }, this.client.options.timeouts.realtimeRequestTimeout); } } @@ -963,7 +826,7 @@ class RealtimeChannel extends Channel { if (this.retryTimer) return; this.retryCount++; - const retryDelay = Utils.getRetryTime(this.realtime.options.timeouts.channelRetryTimeout, this.retryCount); + const retryDelay = Utils.getRetryTime(this.client.options.timeouts.channelRetryTimeout, this.retryCount); this.retryTimer = setTimeout(() => { /* If connection is not connected, just leave in suspended, a reattach @@ -983,49 +846,35 @@ class RealtimeChannel extends Channel { } } - history = function ( + history = async function ( this: RealtimeChannel, params: RealtimeHistoryParams | null, - callback: PaginatedResultCallback - ): void | Promise> { + ): Promise> { Logger.logAction(Logger.LOG_MICRO, 'RealtimeChannel.history()', 'channel = ' + this.name); - /* params and callback are optional; see if params contains the callback */ - if (callback === undefined) { - if (typeof params == 'function') { - callback = params; - params = null; - } else { - if (this.rest.options.promises) { - return Utils.promisify(this, 'history', arguments); - } - callback = noop; - } - } + + // We fetch this first so that any plugin-not-provided error takes priority over other errors + const restMixin = this.client.rest.channelMixin; if (params && params.untilAttach) { if (this.state !== 'attached') { - callback(new ErrorInfo('option untilAttach requires the channel to be attached', 40000, 400)); - return; + throw new ErrorInfo('option untilAttach requires the channel to be attached', 40000, 400); } if (!this.properties.attachSerial) { - callback( - new ErrorInfo( - 'untilAttach was specified and channel is attached, but attachSerial is not defined', - 40000, - 400 - ) + throw new ErrorInfo( + 'untilAttach was specified and channel is attached, but attachSerial is not defined', + 40000, + 400, ); - return; } delete params.untilAttach; params.from_serial = this.properties.attachSerial; } - Channel.prototype._history.call(this, params, callback); + return restMixin.history(this, params); } as any; - whenState = ((state: string, listener: ErrCallback) => { - return EventEmitter.prototype.whenState.call(this, state, this.state, listener); + whenState = ((state: string) => { + return EventEmitter.prototype.whenState.call(this, state, this.state); }) as any; /* @returns null (if can safely be released) | ErrorInfo (if cannot) */ @@ -1038,7 +887,7 @@ class RealtimeChannel extends Channel { 'Can only release a channel in a state where there is no possibility of further updates from the server being received (initialized, detached, or failed); was ' + s, 90001, - 400 + 400, ); } @@ -1046,7 +895,7 @@ class RealtimeChannel extends Channel { Logger.logAction( Logger.LOG_MICRO, 'RealtimeChannel.setChannelSerial()', - 'Updating channel serial; serial = ' + channelSerial + '; previous = ' + this.properties.channelSerial + 'Updating channel serial; serial = ' + channelSerial + '; previous = ' + this.properties.channelSerial, ); // RTP17h: Only update the channel serial if its present (it won't always @@ -1055,9 +904,13 @@ class RealtimeChannel extends Channel { this.properties.channelSerial = channelSerial; } } + + async status(): Promise { + return this.client.rest.channelMixin.status(this); + } } -function omitAgent(channelParams?: API.Types.ChannelParams) { +function omitAgent(channelParams?: API.ChannelParams) { const { agent: _, ...paramsWithoutAgent } = channelParams || {}; return paramsWithoutAgent; } diff --git a/src/common/lib/client/realtimepresence.ts b/src/common/lib/client/realtimepresence.ts index 0b7252a14f..612438aab1 100644 --- a/src/common/lib/client/realtimepresence.ts +++ b/src/common/lib/client/realtimepresence.ts @@ -1,14 +1,17 @@ import * as Utils from '../util/utils'; -import Presence from './presence'; import EventEmitter from '../util/eventemitter'; import Logger from '../util/logger'; -import PresenceMessage from '../types/presencemessage'; -import ErrorInfo, { IPartialErrorInfo, PartialErrorInfo } from '../types/errorinfo'; +import PresenceMessage, { + fromValues as presenceMessageFromValues, + fromData as presenceMessageFromData, + encode as encodePresenceMessage, +} from '../types/presencemessage'; +import ErrorInfo, { PartialErrorInfo } from '../types/errorinfo'; import RealtimeChannel from './realtimechannel'; import Multicaster from '../util/multicaster'; import ChannelStateChange from './channelstatechange'; import { CipherOptions } from '../types/message'; -import { ErrCallback, PaginatedResultCallback, StandardCallback } from '../../types/utils'; +import { ErrCallback } from '../../types/utils'; import { PaginatedResult } from './paginatedresource'; interface RealtimePresenceParams { @@ -26,14 +29,12 @@ interface RealtimeHistoryParams { from_serial?: string | null; } -const noop = function () {}; - function getClientId(realtimePresence: RealtimePresence) { - return realtimePresence.channel.realtime.auth.clientId; + return realtimePresence.channel.client.auth.clientId; } function isAnonymousOrWildcard(realtimePresence: RealtimePresence) { - const realtime = realtimePresence.channel.realtime; + const realtime = realtimePresence.channel.client; /* If not currently connected, we can't assume that we're an anonymous * client, as realtime may inform us of our clientId in the CONNECTED * message. So assume we're not anonymous and leave it to realtime to @@ -53,7 +54,7 @@ function waitAttached(channel: RealtimeChannel, callback: ErrCallback, action: ( case 'detached': case 'detaching': case 'attaching': - channel.attach(function (err: Error) { + Utils.whenPromiseSettles(channel.attach(), function (err: Error | null) { if (err) callback(err); else action(); }); @@ -80,7 +81,7 @@ function newerThan(item: PresenceMessage, existing: PresenceMessage) { } } -class RealtimePresence extends Presence { +class RealtimePresence extends EventEmitter { channel: RealtimeChannel; pendingPresence: { presence: PresenceMessage; callback: ErrCallback }[]; syncComplete: boolean; @@ -90,7 +91,7 @@ class RealtimePresence extends Presence { name?: string; constructor(channel: RealtimeChannel) { - super(channel); + super(); this.channel = channel; this.syncComplete = false; this.members = new PresenceMap(this, (item) => item.clientId + ':' + item.connectionId); @@ -100,60 +101,46 @@ class RealtimePresence extends Presence { this.pendingPresence = []; } - enter(data: unknown, callback: ErrCallback): void | Promise { + async enter(data: unknown): Promise { if (isAnonymousOrWildcard(this)) { throw new ErrorInfo('clientId must be specified to enter a presence channel', 40012, 400); } - return this._enterOrUpdateClient(undefined, undefined, data, 'enter', callback); + return this._enterOrUpdateClient(undefined, undefined, data, 'enter'); } - update(data: unknown, callback: ErrCallback): void | Promise { + async update(data: unknown): Promise { if (isAnonymousOrWildcard(this)) { throw new ErrorInfo('clientId must be specified to update presence data', 40012, 400); } - return this._enterOrUpdateClient(undefined, undefined, data, 'update', callback); + return this._enterOrUpdateClient(undefined, undefined, data, 'update'); } - enterClient(clientId: string, data: unknown, callback: ErrCallback): void | Promise { - return this._enterOrUpdateClient(undefined, clientId, data, 'enter', callback); + async enterClient(clientId: string, data: unknown): Promise { + return this._enterOrUpdateClient(undefined, clientId, data, 'enter'); } - updateClient(clientId: string, data: unknown, callback: ErrCallback): void | Promise { - return this._enterOrUpdateClient(undefined, clientId, data, 'update', callback); + async updateClient(clientId: string, data: unknown): Promise { + return this._enterOrUpdateClient(undefined, clientId, data, 'update'); } - _enterOrUpdateClient( + async _enterOrUpdateClient( id: string | undefined, clientId: string | undefined, data: unknown, action: string, - callback: ErrCallback - ): void | Promise { - if (!callback) { - if (typeof data === 'function') { - callback = data as ErrCallback; - data = null; - } else { - if (this.channel.realtime.options.promises) { - return Utils.promisify(this, '_enterOrUpdateClient', [id, clientId, data, action]); - } - callback = noop; - } - } - + ): Promise { const channel = this.channel; if (!channel.connectionManager.activeState()) { - callback(channel.connectionManager.getError()); - return; + throw channel.connectionManager.getError(); } Logger.logAction( Logger.LOG_MICRO, 'RealtimePresence.' + action + 'Client()', - 'channel = ' + channel.name + ', id = ' + id + ', client = ' + (clientId || '(implicit) ' + getClientId(this)) + 'channel = ' + channel.name + ', id = ' + id + ', client = ' + (clientId || '(implicit) ' + getClientId(this)), ); - const presence = PresenceMessage.fromData(data); + const presence = presenceMessageFromData(data); presence.action = action; if (id) { presence.id = id; @@ -162,185 +149,150 @@ class RealtimePresence extends Presence { presence.clientId = clientId; } - PresenceMessage.encode(presence, channel.channelOptions as CipherOptions, (err: IPartialErrorInfo) => { - if (err) { - callback(err); - return; - } - switch (channel.state) { - case 'attached': - channel.sendPresence(presence, callback); - break; - case 'initialized': - case 'detached': - channel.attach(); - // eslint-disable-next-line no-fallthrough - case 'attaching': + await encodePresenceMessage(presence, channel.channelOptions as CipherOptions); + switch (channel.state) { + case 'attached': + return new Promise((resolve, reject) => { + channel.sendPresence(presence, (err) => (err ? reject(err) : resolve())); + }); + case 'initialized': + case 'detached': + channel.attach(); + // eslint-disable-next-line no-fallthrough + case 'attaching': + return new Promise((resolve, reject) => { this.pendingPresence.push({ presence: presence, - callback: callback, + callback: (err) => (err ? reject(err) : resolve()), }); - break; - default: - err = new PartialErrorInfo( - 'Unable to ' + action + ' presence channel while in ' + channel.state + ' state', - 90001 - ); - err.code = 90001; - callback(err); + }); + default: { + const err = new PartialErrorInfo( + 'Unable to ' + action + ' presence channel while in ' + channel.state + ' state', + 90001, + ); + err.code = 90001; + throw err; } - }); + } } - leave(data: unknown, callback: ErrCallback): void | Promise { + async leave(data: unknown): Promise { if (isAnonymousOrWildcard(this)) { throw new ErrorInfo('clientId must have been specified to enter or leave a presence channel', 40012, 400); } - return this.leaveClient(undefined, data, callback); + return this.leaveClient(undefined, data); } - leaveClient(clientId?: string, data?: unknown, callback?: ErrCallback): void | Promise { - if (!callback) { - if (typeof data === 'function') { - callback = data as ErrCallback; - data = null; - } else { - if (this.channel.realtime.options.promises) { - return Utils.promisify(this, 'leaveClient', [clientId, data]); - } - callback = noop; - } - } - + async leaveClient(clientId?: string, data?: unknown): Promise { const channel = this.channel; if (!channel.connectionManager.activeState()) { - callback?.(channel.connectionManager.getError()); - return; + throw channel.connectionManager.getError(); } Logger.logAction( Logger.LOG_MICRO, 'RealtimePresence.leaveClient()', - 'leaving; channel = ' + this.channel.name + ', client = ' + clientId + 'leaving; channel = ' + this.channel.name + ', client = ' + clientId, ); - const presence = PresenceMessage.fromData(data); + const presence = presenceMessageFromData(data); presence.action = 'leave'; if (clientId) { presence.clientId = clientId; } - switch (channel.state) { - case 'attached': - channel.sendPresence(presence, callback); - break; - case 'attaching': - this.pendingPresence.push({ - presence: presence, - callback: callback, - }); - break; - case 'initialized': - case 'failed': { - /* we're not attached; therefore we let any entered status - * timeout by itself instead of attaching just in order to leave */ - const err = new PartialErrorInfo('Unable to leave presence channel (incompatible state)', 90001); - callback?.(err); - break; + return new Promise((resolve, reject) => { + switch (channel.state) { + case 'attached': + channel.sendPresence(presence, (err) => (err ? reject(err) : resolve())); + break; + case 'attaching': + this.pendingPresence.push({ + presence: presence, + callback: (err) => (err ? reject(err) : resolve()), + }); + break; + case 'initialized': + case 'failed': { + /* we're not attached; therefore we let any entered status + * timeout by itself instead of attaching just in order to leave */ + const err = new PartialErrorInfo('Unable to leave presence channel (incompatible state)', 90001); + reject(err); + break; + } + default: + reject(channel.invalidStateError()); } - default: - callback?.(channel.invalidStateError()); - } + }); } - // Return type is any to avoid conflict with base Presence class - get(this: RealtimePresence, params: RealtimePresenceParams, callback: StandardCallback): any { - const args = Array.prototype.slice.call(arguments); - if (args.length == 1 && typeof args[0] == 'function') args.unshift(null); - - params = args[0] as RealtimePresenceParams; - callback = args[1] as StandardCallback; + async get(params?: RealtimePresenceParams): Promise { const waitForSync = !params || ('waitForSync' in params ? params.waitForSync : true); - if (!callback) { - if (this.channel.realtime.options.promises) { - return Utils.promisify(this, 'get', args); + return new Promise((resolve, reject) => { + function returnMembers(members: PresenceMap) { + resolve(params ? members.list(params) : members.values()); } - callback = noop; - } - - function returnMembers(members: PresenceMap) { - callback(null, params ? members.list(params) : members.values()); - } - /* Special-case the suspended state: can still get (stale) presence set if waitForSync is false */ - if (this.channel.state === 'suspended') { - if (waitForSync) { - callback( - ErrorInfo.fromValues({ - statusCode: 400, - code: 91005, - message: 'Presence state is out of sync due to channel being in the SUSPENDED state', - }) - ); - } else { - returnMembers(this.members); + /* Special-case the suspended state: can still get (stale) presence set if waitForSync is false */ + if (this.channel.state === 'suspended') { + if (waitForSync) { + reject( + ErrorInfo.fromValues({ + statusCode: 400, + code: 91005, + message: 'Presence state is out of sync due to channel being in the SUSPENDED state', + }), + ); + } else { + returnMembers(this.members); + } + return; } - return; - } - waitAttached(this.channel, callback, () => { - const members = this.members; - if (waitForSync) { - members.waitSync(function () { - returnMembers(members); - }); - } else { - returnMembers(members); - } + waitAttached( + this.channel, + (err) => reject(err), + () => { + const members = this.members; + if (waitForSync) { + members.waitSync(function () { + returnMembers(members); + }); + } else { + returnMembers(members); + } + }, + ); }); } - history( - params: RealtimeHistoryParams | null, - callback: PaginatedResultCallback - ): void | Promise> { + async history(params: RealtimeHistoryParams | null): Promise> { Logger.logAction(Logger.LOG_MICRO, 'RealtimePresence.history()', 'channel = ' + this.name); - /* params and callback are optional; see if params contains the callback */ - if (callback === undefined) { - if (typeof params == 'function') { - callback = params; - params = null; - } else { - if (this.channel.realtime.options.promises) { - return Utils.promisify(this, 'history', arguments); - } - callback = noop; - } - } + // We fetch this first so that any plugin-not-provided error takes priority over other errors + const restMixin = this.channel.client.rest.presenceMixin; if (params && params.untilAttach) { if (this.channel.state === 'attached') { delete params.untilAttach; params.from_serial = this.channel.properties.attachSerial; } else { - callback( - new ErrorInfo( - 'option untilAttach requires the channel to be attached, was: ' + this.channel.state, - 40000, - 400 - ) + throw new ErrorInfo( + 'option untilAttach requires the channel to be attached, was: ' + this.channel.state, + 40000, + 400, ); } } - Presence.prototype._history.call(this, params, callback); + return restMixin.history(this, params); } setPresence(presenceSet: PresenceMessage[], isSync: boolean, syncChannelSerial?: string): void { Logger.logAction( Logger.LOG_MICRO, 'RealtimePresence.setPresence()', - 'received presence for ' + presenceSet.length + ' participants; syncChannelSerial = ' + syncChannelSerial + 'received presence for ' + presenceSet.length + ' participants; syncChannelSerial = ' + syncChannelSerial, ); let syncCursor, match; const members = this.members, @@ -356,7 +308,7 @@ class RealtimePresence extends Presence { } for (let i = 0; i < presenceSet.length; i++) { - const presence = PresenceMessage.fromValues(presenceSet[i]); + const presence = presenceMessageFromValues(presenceSet[i]); switch (presence.action) { case 'leave': if (members.remove(presence)) { @@ -395,7 +347,7 @@ class RealtimePresence extends Presence { Logger.logAction( Logger.LOG_MINOR, 'RealtimePresence.onAttached()', - 'channel = ' + this.channel.name + ', hasPresence = ' + hasPresence + 'channel = ' + this.channel.name + ', hasPresence = ' + hasPresence, ); if (hasPresence) { @@ -419,7 +371,7 @@ class RealtimePresence extends Presence { Logger.logAction( Logger.LOG_MICRO, 'RealtimePresence.onAttached', - 'sending ' + pendingPresCount + ' queued presence messages' + 'sending ' + pendingPresCount + ' queued presence messages', ); for (let i = 0; i < pendingPresCount; i++) { const event = pendingPresence[i]; @@ -451,7 +403,7 @@ class RealtimePresence extends Presence { Logger.logAction( Logger.LOG_MINOR, 'RealtimeChannel.failPendingPresence', - 'channel; name = ' + this.channel.name + ', err = ' + Utils.inspectError(err) + 'channel; name = ' + this.channel.name + ', err = ' + Utils.inspectError(err), ); for (let i = 0; i < this.pendingPresence.length; i++) try { @@ -483,62 +435,41 @@ class RealtimePresence extends Presence { Logger.logAction( Logger.LOG_MICRO, 'RealtimePresence._ensureMyMembersPresent()', - 'Auto-reentering clientId "' + entry.clientId + '" into the presence set' + 'Auto-reentering clientId "' + entry.clientId + '" into the presence set', ); // RTP17g: Send ENTER containing the member id, clientId and data // attributes. - this._enterOrUpdateClient(entry.id, entry.clientId, entry.data, 'enter', reenterCb); + Utils.whenPromiseSettles(this._enterOrUpdateClient(entry.id, entry.clientId, entry.data, 'enter'), reenterCb); } } _synthesizeLeaves(items: PresenceMessage[]): void { const subscriptions = this.subscriptions; - Utils.arrForEach(items, function (item) { - const presence = PresenceMessage.fromValues({ + items.forEach(function (item) { + const presence = presenceMessageFromValues({ action: 'leave', connectionId: item.connectionId, clientId: item.clientId, data: item.data, encoding: item.encoding, - timestamp: Utils.now(), + timestamp: Date.now(), }); subscriptions.emit('leave', presence); }); } - /* Deprecated */ - on(...args: unknown[]): void { - Logger.renamedMethod('RealtimePresence', 'on', 'subscribe'); - this.subscribe(...args); - } - - /* Deprecated */ - off(...args: unknown[]): void { - Logger.renamedMethod('RealtimePresence', 'off', 'unsubscribe'); - this.unsubscribe(...args); - } - - subscribe(..._args: unknown[] /* [event], listener, [callback] */): void | Promise { + async subscribe(..._args: unknown[] /* [event], listener */): Promise { const args = RealtimeChannel.processListenerArgs(_args); const event = args[0]; const listener = args[1]; - let callback = args[2]; const channel = this.channel; - if (!callback) { - if (this.channel.realtime.options.promises) { - return Utils.promisify(this, 'subscribe', [event, listener]); - } - callback = noop; - } - if (channel.state === 'failed') { - callback(ErrorInfo.fromValues(channel.invalidStateError())); - return; + throw ErrorInfo.fromValues(channel.invalidStateError()); } this.subscriptions.on(event, listener); - channel.attach(callback); + await channel.attach(); } unsubscribe(..._args: unknown[] /* [event], listener */): void { @@ -597,7 +528,7 @@ class PresenceMap extends EventEmitter { put(item: PresenceMessage) { if (item.action === 'enter' || item.action === 'update') { - item = PresenceMessage.fromValues(item); + item = presenceMessageFromValues(item); item.action = 'present'; } const map = this.map, @@ -635,7 +566,7 @@ class PresenceMap extends EventEmitter { /* RTP2f */ if (this.syncInProgress) { - item = PresenceMessage.fromValues(item); + item = presenceMessageFromValues(item); item.action = 'absent'; map[key] = item; } else { @@ -651,7 +582,7 @@ class PresenceMap extends EventEmitter { Logger.logAction( Logger.LOG_MINOR, 'PresenceMap.startSync()', - 'channel = ' + this.presence.channel.name + '; syncInProgress = ' + syncInProgress + 'channel = ' + this.presence.channel.name + '; syncInProgress = ' + syncInProgress, ); /* we might be called multiple times while a sync is in progress */ if (!this.syncInProgress) { @@ -666,7 +597,7 @@ class PresenceMap extends EventEmitter { Logger.logAction( Logger.LOG_MINOR, 'PresenceMap.endSync()', - 'channel = ' + this.presence.channel.name + '; syncInProgress = ' + syncInProgress + 'channel = ' + this.presence.channel.name + '; syncInProgress = ' + syncInProgress, ); if (syncInProgress) { /* we can now strip out the ABSENT members, as we have @@ -696,7 +627,7 @@ class PresenceMap extends EventEmitter { Logger.logAction( Logger.LOG_MINOR, 'PresenceMap.waitSync()', - 'channel = ' + this.presence.channel.name + '; syncInProgress = ' + syncInProgress + 'channel = ' + this.presence.channel.name + '; syncInProgress = ' + syncInProgress, ); if (!syncInProgress) { callback(); diff --git a/src/common/lib/client/resource.ts b/src/common/lib/client/resource.ts index 169f669942..c5aba126c6 100644 --- a/src/common/lib/client/resource.ts +++ b/src/common/lib/client/resource.ts @@ -4,277 +4,367 @@ import Logger from '../util/logger'; import Auth from './auth'; import HttpMethods from '../../constants/HttpMethods'; import ErrorInfo, { IPartialErrorInfo, PartialErrorInfo } from '../types/errorinfo'; -import Rest from './rest'; -import { ErrnoException } from '../../types/http'; +import BaseClient from './baseclient'; +import { MsgPack } from 'common/types/msgpack'; +import { RequestBody, ResponseHeaders, appendingParams as urlFromPathAndParams, paramString } from 'common/types/http'; import httpStatusCodes from '../../constants/HttpStatusCodes'; -function withAuthDetails( - rest: Rest, - headers: Record, +async function withAuthDetails( + client: BaseClient, + headers: ResponseHeaders | undefined, params: Record, - errCallback: Function, - opCallback: Function -) { - if (rest.http.supportsAuthHeaders) { - rest.auth.getAuthHeaders(function (err: Error, authHeaders: Record) { - if (err) errCallback(err); - else opCallback(Utils.mixin(authHeaders, headers), params); - }); + opCallback: Function, +): Promise> { + if (client.http.supportsAuthHeaders) { + const authHeaders = await client.auth.getAuthHeaders(); + return opCallback(Utils.mixin(authHeaders!, headers), params); } else { - rest.auth.getAuthParams(function (err: Error, authParams: Record) { - if (err) errCallback(err); - else opCallback(headers, Utils.mixin(authParams, params)); - }); + const authParams = await client.auth.getAuthParams(); + return opCallback(headers, Utils.mixin(authParams!, params)); } } -function unenvelope(callback: ResourceCallback, format: Utils.Format | null): ResourceCallback { - return (err, body, outerHeaders, unpacked, outerStatusCode) => { - if (err && !body) { - callback(err); - return; - } +function unenvelope( + result: ResourceResult, + MsgPack: MsgPack | null, + format: Utils.Format | null, +): ResourceResult { + if (result.err && !result.body) { + return { err: result.err }; + } - if (outerStatusCode === httpStatusCodes.NoContent) { - callback(err, [] as any, outerHeaders, true, outerStatusCode); - return; - } + if (result.statusCode === httpStatusCodes.NoContent) { + return { ...result, body: [] as any, unpacked: true }; + } - if (!unpacked) { - try { - body = Utils.decodeBody(body, format); - } catch (e) { - if (Utils.isErrorInfoOrPartialErrorInfo(e)) { - callback(e); - } else { - callback(new PartialErrorInfo(Utils.inspectError(e), null)); - } - return; + let body = result.body; + + if (!result.unpacked) { + try { + body = Utils.decodeBody(body, MsgPack, format); + } catch (e) { + if (Utils.isErrorInfoOrPartialErrorInfo(e)) { + return { err: e }; + } else { + return { err: new PartialErrorInfo(Utils.inspectError(e), null) }; } } + } - if (!body) { - callback(new PartialErrorInfo('unenvelope(): Response body is missing', null)); - return; - } + if (!body) { + return { err: new PartialErrorInfo('unenvelope(): Response body is missing', null) }; + } - const { statusCode: wrappedStatusCode, response, headers: wrappedHeaders } = body as Record; + const { statusCode: wrappedStatusCode, response, headers: wrappedHeaders } = body as Record; - if (wrappedStatusCode === undefined) { - /* Envelope already unwrapped by the transport */ - callback(err, body, outerHeaders, true, outerStatusCode); - return; - } + if (wrappedStatusCode === undefined) { + /* Envelope already unwrapped by the transport */ + return { ...result, body, unpacked: true }; + } - if (wrappedStatusCode < 200 || wrappedStatusCode >= 300) { - /* handle wrapped errors */ - let wrappedErr = (response && response.error) || err; - if (!wrappedErr) { - wrappedErr = new Error('Error in unenveloping ' + body); - wrappedErr.statusCode = wrappedStatusCode; - } - callback(wrappedErr, response, wrappedHeaders, true, wrappedStatusCode); - return; + if (wrappedStatusCode < 200 || wrappedStatusCode >= 300) { + /* handle wrapped errors */ + let wrappedErr = (response && response.error) || result.err; + if (!wrappedErr) { + wrappedErr = new Error('Error in unenveloping ' + body); + wrappedErr.statusCode = wrappedStatusCode; } + return { err: wrappedErr, body: response, headers: wrappedHeaders, unpacked: true, statusCode: wrappedStatusCode }; + } - callback(err, response, wrappedHeaders, true, wrappedStatusCode); - }; + return { err: result.err, body: response, headers: wrappedHeaders, unpacked: true, statusCode: wrappedStatusCode }; } -function paramString(params: Record) { - const paramPairs = []; - if (params) { - for (const needle in params) { - paramPairs.push(needle + '=' + params[needle]); - } +function logResult(result: ResourceResult, method: HttpMethods, path: string, params: Record) { + if (result.err) { + Logger.logAction( + Logger.LOG_MICRO, + 'Resource.' + method + '()', + 'Received Error; ' + urlFromPathAndParams(path, params) + '; Error: ' + Utils.inspectError(result.err), + ); + } else { + Logger.logAction( + Logger.LOG_MICRO, + 'Resource.' + method + '()', + 'Received; ' + + urlFromPathAndParams(path, params) + + '; Headers: ' + + paramString(result.headers as Record) + + '; StatusCode: ' + + result.statusCode + + '; Body: ' + + (Platform.BufferUtils.isBuffer(result.body) + ? ' (Base64): ' + Platform.BufferUtils.base64Encode(result.body) + : ': ' + Platform.Config.inspect(result.body)), + ); } - return paramPairs.join('&'); } -function urlFromPathAndParams(path: string, params: Record) { - return path + (params ? '?' : '') + paramString(params); +export interface ResourceResponse { + body?: T; + headers?: ResponseHeaders; + unpacked?: boolean; + statusCode?: number; } -function logResponseHandler( - callback: ResourceCallback, - method: HttpMethods, - path: string, - params: Record -): ResourceCallback { - return (err, body, headers, unpacked, statusCode) => { - if (err) { - Logger.logAction( - Logger.LOG_MICRO, - 'Resource.' + method + '()', - 'Received Error; ' + urlFromPathAndParams(path, params) + '; Error: ' + Utils.inspectError(err) - ); - } else { - Logger.logAction( - Logger.LOG_MICRO, - 'Resource.' + method + '()', - 'Received; ' + - urlFromPathAndParams(path, params) + - '; Headers: ' + - paramString(headers as Record) + - '; StatusCode: ' + - statusCode + - '; Body: ' + - (Platform.BufferUtils.isBuffer(body) ? body.toString() : body) - ); - } - if (callback) { - callback(err, body as T, headers, unpacked, statusCode); - } - }; +export interface ResourceResult extends ResourceResponse { + /** + * Any error returned by the underlying HTTP client. + */ + err: IPartialErrorInfo | null; } -export type ResourceCallback = ( - err: IPartialErrorInfo | null, - body?: T, - headers?: Record, - unpacked?: boolean, - statusCode?: number -) => void; - class Resource { - static get( - rest: Rest, + /** + * @param throwError Whether to throw any error returned by the underlying HTTP client. + * + * If you specify `true`, then this method will return a `ResourceResponse`, and if the underlying HTTP client returns an error, this method call will throw that error. If you specify `false`, then it will return a `ResourceResult`, whose `err` property contains any error that was returned by the underlying HTTP client. + */ + static async get( + client: BaseClient, + path: string, + headers: Record, + params: Record, + envelope: Utils.Format | null, + throwError: true, + ): Promise>; + static async get( + client: BaseClient, + path: string, + headers: Record, + params: Record, + envelope: Utils.Format | null, + throwError: false, + ): Promise>; + static async get( + client: BaseClient, path: string, headers: Record, params: Record, envelope: Utils.Format | null, - callback: ResourceCallback - ): void { - Resource.do(HttpMethods.Get, rest, path, null, headers, params, envelope, callback); + throwError: boolean, + ): Promise | ResourceResult> { + return Resource.do(HttpMethods.Get, client, path, null, headers, params, envelope, throwError ?? false); } - static delete( - rest: Rest, + /** + * @param throwError Whether to throw any error returned by the underlying HTTP client. + * + * If you specify `true`, then this method will return a `ResourceResponse`, and if the underlying HTTP client returns an error, this method call will throw that error. If you specify `false`, then it will return a `ResourceResult`, whose `err` property contains any error that was returned by the underlying HTTP client. + */ + static async delete( + client: BaseClient, path: string, headers: Record, params: Record, envelope: Utils.Format | null, - callback: ResourceCallback - ): void { - Resource.do(HttpMethods.Delete, rest, path, null, headers, params, envelope, callback); + throwError: true, + ): Promise>; + static async delete( + client: BaseClient, + path: string, + headers: Record, + params: Record, + envelope: Utils.Format | null, + throwError: false, + ): Promise>; + static async delete( + client: BaseClient, + path: string, + headers: Record, + params: Record, + envelope: Utils.Format | null, + throwError: boolean, + ): Promise | ResourceResult> { + return Resource.do(HttpMethods.Delete, client, path, null, headers, params, envelope, throwError); } - static post( - rest: Rest, + /** + * @param throwError Whether to throw any error returned by the underlying HTTP client. + * + * If you specify `true`, then this method will return a `ResourceResponse`, and if the underlying HTTP client returns an error, this method call will throw that error. If you specify `false`, then it will return a `ResourceResult`, whose `err` property contains any error that was returned by the underlying HTTP client. + */ + static async post( + client: BaseClient, + path: string, + body: RequestBody | null, + headers: Record, + params: Record, + envelope: Utils.Format | null, + throwError: true, + ): Promise>; + static async post( + client: BaseClient, + path: string, + body: RequestBody | null, + headers: Record, + params: Record, + envelope: Utils.Format | null, + throwError: false, + ): Promise>; + static async post( + client: BaseClient, path: string, - body: unknown, + body: RequestBody | null, headers: Record, params: Record, envelope: Utils.Format | null, - callback: ResourceCallback - ): void { - Resource.do(HttpMethods.Post, rest, path, body, headers, params, envelope, callback); + throwError: boolean, + ): Promise | ResourceResult> { + return Resource.do(HttpMethods.Post, client, path, body, headers, params, envelope, throwError); } - static patch( - rest: Rest, + /** + * @param throwError Whether to throw any error returned by the underlying HTTP client. + * + * If you specify `true`, then this method will return a `ResourceResponse`, and if the underlying HTTP client returns an error, this method call will throw that error. If you specify `false`, then it will return a `ResourceResult`, whose `err` property contains any error that was returned by the underlying HTTP client. + */ + static async patch( + client: BaseClient, path: string, - body: unknown, + body: RequestBody | null, headers: Record, params: Record, envelope: Utils.Format | null, - callback: ResourceCallback - ): void { - Resource.do(HttpMethods.Patch, rest, path, body, headers, params, envelope, callback); + throwError: true, + ): Promise>; + static async patch( + client: BaseClient, + path: string, + body: RequestBody | null, + headers: Record, + params: Record, + envelope: Utils.Format | null, + throwError: false, + ): Promise>; + static async patch( + client: BaseClient, + path: string, + body: RequestBody | null, + headers: Record, + params: Record, + envelope: Utils.Format | null, + throwError: boolean, + ): Promise | ResourceResult> { + return Resource.do(HttpMethods.Patch, client, path, body, headers, params, envelope, throwError); } - static put( - rest: Rest, + /** + * @param throwError Whether to throw any error returned by the underlying HTTP client. + * + * If you specify `true`, then this method will return a `ResourceResponse`, and if the underlying HTTP client returns an error, this method call will throw that error. If you specify `false`, then it will return a `ResourceResult`, whose `err` property contains any error that was returned by the underlying HTTP client. + */ + static async put( + client: BaseClient, path: string, - body: unknown, + body: RequestBody | null, headers: Record, params: Record, envelope: Utils.Format | null, - callback: ResourceCallback - ): void { - Resource.do(HttpMethods.Put, rest, path, body, headers, params, envelope, callback); + throwError: true, + ): Promise>; + static async put( + client: BaseClient, + path: string, + body: RequestBody | null, + headers: Record, + params: Record, + envelope: Utils.Format | null, + throwError: false, + ): Promise>; + static async put( + client: BaseClient, + path: string, + body: RequestBody | null, + headers: Record, + params: Record, + envelope: Utils.Format | null, + throwError: boolean, + ): Promise | ResourceResult> { + return Resource.do(HttpMethods.Put, client, path, body, headers, params, envelope, throwError); } - static do( + static async do( method: HttpMethods, - rest: Rest, + client: BaseClient, path: string, - body: unknown, + body: RequestBody | null, headers: Record, params: Record, envelope: Utils.Format | null, - callback: ResourceCallback - ): void { - if (Logger.shouldLog(Logger.LOG_MICRO)) { - callback = logResponseHandler(callback, method, path, params); - } - + throwError: boolean, + ): Promise | ResourceResult> { if (envelope) { - callback = callback && unenvelope(callback, envelope); (params = params || {})['envelope'] = envelope; } - function doRequest(this: any, headers: Record, params: Record) { - if (Logger.shouldLog(Logger.LOG_MICRO)) { - Logger.logAction( - Logger.LOG_MICRO, - 'Resource.' + method + '()', - 'Sending; ' + urlFromPathAndParams(path, params) - ); - } - + async function doRequest( + this: any, + headers: Record, + params: Record, + ): Promise> { if (Logger.shouldLog(Logger.LOG_MICRO)) { let decodedBody = body; if (headers['content-type']?.indexOf('msgpack') > 0) { try { - decodedBody = Platform.Config.msgpack.decode(body as Buffer); + if (!client._MsgPack) { + Utils.throwMissingPluginError('MsgPack'); + } + decodedBody = client._MsgPack.decode(body as Buffer); } catch (decodeErr) { Logger.logAction( Logger.LOG_MICRO, 'Resource.' + method + '()', - 'Sending MsgPack Decoding Error: ' + Utils.inspectError(decodeErr) + 'Sending MsgPack Decoding Error: ' + Utils.inspectError(decodeErr), ); } } Logger.logAction( Logger.LOG_MICRO, 'Resource.' + method + '()', - 'Sending; ' + urlFromPathAndParams(path, params) + '; Body: ' + decodedBody + 'Sending; ' + urlFromPathAndParams(path, params) + '; Body: ' + decodedBody, ); } - rest.http.do( - method, - rest, - path, - headers, - body, - params, - function ( - err: ErrorInfo | ErrnoException | null | undefined, - res: any, - resHeaders: Record, - unpacked?: boolean, - statusCode?: number - ) { - if (err && Auth.isTokenErr(err as ErrorInfo)) { - /* token has expired, so get a new one */ - rest.auth.authorize(null, null, function (err: ErrorInfo) { - if (err) { - callback(err); - return; - } - /* retry ... */ - withAuthDetails(rest, headers, params, callback, doRequest); - }); - return; - } - callback(err as ErrorInfo, res, resHeaders, unpacked, statusCode); - } - ); + const httpResult = await client.http.do(method, path, headers, body, params); + + if (httpResult.error && Auth.isTokenErr(httpResult.error as ErrorInfo)) { + /* token has expired, so get a new one */ + await client.auth.authorize(null, null); + /* retry ... */ + return withAuthDetails(client, headers, params, doRequest); + } + + return { + err: httpResult.error as ErrorInfo, + body: httpResult.body as T | undefined, + headers: httpResult.headers, + unpacked: httpResult.unpacked, + statusCode: httpResult.statusCode, + }; + } + + let result = await withAuthDetails(client, headers, params, doRequest); + + if (envelope) { + result = unenvelope(result, client._MsgPack, envelope); + } + + if (Logger.shouldLog(Logger.LOG_MICRO)) { + logResult(result, method, path, params); + } + + if (throwError) { + if (result.err) { + throw result.err; + } else { + const response: Omit, 'err'> & Pick>, 'err'> = { ...result }; + delete response.err; + return response; + } } - withAuthDetails(rest, headers, params, callback, doRequest); + return result; } } diff --git a/src/common/lib/client/rest.ts b/src/common/lib/client/rest.ts index a4dae85f2c..74ce069b04 100644 --- a/src/common/lib/client/rest.ts +++ b/src/common/lib/client/rest.ts @@ -1,274 +1,160 @@ import * as Utils from '../util/utils'; import Logger, { LoggerOptions } from '../util/logger'; import Defaults from '../util/defaults'; -import Auth from './auth'; import Push from './push'; import PaginatedResource, { HttpPaginatedResponse, PaginatedResult } from './paginatedresource'; -import Channel from './channel'; +import RestChannel from './restchannel'; import ErrorInfo from '../types/errorinfo'; import Stats from '../types/stats'; import HttpMethods from '../../constants/HttpMethods'; import { ChannelOptions } from '../../types/channel'; -import { PaginatedResultCallback, StandardCallback } from '../../types/utils'; -import { ErrnoException, IHttp, RequestParams } from '../../types/http'; -import ClientOptions, { DeprecatedClientOptions, NormalisedClientOptions } from '../../types/ClientOptions'; +import { RequestBody, RequestParams } from '../../types/http'; import * as API from '../../../../ably'; +import Resource from './resource'; import Platform from '../../platform'; -import Message from '../types/message'; -import PresenceMessage from '../types/presencemessage'; -import Resource from './resource'; +import BaseClient from './baseclient'; +import { useTokenAuth } from './auth'; +import { RestChannelMixin } from './restchannelmixin'; +import { RestPresenceMixin } from './restpresencemixin'; + +type BatchResult = API.BatchResult; -type BatchResult = API.Types.BatchResult; -type BatchPublishSpec = API.Types.BatchPublishSpec; -type BatchPublishSuccessResult = API.Types.BatchPublishSuccessResult; -type BatchPublishFailureResult = API.Types.BatchPublishFailureResult; +type BatchPublishSpec = API.BatchPublishSpec; +type BatchPublishSuccessResult = API.BatchPublishSuccessResult; +type BatchPublishFailureResult = API.BatchPublishFailureResult; type BatchPublishResult = BatchResult; -type BatchPresenceSuccessResult = API.Types.BatchPresenceSuccessResult; -type BatchPresenceFailureResult = API.Types.BatchPresenceFailureResult; +type BatchPresenceSuccessResult = API.BatchPresenceSuccessResult; +type BatchPresenceFailureResult = API.BatchPresenceFailureResult; type BatchPresenceResult = BatchResult; -const noop = function () {}; -class Rest { - options: NormalisedClientOptions; - baseUri: (host: string) => string; - authority: (host: string) => string; - _currentFallback: null | { - host: string; - validUntil: number; - }; - serverTimeOffset: number | null; - http: IHttp; - auth: Auth; - channels: Channels; - push: Push; - - constructor(options: ClientOptions | string) { - if (!options) { - const msg = 'no options provided'; - Logger.logAction(Logger.LOG_ERROR, 'Rest()', msg); - throw new Error(msg); - } - const optionsObj = Defaults.objectifyOptions(options); - - if (optionsObj.log) { - Logger.setLog(optionsObj.log.level, optionsObj.log.handler); - Logger.deprecated( - 'The `log` client option', - 'Equivalent functionality is provided by the `logLevel` and `logHandler` client options. Update your client options code of the form `{ log: { level: logLevel, handler: logHandler } }` to instead be `{ logLevel, logHandler }`.' - ); - } else { - Logger.setLog(optionsObj.logLevel, optionsObj.logHandler); - } - - Logger.logAction(Logger.LOG_MICRO, 'Rest()', 'initialized with clientOptions ' + Platform.Config.inspect(options)); +type TokenRevocationTargetSpecifier = API.TokenRevocationTargetSpecifier; +type TokenRevocationOptions = API.TokenRevocationOptions; +type TokenRevocationSuccessResult = API.TokenRevocationSuccessResult; +type TokenRevocationFailureResult = API.TokenRevocationFailureResult; +type TokenRevocationResult = BatchResult; - const normalOptions = (this.options = Defaults.normaliseOptions(optionsObj)); +export class Rest { + private readonly client: BaseClient; + readonly channels: Channels; + readonly push: Push; - /* process options */ - if (normalOptions.key) { - const keyMatch = normalOptions.key.match(/^([^:\s]+):([^:.\s]+)$/); - if (!keyMatch) { - const msg = 'invalid key parameter'; - Logger.logAction(Logger.LOG_ERROR, 'Rest()', msg); - throw new ErrorInfo(msg, 40400, 404); - } - normalOptions.keyName = keyMatch[1]; - normalOptions.keySecret = keyMatch[2]; - } + readonly channelMixin = RestChannelMixin; + readonly presenceMixin = RestPresenceMixin; - if ('clientId' in normalOptions) { - if (!(typeof normalOptions.clientId === 'string' || normalOptions.clientId === null)) - throw new ErrorInfo('clientId must be either a string or null', 40012, 400); - else if (normalOptions.clientId === '*') - throw new ErrorInfo( - 'Can’t use "*" as a clientId as that string is reserved. (To change the default token request behaviour to use a wildcard clientId, use {defaultTokenParams: {clientId: "*"}})', - 40012, - 400 - ); - } - - Logger.logAction(Logger.LOG_MINOR, 'Rest()', 'started; version = ' + Defaults.version); - - this.baseUri = this.authority = function (host) { - return Defaults.getHttpScheme(normalOptions) + host + ':' + Defaults.getPort(normalOptions, false); - }; - this._currentFallback = null; - - this.serverTimeOffset = null; - this.http = new Platform.Http(normalOptions); - this.auth = new Auth(this, normalOptions); - this.channels = new Channels(this); - this.push = new Push(this); + constructor(client: BaseClient) { + this.client = client; + this.channels = new Channels(this.client); + this.push = new Push(this.client); } - stats( - params: RequestParams, - callback: StandardCallback> - ): Promise> | void { - /* params and callback are optional; see if params contains the callback */ - if (callback === undefined) { - if (typeof params == 'function') { - callback = params; - params = null; - } else { - if (this.options.promises) { - return Utils.promisify(this, 'stats', [params]) as Promise>; - } - callback = noop; - } - } - const headers = Utils.defaultGetHeaders(this.options), - format = this.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, - envelope = this.http.supportsLinkHeaders ? undefined : format; + async stats(params: RequestParams): Promise> { + const headers = Defaults.defaultGetHeaders(this.client.options), + format = this.client.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, + envelope = this.client.http.supportsLinkHeaders ? undefined : format; - if (this.options.headers) Utils.mixin(headers, this.options.headers); + Utils.mixin(headers, this.client.options.headers); - new PaginatedResource(this, '/stats', headers, envelope, function ( - body: unknown, - headers: Record, - unpacked?: boolean - ) { + return new PaginatedResource(this.client, '/stats', headers, envelope, function (body, headers, unpacked) { const statsValues = unpacked ? body : JSON.parse(body as string); for (let i = 0; i < statsValues.length; i++) statsValues[i] = Stats.fromValues(statsValues[i]); return statsValues; - }).get(params as Record, callback); + }).get(params as Record); } - time(params?: RequestParams | StandardCallback, callback?: StandardCallback): Promise | void { - /* params and callback are optional; see if params contains the callback */ - if (callback === undefined) { - if (typeof params == 'function') { - callback = params; - params = null; - } else { - if (this.options.promises) { - return Utils.promisify(this, 'time', [params]) as Promise; - } - } - } - - const _callback = callback || noop; - - const headers = Utils.defaultGetHeaders(this.options); - if (this.options.headers) Utils.mixin(headers, this.options.headers); + async time(params?: RequestParams): Promise { + const headers = Defaults.defaultGetHeaders(this.client.options); + if (this.client.options.headers) Utils.mixin(headers, this.client.options.headers); const timeUri = (host: string) => { - return this.authority(host) + '/time'; + return this.client.baseUri(host) + '/time'; }; - this.http.do( + + let { error, body, unpacked } = await this.client.http.do( HttpMethods.Get, - this, timeUri, headers, null, params as RequestParams, - ( - err?: ErrorInfo | ErrnoException | null, - res?: unknown, - headers?: Record, - unpacked?: boolean - ) => { - if (err) { - _callback(err); - return; - } - if (!unpacked) res = JSON.parse(res as string); - const time = (res as number[])[0]; - if (!time) { - _callback(new ErrorInfo('Internal error (unexpected result type from GET /time)', 50000, 500)); - return; - } - /* calculate time offset only once for this device by adding to the prototype */ - this.serverTimeOffset = time - Utils.now(); - _callback(null, time); - } ); + + if (error) { + throw error; + } + if (!unpacked) body = JSON.parse(body as string); + const time = (body as number[])[0]; + if (!time) { + throw new ErrorInfo('Internal error (unexpected result type from GET /time)', 50000, 500); + } + /* calculate time offset only once for this device by adding to the prototype */ + this.client.serverTimeOffset = time - Date.now(); + return time; } - request( + async request( method: string, path: string, + version: number, params: RequestParams, body: unknown, customHeaders: Record, - callback: StandardCallback> - ): Promise> | void { - const useBinary = this.options.useBinaryProtocol, - encoder = useBinary ? Platform.Config.msgpack.encode : JSON.stringify, - decoder = useBinary ? Platform.Config.msgpack.decode : JSON.parse, - format = useBinary ? Utils.Format.msgpack : Utils.Format.json, - envelope = this.http.supportsLinkHeaders ? undefined : format; + ): Promise> { + const [encoder, decoder, format] = (() => { + if (this.client.options.useBinaryProtocol) { + if (!this.client._MsgPack) { + Utils.throwMissingPluginError('MsgPack'); + } + return [this.client._MsgPack.encode, this.client._MsgPack.decode, Utils.Format.msgpack]; + } else { + return [JSON.stringify, JSON.parse, Utils.Format.json]; + } + })(); + const envelope = this.client.http.supportsLinkHeaders ? undefined : format; params = params || {}; const _method = method.toLowerCase() as HttpMethods; const headers = - _method == 'get' ? Utils.defaultGetHeaders(this.options, format) : Utils.defaultPostHeaders(this.options, format); - - if (callback === undefined) { - if (this.options.promises) { - return Utils.promisify(this, 'request', [method, path, params, body, customHeaders]) as Promise< - HttpPaginatedResponse - >; - } - callback = noop; - } + _method == 'get' + ? Defaults.defaultGetHeaders(this.client.options, { format, protocolVersion: version }) + : Defaults.defaultPostHeaders(this.client.options, { format, protocolVersion: version }); if (typeof body !== 'string') { - body = encoder(body); - } - if (this.options.headers) { - Utils.mixin(headers, this.options.headers); + body = encoder(body) ?? null; } + Utils.mixin(headers, this.client.options.headers); if (customHeaders) { Utils.mixin(headers, customHeaders); } const paginatedResource = new PaginatedResource( - this, + this.client, path, headers, envelope, - function (resbody: unknown, headers: Record, unpacked?: boolean) { + async function (resbody, headers, unpacked) { return Utils.ensureArray(unpacked ? resbody : decoder(resbody as string & Buffer)); }, - /* useHttpPaginatedResponse: */ true + /* useHttpPaginatedResponse: */ true, ); - if (!Utils.arrIn(Platform.Http.methods, _method)) { + if (!Platform.Http.methods.includes(_method)) { throw new ErrorInfo('Unsupported method ' + _method, 40500, 405); } - if (Utils.arrIn(Platform.Http.methodsWithBody, _method)) { - paginatedResource[_method as HttpMethods.Post](params, body, callback as PaginatedResultCallback); + if (Platform.Http.methodsWithBody.includes(_method)) { + return paginatedResource[_method as HttpMethods.Post](params, body as RequestBody) as Promise< + HttpPaginatedResponse + >; } else { - paginatedResource[_method as HttpMethods.Get | HttpMethods.Delete]( - params, - callback as PaginatedResultCallback - ); + return paginatedResource[_method as HttpMethods.Get | HttpMethods.Delete](params) as Promise< + HttpPaginatedResponse + >; } } - batchPublish( - specOrSpecs: T, - callback: API.Types.StandardCallback - ): void; - batchPublish( - specOrSpecs: T - ): Promise; - batchPublish( + async batchPublish( specOrSpecs: T, - callbackArg?: API.Types.StandardCallback - ): void | Promise { - if (callbackArg === undefined) { - if (this.options.promises) { - return Utils.promisify(this, 'batchPublish', [specOrSpecs]); - } - callbackArg = noop; - } - - const callback = callbackArg; - + ): Promise { let requestBodyDTO: BatchPublishSpec[]; let singleSpecMode: boolean; - if (Utils.isArray(specOrSpecs)) { + if (Array.isArray(specOrSpecs)) { requestBodyDTO = specOrSpecs; singleSpecMode = false; } else { @@ -276,103 +162,92 @@ class Rest { singleSpecMode = true; } - const format = this.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, - headers = Utils.defaultPostHeaders(this.options, format); + const format = this.client.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, + headers = Defaults.defaultPostHeaders(this.client.options, { format }); - if (this.options.headers) Utils.mixin(headers, this.options.headers); + if (this.client.options.headers) Utils.mixin(headers, this.client.options.headers); - const requestBody = Utils.encodeBody(requestBodyDTO, format); - Resource.post( - this, - '/messages', - requestBody, - headers, - { newBatchResponse: 'true' }, - null, - (err, body, headers, unpacked) => { - if (err) { - // TODO remove this type assertion after fixing https://github.com/ably/ably-js/issues/1405 - callback(err as API.Types.ErrorInfo); - return; - } + const requestBody = Utils.encodeBody(requestBodyDTO, this.client._MsgPack, format); - const batchResults = (unpacked ? body : Utils.decodeBody(body, format)) as BatchPublishResult[]; + const response = await Resource.post(this.client, '/messages', requestBody, headers, {}, null, true); - // I don't love the below type assertions for `callback` but not sure how to avoid them - if (singleSpecMode) { - (callback as API.Types.StandardCallback)(null, batchResults[0]); - } else { - (callback as API.Types.StandardCallback)(null, batchResults); - } - } - ); + const batchResults = ( + response.unpacked ? response.body : Utils.decodeBody(response.body, this.client._MsgPack, format) + ) as BatchPublishResult[]; + + // I don't love the below type assertions but not sure how to avoid them + if (singleSpecMode) { + return batchResults[0] as T extends BatchPublishSpec ? BatchPublishResult : BatchPublishResult[]; + } else { + return batchResults as T extends BatchPublishSpec ? BatchPublishResult : BatchPublishResult[]; + } } - batchPresence(channels: string[], callback: API.Types.StandardCallback): void; - batchPresence(channels: string[]): Promise; - batchPresence( - channels: string[], - callbackArg?: API.Types.StandardCallback - ): void | Promise { - if (callbackArg === undefined) { - if (this.options.promises) { - return Utils.promisify(this, 'batchPresence', [channels]); - } - callbackArg = noop; + async batchPresence(channels: string[]): Promise { + const format = this.client.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, + headers = Defaults.defaultPostHeaders(this.client.options, { format }); + + if (this.client.options.headers) Utils.mixin(headers, this.client.options.headers); + + const channelsParam = channels.join(','); + + const response = await Resource.get(this.client, '/presence', headers, { channels: channelsParam }, null, true); + + return ( + response.unpacked ? response.body : Utils.decodeBody(response.body, this.client._MsgPack, format) + ) as BatchPresenceResult; + } + + async revokeTokens( + specifiers: TokenRevocationTargetSpecifier[], + options?: TokenRevocationOptions, + ): Promise { + if (useTokenAuth(this.client.options)) { + throw new ErrorInfo('Cannot revoke tokens when using token auth', 40162, 401); } - const callback = callbackArg; + const keyName = this.client.options.keyName!; - const format = this.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, - headers = Utils.defaultPostHeaders(this.options, format); + let resolvedOptions = options ?? {}; - if (this.options.headers) Utils.mixin(headers, this.options.headers); + const requestBodyDTO = { + targets: specifiers.map((specifier) => `${specifier.type}:${specifier.value}`), + ...resolvedOptions, + }; - const channelsParam = channels.join(','); + const format = this.client.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, + headers = Defaults.defaultPostHeaders(this.client.options, { format }); - Resource.get( - this, - '/presence', - headers, - { newBatchResponse: 'true', channels: channelsParam }, - null, - (err, body, headers, unpacked) => { - if (err) { - // TODO remove this type assertion after fixing https://github.com/ably/ably-js/issues/1405 - callback(err as API.Types.ErrorInfo); - return; - } + if (this.client.options.headers) Utils.mixin(headers, this.client.options.headers); - const batchResult = (unpacked ? body : Utils.decodeBody(body, format)) as BatchPresenceResult; + const requestBody = Utils.encodeBody(requestBodyDTO, this.client._MsgPack, format); - callback(null, batchResult); - } + const response = await Resource.post( + this.client, + `/keys/${keyName}/revokeTokens`, + requestBody, + headers, + {}, + null, + true, ); + + return ( + response.unpacked ? response.body : Utils.decodeBody(response.body, this.client._MsgPack, format) + ) as TokenRevocationResult; } setLog(logOptions: LoggerOptions): void { Logger.setLog(logOptions.level, logOptions.handler); } - - static Promise = function (options: DeprecatedClientOptions): Rest { - options = Defaults.objectifyOptions(options); - options.promises = true; - return new Rest(options); - }; - - static Callbacks = Rest; - static Platform = Platform; - static Crypto?: typeof Platform.Crypto; - static Message = Message; - static PresenceMessage = PresenceMessage; } class Channels { - rest: Rest; - all: Record; + client: BaseClient; + all: Record; - constructor(rest: Rest) { - this.rest = rest; + constructor(client: BaseClient) { + this.client = client; this.all = Object.create(null); } @@ -380,7 +255,7 @@ class Channels { name = String(name); let channel = this.all[name]; if (!channel) { - this.all[name] = channel = new Channel(this.rest, name, channelOptions); + this.all[name] = channel = new RestChannel(this.client, name, channelOptions); } else if (channelOptions) { channel.setOptions(channelOptions); } @@ -394,5 +269,3 @@ class Channels { delete this.all[String(name)]; } } - -export default Rest; diff --git a/src/common/lib/client/restchannel.ts b/src/common/lib/client/restchannel.ts new file mode 100644 index 0000000000..71ac07ea0a --- /dev/null +++ b/src/common/lib/client/restchannel.ts @@ -0,0 +1,134 @@ +import * as Utils from '../util/utils'; +import Logger from '../util/logger'; +import RestPresence from './restpresence'; +import Message, { + fromValues as messageFromValues, + fromValuesArray as messagesFromValuesArray, + encodeArray as encodeMessagesArray, + serialize as serializeMessage, + getMessagesSize, + CipherOptions, +} from '../types/message'; +import ErrorInfo from '../types/errorinfo'; +import { PaginatedResult } from './paginatedresource'; +import Resource from './resource'; +import { ChannelOptions } from '../../types/channel'; +import BaseRest from './baseclient'; +import * as API from '../../../../ably'; +import Defaults, { normaliseChannelOptions } from '../util/defaults'; +import { RestHistoryParams } from './restchannelmixin'; +import { RequestBody } from 'common/types/http'; + +const MSG_ID_ENTROPY_BYTES = 9; + +function allEmptyIds(messages: Array) { + return messages.every(function (message: Message) { + return !message.id; + }); +} + +class RestChannel { + client: BaseRest; + name: string; + presence: RestPresence; + channelOptions: ChannelOptions; + + constructor(client: BaseRest, name: string, channelOptions?: ChannelOptions) { + Logger.logAction(Logger.LOG_MINOR, 'RestChannel()', 'started; name = ' + name); + this.name = name; + this.client = client; + this.presence = new RestPresence(this); + this.channelOptions = normaliseChannelOptions(client._Crypto ?? null, channelOptions); + } + + setOptions(options?: ChannelOptions): void { + this.channelOptions = normaliseChannelOptions(this.client._Crypto ?? null, options); + } + + async history(params: RestHistoryParams | null): Promise> { + Logger.logAction(Logger.LOG_MICRO, 'RestChannel.history()', 'channel = ' + this.name); + return this.client.rest.channelMixin.history(this, params); + } + + async publish(...args: any[]): Promise { + const first = args[0], + second = args[1]; + let messages: Array; + let params: any; + + if (typeof first === 'string' || first === null) { + /* (name, data, ...) */ + messages = [messageFromValues({ name: first, data: second })]; + params = args[2]; + } else if (Utils.isObject(first)) { + messages = [messageFromValues(first)]; + params = args[1]; + } else if (Array.isArray(first)) { + messages = messagesFromValuesArray(first); + params = args[1]; + } else { + throw new ErrorInfo( + 'The single-argument form of publish() expects a message object or an array of message objects', + 40013, + 400, + ); + } + + if (!params) { + /* No params supplied */ + params = {}; + } + + const client = this.client, + options = client.options, + format = options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, + idempotentRestPublishing = client.options.idempotentRestPublishing, + headers = Defaults.defaultPostHeaders(client.options, { format }); + + Utils.mixin(headers, options.headers); + + if (idempotentRestPublishing && allEmptyIds(messages)) { + const msgIdBase = await Utils.randomString(MSG_ID_ENTROPY_BYTES); + messages.forEach(function (message, index) { + message.id = msgIdBase + ':' + index.toString(); + }); + } + + await encodeMessagesArray(messages, this.channelOptions as CipherOptions); + + /* RSL1i */ + const size = getMessagesSize(messages), + maxMessageSize = options.maxMessageSize; + if (size > maxMessageSize) { + throw new ErrorInfo( + 'Maximum size of messages that can be published at once exceeded ( was ' + + size + + ' bytes; limit is ' + + maxMessageSize + + ' bytes)', + 40009, + 400, + ); + } + + await this._publish(serializeMessage(messages, client._MsgPack, format), headers, params); + } + + async _publish(requestBody: RequestBody | null, headers: Record, params: any): Promise { + await Resource.post( + this.client, + this.client.rest.channelMixin.basePath(this) + '/messages', + requestBody, + headers, + params, + null, + true, + ); + } + + async status(): Promise { + return this.client.rest.channelMixin.status(this); + } +} + +export default RestChannel; diff --git a/src/common/lib/client/restchannelmixin.ts b/src/common/lib/client/restchannelmixin.ts new file mode 100644 index 0000000000..26695c8c14 --- /dev/null +++ b/src/common/lib/client/restchannelmixin.ts @@ -0,0 +1,58 @@ +import * as API from '../../../../ably'; +import RestChannel from './restchannel'; +import RealtimeChannel from './realtimechannel'; +import * as Utils from '../util/utils'; +import Message, { fromResponseBody as messageFromResponseBody } from '../types/message'; +import Defaults from '../util/defaults'; +import PaginatedResource, { PaginatedResult } from './paginatedresource'; +import Resource from './resource'; + +export interface RestHistoryParams { + start?: number; + end?: number; + direction?: string; + limit?: number; +} + +export class RestChannelMixin { + static basePath(channel: RestChannel | RealtimeChannel) { + return '/channels/' + encodeURIComponent(channel.name); + } + + static history( + channel: RestChannel | RealtimeChannel, + params: RestHistoryParams | null, + ): Promise> { + const client = channel.client, + format = client.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, + envelope = channel.client.http.supportsLinkHeaders ? undefined : format, + headers = Defaults.defaultGetHeaders(client.options, { format }); + + Utils.mixin(headers, client.options.headers); + + const options = channel.channelOptions; + return new PaginatedResource(client, this.basePath(channel) + '/messages', headers, envelope, async function ( + body, + headers, + unpacked, + ) { + return await messageFromResponseBody(body as Message[], options, client._MsgPack, unpacked ? undefined : format); + }).get(params as Record); + } + + static async status(channel: RestChannel | RealtimeChannel): Promise { + const format = channel.client.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json; + const headers = Defaults.defaultPostHeaders(channel.client.options, { format }); + + const response = await Resource.get( + channel.client, + this.basePath(channel), + headers, + {}, + format, + true, + ); + + return response.body!; + } +} diff --git a/src/common/lib/client/restpresence.ts b/src/common/lib/client/restpresence.ts new file mode 100644 index 0000000000..470752443d --- /dev/null +++ b/src/common/lib/client/restpresence.ts @@ -0,0 +1,48 @@ +import * as Utils from '../util/utils'; +import Logger from '../util/logger'; +import PaginatedResource, { PaginatedResult } from './paginatedresource'; +import PresenceMessage, { fromResponseBody as presenceMessageFromResponseBody } from '../types/presencemessage'; +import { CipherOptions } from '../types/message'; +import RestChannel from './restchannel'; +import Defaults from '../util/defaults'; + +class RestPresence { + channel: RestChannel; + + constructor(channel: RestChannel) { + this.channel = channel; + } + + async get(params: any): Promise> { + Logger.logAction(Logger.LOG_MICRO, 'RestPresence.get()', 'channel = ' + this.channel.name); + const client = this.channel.client, + format = client.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, + envelope = this.channel.client.http.supportsLinkHeaders ? undefined : format, + headers = Defaults.defaultGetHeaders(client.options, { format }); + + Utils.mixin(headers, client.options.headers); + + const options = this.channel.channelOptions; + return new PaginatedResource( + client, + this.channel.client.rest.presenceMixin.basePath(this), + headers, + envelope, + async function (body, headers, unpacked) { + return await presenceMessageFromResponseBody( + body as Record[], + options as CipherOptions, + client._MsgPack, + unpacked ? undefined : format, + ); + }, + ).get(params); + } + + async history(params: any): Promise> { + Logger.logAction(Logger.LOG_MICRO, 'RestPresence.history()', 'channel = ' + this.channel.name); + return this.channel.client.rest.presenceMixin.history(this, params); + } +} + +export default RestPresence; diff --git a/src/common/lib/client/restpresencemixin.ts b/src/common/lib/client/restpresencemixin.ts new file mode 100644 index 0000000000..c42f725a52 --- /dev/null +++ b/src/common/lib/client/restpresencemixin.ts @@ -0,0 +1,40 @@ +import RestPresence from './restpresence'; +import RealtimePresence from './realtimepresence'; +import * as Utils from '../util/utils'; +import Defaults from '../util/defaults'; +import PaginatedResource, { PaginatedResult } from './paginatedresource'; +import PresenceMessage, { fromResponseBody as presenceMessageFromResponseBody } from '../types/presencemessage'; +import { CipherOptions } from '../types/message'; +import { RestChannelMixin } from './restchannelmixin'; + +export class RestPresenceMixin { + static basePath(presence: RestPresence | RealtimePresence) { + return RestChannelMixin.basePath(presence.channel) + '/presence'; + } + + static async history( + presence: RestPresence | RealtimePresence, + params: any, + ): Promise> { + const client = presence.channel.client, + format = client.options.useBinaryProtocol ? Utils.Format.msgpack : Utils.Format.json, + envelope = presence.channel.client.http.supportsLinkHeaders ? undefined : format, + headers = Defaults.defaultGetHeaders(client.options, { format }); + + Utils.mixin(headers, client.options.headers); + + const options = presence.channel.channelOptions; + return new PaginatedResource(client, this.basePath(presence) + '/history', headers, envelope, async function ( + body, + headers, + unpacked, + ) { + return await presenceMessageFromResponseBody( + body as Record[], + options as CipherOptions, + client._MsgPack, + unpacked ? undefined : format, + ); + }).get(params); + } +} diff --git a/src/common/lib/transport/comettransport.ts b/src/common/lib/transport/comettransport.ts index bcb39c7fe2..4ecf0d1473 100644 --- a/src/common/lib/transport/comettransport.ts +++ b/src/common/lib/transport/comettransport.ts @@ -1,5 +1,9 @@ import * as Utils from '../util/utils'; -import ProtocolMessage from '../types/protocolmessage'; +import ProtocolMessage, { + actions, + fromValues as protocolMessageFromValues, + fromDeserialized as protocolMessageFromDeserialized, +} from '../types/protocolmessage'; import Transport from './transport'; import Logger from '../util/logger'; import Defaults from '../util/defaults'; @@ -17,7 +21,7 @@ function shouldBeErrorAction(err: ErrorInfo) { const UNRESOLVABLE_ERROR_CODES = [80015, 80017, 80030]; if (err.code) { if (Auth.isTokenErr(err)) return false; - if (Utils.arrIn(UNRESOLVABLE_ERROR_CODES, err.code)) return true; + if (UNRESOLVABLE_ERROR_CODES.includes(err.code)) return true; return err.code >= 40000 && err.code < 50000; } else { /* Likely a network or transport error of some kind. Certainly not fatal to the connection */ @@ -29,9 +33,9 @@ function protocolMessageFromRawError(err: ErrorInfo) { /* err will be either a legacy (non-protocolmessage) comet error response * (which will have an err.code), or a xhr/network error (which won't). */ if (shouldBeErrorAction(err)) { - return [ProtocolMessage.fromValues({ action: ProtocolMessage.Action.ERROR, error: err })]; + return [protocolMessageFromValues({ action: actions.ERROR, error: err })]; } else { - return [ProtocolMessage.fromValues({ action: ProtocolMessage.Action.DISCONNECTED, error: err })]; + return [protocolMessageFromValues({ action: actions.DISCONNECTED, error: err })]; } } @@ -65,7 +69,7 @@ abstract class CometTransport extends Transport { headers: Record | null, params?: Record | null, body?: unknown, - requestMode?: number + requestMode?: number, ): IXHRRequest; connect(): void { @@ -80,7 +84,7 @@ abstract class CometTransport extends Transport { this.baseUri = cometScheme + host + ':' + port + '/comet/'; const connectUri = this.baseUri + 'connect'; Logger.logAction(Logger.LOG_MINOR, 'CometTransport.connect()', 'uri: ' + connectUri); - this.auth.getAuthParams((err: Error, authParams: Record) => { + Utils.whenPromiseSettles(this.auth.getAuthParams(), (err: Error | null, authParams?: Record) => { if (err) { this.disconnect(err); return; @@ -89,12 +93,12 @@ abstract class CometTransport extends Transport { return; } this.authParams = authParams; - const connectParams = this.params.getConnectParams(authParams); + const connectParams = this.params.getConnectParams(authParams!); if ('stream' in connectParams) this.stream = connectParams.stream; Logger.logAction( Logger.LOG_MINOR, 'CometTransport.connect()', - 'connectParams:' + Utils.toQueryString(connectParams) + 'connectParams:' + Utils.toQueryString(connectParams), ); /* this will be the 'recvRequest' so this connection can stream messages */ @@ -104,7 +108,7 @@ abstract class CometTransport extends Transport { null, connectParams, null, - this.stream ? XHRStates.REQ_RECV_STREAM : XHRStates.REQ_RECV + this.stream ? XHRStates.REQ_RECV_STREAM : XHRStates.REQ_RECV, )); connectRequest.on('data', (data: any) => { @@ -172,7 +176,7 @@ abstract class CometTransport extends Transport { Logger.logAction( Logger.LOG_ERROR, 'CometTransport.request' + (closing ? 'Close()' : 'Disconnect()'), - 'request returned err = ' + Utils.inspectError(err) + 'request returned err = ' + Utils.inspectError(err), ); this.finish('disconnected', err); } @@ -250,7 +254,7 @@ abstract class CometTransport extends Transport { null, this.authParams, this.encodeRequest(items), - XHRStates.REQ_SEND + XHRStates.REQ_SEND, )); sendRequest.on('complete', (err: ErrorInfo, data: string) => { @@ -258,7 +262,7 @@ abstract class CometTransport extends Transport { Logger.logAction( Logger.LOG_ERROR, 'CometTransport.sendItems()', - 'on complete: err = ' + Utils.inspectError(err) + 'on complete: err = ' + Utils.inspectError(err), ); this.sendRequest = null; @@ -309,7 +313,7 @@ abstract class CometTransport extends Transport { null, this.authParams, null, - this.stream ? XHRStates.REQ_RECV_STREAM : XHRStates.REQ_RECV_POLL + this.stream ? XHRStates.REQ_RECV_STREAM : XHRStates.REQ_RECV_POLL, )); recvRequest.on('data', (data: string) => { @@ -344,12 +348,15 @@ abstract class CometTransport extends Transport { try { const items = this.decodeResponse(responseData); if (items && items.length) - for (let i = 0; i < items.length; i++) this.onProtocolMessage(ProtocolMessage.fromDeserialized(items[i])); + for (let i = 0; i < items.length; i++) + this.onProtocolMessage( + protocolMessageFromDeserialized(items[i], this.connectionManager.realtime._RealtimePresence), + ); } catch (e) { Logger.logAction( Logger.LOG_ERROR, 'CometTransport.onData()', - 'Unexpected exception handing channel event: ' + (e as Error).stack + 'Unexpected exception handing channel event: ' + (e as Error).stack, ); } } @@ -363,13 +370,19 @@ abstract class CometTransport extends Transport { return responseData; } - /* For comet, we could do the auth update by aborting the current recv and - * starting a new one with the new token, that'd be sufficient for realtime. - * Problem is JSONP - you can't cancel truly abort a recv once started. So - * we need to send an AUTH for jsonp. In which case it's simpler to keep all - * comet transports the same and do it for all of them. So we send the AUTH - * instead, and don't need to abort the recv */ - onAuthUpdated = (tokenDetails: API.Types.TokenDetails): void => { + /* Historical comment, back from when we supported JSONP: + * + * > For comet, we could do the auth update by aborting the current recv and + * > starting a new one with the new token, that'd be sufficient for realtime. + * > Problem is JSONP - you can't cancel truly abort a recv once started. So + * > we need to send an AUTH for jsonp. In which case it's simpler to keep all + * > comet transports the same and do it for all of them. So we send the AUTH + * > instead, and don't need to abort the recv + * + * Now that we’ve dropped JSONP support, we may be able to revisit the above; + * see https://github.com/ably/ably-js/issues/1214. + */ + onAuthUpdated = (tokenDetails: API.TokenDetails): void => { this.authParams = { access_token: tokenDetails.token }; }; } diff --git a/src/common/lib/transport/connectionmanager.ts b/src/common/lib/transport/connectionmanager.ts index 20cae106ec..07cd440c41 100644 --- a/src/common/lib/transport/connectionmanager.ts +++ b/src/common/lib/transport/connectionmanager.ts @@ -1,8 +1,12 @@ -import ProtocolMessage from 'common/lib/types/protocolmessage'; +import ProtocolMessage, { + actions, + stringify as stringifyProtocolMessage, + fromValues as protocolMessageFromValues, +} from 'common/lib/types/protocolmessage'; import * as Utils from 'common/lib/util/utils'; import Protocol, { PendingMessage } from './protocol'; import Defaults, { getAgentString } from 'common/lib/util/defaults'; -import Platform from 'common/platform'; +import Platform, { TransportImplementations } from 'common/platform'; import EventEmitter from '../util/eventemitter'; import MessageQueue from './messagequeue'; import Logger from '../util/logger'; @@ -10,20 +14,20 @@ import ConnectionStateChange from 'common/lib/client/connectionstatechange'; import ConnectionErrors, { isRetriable } from './connectionerrors'; import ErrorInfo, { IPartialErrorInfo, PartialErrorInfo } from 'common/lib/types/errorinfo'; import Auth from 'common/lib/client/auth'; -import Message from 'common/lib/types/message'; +import Message, { getMessagesSize } from 'common/lib/types/message'; import Multicaster, { MulticasterInstance } from 'common/lib/util/multicaster'; -import WebSocketTransport from './websockettransport'; import Transport, { TransportCtor } from './transport'; import * as API from '../../../../ably'; import { ErrCallback } from 'common/types/utils'; import HttpStatusCodes from 'common/constants/HttpStatusCodes'; +import BaseRealtime from '../client/baserealtime'; +import { NormalisedClientOptions } from 'common/types/ClientOptions'; +import TransportName, { TransportNames } from 'common/constants/TransportName'; -type Realtime = any; -type ClientOptions = any; +let globalObject = typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : self; const haveWebStorage = () => typeof Platform.WebStorage !== 'undefined' && Platform.WebStorage?.localSupported; const haveSessionStorage = () => typeof Platform.WebStorage !== 'undefined' && Platform.WebStorage?.sessionSupported; -const actions = ProtocolMessage.Action; const noop = function () {}; const transportPreferenceName = 'ably-transport-preference'; @@ -38,13 +42,6 @@ function clearSessionRecoverData() { return haveSessionStorage() && Platform.WebStorage?.removeSession?.(sessionRecoveryName); } -function betterTransportThan(a: Transport, b: Transport) { - return ( - Utils.arrIndexOf(Platform.Defaults.transportPreferenceOrder, a.shortName) > - Utils.arrIndexOf(Platform.Defaults.transportPreferenceOrder, b.shortName) - ); -} - function bundleWith(dest: ProtocolMessage, src: ProtocolMessage, maxSize: number) { let action; if (dest.channel !== src.channel) { @@ -61,7 +58,7 @@ function bundleWith(dest: ProtocolMessage, src: ProtocolMessage, maxSize: number } const kind = action === actions.PRESENCE ? 'presence' : 'messages', proposed = (dest as Record)[kind].concat((src as Record)[kind]), - size = Message.getMessagesSize(proposed); + size = getMessagesSize(proposed); if (size > maxSize) { /* RTL6d1 */ return false; @@ -71,7 +68,7 @@ function bundleWith(dest: ProtocolMessage, src: ProtocolMessage, maxSize: number return false; } if ( - !Utils.arrEvery(proposed, function (msg: Message) { + !proposed.every(function (msg: Message) { return !msg.id; }) ) { @@ -89,16 +86,16 @@ type RecoveryContext = { channelSerials: { [name: string]: string }; }; -function decodeRecoveryKey(recoveryKey: string): RecoveryContext | null { +function decodeRecoveryKey(recoveryKey: NormalisedClientOptions['recover']): RecoveryContext | null { try { - return JSON.parse(recoveryKey); + return JSON.parse(recoveryKey as string); } catch (e) { return null; } } export class TransportParams { - options: ClientOptions; + options: NormalisedClientOptions; host: string | null; mode: string; format?: Utils.Format; @@ -106,7 +103,7 @@ export class TransportParams { stream?: any; heartbeats?: boolean; - constructor(options: ClientOptions, host: string | null, mode: string, connectionKey?: string) { + constructor(options: NormalisedClientOptions, host: string | null, mode: string, connectionKey?: string) { this.options = options; this.host = host; this.mode = mode; @@ -118,9 +115,6 @@ export class TransportParams { const params = authParams ? Utils.copy(authParams) : {}; const options = this.options; switch (this.mode) { - case 'upgrade': - params.upgrade = this.connectionKey as string; - break; case 'resume': params.resume = this.connectionKey as string; break; @@ -180,14 +174,14 @@ type ConnectionState = { sendEvents?: boolean; failState?: string; retryDelay?: number; - forceQueueEvents?: boolean; retryImmediately?: boolean; error?: IPartialErrorInfo; }; class ConnectionManager extends EventEmitter { - realtime: Realtime; - options: ClientOptions; + supportedTransports: Partial> = {}; + realtime: BaseRealtime; + options: NormalisedClientOptions; states: Record; state: ConnectionState; errorReason: IPartialErrorInfo | string | null; @@ -198,34 +192,45 @@ class ConnectionManager extends EventEmitter { connectionKey?: string; connectionStateTtl: number; maxIdleInterval: number | null; - transports: string[]; - baseTransport: string; - upgradeTransports: string[]; + transports: TransportName[]; + baseTransport?: TransportName; + webSocketTransportAvailable?: true; transportPreference: string | null; httpHosts: string[]; + wsHosts: string[]; activeProtocol: null | Protocol; - proposedTransports: Transport[]; - pendingTransports: Transport[]; + pendingTransport?: Transport; + proposedTransport?: Transport; host: string | null; lastAutoReconnectAttempt: number | null; lastActivity: number | null; forceFallbackHost: boolean; - connectCounter: number; transitionTimer?: number | NodeJS.Timeout | null; suspendTimer?: number | NodeJS.Timeout | null; retryTimer?: number | NodeJS.Timeout | null; disconnectedRetryCount: number = 0; + pendingChannelMessagesState: { + // Whether a message is currently being processed + isProcessing: boolean; + // The messages remaining to be processed (excluding any message currently being processed) + queue: { message: ProtocolMessage; transport: Transport }[]; + } = { isProcessing: false, queue: [] }; + webSocketSlowTimer: NodeJS.Timeout | null; + wsCheckResult: boolean | null; + webSocketGiveUpTimer: NodeJS.Timeout | null; + abandonedWebSocket: boolean; + connectCounter: number; - constructor(realtime: Realtime, options: ClientOptions) { + constructor(realtime: BaseRealtime, options: NormalisedClientOptions) { super(); - ConnectionManager.initTransports(); this.realtime = realtime; + this.initTransports(); this.options = options; const timeouts = options.timeouts; - /* connectingTimeout: leave preferenceConnectTimeout (~6s) to try the - * preference transport, then realtimeRequestTimeout (~10s) to establish + /* connectingTimeout: leave webSocketConnectTimeout (~6s) to try the + * websocket transport, then realtimeRequestTimeout (~10s) to establish * the base transport in case that fails */ - const connectingTimeout = timeouts.preferenceConnectTimeout + timeouts.realtimeRequestTimeout; + const connectingTimeout = timeouts.webSocketConnectTimeout + timeouts.realtimeRequestTimeout; this.states = { initialized: { state: 'initialized', @@ -249,14 +254,6 @@ class ConnectionManager extends EventEmitter { sendEvents: true, failState: 'disconnected', }, - synchronizing: { - state: 'connected', - terminal: false, - queueEvents: true, - sendEvents: false, - forceQueueEvents: true, - failState: 'disconnected', - }, disconnected: { state: 'disconnected', terminal: false, @@ -295,38 +292,41 @@ class ConnectionManager extends EventEmitter { this.connectionStateTtl = timeouts.connectionStateTtl; this.maxIdleInterval = null; - this.transports = Utils.intersect( - options.transports || Defaults.defaultTransports, - ConnectionManager.supportedTransports - ); - /* baseTransports selects the leftmost transport in the Defaults.baseTransportOrder list - * that's both requested and supported. Normally this will be xhr_polling; - * if xhr isn't supported it will be jsonp. If the user has forced a - * transport, it'll just be that one. */ - this.baseTransport = Utils.intersect(Defaults.baseTransportOrder, this.transports)[0]; - this.upgradeTransports = Utils.intersect(this.transports, Defaults.upgradeTransports); + this.transports = Utils.intersect(options.transports || Defaults.defaultTransports, this.supportedTransports); this.transportPreference = null; + if (this.transports.includes(TransportNames.WebSocket)) { + this.webSocketTransportAvailable = true; + } + if (this.transports.includes(TransportNames.XhrPolling)) { + this.baseTransport = TransportNames.XhrPolling; + } else if (this.transports.includes(TransportNames.Comet)) { + this.baseTransport = TransportNames.Comet; + } + this.httpHosts = Defaults.getHosts(options); + this.wsHosts = Defaults.getHosts(options, true); this.activeProtocol = null; - this.proposedTransports = []; - this.pendingTransports = []; this.host = null; this.lastAutoReconnectAttempt = null; this.lastActivity = null; this.forceFallbackHost = false; this.connectCounter = 0; + this.wsCheckResult = null; + this.webSocketSlowTimer = null; + this.webSocketGiveUpTimer = null; + this.abandonedWebSocket = false; Logger.logAction(Logger.LOG_MINOR, 'Realtime.ConnectionManager()', 'started'); Logger.logAction( Logger.LOG_MICRO, 'Realtime.ConnectionManager()', - 'requested transports = [' + (options.transports || Defaults.defaultTransports) + ']' + 'requested transports = [' + (options.transports || Defaults.defaultTransports) + ']', ); Logger.logAction( Logger.LOG_MICRO, 'Realtime.ConnectionManager()', - 'available transports = [' + this.transports + ']' + 'available transports = [' + this.transports + ']', ); Logger.logAction(Logger.LOG_MICRO, 'Realtime.ConnectionManager()', 'http hosts = [' + this.httpHosts + ']'); @@ -340,7 +340,6 @@ class ConnectionManager extends EventEmitter { if (addEventListener) { /* intercept close event in browser to persist connection id if requested */ if (haveSessionStorage() && typeof options.recover === 'function') { - /* Usually can't use bind as not supported in IE8, but IE doesn't support sessionStorage, so... */ addEventListener('beforeunload', this.persistConnection.bind(this)); } @@ -349,7 +348,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MAJOR, 'Realtime.ConnectionManager()', - 'beforeunload event has triggered the connection to close as closeOnUnload is true' + 'beforeunload event has triggered the connection to close as closeOnUnload is true', ); this.requestState({ state: 'closing' }); }); @@ -361,15 +360,12 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager caught browser ‘online’ event', - 'reattempting connection' + 'reattempting connection', ); this.requestState({ state: 'connecting' }); } else if (this.state == this.states.connecting) { // RTN20c: if 'online' event recieved while CONNECTING, abandon connection attempt and retry - this.pendingTransports.forEach(function (transport) { - // Detach transport listeners to avoid connection state side effects from calling dispose - transport.off(); - }); + this.pendingTransport?.off(); this.disconnectAllTransports(); this.startConnect(); @@ -381,7 +377,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager caught browser ‘offline’ event', - 'disconnecting active transport' + 'disconnecting active transport', ); // Not sufficient to just go to the 'disconnected' state, want to // force all transports to reattempt the connection. Will immediately @@ -396,15 +392,28 @@ class ConnectionManager extends EventEmitter { * transport management *********************/ - static supportedTransports: Record = {}; + // Used by tests + static supportedTransports(additionalImplementations: TransportImplementations) { + const storage: TransportStorage = { supportedTransports: {} }; + this.initTransports(additionalImplementations, storage); + return storage.supportedTransports; + } + + private static initTransports(additionalImplementations: TransportImplementations, storage: TransportStorage) { + const implementations = { ...Platform.Transports.bundledImplementations, ...additionalImplementations }; - static initTransports() { - WebSocketTransport(ConnectionManager); - Utils.arrForEach(Platform.Transports, function (initFn) { - initFn(ConnectionManager); + [TransportNames.WebSocket, ...Platform.Transports.order].forEach((transportName) => { + const transport = implementations[transportName]; + if (transport && transport.isAvailable()) { + storage.supportedTransports[transportName] = transport; + } }); } + initTransports() { + ConnectionManager.initTransports(this.realtime._additionalTransportImplementations, this); + } + createTransportParams(host: string | null, mode: string): TransportParams { return new TransportParams(this.options, host, mode, this.connectionKey); } @@ -427,7 +436,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager.getTransportParams()', - 'Calling clientOptions-provided recover function with last session data' + 'Calling clientOptions-provided recover function with last session data', ); recoverFn(lastSessionData, (shouldRecover?: boolean) => { if (shouldRecover) { @@ -448,7 +457,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager.getTransportParams()', - 'Transport recovery mode = recover; recoveryKey = ' + this.options.recover + 'Transport recovery mode = recover; recoveryKey = ' + this.options.recover, ); const recoveryContext = decodeRecoveryKey(this.options.recover); if (recoveryContext) { @@ -458,7 +467,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager.getTransportParams()', - 'Transport params = ' + transportParams.toString() + 'Transport params = ' + transportParams.toString(), ); } callback(transportParams); @@ -471,11 +480,11 @@ class ConnectionManager extends EventEmitter { * @param candidate, the transport to try * @param callback */ - tryATransport(transportParams: TransportParams, candidate: string, callback: Function): void { + tryATransport(transportParams: TransportParams, candidate: TransportName, callback: Function): void { Logger.logAction(Logger.LOG_MICRO, 'ConnectionManager.tryATransport()', 'trying ' + candidate); - Transport.tryConnect( - ConnectionManager.supportedTransports[candidate], + this.proposedTransport = Transport.tryConnect( + this.supportedTransports[candidate]!, this, this.realtime.auth, transportParams, @@ -486,7 +495,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager.tryATransport()', - 'connection ' + state.state + ' while we were attempting the transport; closing ' + transport + 'connection ' + state.state + ' while we were attempting the transport; closing ' + transport, ); transport.close(); } @@ -498,7 +507,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager.tryATransport()', - 'transport ' + candidate + ' ' + wrappedErr.event + ', err: ' + wrappedErr.error.toString() + 'transport ' + candidate + ' ' + wrappedErr.event + ', err: ' + wrappedErr.error.toString(), ); /* Comet transport onconnect token errors can be dealt with here. @@ -510,7 +519,7 @@ class ConnectionManager extends EventEmitter { ) { this.errorReason = wrappedErr.error; /* re-get a token and try again */ - this.realtime.auth._forceNewToken(null, null, (err: ErrorInfo) => { + Utils.whenPromiseSettles(this.realtime.auth._forceNewToken(null, null), (err: ErrorInfo | null) => { if (err) { this.actOnErrorFromAuthorize(err); return; @@ -537,11 +546,11 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MICRO, 'ConnectionManager.tryATransport()', - 'viable transport ' + candidate + '; setting pending' + 'viable transport ' + candidate + '; setting pending', ); this.setTransportPending(transport as Transport, transportParams); callback(null, transport); - } + }, ); } @@ -556,41 +565,21 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager.setTransportPending()', - 'transport = ' + transport + '; mode = ' + mode + 'transport = ' + transport + '; mode = ' + mode, ); - Utils.arrDeleteValue(this.proposedTransports, transport); - this.pendingTransports.push(transport); - const optimalTransport = - Platform.Defaults.transportPreferenceOrder[Platform.Defaults.transportPreferenceOrder.length - 1]; - transport.once('connected', (error: ErrorInfo, connectionId: string, connectionDetails: Record) => { - if (mode == 'upgrade' && this.activeProtocol) { - /* if ws and xhrs are connecting in parallel, delay xhrs activation to let ws go ahead */ - if ( - transport.shortName !== optimalTransport && - Utils.arrIn(this.getUpgradePossibilities(), optimalTransport) && - this.activeProtocol - ) { - setTimeout(() => { - this.scheduleTransportActivation(error, transport, connectionId, connectionDetails); - }, this.options.timeouts.parallelUpgradeDelay); - } else { - this.scheduleTransportActivation(error, transport, connectionId, connectionDetails); - } - } else { - this.activateTransport(error, transport, connectionId, connectionDetails); + this.pendingTransport = transport; - /* allow connectImpl to start the upgrade process if needed, but allow - * other event handlers, including activating the transport, to run first */ - Platform.Config.nextTick(() => { - this.connectImpl(transportParams); - }); - } + this.cancelWebSocketSlowTimer(); + this.cancelWebSocketGiveUpTimer(); + + transport.once('connected', (error: ErrorInfo, connectionId: string, connectionDetails: Record) => { + this.activateTransport(error, transport, connectionId, connectionDetails); if (mode === 'recover' && this.options.recover) { /* After a successful recovery, we unpersist, as a recovery key cannot * be used more than once */ - this.options.recover = null; + delete this.options.recover; this.unpersistConnection(); } }); @@ -603,166 +592,6 @@ class ConnectionManager extends EventEmitter { this.emit('transport.pending', transport); } - /** - * Called when an upgrade transport is connected, - * to schedule the activation of that transport. - * @param error - * @param transport - * @param connectionId - * @param connectionDetails - */ - scheduleTransportActivation( - error: ErrorInfo, - transport: Transport, - connectionId: string, - connectionDetails: Record - ): void { - const currentTransport = this.activeProtocol && this.activeProtocol.getTransport(), - abandon = () => { - transport.disconnect(); - Utils.arrDeleteValue(this.pendingTransports, transport); - }; - - if (this.state !== this.states.connected && this.state !== this.states.connecting) { - /* This is most likely to happen for the delayed XHRs, when XHRs and ws are scheduled in parallel*/ - Logger.logAction( - Logger.LOG_MINOR, - 'ConnectionManager.scheduleTransportActivation()', - 'Current connection state (' + - this.state.state + - (this.state === this.states.synchronizing ? ', but with an upgrade already in progress' : '') + - ') is not valid to upgrade in; abandoning upgrade to ' + - transport.shortName - ); - abandon(); - return; - } - - if (currentTransport && !betterTransportThan(transport, currentTransport)) { - Logger.logAction( - Logger.LOG_MINOR, - 'ConnectionManager.scheduleTransportActivation()', - 'Proposed transport ' + - transport.shortName + - ' is no better than current active transport ' + - currentTransport.shortName + - ' - abandoning upgrade' - ); - abandon(); - return; - } - - Logger.logAction( - Logger.LOG_MINOR, - 'ConnectionManager.scheduleTransportActivation()', - 'Scheduling transport upgrade; transport = ' + transport - ); - - let oldProtocol: Protocol | null = null; - - if (!transport.isConnected) { - /* This is only possible if the xhr streaming transport was disconnected during the parallelUpgradeDelay */ - Logger.logAction( - Logger.LOG_MINOR, - 'ConnectionManager.scheduleTransportActivation()', - 'Proposed transport ' + transport.shortName + 'is no longer connected; abandoning upgrade' - ); - abandon(); - return; - } - - if (this.state === this.states.connected) { - Logger.logAction( - Logger.LOG_MICRO, - 'ConnectionManager.scheduleTransportActivation()', - 'Currently connected, so temporarily pausing events until the upgrade is complete' - ); - this.state = this.states.synchronizing; - oldProtocol = this.activeProtocol; - } else if (this.state !== this.states.connecting) { - /* Note: upgrading from the connecting state is valid if the old active - * transport was deactivated after the upgrade transport first connected; - * see logic in deactivateTransport */ - Logger.logAction( - Logger.LOG_MINOR, - 'ConnectionManager.scheduleTransportActivation()', - 'Current connection state (' + - this.state.state + - (this.state === this.states.synchronizing ? ', but with an upgrade already in progress' : '') + - ') is not valid to upgrade in; abandoning upgrade to ' + - transport.shortName - ); - abandon(); - return; - } - - Logger.logAction( - Logger.LOG_MINOR, - 'ConnectionManager.scheduleTransportActivation()', - 'Syncing transport; transport = ' + transport - ); - - const finishUpgrade = () => { - Logger.logAction( - Logger.LOG_MINOR, - 'ConnectionManager.scheduleTransportActivation()', - 'Activating transport; transport = ' + transport - ); - - // Send ACTIVATE to tell the server to make this transport the - // active transport, which suspends channels until we re-attach. - transport.send( - ProtocolMessage.fromValues({ - action: actions.ACTIVATE, - }) - ); - - this.activateTransport(error, transport, connectionId, connectionDetails); - /* Restore pre-sync state. If state has changed in the meantime, - * don't touch it -- since the websocket transport waits a tick before - * disposing itself, it's possible for it to have happily synced - * without err while, unknown to it, the connection has closed in the - * meantime and the ws transport is scheduled for death */ - if (this.state === this.states.synchronizing) { - Logger.logAction( - Logger.LOG_MICRO, - 'ConnectionManager.scheduleTransportActivation()', - 'Pre-upgrade protocol idle, sending queued messages on upgraded transport; transport = ' + transport - ); - this.state = this.states.connected; - } else { - Logger.logAction( - Logger.LOG_MINOR, - 'ConnectionManager.scheduleTransportActivation()', - 'Pre-upgrade protocol idle, but state is now ' + this.state.state + ', so leaving unchanged' - ); - } - if (this.state.sendEvents) { - this.sendQueuedMessages(); - } - }; - - /* Wait until sync is done and old transport is idle before activating new transport. This - * guarantees that messages arrive at realtime in the same order they are sent. - * - * If a message times out on the old transport, since it's still the active transport the - * message will be requeued. deactivateTransport will see the pending transport and notify - * the `connecting` state without starting a new connection, so the new transport can take - * over once deactivateTransport clears the old protocol's queue. - * - * If there is no old protocol, that meant that we weren't in the connected state at the - * beginning of the sync - likely the base transport died just before the sync. So can just - * finish the upgrade. If we're actually in closing/failed rather than connecting, that's - * fine, activatetransport will deal with that. */ - if (oldProtocol) { - /* Most of the time this will be already true: the new-transport sync will have given - * enough time for in-flight messages on the old transport to complete. */ - oldProtocol.onceIdle(finishUpgrade); - } else { - finishUpgrade(); - } - } - /** * Called when a transport is connected, and the connectionmanager decides that * it will now be the active transport. Returns whether or not it activated @@ -775,7 +604,7 @@ class ConnectionManager extends EventEmitter { error: ErrorInfo, transport: Transport, connectionId: string, - connectionDetails: Record + connectionDetails: Record, ): boolean { Logger.logAction(Logger.LOG_MINOR, 'ConnectionManager.activateTransport()', 'transport = ' + transport); if (error) { @@ -788,7 +617,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MICRO, 'ConnectionManager.activateTransport()', - 'connectionDetails = ' + JSON.stringify(connectionDetails) + 'connectionDetails = ' + JSON.stringify(connectionDetails), ); } @@ -801,7 +630,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager.activateTransport()', - 'current state = ' + existingState.state + 'current state = ' + existingState.state, ); if ( existingState.state == this.states.closing.state || @@ -811,21 +640,20 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager.activateTransport()', - 'Disconnecting transport and abandoning' + 'Disconnecting transport and abandoning', ); transport.disconnect(); return false; } - /* remove this transport from pending transports */ - Utils.arrDeleteValue(this.pendingTransports, transport); + delete this.pendingTransport; /* if the transport is not connected then don't activate it */ if (!transport.isConnected) { Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager.activateTransport()', - 'Declining to activate transport ' + transport + ' since it appears to no longer be connected' + 'Declining to activate transport ' + transport + ' since it appears to no longer be connected', ); return false; } @@ -853,7 +681,7 @@ class ConnectionManager extends EventEmitter { (connectedErr: ErrorInfo, _connectionId: string, connectionDetails: Record) => { this.onConnectionDetailsUpdate(connectionDetails, transport); this.emit('update', new ConnectionStateChange(connectedState, connectedState, null, connectedErr)); - } + }, ); }); @@ -861,10 +689,7 @@ class ConnectionManager extends EventEmitter { * error). */ if (existingState.state === this.states.connected.state) { if (error) { - /* if upgrading without error, leave any existing errorReason alone */ this.errorReason = this.realtime.connection.errorReason = error; - /* Only bother emitting an upgrade if there's an error; otherwise it's - * just a transport upgrade, so auth details won't have changed */ this.emit('update', new ConnectionStateChange(connectedState, connectedState, null, error)); } } else { @@ -892,7 +717,7 @@ class ConnectionManager extends EventEmitter { transport.shortName + ') finishing with ' + existingActiveProtocol.messageQueue.count() + - ' messages still pending' + ' messages still pending', ); } if (existingActiveProtocol.transport === transport) { @@ -907,37 +732,6 @@ class ConnectionManager extends EventEmitter { } } - /* Terminate any other pending transport(s), and - * abort any not-yet-pending transport attempts */ - Utils.safeArrForEach(this.pendingTransports, (pendingTransport) => { - if (pendingTransport === transport) { - const msg = - 'Assumption violated: activating a transport that is still marked as a pending transport; transport = ' + - transport.shortName + - '; stack = ' + - new Error().stack; - Logger.logAction(Logger.LOG_ERROR, 'ConnectionManager.activateTransport()', msg); - Utils.arrDeleteValue(this.pendingTransports, transport); - } else { - pendingTransport.disconnect(); - } - }); - Utils.safeArrForEach(this.proposedTransports, (proposedTransport: Transport) => { - if (proposedTransport === transport) { - Logger.logAction( - Logger.LOG_ERROR, - 'ConnectionManager.activateTransport()', - 'Assumption violated: activating a transport that is still marked as a proposed transport; transport = ' + - transport.shortName + - '; stack = ' + - new Error().stack - ); - Utils.arrDeleteValue(this.proposedTransports, transport); - } else { - proposedTransport.dispose(); - } - }); - return true; } @@ -949,8 +743,7 @@ class ConnectionManager extends EventEmitter { deactivateTransport(transport: Transport, state: string, error: ErrorInfo): void { const currentProtocol = this.activeProtocol, wasActive = currentProtocol && currentProtocol.getTransport() === transport, - wasPending = Utils.arrDeleteValue(this.pendingTransports, transport), - wasProposed = Utils.arrDeleteValue(this.proposedTransports, transport), + wasPending = transport === this.pendingTransport, noTransportsScheduledForActivation = this.noTransportsScheduledForActivation(); Logger.logAction(Logger.LOG_MINOR, 'ConnectionManager.deactivateTransport()', 'transport = ' + transport); @@ -959,8 +752,8 @@ class ConnectionManager extends EventEmitter { 'ConnectionManager.deactivateTransport()', 'state = ' + state + - (wasActive ? '; was active' : wasPending ? '; was pending' : wasProposed ? '; was proposed' : '') + - (noTransportsScheduledForActivation ? '' : '; another transport is scheduled for activation') + (wasActive ? '; was active' : wasPending ? '; was pending' : '') + + (noTransportsScheduledForActivation ? '' : '; another transport is scheduled for activation'), ); if (error && error.message) Logger.logAction(Logger.LOG_MICRO, 'ConnectionManager.deactivateTransport()', 'reason = ' + error.message); @@ -971,16 +764,11 @@ class ConnectionManager extends EventEmitter { 'ConnectionManager.deactivateTransport()', 'Getting, clearing, and requeuing ' + (this.activeProtocol as Protocol).messageQueue.count() + - ' pending messages' + ' pending messages', ); this.queuePendingMessages((currentProtocol as Protocol).getPendingMessages()); - /* Clear any messages we requeue to allow the protocol to become idle. - * In case of an upgrade, this will trigger an immediate activation of - * the upgrade transport, so delay a tick so this transport can finish - * deactivating */ - Platform.Config.nextTick(function () { - (currentProtocol as Protocol).clearPendingMessages(); - }); + /* Clear any messages we requeue to allow the protocol to become idle.*/ + (currentProtocol as Protocol).clearPendingMessages(); this.activeProtocol = this.host = null; } @@ -999,7 +787,7 @@ class ConnectionManager extends EventEmitter { (wasActive && noTransportsScheduledForActivation) || (wasActive && state === 'failed') || state === 'closed' || - (currentProtocol === null && wasPending && this.pendingTransports.length === 0) + (currentProtocol === null && wasPending) ) { /* If we're disconnected with a 5xx we need to try fallback hosts * (RTN14d), but (a) due to how the upgrade sequence works, the @@ -1024,37 +812,13 @@ class ConnectionManager extends EventEmitter { this.notifyState({ state: newConnectionState, error: error }); return; } - - if (wasActive && state === 'disconnected' && this.state !== this.states.synchronizing) { - /* If we were active but there is another transport scheduled for - * activation, go into to the connecting state until that transport - * activates and sets us back to connected. (manually starting the - * transition timers in case that never happens). (If we were in the - * synchronizing state, then that's fine, the old transport just got its - * disconnected before the new one got the sync -- ignore it and keep - * waiting for the sync. If it fails we have a separate sync timer that - * will expire). */ - Logger.logAction( - Logger.LOG_MICRO, - 'ConnectionManager.deactivateTransport()', - 'wasActive but another transport is connected and scheduled for activation, so going into the connecting state until it activates' - ); - this.startSuspendTimer(); - this.startTransitionTimer(this.states.connecting); - this.notifyState({ state: 'connecting', error: error }); - } } /* Helper that returns true if there are no transports which are pending, * have been connected, and are just waiting for onceNoPending to fire before * being activated */ noTransportsScheduledForActivation(): boolean { - return ( - Utils.isEmpty(this.pendingTransports) || - this.pendingTransports.every(function (transport) { - return !transport.isConnected; - }) - ); + return !this.pendingTransport || !this.pendingTransport.isConnected; } setConnection(connectionId: string, connectionDetails: Record, hasConnectionError?: boolean): void { @@ -1077,7 +841,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager.setConnection()', - 'New connectionId; reattaching any attached channels' + 'New connectionId; reattaching any attached channels', ); } this.realtime.connection.id = this.connectionId = connectionId; @@ -1109,12 +873,12 @@ class ConnectionManager extends EventEmitter { return; } - const sinceLast = Utils.now() - this.lastActivity; + const sinceLast = Date.now() - this.lastActivity; if (sinceLast > this.connectionStateTtl + (this.maxIdleInterval as number)) { Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager.checkConnectionStateFreshness()', - 'Last known activity from realtime was ' + sinceLast + 'ms ago; discarding connection state' + 'Last known activity from realtime was ' + sinceLast + 'ms ago; discarding connection state', ); this.clearConnection(); this.states.connecting.failState = 'suspended'; @@ -1131,8 +895,8 @@ class ConnectionManager extends EventEmitter { if (recoveryKey) { setSessionRecoverData({ recoveryKey: recoveryKey, - disconnectedAt: Utils.now(), - location: global.location, + disconnectedAt: Date.now(), + location: globalObject.location, clientId: this.realtime.auth.clientId, }); } @@ -1164,24 +928,26 @@ class ConnectionManager extends EventEmitter { } enactStateChange(stateChange: ConnectionStateChange): void { - const logLevel = stateChange.current === 'failed' ? Logger.LOG_ERROR : Logger.LOG_MAJOR; - Logger.logAction( - logLevel, - 'Connection state', - stateChange.current + (stateChange.reason ? '; reason: ' + stateChange.reason : '') - ); + const action = 'Connection state'; + const message = stateChange.current + (stateChange.reason ? '; reason: ' + stateChange.reason : ''); + if (stateChange.current === 'failed') { + Logger.logAction(Logger.LOG_ERROR, action, message); + } else { + Logger.logAction(Logger.LOG_MAJOR, action, message); + } Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager.enactStateChange', 'setting new state: ' + stateChange.current + '; reason = ' + - (stateChange.reason && (stateChange.reason as ErrorInfo).message) + (stateChange.reason && (stateChange.reason as ErrorInfo).message), ); const newState = (this.state = this.states[stateChange.current as string]); if (stateChange.reason) { this.errorReason = stateChange.reason; - this.realtime.connection.errorReason = stateChange.reason; + // TODO remove this type assertion after fixing https://github.com/ably/ably-js/issues/1405 + this.realtime.connection.errorReason = stateChange.reason as ErrorInfo; } if (newState.terminal || newState.state === 'suspended') { /* suspended is nonterminal, but once in the suspended state, realtime @@ -1200,7 +966,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager.startTransitionTimer()', - 'transitionState: ' + transitionState.state + 'transitionState: ' + transitionState.state, ); if (this.transitionTimer) { @@ -1214,7 +980,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager ' + transitionState.state + ' timer expired', - 'requesting new state: ' + transitionState.failState + 'requesting new state: ' + transitionState.failState, ); this.notifyState({ state: transitionState.failState as string }); } @@ -1237,7 +1003,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager suspend timer expired', - 'requesting new state: suspended' + 'requesting new state: suspended', ); this.states.connecting.failState = 'suspended'; this.notifyState({ state: 'suspended' }); @@ -1272,6 +1038,92 @@ class ConnectionManager extends EventEmitter { } } + startWebSocketSlowTimer() { + this.webSocketSlowTimer = setTimeout(() => { + Logger.logAction(Logger.LOG_MINOR, 'ConnectionManager WebSocket slow timer', 'checking connectivity'); + if (this.wsCheckResult === null) { + this.checkWsConnectivity() + .then(() => { + Logger.logAction( + Logger.LOG_MINOR, + 'ConnectionManager WebSocket slow timer', + 'ws connectivity check succeeded', + ); + this.wsCheckResult = true; + }) + .catch(() => { + Logger.logAction( + Logger.LOG_MAJOR, + 'ConnectionManager WebSocket slow timer', + 'ws connectivity check failed', + ); + this.wsCheckResult = false; + }); + } + if (this.realtime.http.checkConnectivity) { + Utils.whenPromiseSettles(this.realtime.http.checkConnectivity(), (err, connectivity) => { + if (err || !connectivity) { + Logger.logAction( + Logger.LOG_MAJOR, + 'ConnectionManager WebSocket slow timer', + 'http connectivity check failed', + ); + this.cancelWebSocketGiveUpTimer(); + this.notifyState({ + state: 'disconnected', + error: new ErrorInfo("new ErrorInfo('Unable to connect (network unreachable)'", 80003, 404), + }); + } else { + Logger.logAction( + Logger.LOG_MINOR, + 'ConnectionManager WebSocket slow timer', + 'http connectivity check succeeded', + ); + } + }); + } + }, this.options.timeouts.webSocketSlowTimeout); + } + + cancelWebSocketSlowTimer() { + if (this.webSocketSlowTimer) { + clearTimeout(this.webSocketSlowTimer); + this.webSocketSlowTimer = null; + } + } + + startWebSocketGiveUpTimer(transportParams: TransportParams) { + this.webSocketGiveUpTimer = setTimeout(() => { + if (!this.wsCheckResult) { + Logger.logAction( + Logger.LOG_MINOR, + 'ConnectionManager WebSocket give up timer', + 'websocket connection took more than 10s; ' + (this.baseTransport ? 'trying base transport' : ''), + ); + if (this.baseTransport) { + this.abandonedWebSocket = true; + this.proposedTransport?.dispose(); + this.pendingTransport?.dispose(); + this.connectBase(transportParams, ++this.connectCounter); + } else { + // if we don't have a base transport to fallback to, just let the websocket connection attempt time out + Logger.logAction( + Logger.LOG_MAJOR, + 'ConnectionManager WebSocket give up timer', + 'websocket connectivity appears to be unavailable but no other transports to try', + ); + } + } + }, this.options.timeouts.webSocketConnectTimeout); + } + + cancelWebSocketGiveUpTimer() { + if (this.webSocketGiveUpTimer) { + clearTimeout(this.webSocketGiveUpTimer); + this.webSocketGiveUpTimer = null; + } + } + notifyState(indicated: ConnectionState): void { const state = indicated.state; @@ -1286,7 +1138,6 @@ class ConnectionManager extends EventEmitter { const retryImmediately = state === 'disconnected' && (this.state === this.states.connected || - this.state === this.states.synchronizing || indicated.retryImmediately || (this.state === this.states.connecting && indicated.error && @@ -1296,7 +1147,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager.notifyState()', - 'new state: ' + state + (retryImmediately ? '; will retry connection immediately' : '') + 'new state: ' + state + (retryImmediately ? '; will retry connection immediately' : ''), ); /* do nothing if we're already in the indicated state */ if (state == this.state.state) return; @@ -1305,6 +1156,8 @@ class ConnectionManager extends EventEmitter { * state), as these are superseded by this notification */ this.cancelTransitionTimer(); this.cancelRetryTimer(); + this.cancelWebSocketSlowTimer(); + this.cancelWebSocketGiveUpTimer(); this.checkSuspendTimer(indicated.state); if (state === 'suspended' || state === 'connected') { @@ -1327,17 +1180,17 @@ class ConnectionManager extends EventEmitter { this.state.state, newState.state, retryDelay, - indicated.error || (ConnectionErrors as Partial ErrorInfo>>)[newState.state]?.() + indicated.error || (ConnectionErrors as Partial ErrorInfo>>)[newState.state]?.(), ); if (retryImmediately) { const autoReconnect = () => { if (this.state === this.states.disconnected) { - this.lastAutoReconnectAttempt = Utils.now(); + this.lastAutoReconnectAttempt = Date.now(); this.requestState({ state: 'connecting' }); } }; - const sinceLast = this.lastAutoReconnectAttempt && Utils.now() - this.lastAutoReconnectAttempt + 1; + const sinceLast = this.lastAutoReconnectAttempt && Date.now() - this.lastAutoReconnectAttempt + 1; if (sinceLast && sinceLast < 1000) { Logger.logAction( Logger.LOG_MICRO, @@ -1346,7 +1199,7 @@ class ConnectionManager extends EventEmitter { sinceLast + 'ms ago, waiting another ' + (1000 - sinceLast) + - 'ms before trying again' + 'ms before trying again', ); setTimeout(autoReconnect, 1000 - sinceLast); } else { @@ -1370,7 +1223,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_ERROR, 'ConnectionManager.notifyState()', - 'Broken invariant: attempted to go into connected state, but there is no active protocol' + 'Broken invariant: attempted to go into connected state, but there is no active protocol', ); } @@ -1389,11 +1242,13 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager.requestState()', - 'requested state: ' + state + '; current state: ' + this.state.state + 'requested state: ' + state + '; current state: ' + this.state.state, ); if (state == this.state.state) return; /* silently do nothing */ /* kill running timers, as this request supersedes them */ + this.cancelWebSocketSlowTimer(); + this.cancelWebSocketGiveUpTimer(); this.cancelTransitionTimer(); this.cancelRetryTimer(); /* for suspend timer check rather than cancel -- eg requesting a connecting @@ -1408,7 +1263,7 @@ class ConnectionManager extends EventEmitter { this.state.state, newState.state, null, - request.error || (ConnectionErrors as Partial ErrorInfo>>)[newState.state]?.() + request.error || (ConnectionErrors as Partial ErrorInfo>>)[newState.state]?.(), ); this.enactStateChange(change); @@ -1428,7 +1283,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager.startConnect()', - 'Must be in connecting state to connect, but was ' + this.state.state + 'Must be in connecting state to connect, but was ' + this.state.state, ); return; } @@ -1479,139 +1334,139 @@ class ConnectionManager extends EventEmitter { }; if (this.errorReason && Auth.isTokenErr(this.errorReason as ErrorInfo)) { /* Force a refetch of a new token */ - auth._forceNewToken(null, null, authCb); + Utils.whenPromiseSettles(auth._forceNewToken(null, null), authCb); } else { - auth._ensureValidAuthCredentials(false, authCb); + Utils.whenPromiseSettles(auth._ensureValidAuthCredentials(false), authCb); } } } - /** - * There are three stages in connecting: - * - preference: if there is a cached transport preference, we try to connect - * on that. If that fails or times out we abort the attempt, remove the - * preference and fall back to base. If it succeeds, we try upgrading it if - * needed (will only be in the case where the preference is xhrs and the - * browser supports ws). - * - base: we try to connect with the best transport that we think will - * never fail for this browser (usually this is xhr_polling; for very old - * browsers will be jsonp, for node will be comet). If it doesn't work, we - * try fallback hosts. - * - upgrade: given a connected transport, we see if there are any better - * ones, and if so, try to upgrade to them. + /* + * there are, at most, two transports available with which a connection may + * be attempted: web_socket and/or a base transport (xhr_polling in browsers, + * comet in nodejs). web_socket is always preferred, and the base transport is + * only used in case web_socket connectivity appears to be unavailable. * - * connectImpl works out what stage you're at (which is purely a function of - * the current connection state and whether there are any stored preferences), - * and dispatches accordingly. After a transport has been set pending, - * tryATransport calls connectImpl to see if there's another stage to be done. - * */ - connectImpl(transportParams: TransportParams, connectCount?: number): void { + * connectImpl begins the transport selection process by checking which transports + * are available, and if there is a cached preference. It then defers to the + * transport-specific connect methods: connectWs and connectBase. + * + * It is also responsible for invalidating the cache in the case that a base + * transport preference is stored but web socket connectivity is now available. + * + * handling of the case where we need to failover from web_socket to the base + * transport is implemented in the connectWs method. + */ + connectImpl(transportParams: TransportParams, connectCount: number): void { const state = this.state.state; - - if (state !== this.states.connecting.state && state !== this.states.connected.state) { + if (state !== this.states.connecting.state) { /* Only keep trying as long as in the 'connecting' state (or 'connected' * for upgrading). Any operation can put us into 'disconnected' to cancel * connection attempts and wait before retrying, or 'failed' to fail. */ Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager.connectImpl()', - 'Must be in connecting state to connect (or connected to upgrade), but was ' + state - ); - } else if (this.pendingTransports.length) { - Logger.logAction( - Logger.LOG_MINOR, - 'ConnectionManager.connectImpl()', - 'Transports ' + this.pendingTransports[0].toString() + ' currently pending; taking no action' + 'Must be in connecting state to connect, but was ' + state, ); - } else if (state == this.states.connected.state) { - this.upgradeIfNeeded(transportParams); - } else if (this.transports.length > 1 && this.getTransportPreference()) { - this.connectPreference(transportParams, connectCount); - } else { - this.connectBase(transportParams, connectCount); + return; } - } - connectPreference(transportParams: TransportParams, connectCount?: number): void { - const preference = this.getTransportPreference(); - let preferenceTimeoutExpired = false; + const transportPreference = this.getTransportPreference(); - if (!Utils.arrIn(this.transports, preference)) { - this.unpersistTransportPreference(); - this.connectImpl(transportParams, connectCount); + // If transport preference is for a non-ws transport but websocket is now available, unpersist the preference for next time + if (transportPreference && transportPreference === this.baseTransport && this.webSocketTransportAvailable) { + this.checkWsConnectivity() + .then(() => { + this.wsCheckResult = true; + this.abandonedWebSocket = false; + this.unpersistTransportPreference(); + if (this.state === this.states.connecting) { + Logger.logAction( + Logger.LOG_MINOR, + 'ConnectionManager.connectImpl():', + 'web socket connectivity available, cancelling connection attempt with ' + this.baseTransport, + ); + this.disconnectAllTransports(); + this.connectWs(transportParams, ++this.connectCounter); + } + }) + .catch(noop); } - Logger.logAction( - Logger.LOG_MINOR, - 'ConnectionManager.connectPreference()', - 'Trying to connect with stored transport preference ' + preference - ); + if ( + (transportPreference && transportPreference === this.baseTransport) || + (this.baseTransport && !this.webSocketTransportAvailable) + ) { + this.connectBase(transportParams, connectCount); + } else { + this.connectWs(transportParams, connectCount); + } + } - const preferenceTimeout = setTimeout(() => { - preferenceTimeoutExpired = true; - if (!(this.state.state === this.states.connected.state)) { - Logger.logAction( - Logger.LOG_MINOR, - 'ConnectionManager.connectPreference()', - 'Shortcircuit connection attempt with ' + preference + ' failed; clearing preference and trying from scratch' - ); - /* Abort all connection attempts. (This also disconnects the active - * protocol, but none exists if we're not in the connected state) */ - this.disconnectAllTransports(); - /* Be quite agressive about clearing the stored preference if ever it doesn't work */ - this.unpersistTransportPreference(); - } - this.connectImpl(transportParams, connectCount); - }, this.options.timeouts.preferenceConnectTimeout); - - /* For connectPreference, just use the main host. If host fallback is needed, do it in connectBase. - * The wstransport it will substitute the httphost for an appropriate wshost */ - transportParams.host = this.httpHosts[0]; - this.tryATransport(transportParams, preference, (fatal: boolean, transport: Transport) => { - clearTimeout(preferenceTimeout); - if (preferenceTimeoutExpired && transport) { - /* Viable, but too late - connectImpl() will already be trying - * connectBase, and we weren't in upgrade mode. Just remove the - * onconnected listener and get rid of it */ - transport.off(); - transport.disconnect(); - Utils.arrDeleteValue(this.pendingTransports, transport); - } else if (!transport && !fatal) { - /* Preference failed in a transport-specific way. Try more */ - this.unpersistTransportPreference(); - this.connectImpl(transportParams, connectCount); - } - /* If suceeded, or failed fatally, nothing to do */ + /* + * connectWs starts two timers to monitor the success of a web_socket connection attempt: + * - webSocketSlowTimer: if this timer fires before the connection succeeds, + * cm will simultaneously check websocket and http/xhr connectivity. if the http + * connectivity check fails, we give up the connection sequence entirely and + * transition to disconnected. if the websocket connectivity check fails then + * we assume no ws connectivity and failover to base transport. in the case that + * the checks succeed, we continue with websocket and wait for it to try fallback hosts + * and, if unsuccessful, ultimately transition to disconnected. + * - webSocketGiveUpTimer: if this timer fires, and the preceding websocket + * connectivity check is still pending then we assume that there is an issue + * with the transport and fallback to base transport. + */ + connectWs(transportParams: TransportParams, connectCount: number) { + Logger.logAction(Logger.LOG_DEBUG, 'ConnectionManager.connectWs()'); + this.startWebSocketSlowTimer(); + this.startWebSocketGiveUpTimer(transportParams); + + this.tryTransportWithFallbacks('web_socket', transportParams, true, connectCount, () => { + return this.wsCheckResult !== false && !this.abandonedWebSocket; }); } - /** - * Try to establish a transport on the base transport (the best transport - * such that if it doesn't work, nothing will work) as determined through - * static feature detection, checking for network connectivity and trying - * fallback hosts if applicable. - * @param transportParams - */ - connectBase(transportParams: TransportParams, connectCount?: number): void { + connectBase(transportParams: TransportParams, connectCount: number) { + Logger.logAction(Logger.LOG_DEBUG, 'ConnectionManager.connectBase()'); + if (this.baseTransport) { + this.tryTransportWithFallbacks(this.baseTransport, transportParams, false, connectCount, () => true); + } else { + this.notifyState({ + state: 'disconnected', + error: new ErrorInfo('No transports left to try', 80000, 404), + }); + } + } + + tryTransportWithFallbacks( + transportName: TransportName, + transportParams: TransportParams, + ws: boolean, + connectCount: number, + shouldContinue: () => boolean, + ): void { + Logger.logAction(Logger.LOG_DEBUG, 'ConnectionManager.tryTransportWithFallbacks()', transportName); const giveUp = (err: IPartialErrorInfo) => { this.notifyState({ state: this.states.connecting.failState as string, error: err }); }; - const candidateHosts = this.httpHosts.slice(); + + const candidateHosts = ws ? this.wsHosts.slice() : this.httpHosts.slice(); + const hostAttemptCb = (fatal: boolean, transport: Transport) => { if (connectCount !== this.connectCounter) { return; } + if (!shouldContinue()) { + if (transport) { + transport.dispose(); + } + return; + } if (!transport && !fatal) { tryFallbackHosts(); } }; - Logger.logAction( - Logger.LOG_MINOR, - 'ConnectionManager.connectBase()', - 'Trying to connect with base transport ' + this.baseTransport - ); - /* first try to establish a connection with the priority host with http transport */ const host = candidateHosts.shift(); if (!host) { @@ -1634,26 +1489,32 @@ class ConnectionManager extends EventEmitter { giveUp(new PartialErrorInfo('Internal error: Http.checkConnectivity not set', null, 500)); return; } - this.realtime.http.checkConnectivity((err?: ErrorInfo | null, connectivity?: boolean) => { - if (connectCount !== this.connectCounter) { - return; - } - /* we know err won't happen but handle it here anyway */ - if (err) { - giveUp(err); - return; - } - if (!connectivity) { - /* the internet isn't reachable, so don't try the fallback hosts */ - giveUp(new ErrorInfo('Unable to connect (network unreachable)', 80003, 404)); - return; - } - /* the network is there, so there's a problem with the main host, or - * its dns. Try the fallback hosts. We could try them simultaneously but - * that would potentially cause a huge spike in load on the load balancer */ - transportParams.host = Utils.arrPopRandomElement(candidateHosts); - this.tryATransport(transportParams, this.baseTransport, hostAttemptCb); - }); + Utils.whenPromiseSettles( + this.realtime.http.checkConnectivity(), + (err?: ErrorInfo | null, connectivity?: boolean) => { + if (connectCount !== this.connectCounter) { + return; + } + if (!shouldContinue()) { + return; + } + /* we know err won't happen but handle it here anyway */ + if (err) { + giveUp(err); + return; + } + if (!connectivity) { + /* the internet isn't reachable, so don't try the fallback hosts */ + giveUp(new ErrorInfo('Unable to connect (network unreachable)', 80003, 404)); + return; + } + /* the network is there, so there's a problem with the main host, or + * its dns. Try the fallback hosts. We could try them simultaneously but + * that would potentially cause a huge spike in load on the load balancer */ + transportParams.host = Utils.arrPopRandomElement(candidateHosts); + this.tryATransport(transportParams, transportName, hostAttemptCb); + }, + ); }; if (this.forceFallbackHost && candidateHosts.length) { @@ -1662,35 +1523,7 @@ class ConnectionManager extends EventEmitter { return; } - this.tryATransport(transportParams, this.baseTransport, hostAttemptCb); - } - - getUpgradePossibilities(): string[] { - /* returns the subset of upgradeTransports to the right of the current - * transport in upgradeTransports (if it's in there - if not, currentSerial - * will be -1, so return upgradeTransports.slice(0) == upgradeTransports */ - const current = (this.activeProtocol as Protocol).getTransport().shortName; - const currentSerial = Utils.arrIndexOf(this.upgradeTransports, current); - return this.upgradeTransports.slice(currentSerial + 1) as string[]; - } - - upgradeIfNeeded(transportParams: Record): void { - const upgradePossibilities = this.getUpgradePossibilities(); - Logger.logAction( - Logger.LOG_MINOR, - 'ConnectionManager.upgradeIfNeeded()', - 'upgrade possibilities: ' + Platform.Config.inspect(upgradePossibilities) - ); - - if (!upgradePossibilities.length) { - return; - } - - Utils.arrForEach(upgradePossibilities, (upgradeTransport: string) => { - /* Note: the transport may mutate the params, so give each transport a fresh one */ - const upgradeTransportParams = this.createTransportParams(transportParams.host, 'upgrade'); - this.tryATransport(upgradeTransportParams, upgradeTransport, noop); - }); + this.tryATransport(transportParams, transportName, hostAttemptCb); } closeImpl(): void { @@ -1698,25 +1531,20 @@ class ConnectionManager extends EventEmitter { this.cancelSuspendTimer(); this.startTransitionTimer(this.states.closing); - Utils.safeArrForEach(this.pendingTransports, function (transport) { - Logger.logAction(Logger.LOG_MICRO, 'ConnectionManager.closeImpl()', 'Closing pending transport: ' + transport); - if (transport) transport.close(); - }); - - Utils.safeArrForEach(this.proposedTransports, function (transport) { + if (this.pendingTransport) { Logger.logAction( Logger.LOG_MICRO, 'ConnectionManager.closeImpl()', - 'Disposing of proposed transport: ' + transport + 'Closing pending transport: ' + this.pendingTransport, ); - if (transport) transport.dispose(); - }); + this.pendingTransport.close(); + } if (this.activeProtocol) { Logger.logAction( Logger.LOG_MICRO, 'ConnectionManager.closeImpl()', - 'Closing active transport: ' + this.activeProtocol.getTransport() + 'Closing active transport: ' + this.activeProtocol.getTransport(), ); this.activeProtocol.getTransport().close(); } @@ -1726,31 +1554,14 @@ class ConnectionManager extends EventEmitter { this.notifyState({ state: 'closed' }); } - onAuthUpdated(tokenDetails: API.Types.TokenDetails, callback: Function): void { + onAuthUpdated(tokenDetails: API.TokenDetails, callback: Function): void { switch (this.state.state) { case 'connected': { Logger.logAction( Logger.LOG_MICRO, 'ConnectionManager.onAuthUpdated()', - 'Sending AUTH message on active transport' + 'Sending AUTH message on active transport', ); - /* If there are any proposed/pending transports (eg an upgrade that - * isn't yet scheduled for activation) that hasn't yet started syncing, - * just to get rid of them & restart the upgrade with the new token, to - * avoid a race condition. (If it has started syncing, the AUTH will be - * queued until the upgrade is complete, so everything's fine) */ - if ( - (this.pendingTransports.length || this.proposedTransports.length) && - this.state !== this.states.synchronizing - ) { - this.disconnectAllTransports(/* exceptActive: */ true); - const transportParams = (this.activeProtocol as Protocol).getTransport().params; - Platform.Config.nextTick(() => { - if (this.state.state === 'connected') { - this.upgradeIfNeeded(transportParams); - } - }); - } /* Do any transport-specific new-token action */ const activeTransport = this.activeProtocol?.getTransport(); @@ -1758,7 +1569,7 @@ class ConnectionManager extends EventEmitter { activeTransport.onAuthUpdated(tokenDetails); } - const authMsg = ProtocolMessage.fromValues({ + const authMsg = protocolMessageFromValues({ action: actions.AUTH, auth: { accessToken: tokenDetails.token, @@ -1789,7 +1600,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MICRO, 'ConnectionManager.onAuthUpdated()', - 'Aborting current connection attempts in order to start again with the new auth details' + 'Aborting current connection attempts in order to start again with the new auth details', ); this.disconnectAllTransports(); /* fallthrough to add statechange listener */ @@ -1798,7 +1609,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MICRO, 'ConnectionManager.onAuthUpdated()', - 'Connection state is ' + this.state.state + '; waiting until either connected or failed' + 'Connection state is ' + this.state.state + '; waiting until either connected or failed', ); const listener = (stateChange: ConnectionStateChange) => { switch (stateChange.current) { @@ -1829,41 +1640,37 @@ class ConnectionManager extends EventEmitter { } } - disconnectAllTransports(exceptActive?: boolean): void { - Logger.logAction( - Logger.LOG_MINOR, - 'ConnectionManager.disconnectAllTransports()', - 'Disconnecting all transports' + (exceptActive ? ' except the active transport' : '') - ); + disconnectAllTransports(): void { + Logger.logAction(Logger.LOG_MINOR, 'ConnectionManager.disconnectAllTransports()', 'Disconnecting all transports'); /* This will prevent any connection procedure in an async part of one of its early stages from continuing */ this.connectCounter++; - Utils.safeArrForEach(this.pendingTransports, function (transport) { + if (this.pendingTransport) { Logger.logAction( Logger.LOG_MICRO, 'ConnectionManager.disconnectAllTransports()', - 'Disconnecting pending transport: ' + transport + 'Disconnecting pending transport: ' + this.pendingTransport, ); - if (transport) transport.disconnect(); - }); - this.pendingTransports = []; + this.pendingTransport.disconnect(); + } + delete this.pendingTransport; - Utils.safeArrForEach(this.proposedTransports, function (transport) { + if (this.proposedTransport) { Logger.logAction( Logger.LOG_MICRO, 'ConnectionManager.disconnectAllTransports()', - 'Disposing of proposed transport: ' + transport + 'Disconnecting proposed transport: ' + this.pendingTransport, ); - if (transport) transport.dispose(); - }); - this.proposedTransports = []; + this.proposedTransport.disconnect(); + } + delete this.pendingTransport; - if (this.activeProtocol && !exceptActive) { + if (this.activeProtocol) { Logger.logAction( Logger.LOG_MICRO, 'ConnectionManager.disconnectAllTransports()', - 'Disconnecting active transport: ' + this.activeProtocol.getTransport() + 'Disconnecting active transport: ' + this.activeProtocol.getTransport(), ); this.activeProtocol.getTransport().disconnect(); } @@ -1884,7 +1691,7 @@ class ConnectionManager extends EventEmitter { this.sendImpl(new PendingMessage(msg, callback)); return; } - const shouldQueue = (queueEvent && state.queueEvents) || state.forceQueueEvents; + const shouldQueue = queueEvent && state.queueEvents; if (!shouldQueue) { const err = 'rejecting event, queueEvent was ' + queueEvent + ', state was ' + state.state; Logger.logAction(Logger.LOG_MICRO, 'ConnectionManager.send()', err); @@ -1892,7 +1699,11 @@ class ConnectionManager extends EventEmitter { return; } if (Logger.shouldLog(Logger.LOG_MICRO)) { - Logger.logAction(Logger.LOG_MICRO, 'ConnectionManager.send()', 'queueing msg; ' + ProtocolMessage.stringify(msg)); + Logger.logAction( + Logger.LOG_MICRO, + 'ConnectionManager.send()', + 'queueing msg; ' + stringifyProtocolMessage(msg, this.realtime._RealtimePresence), + ); } this.queue(msg, callback); } @@ -1910,7 +1721,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_ERROR, 'ConnectionManager.sendImpl()', - 'Unexpected exception in transport.send(): ' + (e as Error).stack + 'Unexpected exception in transport.send(): ' + (e as Error).stack, ); } } @@ -1924,10 +1735,10 @@ class ConnectionManager extends EventEmitter { * the dup, they'll be lost */ if (lastQueued && !lastQueued.sendAttempted && bundleWith(lastQueued.message, msg, maxSize)) { if (!lastQueued.merged) { - lastQueued.callback = Multicaster.create([lastQueued.callback as any]); + lastQueued.callback = Multicaster.create([lastQueued.callback]); lastQueued.merged = true; } - (lastQueued.callback as MulticasterInstance).push(callback as any); + (lastQueued.callback as MulticasterInstance).push(callback); } else { this.queuedMessages.push(new PendingMessage(msg, callback)); } @@ -1937,7 +1748,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MICRO, 'ConnectionManager.sendQueuedMessages()', - 'sending ' + this.queuedMessages.count() + ' queued messages' + 'sending ' + this.queuedMessages.count() + ' queued messages', ); let pendingMessage; while ((pendingMessage = this.queuedMessages.shift())) this.sendImpl(pendingMessage); @@ -1948,7 +1759,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_MICRO, 'ConnectionManager.queuePendingMessages()', - 'queueing ' + pendingMessages.length + ' pending messages' + 'queueing ' + pendingMessages.length + ' pending messages', ); this.queuedMessages.prepend(pendingMessages); } @@ -1960,37 +1771,44 @@ class ConnectionManager extends EventEmitter { Logger.logAction( Logger.LOG_ERROR, 'ConnectionManager.failQueuedMessages()', - 'failing ' + numQueued + ' queued messages, err = ' + Utils.inspectError(err) + 'failing ' + numQueued + ' queued messages, err = ' + Utils.inspectError(err), ); this.queuedMessages.completeAllMessages(err); } } onChannelMessage(message: ProtocolMessage, transport: Transport): void { - const onActiveTransport = this.activeProtocol && transport === this.activeProtocol.getTransport(), - onUpgradeTransport = Utils.arrIn(this.pendingTransports, transport) && this.state == this.states.synchronizing; - - /* As the lib now has a period where the upgrade transport is synced but - * before it's become active (while waiting for the old one to become - * idle), message can validly arrive on it even though it isn't active */ - if (onActiveTransport || onUpgradeTransport) { - this.realtime.channels.onChannelMessage(message); - } else { - // Message came in on a defunct transport. Allow only acks, nacks, & errors for outstanding - // messages, no new messages (as sync has been sent on new transport so new messages will - // be resent there, or connection has been closed so don't want new messages) - if (Utils.arrIndexOf([actions.ACK, actions.NACK, actions.ERROR], message.action) > -1) { - this.realtime.channels.onChannelMessage(message); - } else { - Logger.logAction( - Logger.LOG_MICRO, - 'ConnectionManager.onChannelMessage()', - 'received message ' + JSON.stringify(message) + 'on defunct transport; discarding' - ); - } + this.pendingChannelMessagesState.queue.push({ message, transport }); + + if (!this.pendingChannelMessagesState.isProcessing) { + this.processNextPendingChannelMessage(); } } + private processNextPendingChannelMessage() { + if (this.pendingChannelMessagesState.queue.length > 0) { + this.pendingChannelMessagesState.isProcessing = true; + + const pendingChannelMessage = this.pendingChannelMessagesState.queue.shift()!; + this.processChannelMessage(pendingChannelMessage.message) + .catch((err) => { + Logger.logAction( + Logger.LOG_ERROR, + 'ConnectionManager.processNextPendingChannelMessage() received error ', + err, + ); + }) + .finally(() => { + this.pendingChannelMessagesState.isProcessing = false; + this.processNextPendingChannelMessage(); + }); + } + } + + private async processChannelMessage(message: ProtocolMessage) { + await this.realtime.channels.processChannelMessage(message); + } + ping(transport: Transport | null, callback: Function): void { /* if transport is specified, try that */ if (transport) { @@ -2001,14 +1819,14 @@ class ConnectionManager extends EventEmitter { callback(new ErrorInfo('Timeout waiting for heartbeat response', 50000, 500)); }; - const pingStart = Utils.now(), + const pingStart = Date.now(), id = Utils.cheapRandStr(); const onHeartbeat = function (responseId: string) { if (responseId === id) { transport.off('heartbeat', onHeartbeat); clearTimeout(timer); - const responseTime = Utils.now() - pingStart; + const responseTime = Date.now() - pingStart; callback(null, responseTime); } }; @@ -2057,20 +1875,14 @@ class ConnectionManager extends EventEmitter { (this.activeProtocol as Protocol).getTransport().fail(error); } - registerProposedTransport(transport: Transport): void { - this.proposedTransports.push(transport); - } - - getTransportPreference(): string { + getTransportPreference(): TransportName { return this.transportPreference || (haveWebStorage() && Platform.WebStorage?.get?.(transportPreferenceName)); } persistTransportPreference(transport: Transport): void { - if (Utils.arrIn(Defaults.upgradeTransports, transport.shortName)) { - this.transportPreference = transport.shortName; - if (haveWebStorage()) { - Platform.WebStorage?.set?.(transportPreferenceName, transport.shortName); - } + this.transportPreference = transport.shortName; + if (haveWebStorage()) { + Platform.WebStorage?.set?.(transportPreferenceName, transport.shortName); } } @@ -2127,6 +1939,31 @@ class ConnectionManager extends EventEmitter { this.maxIdleInterval = connectionDetails.maxIdleInterval; this.emit('connectiondetails', connectionDetails); } + + checkWsConnectivity() { + const ws = new Platform.Config.WebSocket(Defaults.wsConnectivityUrl); + return new Promise((resolve, reject) => { + let finished = false; + ws.onopen = () => { + if (!finished) { + finished = true; + resolve(); + ws.close(); + } + }; + + ws.onclose = ws.onerror = () => { + if (!finished) { + finished = true; + reject(); + } + }; + }); + } } export default ConnectionManager; + +export interface TransportStorage { + supportedTransports: Partial>; +} diff --git a/src/common/lib/transport/protocol.ts b/src/common/lib/transport/protocol.ts index 630dc70ac3..ee396767ac 100644 --- a/src/common/lib/transport/protocol.ts +++ b/src/common/lib/transport/protocol.ts @@ -1,4 +1,4 @@ -import ProtocolMessage from '../types/protocolmessage'; +import ProtocolMessage, { actions, stringify as stringifyProtocolMessage } from '../types/protocolmessage'; import * as Utils from '../util/utils'; import EventEmitter from '../util/eventemitter'; import Logger from '../util/logger'; @@ -7,8 +7,6 @@ import ErrorInfo from '../types/errorinfo'; import Transport from './transport'; import { ErrCallback } from '../../types/utils'; -const actions = ProtocolMessage.Action; - export class PendingMessage { message: ProtocolMessage; callback?: ErrCallback; @@ -51,7 +49,7 @@ class Protocol extends EventEmitter { Logger.logAction( Logger.LOG_ERROR, 'Protocol.onNack()', - 'serial = ' + serial + '; count = ' + count + '; err = ' + Utils.inspectError(err) + 'serial = ' + serial + '; count = ' + count + '; err = ' + Utils.inspectError(err), ); if (!err) { err = new ErrorInfo('Unable to send message; channel not responding', 50001, 500); @@ -73,10 +71,11 @@ class Protocol extends EventEmitter { this.messageQueue.push(pendingMessage); } if (Logger.shouldLog(Logger.LOG_MICRO)) { - Logger.logAction( + Logger.logActionNoStrip( Logger.LOG_MICRO, 'Protocol.send()', - 'sending msg; ' + ProtocolMessage.stringify(pendingMessage.message) + 'sending msg; ' + + stringifyProtocolMessage(pendingMessage.message, this.transport.connectionManager.realtime._RealtimePresence), ); } pendingMessage.sendAttempted = true; diff --git a/src/common/lib/transport/transport.ts b/src/common/lib/transport/transport.ts index 593de97b85..78a173688d 100644 --- a/src/common/lib/transport/transport.ts +++ b/src/common/lib/transport/transport.ts @@ -1,4 +1,8 @@ -import ProtocolMessage from '../types/protocolmessage'; +import ProtocolMessage, { + actions, + fromValues as protocolMessageFromValues, + stringify as stringifyProtocolMessage, +} from '../types/protocolmessage'; import * as Utils from '../util/utils'; import EventEmitter from '../util/eventemitter'; import Logger from '../util/logger'; @@ -8,22 +12,26 @@ import Auth from '../client/auth'; import * as API from '../../../../ably'; import ConnectionManager, { TransportParams } from './connectionmanager'; import Platform from 'common/platform'; +import TransportName from 'common/constants/TransportName'; export type TryConnectCallback = ( wrappedErr: { error: ErrorInfo; event: string } | null, - transport?: Transport + transport?: Transport, ) => void; -export type TransportCtor = new ( - connectionManager: ConnectionManager, - auth: Auth, - params: TransportParams, - forceJsonProtocol?: boolean -) => Transport; +export interface TransportCtor { + new ( + connectionManager: ConnectionManager, + auth: Auth, + params: TransportParams, + forceJsonProtocol?: boolean, + ): Transport; + + isAvailable(): boolean; +} -const actions = ProtocolMessage.Action; -const closeMessage = ProtocolMessage.fromValues({ action: actions.CLOSE }); -const disconnectMessage = ProtocolMessage.fromValues({ action: actions.DISCONNECT }); +const closeMessage = protocolMessageFromValues({ action: actions.CLOSE }); +const disconnectMessage = protocolMessageFromValues({ action: actions.DISCONNECT }); /* * Transport instances inherit from EventEmitter and emit the following events: @@ -56,7 +64,6 @@ abstract class Transport extends EventEmitter { params.heartbeats = true; } this.connectionManager = connectionManager; - connectionManager.registerProposedTransport(this); this.auth = auth; this.params = params; this.timeouts = params.options.timeouts; @@ -69,7 +76,7 @@ abstract class Transport extends EventEmitter { this.lastActivity = null; } - abstract shortName: string; + abstract shortName: TransportName; abstract send(message: ProtocolMessage): void; connect(): void {} @@ -114,25 +121,25 @@ abstract class Transport extends EventEmitter { onProtocolMessage(message: ProtocolMessage): void { if (Logger.shouldLog(Logger.LOG_MICRO)) { - Logger.logAction( + Logger.logActionNoStrip( Logger.LOG_MICRO, 'Transport.onProtocolMessage()', 'received on ' + this.shortName + ': ' + - ProtocolMessage.stringify(message) + + stringifyProtocolMessage(message, this.connectionManager.realtime._RealtimePresence) + '; connectionId = ' + - this.connectionManager.connectionId + this.connectionManager.connectionId, ); } this.onActivity(); switch (message.action) { case actions.HEARTBEAT: - Logger.logAction( + Logger.logActionNoStrip( Logger.LOG_MICRO, 'Transport.onProtocolMessage()', - this.shortName + ' heartbeat; connectionId = ' + this.connectionManager.connectionId + this.shortName + ' heartbeat; connectionId = ' + this.connectionManager.connectionId, ); this.emit('heartbeat', message.id); break; @@ -159,12 +166,12 @@ abstract class Transport extends EventEmitter { // Ignored. break; case actions.AUTH: - this.auth.authorize(function (err: ErrorInfo) { + Utils.whenPromiseSettles(this.auth.authorize(), function (err: ErrorInfo | null) { if (err) { Logger.logAction( Logger.LOG_ERROR, 'Transport.onProtocolMessage()', - 'Ably requested re-authentication, but unable to obtain a new token: ' + Utils.inspectError(err) + 'Ably requested re-authentication, but unable to obtain a new token: ' + Utils.inspectError(err), ); } }); @@ -177,7 +184,7 @@ abstract class Transport extends EventEmitter { this.connectionManager.connectionId + '; err = ' + Platform.Config.inspect(message.error) + - (message.channel ? ', channel: ' + message.channel : '') + (message.channel ? ', channel: ' + message.channel : ''), ); if (message.channel === undefined) { this.onFatalError(message); @@ -239,9 +246,9 @@ abstract class Transport extends EventEmitter { } ping(id: string): void { - const msg: Record = { action: ProtocolMessage.Action.HEARTBEAT }; + const msg: Record = { action: actions.HEARTBEAT }; if (id) msg.id = id; - this.send(ProtocolMessage.fromValues(msg)); + this.send(protocolMessageFromValues(msg)); } dispose(): void { @@ -254,7 +261,7 @@ abstract class Transport extends EventEmitter { if (!this.maxIdleInterval) { return; } - this.lastActivity = this.connectionManager.lastActivity = Utils.now(); + this.lastActivity = this.connectionManager.lastActivity = Date.now(); this.setIdleTimer(this.maxIdleInterval + 100); } @@ -271,7 +278,7 @@ abstract class Transport extends EventEmitter { throw new Error('Transport.onIdleTimerExpire(): lastActivity/maxIdleInterval not set'); } this.idleTimer = null; - const sinceLast = Utils.now() - this.lastActivity; + const sinceLast = Date.now() - this.lastActivity; const timeRemaining = this.maxIdleInterval - sinceLast; if (timeRemaining <= 0) { const msg = 'No activity seen from realtime in ' + sinceLast + 'ms; assuming connection has dropped'; @@ -287,8 +294,8 @@ abstract class Transport extends EventEmitter { connectionManager: ConnectionManager, auth: Auth, transportParams: TransportParams, - callback: TryConnectCallback - ): void { + callback: TryConnectCallback, + ): Transport { const transport = new transportCtor(connectionManager, auth, transportParams); let transportAttemptTimer: NodeJS.Timeout | number; @@ -304,7 +311,7 @@ abstract class Transport extends EventEmitter { transport.dispose(); errorCb.call( { event: 'disconnected' }, - new ErrorInfo('Timeout waiting for transport to indicate itself viable', 50000, 500) + new ErrorInfo('Timeout waiting for transport to indicate itself viable', 50000, 500), ); }, realtimeRequestTimeout); @@ -316,9 +323,14 @@ abstract class Transport extends EventEmitter { callback(null, transport); }); transport.connect(); + return transport; } - onAuthUpdated?: (tokenDetails: API.Types.TokenDetails) => void; + onAuthUpdated?: (tokenDetails: API.TokenDetails) => void; + + static isAvailable(): boolean { + throw new ErrorInfo('isAvailable not implemented for transport', 50000, 500); + } } export default Transport; diff --git a/src/common/lib/transport/websockettransport.ts b/src/common/lib/transport/websockettransport.ts index fc12dddf27..0efe0eba0e 100644 --- a/src/common/lib/transport/websockettransport.ts +++ b/src/common/lib/transport/websockettransport.ts @@ -3,13 +3,17 @@ import * as Utils from '../util/utils'; import Transport from './transport'; import Defaults from '../util/defaults'; import Logger from '../util/logger'; -import ProtocolMessage from '../types/protocolmessage'; +import ProtocolMessage, { + serialize as serializeProtocolMessage, + deserialize as deserializeProtocolMessage, +} from '../types/protocolmessage'; import ErrorInfo from '../types/errorinfo'; import NodeWebSocket from 'ws'; import ConnectionManager, { TransportParams } from './connectionmanager'; import Auth from '../client/auth'; +import { TransportNames } from 'common/constants/TransportName'; -const shortName = 'web_socket'; +const shortName = TransportNames.WebSocket; function isNodeWebSocket(ws: WebSocket | NodeWebSocket): ws is NodeWebSocket { return !!(ws as NodeWebSocket).on; @@ -25,7 +29,7 @@ class WebSocketTransport extends Transport { super(connectionManager, auth, params); /* If is a browser, can't detect pings, so request protocol heartbeats */ params.heartbeats = Platform.Config.useProtocolHeartbeats; - this.wsHost = Defaults.getHost(params.options, params.host, true); + this.wsHost = params.host as string; } static isAvailable() { @@ -50,49 +54,52 @@ class WebSocketTransport extends Transport { const wsScheme = options.tls ? 'wss://' : 'ws://'; const wsUri = wsScheme + this.wsHost + ':' + Defaults.getPort(options) + '/'; Logger.logAction(Logger.LOG_MINOR, 'WebSocketTransport.connect()', 'uri: ' + wsUri); - this.auth.getAuthParams(function (err: ErrorInfo, authParams: Record) { - if (self.isDisposed) { - return; - } - let paramStr = ''; - for (const param in authParams) paramStr += ' ' + param + ': ' + authParams[param] + ';'; - Logger.logAction(Logger.LOG_MINOR, 'WebSocketTransport.connect()', 'authParams:' + paramStr + ' err: ' + err); - if (err) { - self.disconnect(err); - return; - } - const connectParams = params.getConnectParams(authParams); - try { - const wsConnection = (self.wsConnection = self.createWebSocket(wsUri, connectParams)); - wsConnection.binaryType = Platform.Config.binaryType; - wsConnection.onopen = function () { - self.onWsOpen(); - }; - wsConnection.onclose = function (ev: CloseEvent) { - self.onWsClose(ev); - }; - wsConnection.onmessage = function (ev: MessageEvent) { - self.onWsData(ev.data); - }; - wsConnection.onerror = function (ev: Event) { - self.onWsError(ev as ErrorEvent); - }; - if (isNodeWebSocket(wsConnection)) { - /* node; browsers currently don't have a general eventemitter and can't detect - * pings. Also, no need to reply with a pong explicitly, ws lib handles that */ - wsConnection.on('ping', function () { - self.onActivity(); - }); + Utils.whenPromiseSettles( + this.auth.getAuthParams(), + function (err: ErrorInfo | null, authParams?: Record) { + if (self.isDisposed) { + return; } - } catch (e) { - Logger.logAction( - Logger.LOG_ERROR, - 'WebSocketTransport.connect()', - 'Unexpected exception creating websocket: err = ' + ((e as Error).stack || (e as Error).message) - ); - self.disconnect(e as Error); - } - }); + let paramStr = ''; + for (const param in authParams) paramStr += ' ' + param + ': ' + authParams[param] + ';'; + Logger.logAction(Logger.LOG_MINOR, 'WebSocketTransport.connect()', 'authParams:' + paramStr + ' err: ' + err); + if (err) { + self.disconnect(err); + return; + } + const connectParams = params.getConnectParams(authParams!); + try { + const wsConnection = (self.wsConnection = self.createWebSocket(wsUri, connectParams)); + wsConnection.binaryType = Platform.Config.binaryType; + wsConnection.onopen = function () { + self.onWsOpen(); + }; + wsConnection.onclose = function (ev: CloseEvent) { + self.onWsClose(ev); + }; + wsConnection.onmessage = function (ev: MessageEvent) { + self.onWsData(ev.data); + }; + wsConnection.onerror = function (ev: Event) { + self.onWsError(ev as ErrorEvent); + }; + if (isNodeWebSocket(wsConnection)) { + /* node; browsers currently don't have a general eventemitter and can't detect + * pings. Also, no need to reply with a pong explicitly, ws lib handles that */ + wsConnection.on('ping', function () { + self.onActivity(); + }); + } + } catch (e) { + Logger.logAction( + Logger.LOG_ERROR, + 'WebSocketTransport.connect()', + 'Unexpected exception creating websocket: err = ' + ((e as Error).stack || (e as Error).message), + ); + self.disconnect(e as Error); + } + }, + ); } send(message: ProtocolMessage) { @@ -102,7 +109,9 @@ class WebSocketTransport extends Transport { return; } try { - wsConnection.send(ProtocolMessage.serialize(message, this.params.format)); + (wsConnection as NodeWebSocket).send( + serializeProtocolMessage(message, this.connectionManager.realtime._MsgPack, this.params.format), + ); } catch (e) { const msg = 'Exception from ws connection when trying to send: ' + Utils.inspectError(e); Logger.logAction(Logger.LOG_ERROR, 'WebSocketTransport.send()', msg); @@ -116,15 +125,22 @@ class WebSocketTransport extends Transport { Logger.logAction( Logger.LOG_MICRO, 'WebSocketTransport.onWsData()', - 'data received; length = ' + data.length + '; type = ' + typeof data + 'data received; length = ' + data.length + '; type = ' + typeof data, ); try { - this.onProtocolMessage(ProtocolMessage.deserialize(data, this.format)); + this.onProtocolMessage( + deserializeProtocolMessage( + data, + this.connectionManager.realtime._MsgPack, + this.connectionManager.realtime._RealtimePresence, + this.format, + ), + ); } catch (e) { Logger.logAction( Logger.LOG_ERROR, 'WebSocketTransport.onWsData()', - 'Unexpected exception handing channel message: ' + (e as Error).stack + 'Unexpected exception handing channel message: ' + (e as Error).stack, ); } } @@ -193,10 +209,4 @@ class WebSocketTransport extends Transport { } } -function initialiseTransport(connectionManager: any): typeof WebSocketTransport { - if (WebSocketTransport.isAvailable()) connectionManager.supportedTransports[shortName] = WebSocketTransport; - - return WebSocketTransport; -} - -export default initialiseTransport; +export default WebSocketTransport; diff --git a/src/common/lib/types/defaultmessage.ts b/src/common/lib/types/defaultmessage.ts new file mode 100644 index 0000000000..286ceeef5d --- /dev/null +++ b/src/common/lib/types/defaultmessage.ts @@ -0,0 +1,43 @@ +import Message, { + CipherOptions, + fromEncoded, + fromEncodedArray, + encode, + decode, + EncodingDecodingContext, +} from './message'; +import * as API from '../../../../ably'; +import Platform from 'common/platform'; +import PresenceMessage from './presencemessage'; +import { ChannelOptions } from 'common/types/channel'; + +/** + `DefaultMessage` is the class returned by `DefaultRest` and `DefaultRealtime`’s `Message` static property. It introduces the static methods described in the `MessageStatic` interface of the public API of the non tree-shakable version of the library. + */ +export class DefaultMessage extends Message { + static async fromEncoded(encoded: unknown, inputOptions?: API.ChannelOptions): Promise { + return fromEncoded(Platform.Crypto, encoded, inputOptions); + } + + static async fromEncodedArray(encodedArray: Array, options?: API.ChannelOptions): Promise { + return fromEncodedArray(Platform.Crypto, encodedArray, options); + } + + // Used by tests + static fromValues(values: unknown): Message { + return Object.assign(new Message(), values); + } + + // Used by tests + static async encode(msg: T, options: CipherOptions): Promise { + return encode(msg, options); + } + + // Used by tests + static async decode( + message: Message | PresenceMessage, + inputContext: CipherOptions | EncodingDecodingContext | ChannelOptions, + ): Promise { + return decode(message, inputContext); + } +} diff --git a/src/common/lib/types/defaultpresencemessage.ts b/src/common/lib/types/defaultpresencemessage.ts new file mode 100644 index 0000000000..d6639af31d --- /dev/null +++ b/src/common/lib/types/defaultpresencemessage.ts @@ -0,0 +1,22 @@ +import * as API from '../../../../ably'; +import PresenceMessage, { fromEncoded, fromEncodedArray, fromValues } from './presencemessage'; + +/** + `DefaultPresenceMessage` is the class returned by `DefaultRest` and `DefaultRealtime`’s `PresenceMessage` static property. It introduces the static methods described in the `PresenceMessageStatic` interface of the public API of the non tree-shakable version of the library. + */ +export class DefaultPresenceMessage extends PresenceMessage { + static async fromEncoded(encoded: unknown, inputOptions?: API.ChannelOptions): Promise { + return fromEncoded(encoded, inputOptions); + } + + static async fromEncodedArray( + encodedArray: Array, + options?: API.ChannelOptions, + ): Promise { + return fromEncodedArray(encodedArray, options); + } + + static fromValues(values: PresenceMessage | Record, stringifyAction?: boolean): PresenceMessage { + return fromValues(values, stringifyAction); + } +} diff --git a/src/common/lib/types/devicedetails.ts b/src/common/lib/types/devicedetails.ts index e7ba9ffde5..24c64ed8aa 100644 --- a/src/common/lib/types/devicedetails.ts +++ b/src/common/lib/types/devicedetails.ts @@ -1,5 +1,6 @@ +import { MsgPack } from 'common/types/msgpack'; import * as Utils from '../util/utils'; -import ErrorInfo from './errorinfo'; +import ErrorInfo, { IConvertibleToErrorInfo } from './errorinfo'; enum DeviceFormFactor { Phone = 'phone', @@ -70,17 +71,20 @@ class DeviceDetails { return result; } - static toRequestBody = Utils.encodeBody; + static toRequestBody(body: unknown, MsgPack: MsgPack | null, format?: Utils.Format) { + return Utils.encodeBody(body, MsgPack, format); + } static fromResponseBody( body: Array> | Record, - format?: Utils.Format + MsgPack: MsgPack | null, + format?: Utils.Format, ): DeviceDetails | DeviceDetails[] { if (format) { - body = Utils.decodeBody(body, format); + body = Utils.decodeBody(body, MsgPack, format); } - if (Utils.isArray(body)) { + if (Array.isArray(body)) { return DeviceDetails.fromValuesArray(body); } else { return DeviceDetails.fromValues(body); @@ -88,7 +92,7 @@ class DeviceDetails { } static fromValues(values: Record): DeviceDetails { - values.error = values.error && ErrorInfo.fromValues(values.error as Error); + values.error = values.error && ErrorInfo.fromValues(values.error as IConvertibleToErrorInfo); return Object.assign(new DeviceDetails(), values); } diff --git a/src/common/lib/types/errorinfo.ts b/src/common/lib/types/errorinfo.ts index 88bfd27ca7..86b267159e 100644 --- a/src/common/lib/types/errorinfo.ts +++ b/src/common/lib/types/errorinfo.ts @@ -20,7 +20,13 @@ function toString(err: ErrorInfo | PartialErrorInfo) { return result; } -export default class ErrorInfo extends Error implements IPartialErrorInfo, API.Types.ErrorInfo { +export interface IConvertibleToErrorInfo { + message: string; + code: number; + statusCode: number; +} + +export default class ErrorInfo extends Error implements IPartialErrorInfo, API.ErrorInfo { code: number; statusCode: number; cause?: string | Error | ErrorInfo; @@ -40,8 +46,8 @@ export default class ErrorInfo extends Error implements IPartialErrorInfo, API.T return toString(this); } - static fromValues(values: Record | ErrorInfo | Error): ErrorInfo { - const { message, code, statusCode } = values as ErrorInfo; + static fromValues(values: IConvertibleToErrorInfo): ErrorInfo { + const { message, code, statusCode } = values; if (typeof message !== 'string' || typeof code !== 'number' || typeof statusCode !== 'number') { throw new Error('ErrorInfo.fromValues(): invalid values: ' + Platform.Config.inspect(values)); } diff --git a/src/common/lib/types/message.ts b/src/common/lib/types/message.ts index 45dcc9a2e0..f1c44cb26d 100644 --- a/src/common/lib/types/message.ts +++ b/src/common/lib/types/message.ts @@ -6,6 +6,8 @@ import PresenceMessage from './presencemessage'; import * as Utils from '../util/utils'; import { Bufferlike as BrowserBufferlike } from '../../../platform/web/lib/util/bufferutils'; import * as API from '../../../../ably'; +import { IUntypedCryptoStatic } from 'common/types/ICryptoStatic'; +import { MsgPack } from 'common/types/msgpack'; export type CipherOptions = { channelCipher: { @@ -20,12 +22,11 @@ export type CipherOptions = { }; }; -type EncodingDecodingContext = { +export type EncodingDecodingContext = { channelOptions: ChannelOptions; plugins: { vcdiff?: { - encrypt: Function; - decode: Function; + decode: (delta: Uint8Array, source: Uint8Array) => Uint8Array; }; }; baseEncodedPreviousPayload?: Buffer | BrowserBufferlike; @@ -42,10 +43,13 @@ function normaliseContext(context: CipherOptions | EncodingDecodingContext | Cha return context as EncodingDecodingContext; } -function normalizeCipherOptions(options: API.Types.ChannelOptions | null): ChannelOptions { +function normalizeCipherOptions( + Crypto: IUntypedCryptoStatic | null, + options: API.ChannelOptions | null, +): ChannelOptions { if (options && options.cipher) { - if (!Platform.Crypto) throw new Error('Encryption not enabled; use ably.encryption.js instead'); - const cipher = Platform.Crypto.getCipher(options.cipher); + if (!Crypto) Utils.throwMissingPluginError('Crypto'); + const cipher = Crypto.getCipher(options.cipher); return { cipher: cipher.cipherParams, channelCipher: cipher.cipher, @@ -71,6 +75,220 @@ function getMessageSize(msg: Message) { return size; } +export async function fromEncoded( + Crypto: IUntypedCryptoStatic | null, + encoded: unknown, + inputOptions?: API.ChannelOptions, +): Promise { + const msg = fromValues(encoded); + const options = normalizeCipherOptions(Crypto, inputOptions ?? null); + /* if decoding fails at any point, catch and return the message decoded to + * the fullest extent possible */ + try { + await decode(msg, options); + } catch (e) { + Logger.logAction(Logger.LOG_ERROR, 'Message.fromEncoded()', (e as Error).toString()); + } + return msg; +} + +export async function fromEncodedArray( + Crypto: IUntypedCryptoStatic | null, + encodedArray: Array, + options?: API.ChannelOptions, +): Promise { + return Promise.all( + encodedArray.map(function (encoded) { + return fromEncoded(Crypto, encoded, options); + }), + ); +} + +async function encrypt(msg: T, options: CipherOptions): Promise { + let data = msg.data, + encoding = msg.encoding, + cipher = options.channelCipher; + + encoding = encoding ? encoding + '/' : ''; + if (!Platform.BufferUtils.isBuffer(data)) { + data = Platform.BufferUtils.utf8Encode(String(data)); + encoding = encoding + 'utf-8/'; + } + const ciphertext = await cipher.encrypt(data); + msg.data = ciphertext; + msg.encoding = encoding + 'cipher+' + cipher.algorithm; + return msg; +} + +export async function encode(msg: T, options: CipherOptions): Promise { + const data = msg.data; + const nativeDataType = + typeof data == 'string' || Platform.BufferUtils.isBuffer(data) || data === null || data === undefined; + + if (!nativeDataType) { + if (Utils.isObject(data) || Array.isArray(data)) { + msg.data = JSON.stringify(data); + msg.encoding = msg.encoding ? msg.encoding + '/json' : 'json'; + } else { + throw new ErrorInfo('Data type is unsupported', 40013, 400); + } + } + + if (options != null && options.cipher) { + return encrypt(msg, options); + } else { + return msg; + } +} + +export async function encodeArray(messages: Array, options: CipherOptions): Promise> { + return Promise.all(messages.map((message) => encode(message, options))); +} + +export const serialize = Utils.encodeBody; + +export async function decode( + message: Message | PresenceMessage, + inputContext: CipherOptions | EncodingDecodingContext | ChannelOptions, +): Promise { + const context = normaliseContext(inputContext); + + let lastPayload = message.data; + const encoding = message.encoding; + if (encoding) { + const xforms = encoding.split('/'); + let lastProcessedEncodingIndex, + encodingsToProcess = xforms.length, + data = message.data; + + let xform = ''; + try { + while ((lastProcessedEncodingIndex = encodingsToProcess) > 0) { + // eslint-disable-next-line security/detect-unsafe-regex + const match = xforms[--encodingsToProcess].match(/([-\w]+)(\+([\w-]+))?/); + if (!match) break; + xform = match[1]; + switch (xform) { + case 'base64': + data = Platform.BufferUtils.base64Decode(String(data)); + if (lastProcessedEncodingIndex == xforms.length) { + lastPayload = data; + } + continue; + case 'utf-8': + data = Platform.BufferUtils.utf8Decode(data); + continue; + case 'json': + data = JSON.parse(data); + continue; + case 'cipher': + if ( + context.channelOptions != null && + context.channelOptions.cipher && + context.channelOptions.channelCipher + ) { + const xformAlgorithm = match[3], + cipher = context.channelOptions.channelCipher; + /* don't attempt to decrypt unless the cipher params are compatible */ + if (xformAlgorithm != cipher.algorithm) { + throw new Error('Unable to decrypt message with given cipher; incompatible cipher params'); + } + data = await cipher.decrypt(data); + continue; + } else { + throw new Error('Unable to decrypt message; not an encrypted channel'); + } + case 'vcdiff': + if (!context.plugins || !context.plugins.vcdiff) { + throw new ErrorInfo('Missing Vcdiff decoder (https://github.com/ably-forks/vcdiff-decoder)', 40019, 400); + } + if (typeof Uint8Array === 'undefined') { + throw new ErrorInfo( + 'Delta decoding not supported on this browser (need ArrayBuffer & Uint8Array)', + 40020, + 400, + ); + } + try { + let deltaBase = context.baseEncodedPreviousPayload; + if (typeof deltaBase === 'string') { + deltaBase = Platform.BufferUtils.utf8Encode(deltaBase); + } + + // vcdiff expects Uint8Arrays, can't copy with ArrayBuffers. + const deltaBaseBuffer = Platform.BufferUtils.toBuffer(deltaBase as Buffer); + data = Platform.BufferUtils.toBuffer(data); + + data = Platform.BufferUtils.arrayBufferViewToBuffer(context.plugins.vcdiff.decode(data, deltaBaseBuffer)); + lastPayload = data; + } catch (e) { + throw new ErrorInfo('Vcdiff delta decode failed with ' + e, 40018, 400); + } + continue; + default: + throw new Error('Unknown encoding'); + } + } + } catch (e) { + const err = e as ErrorInfo; + throw new ErrorInfo( + 'Error processing the ' + xform + ' encoding, decoder returned ‘' + err.message + '’', + err.code || 40013, + 400, + ); + } finally { + message.encoding = + (lastProcessedEncodingIndex as number) <= 0 ? null : xforms.slice(0, lastProcessedEncodingIndex).join('/'); + message.data = data; + } + } + context.baseEncodedPreviousPayload = lastPayload; +} + +export async function fromResponseBody( + body: Array, + options: ChannelOptions | EncodingDecodingContext, + MsgPack: MsgPack | null, + format?: Utils.Format, +): Promise { + if (format) { + body = Utils.decodeBody(body, MsgPack, format); + } + + for (let i = 0; i < body.length; i++) { + const msg = (body[i] = fromValues(body[i])); + try { + await decode(msg, options); + } catch (e) { + Logger.logAction(Logger.LOG_ERROR, 'Message.fromResponseBody()', (e as Error).toString()); + } + } + return body; +} + +export function fromValues(values: unknown): Message { + return Object.assign(new Message(), values); +} + +export function fromValuesArray(values: unknown[]): Message[] { + const count = values.length, + result = new Array(count); + for (let i = 0; i < count; i++) result[i] = fromValues(values[i]); + return result; +} + +/* This should be called on encode()d (and encrypt()d) Messages (as it + * assumes the data is a string or buffer) */ +export function getMessagesSize(messages: Message[]): number { + let msg, + total = 0; + for (let i = 0; i < messages.length; i++) { + msg = messages[i]; + total += msg.size || (msg.size = getMessageSize(msg)); + } + return total; +} + class Message { name?: string; id?: string; @@ -136,232 +354,6 @@ class Message { result += ']'; return result; } - - static encrypt(msg: Message | PresenceMessage, options: CipherOptions, callback: Function) { - let data = msg.data, - encoding = msg.encoding, - cipher = options.channelCipher; - - encoding = encoding ? encoding + '/' : ''; - if (!Platform.BufferUtils.isBuffer(data)) { - data = Platform.BufferUtils.utf8Encode(String(data)); - encoding = encoding + 'utf-8/'; - } - cipher.encrypt(data, function (err: Error, data: unknown) { - if (err) { - callback(err); - return; - } - msg.data = data; - msg.encoding = encoding + 'cipher+' + cipher.algorithm; - callback(null, msg); - }); - } - - static encode(msg: Message | PresenceMessage, options: CipherOptions, callback: Function): void { - const data = msg.data; - const nativeDataType = - typeof data == 'string' || Platform.BufferUtils.isBuffer(data) || data === null || data === undefined; - - if (!nativeDataType) { - if (Utils.isObject(data) || Utils.isArray(data)) { - msg.data = JSON.stringify(data); - msg.encoding = msg.encoding ? msg.encoding + '/json' : 'json'; - } else { - throw new ErrorInfo('Data type is unsupported', 40013, 400); - } - } - - if (options != null && options.cipher) { - Message.encrypt(msg, options, callback); - } else { - callback(null, msg); - } - } - - static encodeArray(messages: Array, options: CipherOptions, callback: Function): void { - let processed = 0; - for (let i = 0; i < messages.length; i++) { - Message.encode(messages[i], options, function (err: Error) { - if (err) { - callback(err); - return; - } - processed++; - if (processed == messages.length) { - callback(null, messages); - } - }); - } - } - - static serialize = Utils.encodeBody; - - static decode( - message: Message | PresenceMessage, - inputContext: CipherOptions | EncodingDecodingContext | ChannelOptions - ): void { - const context = normaliseContext(inputContext); - - let lastPayload = message.data; - const encoding = message.encoding; - if (encoding) { - const xforms = encoding.split('/'); - let lastProcessedEncodingIndex, - encodingsToProcess = xforms.length, - data = message.data; - - let xform = ''; - try { - while ((lastProcessedEncodingIndex = encodingsToProcess) > 0) { - // eslint-disable-next-line security/detect-unsafe-regex - const match = xforms[--encodingsToProcess].match(/([-\w]+)(\+([\w-]+))?/); - if (!match) break; - xform = match[1]; - switch (xform) { - case 'base64': - data = Platform.BufferUtils.base64Decode(String(data)); - if (lastProcessedEncodingIndex == xforms.length) { - lastPayload = data; - } - continue; - case 'utf-8': - data = Platform.BufferUtils.utf8Decode(data); - continue; - case 'json': - data = JSON.parse(data); - continue; - case 'cipher': - if ( - context.channelOptions != null && - context.channelOptions.cipher && - context.channelOptions.channelCipher - ) { - const xformAlgorithm = match[3], - cipher = context.channelOptions.channelCipher; - /* don't attempt to decrypt unless the cipher params are compatible */ - if (xformAlgorithm != cipher.algorithm) { - throw new Error('Unable to decrypt message with given cipher; incompatible cipher params'); - } - data = cipher.decrypt(data); - continue; - } else { - throw new Error('Unable to decrypt message; not an encrypted channel'); - } - case 'vcdiff': - if (!context.plugins || !context.plugins.vcdiff) { - throw new ErrorInfo( - 'Missing Vcdiff decoder (https://github.com/ably-forks/vcdiff-decoder)', - 40019, - 400 - ); - } - if (typeof Uint8Array === 'undefined') { - throw new ErrorInfo( - 'Delta decoding not supported on this browser (need ArrayBuffer & Uint8Array)', - 40020, - 400 - ); - } - try { - let deltaBase = context.baseEncodedPreviousPayload; - if (typeof deltaBase === 'string') { - deltaBase = Platform.BufferUtils.utf8Encode(deltaBase); - } - - /* vcdiff expects Uint8Arrays, can't copy with ArrayBuffers. (also, if we - * don't have a TextDecoder, deltaBase might be a WordArray here, so need - * to process it into a buffer anyway) */ - deltaBase = Platform.BufferUtils.toBuffer(deltaBase as Buffer); - data = Platform.BufferUtils.toBuffer(data); - - data = Platform.BufferUtils.typedArrayToBuffer(context.plugins.vcdiff.decode(data, deltaBase)); - lastPayload = data; - } catch (e) { - throw new ErrorInfo('Vcdiff delta decode failed with ' + e, 40018, 400); - } - continue; - default: - throw new Error('Unknown encoding'); - } - } - } catch (e) { - const err = e as ErrorInfo; - throw new ErrorInfo( - 'Error processing the ' + xform + ' encoding, decoder returned ‘' + err.message + '’', - err.code || 40013, - 400 - ); - } finally { - message.encoding = - (lastProcessedEncodingIndex as number) <= 0 ? null : xforms.slice(0, lastProcessedEncodingIndex).join('/'); - message.data = data; - } - } - context.baseEncodedPreviousPayload = lastPayload; - } - - static fromResponseBody( - body: Array, - options: ChannelOptions | EncodingDecodingContext, - format?: Utils.Format - ): Message[] { - if (format) { - body = Utils.decodeBody(body, format); - } - - for (let i = 0; i < body.length; i++) { - const msg = (body[i] = Message.fromValues(body[i])); - try { - Message.decode(msg, options); - } catch (e) { - Logger.logAction(Logger.LOG_ERROR, 'Message.fromResponseBody()', (e as Error).toString()); - } - } - return body; - } - - static fromValues(values: unknown): Message { - return Object.assign(new Message(), values); - } - - static fromValuesArray(values: unknown[]): Message[] { - const count = values.length, - result = new Array(count); - for (let i = 0; i < count; i++) result[i] = Message.fromValues(values[i]); - return result; - } - - static fromEncoded(encoded: unknown, inputOptions?: API.Types.ChannelOptions): Message { - const msg = Message.fromValues(encoded); - const options = normalizeCipherOptions(inputOptions ?? null); - /* if decoding fails at any point, catch and return the message decoded to - * the fullest extent possible */ - try { - Message.decode(msg, options); - } catch (e) { - Logger.logAction(Logger.LOG_ERROR, 'Message.fromEncoded()', (e as Error).toString()); - } - return msg; - } - - static fromEncodedArray(encodedArray: Array, options?: API.Types.ChannelOptions): Message[] { - return encodedArray.map(function (encoded) { - return Message.fromEncoded(encoded, options); - }); - } - - /* This should be called on encode()d (and encrypt()d) Messages (as it - * assumes the data is a string or buffer) */ - static getMessagesSize(messages: Message[]): number { - let msg, - total = 0; - for (let i = 0; i < messages.length; i++) { - msg = messages[i]; - total += msg.size || (msg.size = getMessageSize(msg)); - } - return total; - } } export default Message; diff --git a/src/common/lib/types/presencemessage.ts b/src/common/lib/types/presencemessage.ts index 593172acb2..5cb1b690c8 100644 --- a/src/common/lib/types/presencemessage.ts +++ b/src/common/lib/types/presencemessage.ts @@ -1,13 +1,92 @@ import Logger from '../util/logger'; import Platform from 'common/platform'; -import Message, { CipherOptions } from './message'; +import { encode as encodeMessage, decode as decodeMessage, getMessagesSize, CipherOptions } from './message'; import * as Utils from '../util/utils'; import * as API from '../../../../ably'; +import { MsgPack } from 'common/types/msgpack'; + +const actions = ['absent', 'present', 'enter', 'leave', 'update']; function toActionValue(actionString: string) { - return PresenceMessage.Actions.indexOf(actionString); + return actions.indexOf(actionString); +} + +export async function fromEncoded(encoded: unknown, options?: API.ChannelOptions): Promise { + const msg = fromValues(encoded as PresenceMessage | Record, true); + /* if decoding fails at any point, catch and return the message decoded to + * the fullest extent possible */ + try { + await decode(msg, options ?? {}); + } catch (e) { + Logger.logAction(Logger.LOG_ERROR, 'PresenceMessage.fromEncoded()', (e as Error).toString()); + } + return msg; +} + +export async function fromEncodedArray( + encodedArray: unknown[], + options?: API.ChannelOptions, +): Promise { + return Promise.all( + encodedArray.map(function (encoded) { + return fromEncoded(encoded, options); + }), + ); +} + +export function fromValues( + values: PresenceMessage | Record, + stringifyAction?: boolean, +): PresenceMessage { + if (stringifyAction) { + values.action = actions[values.action as number]; + } + return Object.assign(new PresenceMessage(), values); +} + +export { encodeMessage as encode }; +export const decode = decodeMessage; + +export async function fromResponseBody( + body: Record[], + options: CipherOptions, + MsgPack: MsgPack | null, + format?: Utils.Format, +): Promise { + const messages: PresenceMessage[] = []; + if (format) { + body = Utils.decodeBody(body, MsgPack, format); + } + + for (let i = 0; i < body.length; i++) { + const msg = (messages[i] = fromValues(body[i], true)); + try { + await decode(msg, options); + } catch (e) { + Logger.logAction(Logger.LOG_ERROR, 'PresenceMessage.fromResponseBody()', (e as Error).toString()); + } + } + return messages; +} + +export function fromValuesArray(values: unknown[]): PresenceMessage[] { + const count = values.length, + result = new Array(count); + for (let i = 0; i < count; i++) result[i] = fromValues(values[i] as Record); + return result; +} + +export function fromData(data: unknown): PresenceMessage { + if (data instanceof PresenceMessage) { + return data; + } + return fromValues({ + data, + }); } +export { getMessagesSize }; + class PresenceMessage { action?: string | number; id?: string; @@ -19,8 +98,6 @@ class PresenceMessage { extras?: any; size?: number; - static Actions = ['absent', 'present', 'enter', 'leave', 'update']; - /* Returns whether this presenceMessage is synthesized, i.e. was not actually * sent by the connection (usually means a leave event sent 15s after a * disconnection). This is useful because synthesized messages cannot be @@ -104,73 +181,6 @@ class PresenceMessage { result += ']'; return result; } - - static encode = Message.encode; - static decode = Message.decode; - - static fromResponseBody( - body: Record[], - options: CipherOptions, - format?: Utils.Format - ): PresenceMessage[] { - const messages: PresenceMessage[] = []; - if (format) { - body = Utils.decodeBody(body, format); - } - - for (let i = 0; i < body.length; i++) { - const msg = (messages[i] = PresenceMessage.fromValues(body[i], true)); - try { - PresenceMessage.decode(msg, options); - } catch (e) { - Logger.logAction(Logger.LOG_ERROR, 'PresenceMessage.fromResponseBody()', (e as Error).toString()); - } - } - return messages; - } - - static fromValues(values: PresenceMessage | Record, stringifyAction?: boolean): PresenceMessage { - if (stringifyAction) { - values.action = PresenceMessage.Actions[values.action as number]; - } - return Object.assign(new PresenceMessage(), values); - } - - static fromValuesArray(values: unknown[]): PresenceMessage[] { - const count = values.length, - result = new Array(count); - for (let i = 0; i < count; i++) result[i] = PresenceMessage.fromValues(values[i] as Record); - return result; - } - - static fromEncoded(encoded: unknown, options?: API.Types.ChannelOptions): PresenceMessage { - const msg = PresenceMessage.fromValues(encoded as PresenceMessage | Record, true); - /* if decoding fails at any point, catch and return the message decoded to - * the fullest extent possible */ - try { - PresenceMessage.decode(msg, options ?? {}); - } catch (e) { - Logger.logAction(Logger.LOG_ERROR, 'PresenceMessage.fromEncoded()', (e as Error).toString()); - } - return msg; - } - - static fromEncodedArray(encodedArray: unknown[], options?: API.Types.ChannelOptions): PresenceMessage[] { - return encodedArray.map(function (encoded) { - return PresenceMessage.fromEncoded(encoded, options); - }); - } - - static fromData(data: unknown): PresenceMessage { - if (data instanceof PresenceMessage) { - return data; - } - return PresenceMessage.fromValues({ - data, - }); - } - - static getMessagesSize = Message.getMessagesSize; } export default PresenceMessage; diff --git a/src/common/lib/types/protocolmessage.ts b/src/common/lib/types/protocolmessage.ts index 20acbdf380..eaa622a8d9 100644 --- a/src/common/lib/types/protocolmessage.ts +++ b/src/common/lib/types/protocolmessage.ts @@ -1,10 +1,15 @@ -import { Types } from '../../../../ably'; +import { MsgPack } from 'common/types/msgpack'; +import * as API from '../../../../ably'; +import { PresenceMessagePlugin } from '../client/modularplugins'; import * as Utils from '../util/utils'; import ErrorInfo from './errorinfo'; -import Message from './message'; -import PresenceMessage from './presencemessage'; +import Message, { fromValues as messageFromValues, fromValuesArray as messagesFromValuesArray } from './message'; +import PresenceMessage, { + fromValues as presenceMessageFromValues, + fromValuesArray as presenceMessagesFromValuesArray, +} from './presencemessage'; -const actions = { +export const actions = { HEARTBEAT: 0, ACK: 1, NACK: 2, @@ -26,7 +31,7 @@ const actions = { ACTIVATE: 18, }; -const ActionName: string[] = []; +export const ActionName: string[] = []; Object.keys(actions).forEach(function (name) { ActionName[(actions as { [key: string]: number })[name]] = name; }); @@ -57,7 +62,81 @@ function toStringArray(array?: any[]): string { return '[ ' + result.join(', ') + ' ]'; } -const simpleAttributes = 'id channel channelSerial connectionId count msgSerial timestamp'.split(' '); +export const channelModes = ['PRESENCE', 'PUBLISH', 'SUBSCRIBE', 'PRESENCE_SUBSCRIBE']; + +export const serialize = Utils.encodeBody; + +export function deserialize( + serialized: unknown, + MsgPack: MsgPack | null, + presenceMessagePlugin: PresenceMessagePlugin | null, + format?: Utils.Format, +): ProtocolMessage { + const deserialized = Utils.decodeBody>(serialized, MsgPack, format); + return fromDeserialized(deserialized, presenceMessagePlugin); +} + +export function fromDeserialized( + deserialized: Record, + presenceMessagePlugin: PresenceMessagePlugin | null, +): ProtocolMessage { + const error = deserialized.error; + if (error) deserialized.error = ErrorInfo.fromValues(error as ErrorInfo); + const messages = deserialized.messages as Message[]; + if (messages) for (let i = 0; i < messages.length; i++) messages[i] = messageFromValues(messages[i]); + + const presence = presenceMessagePlugin ? (deserialized.presence as PresenceMessage[]) : undefined; + if (presenceMessagePlugin) { + if (presence && presenceMessagePlugin) + for (let i = 0; i < presence.length; i++) + presence[i] = presenceMessagePlugin.presenceMessageFromValues(presence[i], true); + } + return Object.assign(new ProtocolMessage(), { ...deserialized, presence }); +} + +/** + * Used by the tests. + */ +export function fromDeserializedIncludingDependencies(deserialized: Record): ProtocolMessage { + return fromDeserialized(deserialized, { presenceMessageFromValues, presenceMessagesFromValuesArray }); +} + +export function fromValues(values: unknown): ProtocolMessage { + return Object.assign(new ProtocolMessage(), values); +} + +export function stringify(msg: any, presenceMessagePlugin: PresenceMessagePlugin | null): string { + let result = '[ProtocolMessage'; + if (msg.action !== undefined) result += '; action=' + ActionName[msg.action] || msg.action; + + const simpleAttributes = ['id', 'channel', 'channelSerial', 'connectionId', 'count', 'msgSerial', 'timestamp']; + let attribute; + for (let attribIndex = 0; attribIndex < simpleAttributes.length; attribIndex++) { + attribute = simpleAttributes[attribIndex]; + if (msg[attribute] !== undefined) result += '; ' + attribute + '=' + msg[attribute]; + } + + if (msg.messages) result += '; messages=' + toStringArray(messagesFromValuesArray(msg.messages)); + if (msg.presence && presenceMessagePlugin) + result += '; presence=' + toStringArray(presenceMessagePlugin.presenceMessagesFromValuesArray(msg.presence)); + if (msg.error) result += '; error=' + ErrorInfo.fromValues(msg.error).toString(); + if (msg.auth && msg.auth.accessToken) result += '; token=' + msg.auth.accessToken; + if (msg.flags) result += '; flags=' + flagNames.filter(msg.hasFlag).join(','); + if (msg.params) { + let stringifiedParams = ''; + Utils.forInOwnNonNullProperties(msg.params, function (prop: string) { + if (stringifiedParams.length > 0) { + stringifiedParams += '; '; + } + stringifiedParams += prop + '=' + msg.params[prop]; + }); + if (stringifiedParams.length > 0) { + result += '; params=[' + stringifiedParams + ']'; + } + } + result += ']'; + return result; +} class ProtocolMessage { action?: number; @@ -71,21 +150,16 @@ class ProtocolMessage { channelSerial?: string | null; msgSerial?: number; messages?: Message[]; + // This will be undefined if we skipped decoding this property due to user not requesting presence functionality — see `fromDeserialized` presence?: PresenceMessage[]; auth?: unknown; connectionDetails?: Record; - static Action = actions; - - static channelModes = ['PRESENCE', 'PUBLISH', 'SUBSCRIBE', 'PRESENCE_SUBSCRIBE']; - - static ActionName = ActionName; - hasFlag = (flag: string): boolean => { return ((this.flags as number) & flags[flag]) > 0; }; - setFlag(flag: Types.ChannelMode): number { + setFlag(flag: API.ChannelMode): number { return (this.flags = (this.flags as number) | flags[flag]); } @@ -93,71 +167,19 @@ class ProtocolMessage { return this.flags && this.flags & flags.MODE_ALL; } - encodeModesToFlags(modes: Types.ChannelMode[]): void { + encodeModesToFlags(modes: API.ChannelMode[]): void { modes.forEach((mode) => this.setFlag(mode)); } decodeModesFromFlags(): string[] | undefined { const modes: string[] = []; - ProtocolMessage.channelModes.forEach((mode) => { + channelModes.forEach((mode) => { if (this.hasFlag(mode)) { modes.push(mode); } }); return modes.length > 0 ? modes : undefined; } - - static serialize = Utils.encodeBody; - - static deserialize = function (serialized: unknown, format?: Utils.Format): ProtocolMessage { - const deserialized = Utils.decodeBody>(serialized, format); - return ProtocolMessage.fromDeserialized(deserialized); - }; - - static fromDeserialized = function (deserialized: Record): ProtocolMessage { - const error = deserialized.error; - if (error) deserialized.error = ErrorInfo.fromValues(error as ErrorInfo); - const messages = deserialized.messages as Message[]; - if (messages) for (let i = 0; i < messages.length; i++) messages[i] = Message.fromValues(messages[i]); - const presence = deserialized.presence as PresenceMessage[]; - if (presence) for (let i = 0; i < presence.length; i++) presence[i] = PresenceMessage.fromValues(presence[i], true); - return Object.assign(new ProtocolMessage(), deserialized); - }; - - static fromValues(values: unknown): ProtocolMessage { - return Object.assign(new ProtocolMessage(), values); - } - - static stringify = function (msg: any): string { - let result = '[ProtocolMessage'; - if (msg.action !== undefined) result += '; action=' + ProtocolMessage.ActionName[msg.action] || msg.action; - - let attribute; - for (let attribIndex = 0; attribIndex < simpleAttributes.length; attribIndex++) { - attribute = simpleAttributes[attribIndex]; - if (msg[attribute] !== undefined) result += '; ' + attribute + '=' + msg[attribute]; - } - - if (msg.messages) result += '; messages=' + toStringArray(Message.fromValuesArray(msg.messages)); - if (msg.presence) result += '; presence=' + toStringArray(PresenceMessage.fromValuesArray(msg.presence)); - if (msg.error) result += '; error=' + ErrorInfo.fromValues(msg.error).toString(); - if (msg.auth && msg.auth.accessToken) result += '; token=' + msg.auth.accessToken; - if (msg.flags) result += '; flags=' + flagNames.filter(msg.hasFlag).join(','); - if (msg.params) { - let stringifiedParams = ''; - Utils.forInOwnNonNullProperties(msg.params, function (prop: string) { - if (stringifiedParams.length > 0) { - stringifiedParams += '; '; - } - stringifiedParams += prop + '=' + msg.params[prop]; - }); - if (stringifiedParams.length > 0) { - result += '; params=[' + stringifiedParams + ']'; - } - } - result += ']'; - return result; - }; } export default ProtocolMessage; diff --git a/src/common/lib/types/pushchannelsubscription.ts b/src/common/lib/types/pushchannelsubscription.ts index b4056c006c..a38f3ff9de 100644 --- a/src/common/lib/types/pushchannelsubscription.ts +++ b/src/common/lib/types/pushchannelsubscription.ts @@ -1,3 +1,4 @@ +import { MsgPack } from 'common/types/msgpack'; import * as Utils from '../util/utils'; type PushChannelSubscriptionObject = { @@ -36,13 +37,14 @@ class PushChannelSubscription { static fromResponseBody( body: Array> | Record, - format?: Utils.Format + MsgPack: MsgPack | null, + format?: Utils.Format, ): PushChannelSubscription | PushChannelSubscription[] { if (format) { - body = Utils.decodeBody(body, format) as Record; + body = Utils.decodeBody(body, MsgPack, format) as Record; } - if (Utils.isArray(body)) { + if (Array.isArray(body)) { return PushChannelSubscription.fromValuesArray(body); } else { return PushChannelSubscription.fromValues(body); diff --git a/src/common/lib/types/stats.ts b/src/common/lib/types/stats.ts index 8905a8a7ba..e9811f4d45 100644 --- a/src/common/lib/types/stats.ts +++ b/src/common/lib/types/stats.ts @@ -1,304 +1,24 @@ -import * as Utils from '../util/utils'; - -type MessageValues = { - count?: number; - data?: number; - uncompressedData?: number; - failed?: number; - refused?: number; - category?: Record; -}; - -type ResourceValues = { - peak?: number; - min?: number; - mean?: number; - opened?: number; - refused?: number; -}; - -type RequestValues = { - succeeded?: number; - failed?: number; - refused?: number; -}; - -type ConnectionTypesValues = { - plain?: ResourceValues; - tls?: ResourceValues; - all?: ResourceValues; -}; - -type MessageTypesValues = { - messages?: MessageValues; - presence?: MessageValues; - all?: MessageValues; -}; - -type MessageTrafficValues = { - realtime?: MessageTypesValues; - rest?: MessageTypesValues; - webhook?: MessageTypesValues; - sharedQueue?: MessageTypesValues; - externalQueue?: MessageTypesValues; - httpEvent?: MessageTypesValues; - push?: MessageTypesValues; - all?: MessageTypesValues; -}; - -type MessageDirectionsValues = { - all?: MessageTypesValues; - inbound?: MessageTrafficValues; - outbound?: MessageTrafficValues; -}; - -type XchgMessagesValues = { - all?: MessageTypesValues; - producerPaid?: MessageDirectionsValues; - consumerPaid?: MessageDirectionsValues; -}; - -type NotificationsValues = { - invalid?: number; - attempted?: number; - successful?: number; - failed?: number; -}; - -type PushValues = { - messages?: number; - notifications?: NotificationsValues; - directPublishes?: number; -}; - -type ProcessedCountValues = { - succeeded?: number; - skipped?: number; - failed?: number; -}; - -type ProcessedMessagesValues = { - delta?: Record; -}; - type StatsValues = { - all?: MessageTypesValues; - inbound?: MessageTrafficValues; - outbound?: MessageTrafficValues; - persisted?: MessageTypesValues; - connections?: ConnectionTypesValues; - channels?: ResourceValues; - apiRequests?: RequestValues; - tokenRequests?: RequestValues; - xchgProducer?: XchgMessagesValues; - xchgConsumer?: XchgMessagesValues; - pushStats?: PushValues; - processed?: ProcessedMessagesValues; + entries?: Partial>; + schema?: string; + appId?: string; inProgress?: never; unit?: never; intervalId?: never; }; -class MessageCount { - count?: number; - data?: number; - uncompressedData?: number; - failed?: number; - refused?: number; - - constructor(values?: MessageValues) { - this.count = (values && values.count) || 0; - this.data = (values && values.data) || 0; - this.uncompressedData = (values && values.uncompressedData) || 0; - this.failed = (values && values.failed) || 0; - this.refused = (values && values.refused) || 0; - } -} - -class MessageCategory extends MessageCount { - category?: Record; - constructor(values?: MessageValues) { - super(values); - if (values && values.category) { - this.category = {}; - Utils.forInOwnNonNullProperties(values.category, (prop: string) => { - (this.category as Record)[prop] = new MessageCount( - (values.category as Record)[prop] - ); - }); - } - } -} - -class ResourceCount { - peak?: number; - min?: number; - mean?: number; - opened?: number; - refused?: number; - - constructor(values?: ResourceValues) { - this.peak = (values && values.peak) || 0; - this.min = (values && values.min) || 0; - this.mean = (values && values.mean) || 0; - this.opened = (values && values.opened) || 0; - this.refused = (values && values.refused) || 0; - } -} - -class RequestCount { - succeeded?: number; - failed?: number; - refused?: number; - - constructor(values?: RequestValues) { - this.succeeded = (values && values.succeeded) || 0; - this.failed = (values && values.failed) || 0; - this.refused = (values && values.refused) || 0; - } -} - -class ConnectionTypes { - plain?: ResourceCount; - tls?: ResourceCount; - all?: ResourceCount; - - constructor(values?: ConnectionTypesValues) { - this.plain = new ResourceCount(values && values.plain); - this.tls = new ResourceCount(values && values.tls); - this.all = new ResourceCount(values && values.all); - } -} - -class MessageTypes { - messages?: MessageCategory; - presence?: MessageCategory; - all?: MessageCategory; - - constructor(values?: MessageTypesValues) { - this.messages = new MessageCategory(values && values.messages); - this.presence = new MessageCategory(values && values.presence); - this.all = new MessageCategory(values && values.all); - } -} - -class MessageTraffic { - realtime?: MessageTypes; - rest?: MessageTypes; - webhook?: MessageTypes; - sharedQueue?: MessageTypes; - externalQueue?: MessageTypes; - httpEvent?: MessageTypes; - push?: MessageTypes; - all?: MessageTypes; - - constructor(values?: MessageTrafficValues) { - this.realtime = new MessageTypes(values && values.realtime); - this.rest = new MessageTypes(values && values.rest); - this.webhook = new MessageTypes(values && values.webhook); - this.sharedQueue = new MessageTypes(values && values.sharedQueue); - this.externalQueue = new MessageTypes(values && values.externalQueue); - this.httpEvent = new MessageTypes(values && values.httpEvent); - this.push = new MessageTypes(values && values.push); - this.all = new MessageTypes(values && values.all); - } -} - -class MessageDirections { - all?: MessageTypes; - inbound?: MessageTraffic; - outbound?: MessageTraffic; - - constructor(values?: MessageDirectionsValues) { - this.all = new MessageTypes(values && values.all); - this.inbound = new MessageTraffic(values && values.inbound); - this.outbound = new MessageTraffic(values && values.outbound); - } -} - -class XchgMessages { - all?: MessageTypes; - producerPaid?: MessageDirections; - consumerPaid?: MessageDirections; - - constructor(values?: XchgMessagesValues) { - this.all = new MessageTypes(values && values.all); - this.producerPaid = new MessageDirections(values && values.producerPaid); - this.consumerPaid = new MessageDirections(values && values.consumerPaid); - } -} - -class PushStats { - messages?: number; - notifications?: NotificationsValues; - directPublishes?: number; - - constructor(values?: PushValues) { - this.messages = (values && values.messages) || 0; - const notifications = values && values.notifications; - this.notifications = { - invalid: (notifications && notifications.invalid) || 0, - attempted: (notifications && notifications.attempted) || 0, - successful: (notifications && notifications.successful) || 0, - failed: (notifications && notifications.failed) || 0, - }; - this.directPublishes = (values && values.directPublishes) || 0; - } -} - -class ProcessedCount { - succeeded?: number; - skipped?: number; - failed?: number; - - constructor(values: ProcessedCountValues) { - this.succeeded = (values && values.succeeded) || 0; - this.skipped = (values && values.skipped) || 0; - this.failed = (values && values.failed) || 0; - } -} - -class ProcessedMessages { - delta?: Record; - - constructor(values?: ProcessedMessagesValues) { - this.delta = undefined; - if (values && values.delta) { - this.delta = {}; - Utils.forInOwnNonNullProperties(values.delta, (prop: string) => { - (this.delta as Record)[prop] = new ProcessedCount( - (values.delta as Record)[prop] - ); - }); - } - } -} - -class Stats extends MessageDirections { - persisted?: MessageTypes; - connections?: ConnectionTypes; - channels?: ResourceCount; - apiRequests?: RequestCount; - tokenRequests?: RequestCount; - xchgProducer?: XchgMessages; - xchgConsumer?: XchgMessages; - push?: PushStats; - processed?: ProcessedMessages; +class Stats { + entries?: Partial>; + schema?: string; + appId?: string; inProgress?: never; unit?: never; intervalId?: never; constructor(values?: StatsValues) { - super(values as MessageDirectionsValues); - this.persisted = new MessageTypes(values && values.persisted); - this.connections = new ConnectionTypes(values && values.connections); - this.channels = new ResourceCount(values && values.channels); - this.apiRequests = new RequestCount(values && values.apiRequests); - this.tokenRequests = new RequestCount(values && values.tokenRequests); - this.xchgProducer = new XchgMessages(values && values.xchgProducer); - this.xchgConsumer = new XchgMessages(values && values.xchgConsumer); - this.push = new PushStats(values && values.pushStats); - this.processed = new ProcessedMessages(values && values.processed); + this.entries = (values && values.entries) || undefined; + this.schema = (values && values.schema) || undefined; + this.appId = (values && values.appId) || undefined; this.inProgress = (values && values.inProgress) || undefined; this.unit = (values && values.unit) || undefined; this.intervalId = (values && values.intervalId) || undefined; diff --git a/src/common/lib/util/defaults.ts b/src/common/lib/util/defaults.ts index 86f292a558..16d3738112 100644 --- a/src/common/lib/util/defaults.ts +++ b/src/common/lib/util/defaults.ts @@ -3,8 +3,12 @@ import * as Utils from './utils'; import Logger from './logger'; import ErrorInfo from 'common/lib/types/errorinfo'; import { version } from '../../../../package.json'; -import ClientOptions, { DeprecatedClientOptions, NormalisedClientOptions } from 'common/types/ClientOptions'; +import ClientOptions, { NormalisedClientOptions } from 'common/types/ClientOptions'; import IDefaults from '../../types/IDefaults'; +import { MsgPack } from 'common/types/msgpack'; +import { IUntypedCryptoStatic } from 'common/types/ICryptoStatic'; +import { ChannelOptions } from 'common/types/channel'; +import { ModularPlugins } from '../client/modularplugins'; let agent = 'ably-js/' + version; @@ -24,8 +28,8 @@ type CompleteDefaults = IDefaults & { connectionStateTtl: number; realtimeRequestTimeout: number; recvTimeout: number; - preferenceConnectTimeout: number; - parallelUpgradeDelay: number; + webSocketConnectTimeout: number; + webSocketSlowTimeout: number; }; httpMaxRetryCount: number; maxMessageSize: number; @@ -37,11 +41,18 @@ type CompleteDefaults = IDefaults & { getHttpScheme(options: ClientOptions): string; environmentFallbackHosts(environment: string): string[]; getFallbackHosts(options: NormalisedClientOptions): string[]; - getHosts(options: NormalisedClientOptions): string[]; + getHosts(options: NormalisedClientOptions, ws?: boolean): string[]; checkHost(host: string): void; getRealtimeHost(options: ClientOptions, production: boolean, environment: string): string; - objectifyOptions(options: ClientOptions | string): ClientOptions; - normaliseOptions(options: DeprecatedClientOptions): NormalisedClientOptions; + objectifyOptions( + options: undefined | ClientOptions | string, + allowKeyOrToken: boolean, + sourceForErrorMessage: string, + modularPluginsToInclude?: ModularPlugins, + ): ClientOptions; + normaliseOptions(options: ClientOptions, MsgPack: MsgPack | null): NormalisedClientOptions; + defaultGetHeaders(options: NormalisedClientOptions, headersOptions?: HeadersOptions): Record; + defaultPostHeaders(options: NormalisedClientOptions, headersOptions?: HeadersOptions): Record; }; const Defaults = { @@ -69,14 +80,14 @@ const Defaults = { connectionStateTtl: 120000, realtimeRequestTimeout: 10000, recvTimeout: 90000, - preferenceConnectTimeout: 6000, - parallelUpgradeDelay: 6000, + webSocketConnectTimeout: 10000, + webSocketSlowTimeout: 4000, }, httpMaxRetryCount: 3, maxMessageSize: 65536, version, - protocolVersion: 2, + protocolVersion: 3, agent, getHost, getPort, @@ -87,6 +98,8 @@ const Defaults = { checkHost, objectifyOptions, normaliseOptions, + defaultGetHeaders, + defaultPostHeaders, }; export function getHost(options: ClientOptions, host?: string | null, ws?: boolean): string { @@ -123,8 +136,9 @@ export function getFallbackHosts(options: NormalisedClientOptions): string[] { return fallbackHosts ? Utils.arrChooseN(fallbackHosts, httpMaxRetryCount) : []; } -export function getHosts(options: NormalisedClientOptions): string[] { - return [options.restHost].concat(getFallbackHosts(options)); +export function getHosts(options: NormalisedClientOptions, ws?: boolean): string[] { + const hosts = [options.restHost].concat(getFallbackHosts(options)); + return ws ? hosts.map((host) => getHost(options, host, true)) : hosts; } function checkHost(host: string): void { @@ -148,7 +162,7 @@ function getRealtimeHost(options: ClientOptions, production: boolean, environmen options.restHost + '" but realtimeHost is not set, so setting realtimeHost to "' + options.restHost + - '" too. If this is not what you want, please set realtimeHost explicitly.' + '" too. If this is not what you want, please set realtimeHost explicitly.', ); return options.restHost; } @@ -174,82 +188,57 @@ export function getAgentString(options: ClientOptions): string { return agentStr; } -export function objectifyOptions(options: ClientOptions | string): ClientOptions { - if (typeof options == 'string') { - return options.indexOf(':') == -1 ? { token: options } : { key: options }; +export function objectifyOptions( + options: undefined | ClientOptions | string, + allowKeyOrToken: boolean, + sourceForErrorMessage: string, + modularPluginsToInclude?: ModularPlugins, +): ClientOptions { + if (options === undefined) { + const msg = allowKeyOrToken + ? `${sourceForErrorMessage} must be initialized with either a client options object, an Ably API key, or an Ably Token` + : `${sourceForErrorMessage} must be initialized with a client options object`; + Logger.logAction(Logger.LOG_ERROR, `${sourceForErrorMessage}()`, msg); + throw new Error(msg); } - return options; -} -export function normaliseOptions(options: DeprecatedClientOptions): NormalisedClientOptions { - /* Deprecated options */ - if (options.host) { - Logger.renamedClientOption('host', 'restHost'); - options.restHost = options.host; - } - if (options.wsHost) { - Logger.renamedClientOption('wsHost', 'realtimeHost'); - options.realtimeHost = options.wsHost; - } - if (options.queueEvents) { - Logger.renamedClientOption('queueEvents', 'queueMessages'); - options.queueMessages = options.queueEvents; - } - if (options.headers) { - Logger.deprecated( - 'the `headers` client option', - '' /* there is no replacement; see DeprecatedClientOptions.headers */ - ); - } + let optionsObj: ClientOptions; - if (options.fallbackHostsUseDefault) { - /* fallbackHostsUseDefault and fallbackHosts are mutually exclusive as per TO3k7 */ - if (options.fallbackHosts) { - const msg = 'fallbackHosts and fallbackHostsUseDefault cannot both be set'; - Logger.logAction(Logger.LOG_ERROR, 'Defaults.normaliseOptions', msg); - throw new ErrorInfo(msg, 40000, 400); - } - - /* default fallbacks can't be used with custom ports */ - if (options.port || options.tlsPort) { - const msg = 'fallbackHostsUseDefault cannot be set when port or tlsPort are set'; - Logger.logAction(Logger.LOG_ERROR, 'Defaults.normaliseOptions', msg); - throw new ErrorInfo(msg, 40000, 400); - } + if (typeof options === 'string') { + if (options.indexOf(':') == -1) { + if (!allowKeyOrToken) { + const msg = `${sourceForErrorMessage} cannot be initialized with just an Ably Token; you must provide a client options object with a \`plugins\` property. (Set this Ably Token as the object’s \`token\` property.)`; + Logger.logAction(Logger.LOG_ERROR, `${sourceForErrorMessage}()`, msg); + throw new Error(msg); + } - /* emit an appropriate deprecation warning */ - if (options.environment) { - Logger.deprecated( - 'The `fallbackHostsUseDefault` client option', - 'If you’re using this client option to force the library to make use of fallback hosts even though you’ve set the `environment` client option, then this is no longer necessary: remove your usage of the `fallbackHostsUseDefault` client option and the library will then automatically choose the correct fallback hosts to use for the specified environment.' - ); + optionsObj = { token: options }; } else { - Logger.deprecated( - 'The `fallbackHostsUseDefault` client option', - 'If you’re using this client option to force the library to make use of fallback hosts even though you’re not using the primary Ably environment, then stop using `fallbackHostsUseDefault`, and update your code to either pass the `environment` client option (in which case the library will automatically choose the correct fallback hosts to use for the specified environment), or to pass the `fallbackHosts` client option to specify a custom list of fallback hosts to use (for example, if you’re using a custom CNAME, in which case Ably will have provided you with an explicit list of fallback hosts).' - ); - } + if (!allowKeyOrToken) { + const msg = `${sourceForErrorMessage} cannot be initialized with just an Ably API key; you must provide a client options object with a \`plugins\` property. (Set this Ably API key as the object’s \`key\` property.)`; + Logger.logAction(Logger.LOG_ERROR, `${sourceForErrorMessage}()`, msg); + throw new Error(msg); + } - /* use the default fallback hosts as requested */ - options.fallbackHosts = Defaults.FALLBACK_HOSTS; + optionsObj = { key: options }; + } + } else { + optionsObj = options; } - /* options.recover as a boolean is deprecated, and therefore is not part of the public typing */ - if ((options.recover as any) === true) { - Logger.deprecated( - 'The ability to use a boolean value for the `recover` client option', - 'If you wish for the connection to always be recovered, replace `{ recover: true }` with a function that always passes `true` to its callback: `{ recover: function(lastConnectionDetails, cb) { cb(true); } }`' - ); - options.recover = function (lastConnectionDetails: unknown, cb: (shouldRecover: boolean) => void) { - cb(true); - }; + if (modularPluginsToInclude) { + optionsObj = { ...optionsObj, plugins: { ...modularPluginsToInclude, ...optionsObj.plugins } }; } + return optionsObj; +} + +export function normaliseOptions(options: ClientOptions, MsgPack: MsgPack | null): NormalisedClientOptions { if (typeof options.recover === 'function' && options.closeOnUnload === true) { Logger.logAction( Logger.LOG_ERROR, 'Defaults.normaliseOptions', - 'closeOnUnload was true and a session recovery function was set - these are mutually exclusive, so unsetting the latter' + 'closeOnUnload was true and a session recovery function was set - these are mutually exclusive, so unsetting the latter', ); options.recover = undefined; } @@ -260,14 +249,6 @@ export function normaliseOptions(options: DeprecatedClientOptions): NormalisedCl options.closeOnUnload = !options.recover; } - if (options.transports && Utils.arrIn(options.transports, 'xhr')) { - Logger.deprecationWarning( - 'The "xhr" transport has been renamed to "xhr_streaming". Please update your client options code to use `transports: ["xhr_streaming"]` instead. The ability to use `transports: ["xhr"]` will be removed in a future version.' - ); - Utils.arrDeleteValue(options.transports, 'xhr'); - options.transports.push('xhr_streaming'); - } - if (!('queueMessages' in options)) options.queueMessages = true; /* infer hosts and fallbacks based on the configured environment */ @@ -281,7 +262,7 @@ export function normaliseOptions(options: DeprecatedClientOptions): NormalisedCl const restHost = options.restHost || (production ? Defaults.REST_HOST : environment + '-' + Defaults.REST_HOST); const realtimeHost = getRealtimeHost(options, production, environment); - Utils.arrForEach((options.fallbackHosts || []).concat(restHost, realtimeHost), checkHost); + (options.fallbackHosts || []).concat(restHost, realtimeHost).forEach(checkHost); options.port = options.port || Defaults.PORT; options.tlsPort = options.tlsPort || Defaults.TLS_PORT; @@ -289,14 +270,18 @@ export function normaliseOptions(options: DeprecatedClientOptions): NormalisedCl const timeouts = getTimeouts(options); - if ('useBinaryProtocol' in options) { - options.useBinaryProtocol = Platform.Config.supportsBinary && options.useBinaryProtocol; + if (MsgPack) { + if ('useBinaryProtocol' in options) { + options.useBinaryProtocol = Platform.Config.supportsBinary && options.useBinaryProtocol; + } else { + options.useBinaryProtocol = Platform.Config.preferBinary; + } } else { - options.useBinaryProtocol = Platform.Config.preferBinary; + options.useBinaryProtocol = false; } + const headers: Record = {}; if (options.clientId) { - const headers = (options.headers = options.headers || {}); headers['X-Ably-ClientId'] = Platform.BufferUtils.base64Encode(Platform.BufferUtils.utf8Encode(options.clientId)); } @@ -304,15 +289,6 @@ export function normaliseOptions(options: DeprecatedClientOptions): NormalisedCl options.idempotentRestPublishing = true; } - if (options.promises && !Platform.Config.Promise) { - Logger.logAction( - Logger.LOG_ERROR, - 'Defaults.normaliseOptions', - '{promises: true} was specified, but no Promise constructor found; disabling promises' - ); - options.promises = false; - } - let connectivityCheckParams = null; let connectivityCheckUrl = options.connectivityCheckUrl; if (options.connectivityCheckUrl) { @@ -326,16 +302,79 @@ export function normaliseOptions(options: DeprecatedClientOptions): NormalisedCl return { ...options, - useBinaryProtocol: - 'useBinaryProtocol' in options - ? Platform.Config.supportsBinary && options.useBinaryProtocol - : Platform.Config.preferBinary, realtimeHost, restHost, maxMessageSize: options.maxMessageSize || Defaults.maxMessageSize, timeouts, connectivityCheckParams, connectivityCheckUrl, + headers, + }; +} + +export function normaliseChannelOptions(Crypto: IUntypedCryptoStatic | null, options?: ChannelOptions) { + const channelOptions = options || {}; + if (channelOptions.cipher) { + if (!Crypto) Utils.throwMissingPluginError('Crypto'); + const cipher = Crypto.getCipher(channelOptions.cipher); + channelOptions.cipher = cipher.cipherParams; + channelOptions.channelCipher = cipher.cipher; + } else if ('cipher' in channelOptions) { + /* Don't deactivate an existing cipher unless options + * has a 'cipher' key that's falsey */ + channelOptions.cipher = undefined; + channelOptions.channelCipher = null; + } + return channelOptions; +} + +const contentTypes = { + json: 'application/json', + xml: 'application/xml', + html: 'text/html', + msgpack: 'application/x-msgpack', +}; + +export interface HeadersOptions { + format?: Utils.Format; + protocolVersion?: number; +} + +const defaultHeadersOptions: Required = { + format: Utils.Format.json, + protocolVersion: Defaults.protocolVersion, +}; + +export function defaultGetHeaders( + options: NormalisedClientOptions, + { + format = defaultHeadersOptions.format, + protocolVersion = defaultHeadersOptions.protocolVersion, + }: HeadersOptions = {}, +): Record { + const accept = contentTypes[format]; + return { + accept: accept, + 'X-Ably-Version': protocolVersion.toString(), + 'Ably-Agent': getAgentString(options), + }; +} + +export function defaultPostHeaders( + options: NormalisedClientOptions, + { + format = defaultHeadersOptions.format, + protocolVersion = defaultHeadersOptions.protocolVersion, + }: HeadersOptions = {}, +): Record { + let contentType; + const accept = (contentType = contentTypes[format]); + + return { + accept: accept, + 'content-type': contentType, + 'X-Ably-Version': protocolVersion.toString(), + 'Ably-Agent': getAgentString(options), }; } diff --git a/src/common/lib/util/eventemitter.ts b/src/common/lib/util/eventemitter.ts index d24e448e8b..830962ddd4 100644 --- a/src/common/lib/util/eventemitter.ts +++ b/src/common/lib/util/eventemitter.ts @@ -10,7 +10,7 @@ function callListener(eventThis: { event: string }, listener: Function, args: un Logger.logAction( Logger.LOG_ERROR, 'EventEmitter.emit()', - 'Unexpected listener exception: ' + e + '; stack = ' + (e && (e as Error).stack) + 'Unexpected listener exception: ' + e + '; stack = ' + (e && (e as Error).stack), ); } } @@ -32,8 +32,8 @@ function removeListener(targetListeners: any, listener: Function, eventFilter?: listeners = listeners[eventFilter] as Record; } - if (Utils.isArray(listeners)) { - while ((index = Utils.arrIndexOf(listeners, listener)) !== -1) { + if (Array.isArray(listeners)) { + while ((index = listeners.indexOf(listener)) !== -1) { listeners.splice(index, 1); } /* If events object has an event name key with no listeners then @@ -44,7 +44,7 @@ function removeListener(targetListeners: any, listener: Function, eventFilter?: } else if (Utils.isObject(listeners)) { /* events */ for (eventName in listeners) { - if (Object.prototype.hasOwnProperty.call(listeners, eventName) && Utils.isArray(listeners[eventName])) { + if (Object.prototype.hasOwnProperty.call(listeners, eventName) && Array.isArray(listeners[eventName])) { removeListener([listeners], listener, eventName); } } @@ -92,9 +92,9 @@ class EventEmitter { if (typeof listener !== 'function') { throw new Error('EventListener.on(): Invalid arguments: ' + Platform.Config.inspect(args)); } - if (Utils.isEmptyArg(event)) { + if (Utils.isNil(event)) { this.any.push(listener); - } else if (Utils.isArray(event)) { + } else if (Array.isArray(event)) { event.forEach((eventName) => { this.on(eventName, listener); }); @@ -126,7 +126,7 @@ class EventEmitter { off(event: string | string[] | null, listener?: Function | null): void; off(...args: unknown[]) { - if (args.length == 0 || (Utils.isEmptyArg(args[0]) && Utils.isEmptyArg(args[1]))) { + if (args.length == 0 || (Utils.isNil(args[0]) && Utils.isNil(args[1]))) { this.any = []; this.events = Object.create(null); this.anyOnce = []; @@ -151,12 +151,12 @@ class EventEmitter { [event, listener] = [firstArg, secondArg]; } - if (listener && Utils.isEmptyArg(event)) { + if (listener && Utils.isNil(event)) { removeListener([this.any, this.events, this.anyOnce, this.eventsOnce], listener); return; } - if (Utils.isArray(event)) { + if (Array.isArray(event)) { event.forEach((eventName) => { this.off(eventName, listener); }); @@ -215,7 +215,7 @@ class EventEmitter { Array.prototype.push.apply(listeners, eventsListeners); } - Utils.arrForEach(listeners, function (listener) { + listeners.forEach(function (listener) { callListener(eventThis, listener, args); }); } @@ -241,9 +241,9 @@ class EventEmitter { once(...args: unknown[]): void | Promise { const argCount = args.length; - if ((argCount === 0 || (argCount === 1 && typeof args[0] !== 'function')) && Platform.Config.Promise) { + if (argCount === 0 || (argCount === 1 && typeof args[0] !== 'function')) { const event = args[0]; - return new Platform.Config.Promise((resolve) => { + return new Promise((resolve) => { this.once(event as string | string[] | null, resolve); }); } @@ -251,16 +251,16 @@ class EventEmitter { const [firstArg, secondArg] = args; if (args.length === 1 && typeof firstArg === 'function') { this.anyOnce.push(firstArg); - } else if (Utils.isEmptyArg(firstArg)) { + } else if (Utils.isNil(firstArg)) { if (typeof secondArg !== 'function') { throw new Error('EventEmitter.once(): Invalid arguments:' + Platform.Config.inspect(args)); } this.anyOnce.push(secondArg); - } else if (Utils.isArray(firstArg)) { + } else if (Array.isArray(firstArg)) { const self = this; const listenerWrapper = function (this: any) { const innerArgs = Array.prototype.slice.call(arguments); - Utils.arrForEach(firstArg, function (eventName) { + firstArg.forEach(function (eventName) { self.off(eventName, listenerWrapper); }); if (typeof secondArg !== 'function') { @@ -268,7 +268,7 @@ class EventEmitter { } secondArg.apply(this, innerArgs); }; - Utils.arrForEach(firstArg, function (eventName) { + firstArg.forEach(function (eventName) { self.on(eventName, listenerWrapper); }); } else { @@ -286,32 +286,18 @@ class EventEmitter { } /** - * Private API - * * Listen for a single occurrence of a state event and fire immediately if currentState matches targetState * @param targetState the name of the state event to listen to * @param currentState the name of the current state of this object - * @param listener the listener to be called - * @param listenerArgs */ - whenState(targetState: string, currentState: string, listener: Function, ...listenerArgs: unknown[]) { - const eventThis = { event: targetState }; - + async whenState(targetState: string, currentState: string) { if (typeof targetState !== 'string' || typeof currentState !== 'string') { - throw 'whenState requires a valid event String argument'; - } - if (typeof listener !== 'function' && Platform.Config.Promise) { - return new Platform.Config.Promise((resolve) => { - EventEmitter.prototype.whenState.apply( - this, - [targetState, currentState, resolve].concat(listenerArgs as any[]) as any - ); - }); + throw new Error('whenState requires a valid state String argument'); } if (targetState === currentState) { - callListener(eventThis, listener, listenerArgs); + return null; } else { - this.once(targetState, listener); + return this.once(targetState); } } } diff --git a/src/common/lib/util/logger.ts b/src/common/lib/util/logger.ts index f81a8c076a..84b743a675 100644 --- a/src/common/lib/util/logger.ts +++ b/src/common/lib/util/logger.ts @@ -8,7 +8,7 @@ type LoggerFunction = (...args: string[]) => void; // Workaround for salesforce lightning locker compatibility // This is a shorthand version of Utils.getGlobalObject (which we can't use here without creating a circular import) -let globalObject = global || (typeof window !== 'undefined' ? window : self); +let globalObject = typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : self; enum LogLevels { None = 0, @@ -35,7 +35,7 @@ function getHandler(logger: Function): Function { '.' + pad(time.getMilliseconds(), 1) + ' ' + - msg + msg, ); } : logger; @@ -45,27 +45,19 @@ const getDefaultLoggers = (): [Function, Function] => { let consoleLogger; let errorLogger; - /* Can't just check for console && console.log; fails in IE <=9 */ - if ( - (typeof Window === 'undefined' && typeof WorkerGlobalScope === 'undefined') /* node */ || - typeof globalObject?.console?.log?.apply === 'function' /* sensible browsers */ - ) { + // we expect ably-js to be run in environments which have `console` object available with its `log` function + if (typeof globalObject?.console?.log === 'function') { consoleLogger = function (...args: unknown[]) { console.log.apply(console, args); }; + errorLogger = console.warn ? function (...args: unknown[]) { console.warn.apply(console, args); } : consoleLogger; - } else if (globalObject?.console.log as unknown) { - /* IE <= 9 with the console open -- console.log does not - * inherit from Function, so has no apply method */ - consoleLogger = errorLogger = function () { - Function.prototype.apply.call(console.log, console, arguments); - }; } else { - /* IE <= 9 when dev tools are closed - window.console not even defined */ + // otherwise we should fallback to noop for log functions consoleLogger = errorLogger = function () {}; } @@ -98,11 +90,23 @@ class Logger { } /* public static functions */ + /** + * In the modular variant of the SDK, the `stripLogs` esbuild plugin strips out all calls to this method (when invoked as `Logger.logAction(...)`) except when called with level `Logger.LOG_ERROR`. If you wish for a log statement to never be stripped, use the {@link logActionNoStrip} method instead. + * + * The aforementioned plugin expects `level` to be an expression of the form `Logger.LOG_*`; that is, you can’t dynamically specify the log level. + */ static logAction = (level: LogLevels, action: string, message?: string) => { + this.logActionNoStrip(level, action, message); + }; + + /** + * Calls to this method are never stripped by the `stripLogs` esbuild plugin. Use it for log statements that you wish to always be included in the modular variant of the SDK. + */ + static logActionNoStrip(level: LogLevels, action: string, message?: string) { if (Logger.shouldLog(level)) { (level === LogLevels.Error ? Logger.logErrorHandler : Logger.logHandler)('Ably: ' + action + ': ' + message); } - }; + } static deprecated = (description: string, msg: string) => { Logger.deprecationWarning(`${description} is deprecated and will be removed in a future version. ${msg}`); @@ -110,13 +114,13 @@ class Logger { static renamedClientOption(oldName: string, newName: string) { Logger.deprecationWarning( - `The \`${oldName}\` client option has been renamed to \`${newName}\`. Please update your code to use \`${newName}\` instead. \`${oldName}\` will be removed in a future version.` + `The \`${oldName}\` client option has been renamed to \`${newName}\`. Please update your code to use \`${newName}\` instead. \`${oldName}\` will be removed in a future version.`, ); } static renamedMethod(className: string, oldName: string, newName: string) { Logger.deprecationWarning( - `\`${className}\`’s \`${oldName}\` method has been renamed to \`${newName}\`. Please update your code to use \`${newName}\` instead. \`${oldName}\` will be removed in a future version.` + `\`${className}\`’s \`${oldName}\` method has been renamed to \`${newName}\`. Please update your code to use \`${newName}\` instead. \`${oldName}\` will be removed in a future version.`, ); } diff --git a/src/common/lib/util/multicaster.ts b/src/common/lib/util/multicaster.ts index 068e3a798b..bf5c32c64c 100644 --- a/src/common/lib/util/multicaster.ts +++ b/src/common/lib/util/multicaster.ts @@ -1,44 +1,75 @@ +import { StandardCallback } from 'common/types/utils'; +import ErrorInfo from 'common/lib/types/errorinfo'; import Logger from './logger'; -type AnyFunction = (...args: any[]) => unknown; - -export interface MulticasterInstance extends Function { - (...args: unknown[]): void; - push: (fn: AnyFunction) => void; +export interface MulticasterInstance extends Function { + (err?: ErrorInfo | null, result?: T): void; + push: (fn: StandardCallback) => void; + /** + * Creates a promise that will be resolved or rejected when this instance is called. + */ + createPromise: () => Promise; + /** + * Syntatic sugar for when working in a context that uses promises; equivalent to calling as a function with arguments (null, result). + */ + resolveAll(result: T): void; + /** + * Syntatic sugar for when working in a context that uses promises; equivalent to calling as a function with arguments (err). + */ + rejectAll(err: ErrorInfo): void; } -class Multicaster { - members: Array; +class Multicaster { + members: Array>; // Private constructor; use static Multicaster.create instead - private constructor(members?: Array) { - this.members = (members as Array) || []; + private constructor(members?: Array | undefined>) { + this.members = (members as Array>) || []; } - call(...args: unknown[]): void { + private call(err?: ErrorInfo | null, result?: T): void { for (const member of this.members) { if (member) { try { - member(...args); + member(err, result); } catch (e) { Logger.logAction( Logger.LOG_ERROR, 'Multicaster multiple callback handler', - 'Unexpected exception: ' + e + '; stack = ' + (e as Error).stack + 'Unexpected exception: ' + e + '; stack = ' + (e as Error).stack, ); } } } } - push(...args: Array): void { + push(...args: Array>): void { this.members.push(...args); } - static create(members?: Array): MulticasterInstance { + createPromise(): Promise { + return new Promise((resolve, reject) => { + this.push((err, result) => { + err ? reject(err) : resolve(result!); + }); + }); + } + + resolveAll(result: T) { + this.call(null, result); + } + + rejectAll(err: ErrorInfo) { + this.call(err); + } + + static create(members?: Array | undefined>): MulticasterInstance { const instance = new Multicaster(members); - return Object.assign((...args: unknown[]) => instance.call(...args), { - push: (fn: AnyFunction) => instance.push(fn), + return Object.assign((err?: ErrorInfo | null, result?: T) => instance.call(err, result), { + push: (fn: StandardCallback) => instance.push(fn), + createPromise: () => instance.createPromise(), + resolveAll: (result: T) => instance.resolveAll(result), + rejectAll: (err: ErrorInfo) => instance.rejectAll(err), }); } } diff --git a/src/common/lib/util/utils.ts b/src/common/lib/util/utils.ts index cbd1214fd6..1761ca03cb 100644 --- a/src/common/lib/util/utils.ts +++ b/src/common/lib/util/utils.ts @@ -1,19 +1,18 @@ import Platform from 'common/platform'; -import Defaults, { getAgentString } from './defaults'; import ErrorInfo, { PartialErrorInfo } from 'common/lib/types/errorinfo'; -import { NormalisedClientOptions } from 'common/types/ClientOptions'; -import { stringify as stringifyBase64 } from 'crypto-js/build/enc-base64'; -import { parse as parseUtf8 } from 'crypto-js/build/enc-utf8'; +import { ModularPlugins } from '../client/modularplugins'; +import { MsgPack } from 'common/types/msgpack'; function randomPosn(arrOrStr: Array | string) { return Math.floor(Math.random() * arrOrStr.length); } -/* +/** * Add a set of properties to a target object - * target: the target object - * props: an object whose enumerable properties are - * added, by reference only + * + * @param target the target object + * @param args objects, which enumerable properties are added to target, by reference only + * @returns target object with added properties */ export function mixin( target: Record, @@ -24,9 +23,9 @@ export function mixin( if (!source) { break; } - const hasOwnProperty = Object.prototype.hasOwnProperty; + for (const key in source) { - if (!hasOwnProperty || hasOwnProperty.call(source, key)) { + if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = (source as Record)[key]; } } @@ -34,36 +33,26 @@ export function mixin( return target; } -/* - * Add a set of properties to a target object - * target: the target object - * props: an object whose enumerable properties are - * added, by reference only +/** + * Creates a copy of enumerable properties of the source object + * + * @param src object to copy + * @returns copy of src */ export function copy>(src: T | Record | null | undefined): T { return mixin({}, src as Record) as T; } -/* - * Determine whether or not a given object is - * an array. - */ -export const isArray = - Array.isArray || - function (value: unknown): value is Array { - return Object.prototype.toString.call(value) == '[object Array]'; - }; - /* * Ensures that an Array object is always returned * returning the original Array of obj is an Array * else wrapping the obj in a single element Array */ export function ensureArray(obj: Record): unknown[] { - if (isEmptyArg(obj)) { + if (isNil(obj)) { return []; } - if (isArray(obj)) { + if (Array.isArray(obj)) { return obj; } return [obj]; @@ -83,26 +72,13 @@ export function isEmpty(ob: Record | unknown[]): boolean { return true; } -export function isOnlyPropIn(ob: Record, property: string): boolean { - for (const prop in ob) { - if (prop !== property) { - return false; - } - } - return true; -} - -/* - * Determine whether or not an argument to an overloaded function is - * undefined (missing) or null. - * This method is useful when constructing functions such as (WebIDL terminology): - * off([TreatUndefinedAs=Null] DOMString? event) - * as you can then confirm the argument using: - * Utils.isEmptyArg(event) +/** + * Checks if `value` is `null` or `undefined`. + * + * Source: https://github.com/lodash/lodash/blob/main/src/isNil.ts */ - -export function isEmptyArg(arg: unknown): arg is null | undefined { - return arg === null || arg === undefined; +export function isNil(arg: unknown): arg is null | undefined { + return arg == null; } /* @@ -129,7 +105,7 @@ export function shallowClone(ob: Record): Record, - ownProperties: Record + ownProperties: Record, ): Record { class F {} F.prototype = ob; @@ -167,20 +143,20 @@ export function containsValue(ob: Record, val: unknown): boolea return false; } -export function intersect(arr: Array, ob: string[] | Record): string[] { - return isArray(ob) ? arrIntersect(arr, ob) : arrIntersectOb(arr, ob); +export function intersect(arr: Array, ob: K[] | Partial>): K[] { + return Array.isArray(ob) ? arrIntersect(arr, ob) : arrIntersectOb(arr, ob); } export function arrIntersect(arr1: Array, arr2: Array): Array { const result = []; for (let i = 0; i < arr1.length; i++) { const member = arr1[i]; - if (arrIndexOf(arr2, member) != -1) result.push(member); + if (arr2.indexOf(member) != -1) result.push(member); } return result; } -export function arrIntersectOb(arr: Array, ob: Record): T[] { +export function arrIntersectOb(arr: Array, ob: Partial>): K[] { const result = []; for (let i = 0; i < arr.length; i++) { const member = arr[i]; @@ -193,32 +169,13 @@ export function arrSubtract(arr1: Array, arr2: Array): Array { const result = []; for (let i = 0; i < arr1.length; i++) { const element = arr1[i]; - if (arrIndexOf(arr2, element) == -1) result.push(element); + if (arr2.indexOf(element) == -1) result.push(element); } return result; } -export const arrIndexOf = (Array.prototype.indexOf as unknown) - ? function (arr: Array, elem: unknown, fromIndex?: number) { - return arr.indexOf(elem, fromIndex); - } - : function (arr: Array, elem: unknown, fromIndex?: number) { - fromIndex = fromIndex || 0; - const len = arr.length; - for (; fromIndex < len; fromIndex++) { - if (arr[fromIndex] === elem) { - return fromIndex; - } - } - return -1; - }; - -export function arrIn(arr: Array, val: unknown): boolean { - return arrIndexOf(arr, val) !== -1; -} - export function arrDeleteValue(arr: Array, val: T): boolean { - const idx = arrIndexOf(arr, val); + const idx = arr.indexOf(val); const res = idx != -1; if (res) arr.splice(idx, 1); return res; @@ -270,104 +227,19 @@ export function forInOwnNonNullProperties(ob: Record, fn: (prop } } -export const arrForEach = (Array.prototype.forEach as unknown) - ? function (arr: Array, fn: (value: T, index: number, arr: Array) => unknown) { - arr.forEach(fn); - } - : function (arr: Array, fn: (value: T, index: number, arr: Array) => unknown) { - const len = arr.length; - for (let i = 0; i < len; i++) { - fn(arr[i], i, arr); - } - }; - -/* Useful when the function may mutate the array */ -export function safeArrForEach( - arr: Array, - fn: (value: T, index: number, arr: Array) => unknown -): void { - return arrForEach(arr.slice(), fn); -} - -export const arrMap = (Array.prototype.map as unknown) - ? function (arr: Array, fn: (value: T1, index?: number, arr?: Array) => T2) { - return arr.map(fn); - } - : function (arr: Array, fn: (value: T, index?: number, arr?: Array) => unknown) { - const result = []; - const len = arr.length; - for (let i = 0; i < len; i++) { - result.push(fn(arr[i], i, arr)); - } - return result; - }; - -export const arrFilter = (Array.prototype.filter as unknown) - ? function (arr: Array, fn: (value: T, index?: number, arr?: Array) => boolean) { - return arr.filter(fn); - } - : function (arr: Array, fn: (value: T, index?: number, arr?: Array) => boolean) { - const result = [], - len = arr.length; - for (let i = 0; i < len; i++) { - if (fn(arr[i])) { - result.push(arr[i]); - } - } - return result; - }; - -export const arrEvery = (Array.prototype.every as unknown) - ? function (arr: Array, fn: (value: T, index: number, arr: Array) => boolean) { - return arr.every(fn); - } - : function (arr: Array, fn: (value: T, index: number, arr: Array) => boolean) { - const len = arr.length; - for (let i = 0; i < len; i++) { - if (!fn(arr[i], i, arr)) { - return false; - } - } - return true; - }; - export function allSame(arr: Array>, prop: string): boolean { if (arr.length === 0) { return true; } const first = arr[0][prop]; - return arrEvery(arr, function (item) { + return arr.every(function (item) { return item[prop] === first; }); } -const contentTypes = { - json: 'application/json', - jsonp: 'application/javascript', - xml: 'application/xml', - html: 'text/html', - msgpack: 'application/x-msgpack', -}; - -export function defaultGetHeaders(options: NormalisedClientOptions, format?: Format): Record { - const accept = contentTypes[format || Format.json]; - return { - accept: accept, - 'X-Ably-Version': Defaults.protocolVersion.toString(), - 'Ably-Agent': getAgentString(options), - }; -} - -export function defaultPostHeaders(options: NormalisedClientOptions, format?: Format): Record { - let contentType; - const accept = (contentType = contentTypes[format || Format.json]); - - return { - accept: accept, - 'content-type': contentType, - 'X-Ably-Version': Defaults.protocolVersion.toString(), - 'Ably-Agent': getAgentString(options), - }; +export enum Format { + msgpack = 'msgpack', + json = 'json', } export function arrPopRandomElement(arr: Array): T { @@ -392,19 +264,8 @@ export function parseQueryString(query: string): Record { return result; } -export const now = - Date.now || - function () { - /* IE 8 */ - return new Date().getTime(); - }; - export function isErrorInfoOrPartialErrorInfo(err: unknown): err is ErrorInfo | PartialErrorInfo { - return ( - typeof err == 'object' && - err !== null && - (err.constructor.name == 'ErrorInfo' || err.constructor.name == 'PartialErrorInfo') - ); + return typeof err == 'object' && err !== null && (err instanceof ErrorInfo || err instanceof PartialErrorInfo); } export function inspectError(err: unknown): string { @@ -445,38 +306,9 @@ export function cheapRandStr(): string { /* Takes param the minimum number of bytes of entropy the string must * include, not the length of the string. String length produced is not * guaranteed. */ -export const randomString = (numBytes: number): string => { - if (Platform.Config.getRandomValues && typeof Uint8Array !== 'undefined') { - const uIntArr = new Uint8Array(numBytes); - (Platform.Config.getRandomValues as Function)(uIntArr); - return Platform.BufferUtils.base64Encode(uIntArr); - } - /* Old browser; fall back to Math.random. Could just use a - * CryptoJS version of the above, but want this to still work in nocrypto - * versions of the library */ - const charset = Platform.BufferUtils.base64CharSet; - /* base64 has 33% overhead; round length up */ - const length = Math.round((numBytes * 4) / 3); - let result = ''; - for (let i = 0; i < length; i++) { - result += charset[randomPosn(charset)]; - } - return result; -}; - -export const randomHexString = (numBytes: number): string => { - if (Platform.Config.getRandomValues && typeof Uint8Array !== 'undefined') { - const uIntArr = new Uint8Array(numBytes); - (Platform.Config.getRandomValues as Function)(uIntArr); - return Platform.BufferUtils.hexEncode(uIntArr); - } - const charset = Platform.BufferUtils.hexCharSet; - const length = numBytes * 2; - let result = ''; - for (let i = 0; i < length; i++) { - result += charset[randomPosn(charset)]; - } - return result; +export const randomString = async (numBytes: number): Promise => { + const buffer = await Platform.Config.getRandomArrayBuffer(numBytes); + return Platform.BufferUtils.base64Encode(buffer); }; /* Pick n elements at random without replacement from an array */ @@ -490,33 +322,43 @@ export function arrChooseN(arr: Array, n: number): Array { return result; } -export const trim = (String.prototype.trim as unknown) - ? function (str: string) { - return str.trim(); - } - : function (str: string) { - return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); - }; - -export function promisify(ob: Record, fnName: string, args: IArguments | unknown[]): Promise { - return new Promise(function (resolve, reject) { - ob[fnName](...(args as unknown[]), function (err: Error, res: unknown) { - err ? reject(err) : resolve(res as T); +/** + * Uses a callback to communicate the result of a `Promise`. The first argument passed to the callback will be either an error (when the promise is rejected) or `null` (when the promise is fulfilled). In the case where the promise is fulfilled, the resulting value will be passed to the callback as a second argument. + */ +export function whenPromiseSettles( + promise: Promise, + callback?: (err: E | null, result?: T) => void, +) { + promise + .then((result) => { + callback?.(null, result); + }) + .catch((err: unknown) => { + // We make no guarantees about the type of the error that gets passed to the callback. Issue https://github.com/ably/ably-js/issues/1617 will think about how to correctly handle error types. + callback?.(err as E); }); - }); } -export enum Format { - msgpack = 'msgpack', - json = 'json', -} +export function decodeBody(body: unknown, MsgPack: MsgPack | null, format?: Format | null): T { + if (format == 'msgpack') { + if (!MsgPack) { + throwMissingPluginError('MsgPack'); + } + return MsgPack.decode(body as Buffer); + } -export function decodeBody(body: unknown, format?: Format | null): T { - return format == 'msgpack' ? Platform.Config.msgpack.decode(body as Buffer) : JSON.parse(String(body)); + return JSON.parse(String(body)); } -export function encodeBody(body: unknown, format?: Format): string | Buffer { - return format == 'msgpack' ? (Platform.Config.msgpack.encode(body, true) as Buffer) : JSON.stringify(body); +export function encodeBody(body: unknown, MsgPack: MsgPack | null, format?: Format): string | Buffer { + if (format == 'msgpack') { + if (!MsgPack) { + throwMissingPluginError('MsgPack'); + } + return MsgPack.encode(body, true) as Buffer; + } + + return JSON.stringify(body); } export function allToLowerCase(arr: Array): Array { @@ -554,7 +396,7 @@ export function getRetryTime(initialTimeout: number, retryAttempt: number) { } export function getGlobalObject() { - if (global) { + if (typeof global !== 'undefined') { return global; } @@ -599,17 +441,24 @@ export function matchDerivedChannel(name: string) { } export function toBase64(str: string) { - if (Platform.Config.createHmac) { - return Buffer.from(str, 'ascii').toString('base64'); - } - return stringifyBase64(parseUtf8(str)); + const bufferUtils = Platform.BufferUtils; + const textBuffer = bufferUtils.utf8Encode(str); + return bufferUtils.base64Encode(textBuffer); } export function arrEquals(a: any[], b: any[]) { return ( a.length === b.length && - arrEvery(a, function (val, i) { + a.every(function (val, i) { return val === b[i]; }) ); } + +export function createMissingPluginError(pluginName: keyof ModularPlugins): ErrorInfo { + return new ErrorInfo(`${pluginName} plugin not provided`, 40019, 400); +} + +export function throwMissingPluginError(pluginName: keyof ModularPlugins): never { + throw createMissingPluginError(pluginName); +} diff --git a/src/common/platform.ts b/src/common/platform.ts index ff6ad04d66..1f695eb45a 100644 --- a/src/common/platform.ts +++ b/src/common/platform.ts @@ -1,17 +1,19 @@ import { IPlatformConfig } from './types/IPlatformConfig'; -import { IHttp } from './types/http'; -import ConnectionManager from './lib/transport/connectionmanager'; +import { IPlatformHttpStatic } from './types/http'; import IDefaults from './types/IDefaults'; import IWebStorage from './types/IWebStorage'; import IBufferUtils from './types/IBufferUtils'; -import Transport from './lib/transport/transport'; import * as WebBufferUtils from '../platform/web/lib/util/bufferutils'; import * as NodeBufferUtils from '../platform/nodejs/lib/util/bufferutils'; +import { IUntypedCryptoStatic } from '../common/types/ICryptoStatic'; +import TransportName from './constants/TransportName'; +import { TransportCtor } from './lib/transport/transport'; -type Bufferlike = WebBufferUtils.Bufferlike | NodeBufferUtils.Bufferlike; +export type Bufferlike = WebBufferUtils.Bufferlike | NodeBufferUtils.Bufferlike; type BufferUtilsOutput = WebBufferUtils.Output | NodeBufferUtils.Output; type ToBufferOutput = WebBufferUtils.ToBufferOutput | NodeBufferUtils.ToBufferOutput; -type ComparableBuffer = WebBufferUtils.ComparableBuffer | NodeBufferUtils.ComparableBuffer; + +export type TransportImplementations = Partial>; export default class Platform { static Config: IPlatformConfig; @@ -22,10 +24,19 @@ export default class Platform { BufferUtils object that accepts a broader range of data types than it can in reality handle. */ - static BufferUtils: IBufferUtils; - static Crypto: any; //Not typed - static Http: typeof IHttp; - static Transports: Array<(connectionManager: typeof ConnectionManager) => Transport>; + static BufferUtils: IBufferUtils; + /* + We’d like this to be ICryptoStatic with the correct generic arguments, + but Platform doesn’t currently allow that, as described in the BufferUtils + comment above. + */ + static Crypto: IUntypedCryptoStatic | null; + static Http: IPlatformHttpStatic; + static Transports: { + order: TransportName[]; + // Transport implementations that always come with this platform + bundledImplementations: TransportImplementations; + }; static Defaults: IDefaults; static WebStorage: IWebStorage | null; } diff --git a/src/common/types/ClientOptions.ts b/src/common/types/ClientOptions.ts index fe9e97eaad..e0a44064cf 100644 --- a/src/common/types/ClientOptions.ts +++ b/src/common/types/ClientOptions.ts @@ -1,33 +1,20 @@ import { Modify } from './utils'; import * as API from '../../../ably'; +import { ModularPlugins } from 'common/lib/client/modularplugins'; export type RestAgentOptions = { keepAlive: boolean; maxSockets: number; }; -export default interface ClientOptions extends API.Types.ClientOptions { +export default interface ClientOptions extends API.ClientOptions { restAgentOptions?: RestAgentOptions; pushFullWait?: boolean; agents?: Record; } -export type DeprecatedClientOptions = Modify< - ClientOptions, - { - host?: string; - wsHost?: string; - queueEvents?: boolean; - promises?: boolean; - /** - * This option dates back to the initial commit of the repo but was never in the specification and sounds like nobody is depending on it; Paddy said we can remove in v2 (see https://ably-real-time.slack.com/archives/CURL4U2FP/p1709909310332169?thread_ts=1709908997.753599&cid=CURL4U2FP) - */ - headers?: Record; - } ->; - export type NormalisedClientOptions = Modify< - DeprecatedClientOptions, + ClientOptions, { realtimeHost: string; restHost: string; @@ -36,5 +23,6 @@ export type NormalisedClientOptions = Modify< timeouts: Record; maxMessageSize: number; connectivityCheckParams: Record | null; + headers: Record; } >; diff --git a/src/common/types/IBufferUtils.ts b/src/common/types/IBufferUtils.ts index e98c9bccbd..efa1b51209 100644 --- a/src/common/types/IBufferUtils.ts +++ b/src/common/types/IBufferUtils.ts @@ -1,10 +1,7 @@ -import { TypedArray } from './IPlatformConfig'; - -export default interface IBufferUtils { +export default interface IBufferUtils { base64CharSet: string; hexCharSet: string; isBuffer: (buffer: unknown) => buffer is Bufferlike; - isArrayBuffer: (buffer: unknown) => buffer is ArrayBuffer; // On browser this returns a Uint8Array, on node a Buffer toBuffer: (buffer: Bufferlike) => ToBufferOutput; toArrayBuffer: (buffer: Bufferlike) => ArrayBuffer; @@ -14,7 +11,8 @@ export default interface IBufferUtils Output; utf8Encode: (string: string) => Output; utf8Decode: (buffer: Bufferlike) => string; - bufferCompare: (buffer1: ComparableBuffer, buffer2: ComparableBuffer) => number; + areBuffersEqual: (buffer1: Bufferlike, buffer2: Bufferlike) => boolean; byteLength: (buffer: Bufferlike) => number; - typedArrayToBuffer: (typedArray: TypedArray) => Bufferlike; + arrayBufferViewToBuffer: (arrayBufferView: ArrayBufferView) => Bufferlike; + hmacSha256(message: Bufferlike, key: Bufferlike): Output; } diff --git a/src/common/types/ICipher.ts b/src/common/types/ICipher.ts new file mode 100644 index 0000000000..c508a6cca7 --- /dev/null +++ b/src/common/types/ICipher.ts @@ -0,0 +1,5 @@ +export default interface ICipher { + algorithm: string; + encrypt: (plaintext: InputPlaintext) => Promise; + decrypt: (ciphertext: InputCiphertext) => Promise; +} diff --git a/src/common/types/ICryptoStatic.ts b/src/common/types/ICryptoStatic.ts new file mode 100644 index 0000000000..38648449c2 --- /dev/null +++ b/src/common/types/ICryptoStatic.ts @@ -0,0 +1,23 @@ +import * as API from '../../../ably'; +import ICipher from './ICipher'; + +export type IGetCipherParams = (API.CipherParams | API.CipherParamOptions) & { iv?: IV }; +export interface IGetCipherReturnValue { + cipher: Cipher; + cipherParams: API.CipherParams; +} + +export default interface ICryptoStatic + extends API.Crypto { + getCipher( + params: IGetCipherParams, + ): IGetCipherReturnValue>; +} + +/* + A less strongly typed version of ICryptoStatic to use until we + can make Platform a generic type (see comment there). + */ +export interface IUntypedCryptoStatic extends API.Crypto { + getCipher(params: any): any; +} diff --git a/src/common/types/IDefaults.d.ts b/src/common/types/IDefaults.d.ts index 7c4db18aaf..5e2e12d37d 100644 --- a/src/common/types/IDefaults.d.ts +++ b/src/common/types/IDefaults.d.ts @@ -1,12 +1,9 @@ -import TransportNames from '../constants/TransportNames'; +import TransportName from '../constants/TransportName'; import { RestAgentOptions } from './ClientOptions'; export default interface IDefaults { connectivityCheckUrl: string; - jsonpInternetUpUrl?: string; - defaultTransports: TransportNames[]; - baseTransportOrder: TransportNames[]; - transportPreferenceOrder: TransportNames[]; - upgradeTransports: TransportNames[]; + wsConnectivityUrl: string; + defaultTransports: TransportName[]; restAgentOptions?: RestAgentOptions; } diff --git a/src/common/types/IPlatformConfig.d.ts b/src/common/types/IPlatformConfig.d.ts index 4b4d9091e1..7ceeddc1c8 100644 --- a/src/common/types/IPlatformConfig.d.ts +++ b/src/common/types/IPlatformConfig.d.ts @@ -1,53 +1,40 @@ -export type TypedArray = - | Int8Array - | Uint8Array - | Int16Array - | Uint16Array - | Int32Array - | Uint32Array - | Uint8ClampedArray - | Float32Array - | Float64Array; - -interface MsgPack { - encode(value: any, sparse?: boolean): Buffer | ArrayBuffer | undefined; - decode(buffer: Buffer): any; -} - -export interface IPlatformConfig { +/** + * Interface for common config properties shared between all platforms and that are relevant for all platforms. + * + * These properties must always be required and set for each platform. + */ +export interface ICommonPlatformConfig { agent: string; logTimestamps: boolean; binaryType: BinaryType; WebSocket: typeof WebSocket | typeof import('ws'); useProtocolHeartbeats: boolean; - createHmac: - | ((algorithm: string, key: import('crypto').BinaryLike | import('crypto').KeyObject) => import('crypto').Hmac) - | null; - msgpack: MsgPack; supportsBinary: boolean; preferBinary: boolean; nextTick: process.nextTick; inspect: (value: unknown) => string; stringByteSize: Buffer.byteLength; + getRandomArrayBuffer: (byteLength: number) => Promise; +} + +/** + * Interface for platform specific config properties that do make sense on some platforms but not on others. + * + * These properties should always be optional, so that only relevant platforms would set them. + */ +export interface ISpecificPlatformConfig { addEventListener?: typeof window.addEventListener | typeof global.addEventListener | null; - Promise: typeof Promise; - getRandomValues?: (arr: TypedArray, callback?: (error?: Error | null) => void) => void; userAgent?: string | null; inherits?: typeof import('util').inherits; currentUrl?: string; - noUpgrade?: boolean | string; fetchSupported?: boolean; xhrSupported?: boolean; - jsonpSupported?: boolean; allowComet?: boolean; - streamingSupported?: boolean; ArrayBuffer?: typeof ArrayBuffer | false; atob?: typeof atob | null; TextEncoder?: typeof TextEncoder; TextDecoder?: typeof TextDecoder; - getRandomWordArray?: ( - byteLength: number, - callback: (err: Error, result: boolean | CryptoJS.lib.WordArray) => void - ) => void; isWebworker?: boolean; } + +export type IPlatformConfig = ICommonPlatformConfig & ISpecificPlatformConfig; diff --git a/src/common/types/channel.d.ts b/src/common/types/channel.d.ts index 36b8c1c964..072c814a0a 100644 --- a/src/common/types/channel.d.ts +++ b/src/common/types/channel.d.ts @@ -1,6 +1,6 @@ import * as API from '../../../ably'; -export interface ChannelOptions extends API.Types.ChannelOptions { +export interface ChannelOptions extends API.ChannelOptions { channelCipher?: { algorithm: string; encrypt: Function; diff --git a/src/common/types/crypto-js.d.ts b/src/common/types/crypto-js.d.ts deleted file mode 100644 index 8a0c9f9e4b..0000000000 --- a/src/common/types/crypto-js.d.ts +++ /dev/null @@ -1,27 +0,0 @@ -declare module 'crypto-js/build/enc-base64' { - import CryptoJS from 'crypto-js'; - export const parse: typeof CryptoJS.enc.Base64.parse; - export const stringify: typeof CryptoJS.enc.Base64.stringify; -} - -declare module 'crypto-js/build/enc-hex' { - import CryptoJS from 'crypto-js'; - export const parse: typeof CryptoJS.enc.Hex.parse; - export const stringify: typeof CryptoJS.enc.Hex.stringify; -} - -declare module 'crypto-js/build/enc-utf8' { - import CryptoJS from 'crypto-js'; - export const parse: typeof CryptoJS.enc.Utf8.parse; - export const stringify: typeof CryptoJS.enc.Utf8.stringify; -} - -declare module 'crypto-js/build/lib-typedarrays' { - import CryptoJS from 'crypto-js'; - export default CryptoJS.lib.WordArray; -} - -declare module 'crypto-js/build/hmac-sha256' { - import CryptoJS from 'crypto-js'; - export default CryptoJS.HmacSHA256; -} diff --git a/src/common/types/cryptoDataTypes.ts b/src/common/types/cryptoDataTypes.ts new file mode 100644 index 0000000000..aed70f6489 --- /dev/null +++ b/src/common/types/cryptoDataTypes.ts @@ -0,0 +1,25 @@ +/** + Allows us to derive the generic type arguments for a platform’s `ICryptoStatic` implementation, given other properties of the platform. + */ +export namespace CryptoDataTypes { + /** + The type of initialization vector that the platform is expected to be able to use when creating a cipher. + */ + export type IV = BufferUtilsOutput; + + /** + The type of plaintext that the platform is expected to be able to encrypt. + + - `Bufferlike`: The `Bufferlike` of the platform’s `IBufferUtils` implementation. + - `BufferUtilsOutput`: The `Output` of the platform’s `IBufferUtils` implementation. + */ + export type InputPlaintext = Bufferlike | BufferUtilsOutput; + + /** + The type of ciphertext that the platform is expected to be able to decrypt. + + - `MessagePackBinaryType`: The type to which this platform’s MessagePack implementation deserializes elements of the `bin` or `ext` type. + - `BufferUtilsOutput`: The `Output` of the platform’s `IBufferUtils` implementation. + */ + export type InputCiphertext = MessagePackBinaryType | BufferUtilsOutput; +} diff --git a/src/common/types/http.d.ts b/src/common/types/http.d.ts deleted file mode 100644 index c3f454d475..0000000000 --- a/src/common/types/http.d.ts +++ /dev/null @@ -1,64 +0,0 @@ -import HttpMethods from '../constants/HttpMethods'; -import Rest from '../lib/client/rest'; -import ErrorInfo from '../lib/types/errorinfo'; -import { Agents } from 'got'; - -export type PathParameter = string | ((host: string) => string); -export type RequestCallback = ( - error?: ErrnoException | IPartialErrorInfo | null, - body?: unknown, - headers?: IncomingHttpHeaders, - unpacked?: boolean, - statusCode?: number -) => void; -export type RequestParams = Record | null; - -export declare class IHttp { - constructor(options: NormalisedClientOptions); - static methods: Array; - static methodsWithBody: Array; - static methodsWithoutBody: Array; - supportsAuthHeaders: boolean; - supportsLinkHeaders: boolean; - agent?: Agents | null; - options: NormalisedClientOptions; - - Request?: ( - method: HttpMethods, - rest: Rest | null, - uri: string, - headers: Record | null, - params: RequestParams, - body: unknown, - callback: RequestCallback - ) => void; - _getHosts: (client: Rest | Realtime) => string[]; - do( - method: HttpMethods, - rest: Rest | null, - path: PathParameter, - headers: Record | null, - body: unknown, - params: RequestParams, - callback?: RequestCallback - ): void; - doUri( - method: HttpMethods, - rest: Rest | null, - uri: string, - headers: Record | null, - body: unknown, - params: RequestParams, - callback?: RequestCallback - ): void; - checkConnectivity?: (callback: (err?: ErrorInfo | null, connected?: boolean) => void) => void; -} - -export interface ErrnoException extends Error { - errno?: number; - code?: string; - path?: string; - syscall?: string; - stack?: string; - statusCode: number; -} diff --git a/src/common/types/http.ts b/src/common/types/http.ts new file mode 100644 index 0000000000..6cda771048 --- /dev/null +++ b/src/common/types/http.ts @@ -0,0 +1,251 @@ +import Defaults from 'common/lib/util/defaults'; +import Platform from 'common/platform'; +import BaseRealtime from 'common/lib/client/baserealtime'; +import HttpMethods from '../constants/HttpMethods'; +import BaseClient from '../lib/client/baseclient'; +import ErrorInfo, { IPartialErrorInfo } from '../lib/types/errorinfo'; +import Logger from 'common/lib/util/logger'; +import * as Utils from 'common/lib/util/utils'; + +export type PathParameter = string | ((host: string) => string); +export type ResponseHeaders = Partial>; +export type RequestResultError = ErrnoException | IPartialErrorInfo; + +/** + * The `body`, `headers`, `unpacked`, and `statusCode` properties of a `RequestResult` may be populated even if its `error` property is non-null. + */ +export type RequestResult = { + error: RequestResultError | null; + body?: unknown; + headers?: ResponseHeaders; + unpacked?: boolean; + statusCode?: number; +}; + +export type RequestParams = Record | null; +export type RequestBody = + | Buffer // only on Node + | ArrayBuffer // only on web + | string; + +export interface IPlatformHttpStatic { + new (client?: BaseClient): IPlatformHttp; + methods: Array; + methodsWithBody: Array; + methodsWithoutBody: Array; +} + +export interface IPlatformHttp { + supportsAuthHeaders: boolean; + supportsLinkHeaders: boolean; + + /** + * This method should not throw any errors; rather, it should communicate any error by populating the {@link RequestResult.error} property of the returned {@link RequestResult}. + */ + doUri( + method: HttpMethods, + uri: string, + headers: Record | null, + body: RequestBody | null, + params: RequestParams, + ): Promise; + + checkConnectivity?: () => Promise; + + /** + * @param error An error from the {@link RequestResult.error} property of a result returned by {@link doUri}. + */ + shouldFallback(error: RequestResultError): boolean; +} + +export function paramString(params: Record | null) { + const paramPairs = []; + if (params) { + for (const needle in params) { + paramPairs.push(needle + '=' + params[needle]); + } + } + return paramPairs.join('&'); +} + +export function appendingParams(uri: string, params: Record | null) { + return uri + (params ? '?' : '') + paramString(params); +} + +function logResult(result: RequestResult, method: HttpMethods, uri: string, params: Record | null) { + if (result.error) { + Logger.logActionNoStrip( + Logger.LOG_MICRO, + 'Http.' + method + '()', + 'Received Error; ' + appendingParams(uri, params) + '; Error: ' + Utils.inspectError(result.error), + ); + } else { + Logger.logActionNoStrip( + Logger.LOG_MICRO, + 'Http.' + method + '()', + 'Received; ' + + appendingParams(uri, params) + + '; Headers: ' + + paramString(result.headers as Record) + + '; StatusCode: ' + + result.statusCode + + '; Body' + + (Platform.BufferUtils.isBuffer(result.body) + ? ' (Base64): ' + Platform.BufferUtils.base64Encode(result.body) + : ': ' + result.body), + ); + } +} + +function logRequest(method: HttpMethods, uri: string, body: RequestBody | null, params: RequestParams) { + if (Logger.shouldLog(Logger.LOG_MICRO)) { + Logger.logActionNoStrip( + Logger.LOG_MICRO, + 'Http.' + method + '()', + 'Sending; ' + + appendingParams(uri, params) + + '; Body' + + (Platform.BufferUtils.isBuffer(body) ? ' (Base64): ' + Platform.BufferUtils.base64Encode(body) : ': ' + body), + ); + } +} + +export class Http { + private readonly platformHttp: IPlatformHttp; + checkConnectivity?: () => Promise; + + constructor(private readonly client?: BaseClient) { + this.platformHttp = new Platform.Http(client); + + this.checkConnectivity = this.platformHttp.checkConnectivity + ? () => this.platformHttp.checkConnectivity!() + : undefined; + } + + get supportsAuthHeaders() { + return this.platformHttp.supportsAuthHeaders; + } + + get supportsLinkHeaders() { + return this.platformHttp.supportsLinkHeaders; + } + + _getHosts(client: BaseClient) { + /* If we're a connected realtime client, try the endpoint we're connected + * to first -- but still have fallbacks, being connected is not an absolute + * guarantee that a datacenter has free capacity to service REST requests. */ + const connection = (client as BaseRealtime).connection, + connectionHost = connection && connection.connectionManager.host; + + if (connectionHost) { + return [connectionHost].concat(Defaults.getFallbackHosts(client.options)); + } + + return Defaults.getHosts(client.options); + } + + /** + * This method will not throw any errors; rather, it will communicate any error by populating the {@link RequestResult.error} property of the returned {@link RequestResult}. + */ + async do( + method: HttpMethods, + path: PathParameter, + headers: Record | null, + body: RequestBody | null, + params: RequestParams, + ): Promise { + try { + /* Unlike for doUri, the presence of `this.client` here is mandatory, as it's used to generate the hosts */ + const client = this.client; + if (!client) { + return { error: new ErrorInfo('http.do called without client', 50000, 500) }; + } + + const uriFromHost = + typeof path === 'function' + ? path + : function (host: string) { + return client.baseUri(host) + path; + }; + + const currentFallback = client._currentFallback; + if (currentFallback) { + if (currentFallback.validUntil > Date.now()) { + /* Use stored fallback */ + const result = await this.doUri(method, uriFromHost(currentFallback.host), headers, body, params); + if (result.error && this.platformHttp.shouldFallback(result.error as ErrnoException)) { + /* unstore the fallback and start from the top with the default sequence */ + client._currentFallback = null; + return this.do(method, path, headers, body, params); + } + return result; + } else { + /* Fallback expired; remove it and fallthrough to normal sequence */ + client._currentFallback = null; + } + } + + const hosts = this._getHosts(client); + + /* see if we have one or more than one host */ + if (hosts.length === 1) { + return this.doUri(method, uriFromHost(hosts[0]), headers, body, params); + } + + const tryAHost = async (candidateHosts: Array, persistOnSuccess?: boolean): Promise => { + const host = candidateHosts.shift(); + const result = await this.doUri(method, uriFromHost(host as string), headers, body, params); + if (result.error && this.platformHttp.shouldFallback(result.error as ErrnoException) && candidateHosts.length) { + return tryAHost(candidateHosts, true); + } + if (persistOnSuccess) { + /* RSC15f */ + client._currentFallback = { + host: host as string, + validUntil: Date.now() + client.options.timeouts.fallbackRetryTimeout, + }; + } + return result; + }; + return tryAHost(hosts); + } catch (err) { + // Handle any unexpected error, to ensure we always meet our contract of not throwing any errors + return { error: new ErrorInfo(`Unexpected error in Http.do: ${Utils.inspectError(err)}`, 500, 50000) }; + } + } + + /** + * This method will not throw any errors; rather, it will communicate any error by populating the {@link RequestResult.error} property of the returned {@link RequestResult}. + */ + async doUri( + method: HttpMethods, + uri: string, + headers: Record | null, + body: RequestBody | null, + params: RequestParams, + ): Promise { + try { + logRequest(method, uri, body, params); + + const result = await this.platformHttp.doUri(method, uri, headers, body, params); + + if (Logger.shouldLog(Logger.LOG_MICRO)) { + logResult(result, method, uri, params); + } + + return result; + } catch (err) { + // Handle any unexpected error, to ensure we always meet our contract of not throwing any errors + return { error: new ErrorInfo(`Unexpected error in Http.doUri: ${Utils.inspectError(err)}`, 500, 50000) }; + } + } +} + +export interface ErrnoException extends Error { + errno?: number; + code?: string; + path?: string; + syscall?: string; + stack?: string; + statusCode: number; +} diff --git a/src/common/types/msgpack.ts b/src/common/types/msgpack.ts new file mode 100644 index 0000000000..0667def08a --- /dev/null +++ b/src/common/types/msgpack.ts @@ -0,0 +1,4 @@ +export interface MsgPack { + encode(value: any, sparse?: boolean): Buffer | ArrayBuffer | undefined; + decode(buffer: Buffer): any; +} diff --git a/src/platform/nativescript/config.js b/src/platform/nativescript/config.js index 5da256046d..db739aaf30 100644 --- a/src/platform/nativescript/config.js +++ b/src/platform/nativescript/config.js @@ -1,5 +1,4 @@ /* eslint-disable no-undef */ -import msgpack from '../web/lib/util/msgpack'; require('nativescript-websockets'); var randomBytes; @@ -21,16 +20,11 @@ if (global.android) { var Config = { agent: 'nativescript', logTimestamps: true, - noUpgrade: false, binaryType: 'arraybuffer', WebSocket: WebSocket, xhrSupported: XMLHttpRequest, allowComet: true, - jsonpSupported: false, - streamingSupported: false, useProtocolHeartbeats: true, - createHmac: null, - msgpack: msgpack, supportsBinary: typeof TextDecoder !== 'undefined' && TextDecoder, preferBinary: false, // Motivation as on web; see `preferBinary` comment there. ArrayBuffer: ArrayBuffer, @@ -49,15 +43,9 @@ var Config = { }, TextEncoder: global.TextEncoder, TextDecoder: global.TextDecoder, - Promise: global.Promise, - getRandomValues: function (arr, callback) { - var bytes = randomBytes(arr.length); - for (var i = 0; i < arr.length; i++) { - arr[i] = bytes[i]; - } - if (callback) { - callback(null); - } + getRandomArrayBuffer: async function (byteLength) { + var bytes = randomBytes(byteLength); + return bytes; }, }; diff --git a/src/platform/nativescript/index.ts b/src/platform/nativescript/index.ts index fb4cfea6d8..5a57dbe073 100644 --- a/src/platform/nativescript/index.ts +++ b/src/platform/nativescript/index.ts @@ -1,14 +1,15 @@ // Common -import Rest from '../../common/lib/client/rest'; -import Realtime from '../../common/lib/client/realtime'; +import { DefaultRest } from '../../common/lib/client/defaultrest'; +import { DefaultRealtime } from '../../common/lib/client/defaultrealtime'; import Platform from '../../common/platform'; import ErrorInfo from '../../common/lib/types/errorinfo'; +import { fromDeserializedIncludingDependencies as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage'; // Platform Specific import BufferUtils from '../web/lib/util/bufferutils'; // @ts-ignore -import CryptoFactory from '../web/lib/util/crypto'; -import Http from '../web/lib/util/http'; +import { createCryptoClass } from '../web/lib/util/crypto'; +import Http from '../web/lib/http/http'; // @ts-ignore import Config from './config'; // @ts-ignore @@ -19,8 +20,9 @@ import { getDefaults } from '../../common/lib/util/defaults'; import WebStorage from './lib/util/webstorage'; import PlatformDefaults from '../web/lib/util/defaults'; import msgpack from '../web/lib/util/msgpack'; +import { defaultBundledRequestImplementations } from '../web/lib/http/request'; -const Crypto = CryptoFactory(Config, BufferUtils); +const Crypto = createCryptoClass(Config, BufferUtils); Platform.Crypto = Crypto; Platform.BufferUtils = BufferUtils; @@ -29,8 +31,12 @@ Platform.Config = Config; Platform.Transports = Transports; Platform.WebStorage = WebStorage; -Rest.Crypto = Crypto; -Realtime.Crypto = Crypto; +for (const clientClass of [DefaultRest, DefaultRealtime]) { + clientClass.Crypto = Crypto; + clientClass._MsgPack = msgpack; +} + +Http.bundledRequestImplementations = defaultBundledRequestImplementations; Logger.initLogHandlers(); @@ -43,7 +49,8 @@ if (Platform.Config.agent) { export default { ErrorInfo, - Rest, - Realtime, + Rest: DefaultRest, + Realtime: DefaultRealtime, msgpack, + protocolMessageFromDeserialized, }; diff --git a/src/platform/nativescript/lib/util/webstorage.js b/src/platform/nativescript/lib/util/webstorage.js index cfb874d02c..90d169a615 100644 --- a/src/platform/nativescript/lib/util/webstorage.js +++ b/src/platform/nativescript/lib/util/webstorage.js @@ -1,4 +1,3 @@ -import * as Utils from '../../../../common/lib/util/utils'; import appSettings from '@nativescript/core/application-settings'; var WebStorage = (function () { @@ -7,7 +6,7 @@ var WebStorage = (function () { function set(name, value, ttl) { var wrappedValue = { value: value }; if (ttl) { - wrappedValue.expires = Utils.now() + ttl; + wrappedValue.expires = Date.now() + ttl; } return appSettings.setString(name, JSON.stringify(wrappedValue)); } @@ -16,7 +15,7 @@ var WebStorage = (function () { var rawItem = appSettings.getString(name); if (!rawItem) return null; var wrappedValue = JSON.parse(rawItem); - if (wrappedValue.expires && wrappedValue.expires < Utils.now()) { + if (wrappedValue.expires && wrappedValue.expires < Date.now()) { appSettings.remove(name); return null; } diff --git a/src/platform/nodejs/config.ts b/src/platform/nodejs/config.ts index 86d0a7e5b7..fc116ce086 100644 --- a/src/platform/nodejs/config.ts +++ b/src/platform/nodejs/config.ts @@ -1,4 +1,4 @@ -import { TypedArray, IPlatformConfig } from '../../common/types/IPlatformConfig'; +import { IPlatformConfig } from '../../common/types/IPlatformConfig'; import crypto from 'crypto'; import WebSocket from 'ws'; import util from 'util'; @@ -10,8 +10,6 @@ const Config: IPlatformConfig = { binaryType: 'nodebuffer' as BinaryType, WebSocket, useProtocolHeartbeats: false, - createHmac: crypto.createHmac, - msgpack: require('@ably/msgpack-js'), supportsBinary: true, preferBinary: true, nextTick: process.nextTick, @@ -19,14 +17,9 @@ const Config: IPlatformConfig = { stringByteSize: Buffer.byteLength, inherits: util.inherits, addEventListener: null, - getRandomValues: function (arr: TypedArray, callback?: (err?: Error | null) => void): void { - const bytes = crypto.randomBytes(arr.length); - arr.set(bytes); - if (callback) { - callback(null); - } + getRandomArrayBuffer: async function (byteLength: number): Promise { + return util.promisify(crypto.randomBytes)(byteLength); }, - Promise: global && global.Promise, }; export default Config; diff --git a/src/platform/nodejs/index.ts b/src/platform/nodejs/index.ts index a819eccd86..057d412e66 100644 --- a/src/platform/nodejs/index.ts +++ b/src/platform/nodejs/index.ts @@ -1,13 +1,14 @@ // Common -import Rest from '../../common/lib/client/rest'; -import Realtime from '../../common/lib/client/realtime'; +import { DefaultRest } from '../../common/lib/client/defaultrest'; +import { DefaultRealtime } from '../../common/lib/client/defaultrealtime'; import Platform from '../../common/platform'; import ErrorInfo from '../../common/lib/types/errorinfo'; +import { fromDeserializedIncludingDependencies as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage'; // Platform Specific import BufferUtils from './lib/util/bufferutils'; // @ts-ignore -import Crypto from './lib/util/crypto'; +import { createCryptoClass } from './lib/util/crypto'; import Http from './lib/util/http'; import Config from './config'; // @ts-ignore @@ -15,6 +16,9 @@ import Transports from './lib/transport'; import Logger from '../../common/lib/util/logger'; import { getDefaults } from '../../common/lib/util/defaults'; import PlatformDefaults from './lib/util/defaults'; +import msgpack = require('@ably/msgpack-js'); + +const Crypto = createCryptoClass(BufferUtils); Platform.Crypto = Crypto; Platform.BufferUtils = BufferUtils as typeof Platform.BufferUtils; @@ -23,8 +27,10 @@ Platform.Config = Config; Platform.Transports = Transports; Platform.WebStorage = null; -Rest.Crypto = Crypto; -Realtime.Crypto = Crypto; +for (const clientClass of [DefaultRest, DefaultRealtime]) { + clientClass.Crypto = Crypto; + clientClass._MsgPack = msgpack; +} Logger.initLogHandlers(); @@ -35,9 +41,10 @@ if (Platform.Config.agent) { Platform.Defaults.agent += ' ' + Platform.Config.agent; } -export default { +module.exports = { ErrorInfo, - Rest, - Realtime, + Rest: DefaultRest, + Realtime: DefaultRealtime, msgpack: null, + protocolMessageFromDeserialized, }; diff --git a/src/platform/nodejs/lib/transport/index.js b/src/platform/nodejs/lib/transport/index.js deleted file mode 100644 index 2d9be41828..0000000000 --- a/src/platform/nodejs/lib/transport/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import NodeCometTransport from './nodecomettransport'; - -export default [NodeCometTransport]; diff --git a/src/platform/nodejs/lib/transport/index.ts b/src/platform/nodejs/lib/transport/index.ts new file mode 100644 index 0000000000..7cd942bd67 --- /dev/null +++ b/src/platform/nodejs/lib/transport/index.ts @@ -0,0 +1,12 @@ +import { TransportNames } from 'common/constants/TransportName'; +import NodeCometTransport from './nodecomettransport'; +import { default as WebSocketTransport } from '../../../../common/lib/transport/websockettransport'; +import { TransportCtor } from 'common/lib/transport/transport'; + +export default { + order: [TransportNames.Comet], + bundledImplementations: { + [TransportNames.WebSocket]: WebSocketTransport as TransportCtor, + [TransportNames.Comet]: NodeCometTransport as unknown as TransportCtor, + }, +}; diff --git a/src/platform/nodejs/lib/transport/nodecomettransport.d.ts b/src/platform/nodejs/lib/transport/nodecomettransport.d.ts new file mode 100644 index 0000000000..a069964ba4 --- /dev/null +++ b/src/platform/nodejs/lib/transport/nodecomettransport.d.ts @@ -0,0 +1,7 @@ +import Transport from '../../../../common/lib/transport/transport'; + +declare class NodeCometTransport extends Transport { + static isAvailable(): boolean; +} + +export default NodeCometTransport; diff --git a/src/platform/nodejs/lib/transport/nodecomettransport.js b/src/platform/nodejs/lib/transport/nodecomettransport.js index 7ba730eb10..9a4eb0a9e8 100644 --- a/src/platform/nodejs/lib/transport/nodecomettransport.js +++ b/src/platform/nodejs/lib/transport/nodecomettransport.js @@ -10,30 +10,29 @@ import http from 'http'; import https from 'https'; import url from 'url'; import util from 'util'; - -var NodeCometTransport = function (connectionManager) { - var noop = function () {}; - var shortName = 'comet'; - - /* - * A transport to use with nodejs - * to simulate an XHR transport for test purposes - */ - function NodeCometTransport(connectionManager, auth, params) { - CometTransport.call(this, connectionManager, auth, params); +import { TransportNames } from '../../../../common/constants/TransportName'; + +var noop = function () {}; +var shortName = TransportNames.Comet; + +/* + * A transport to use with nodejs + * to simulate an XHR transport for test purposes + */ +class NodeCometTransport extends CometTransport { + constructor(connectionManager, auth, params) { + super(connectionManager, auth, params); this.httpAgent = null; this.httpsAgent = null; this.pendingRequests = 0; this.shortName = shortName; } - util.inherits(NodeCometTransport, CometTransport); - NodeCometTransport.isAvailable = function () { + static isAvailable() { return true; - }; - connectionManager.supportedTransports[shortName] = NodeCometTransport; + } - NodeCometTransport.prototype.toString = function () { + toString() { return ( 'NodeCometTransport; uri=' + this.baseUri + @@ -44,58 +43,61 @@ var NodeCometTransport = function (connectionManager) { '; stream=' + this.stream ); - }; + } - NodeCometTransport.prototype.getAgent = function (tls) { + getAgent(tls) { var prop = tls ? 'httpsAgent' : 'httpAgent', agent = this[prop]; if (!agent) agent = this[prop] = new (tls ? https : http).Agent({ keepAlive: true }); return agent; - }; + } - NodeCometTransport.prototype.dispose = function () { + dispose() { var self = this; this.onceNoPending(function () { if (self.httpAgent) self.httpAgent.destroy(); if (self.httpsAgent) self.httpsAgent.destroy(); }); CometTransport.prototype.dispose.call(this); - }; + } /* valid in non-streaming mode only, or data only contains last update */ - NodeCometTransport.prototype.request = function (uri, params, body, requestMode, callback) { + request(uri, params, body, requestMode, callback) { var req = this.createRequest(uri, params, body, requestMode); req.once('complete', callback); req.exec(); return req; - }; + } - NodeCometTransport.prototype.createRequest = function (uri, headers, params, body, requestMode) { + createRequest(uri, headers, params, body, requestMode) { return new Request(uri, headers, params, body, requestMode, this.format, this.timeouts, this); - }; + } - NodeCometTransport.prototype.addPending = function () { + addPending() { ++this.pendingRequests; - }; + } - NodeCometTransport.prototype.removePending = function () { + removePending() { if (--this.pendingRequests <= 0) { this.emit('nopending'); } - }; + } - NodeCometTransport.prototype.onceNoPending = function (listener) { + onceNoPending(listener) { if (this.pendingRequests == 0) { listener(); return; } this.once('nopending', listener); - }; + } +} + +class Request extends EventEmitter { + constructor(uri, headers, params, body, requestMode, format, timeouts, transport) { + super(); - function Request(uri, headers, params, body, requestMode, format, timeouts, transport) { - EventEmitter.call(this); if (typeof uri == 'string') uri = url.parse(uri); var tls = uri.protocol == 'https:'; this.client = tls ? https : http; @@ -130,9 +132,8 @@ var NodeCometTransport = function (connectionManager) { }); if (transport) requestOptions.agent = transport.getAgent(tls); } - Utils.inherits(Request, EventEmitter); - Request.prototype.exec = function () { + exec() { var timeout = this.requestMode == XHRStates.REQ_SEND ? this.timeouts.httpRequestTimeout : this.timeouts.recvTimeout, self = this; @@ -148,7 +149,7 @@ var NodeCometTransport = function (connectionManager) { clearTimeout(timer); self.timer = null; self.complete(err); - }) + }), ); req.on('response', function (res) { @@ -168,7 +169,7 @@ var NodeCometTransport = function (connectionManager) { (self.onResError = function (err) { err = new PartialErrorInfo('Response error: ' + err.message, null, 400); self.complete(err); - }) + }), ); self.res = res; @@ -183,9 +184,9 @@ var NodeCometTransport = function (connectionManager) { if (this.transport) this.transport.addPending(); req.end(this.body); - }; + } - Request.prototype.readStream = function () { + readStream() { var res = this.res, self = this; @@ -226,7 +227,7 @@ var NodeCometTransport = function (connectionManager) { /* the remaining new chunks are complete */ newChunks.map(onChunk); - }) + }), ); res.on('end', function () { @@ -235,9 +236,9 @@ var NodeCometTransport = function (connectionManager) { self.complete(); }); }); - }; + } - Request.prototype.readFully = function () { + readFully() { var res = this.res, chunks = [], self = this; @@ -264,7 +265,7 @@ var NodeCometTransport = function (connectionManager) { * is contains an error action (hence the nonsuccess statuscode), we can * consider the request to have succeeded, just pass it on to * onProtocolMessage to decide what to do */ - if (statusCode < 400 || Utils.isArray(body)) { + if (statusCode < 400 || Array.isArray(body)) { self.complete(null, body); return; } @@ -274,15 +275,15 @@ var NodeCometTransport = function (connectionManager) { err = new PartialErrorInfo( 'Error response received from server: ' + statusCode + ', body was: ' + util.inspect(body), null, - statusCode + statusCode, ); } self.complete(err); }); }); - }; + } - Request.prototype.complete = function (err, body) { + complete(err, body) { if (!this.requestComplete) { this.requestComplete = true; if (body) this.emit('data', body); @@ -297,9 +298,9 @@ var NodeCometTransport = function (connectionManager) { this.transport.removePending(); } } - }; + } - Request.prototype.abort = function () { + abort() { Logger.logAction(Logger.LOG_MINOR, 'NodeCometTransport.Request.abort()', ''); var timer = this.timer; if (timer) { @@ -315,9 +316,7 @@ var NodeCometTransport = function (connectionManager) { this.req = null; } this.complete({ statusCode: 400, code: 80003, message: 'Cancelled' }); - }; - - return NodeCometTransport; -}; + } +} export default NodeCometTransport; diff --git a/src/platform/nodejs/lib/util/bufferutils.ts b/src/platform/nodejs/lib/util/bufferutils.ts index c972886e40..e1c6fbcb67 100644 --- a/src/platform/nodejs/lib/util/bufferutils.ts +++ b/src/platform/nodejs/lib/util/bufferutils.ts @@ -1,12 +1,11 @@ -import { TypedArray } from 'common/types/IPlatformConfig'; import IBufferUtils from 'common/types/IBufferUtils'; +import crypto from 'crypto'; -export type Bufferlike = Buffer | ArrayBuffer | TypedArray; +export type Bufferlike = Buffer | ArrayBuffer | ArrayBufferView; export type Output = Buffer; export type ToBufferOutput = Buffer; -export type ComparableBuffer = Buffer; -class BufferUtils implements IBufferUtils { +class BufferUtils implements IBufferUtils { base64CharSet: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; hexCharSet: string = '0123456789abcdef'; @@ -18,10 +17,9 @@ class BufferUtils implements IBufferUtils; +type InputPlaintext = CryptoDataTypes.InputPlaintext; +type OutputCiphertext = Buffer; +type InputCiphertext = CryptoDataTypes.InputCiphertext; +type OutputPlaintext = Buffer; + +var createCryptoClass = function (bufferUtils: typeof BufferUtils) { + var DEFAULT_ALGORITHM = 'aes'; + var DEFAULT_KEYLENGTH = 256; // bits + var DEFAULT_MODE = 'cbc'; + var DEFAULT_BLOCKLENGTH = 16; // bytes + + /** + * Internal: generate a buffer of secure random bytes of the given length + * @param bytes + */ + async function generateRandom(bytes: number): Promise { + return util.promisify(crypto.randomBytes)(bytes); + } + + /** + * Internal: calculate the padded length of a given plaintext + * using PKCS5. + * @param plaintextLength + * @return + */ + function getPaddedLength(plaintextLength: number) { + return (plaintextLength + DEFAULT_BLOCKLENGTH) & -DEFAULT_BLOCKLENGTH; + } + + /** + * Internal: checks that the cipherParams are a valid combination. Currently + * just checks that the calculated keyLength is a valid one for aes-cbc + */ + function validateCipherParams(params: API.CipherParams) { + if (params.algorithm === 'aes' && params.mode === 'cbc') { + if (params.keyLength === 128 || params.keyLength === 256) { + return; + } + throw new Error( + 'Unsupported key length ' + + params.keyLength + + ' for aes-cbc encryption. Encryption key must be 128 or 256 bits (16 or 32 ASCII characters)', + ); + } + } + + function normaliseBase64(string: string) { + /* url-safe base64 strings use _ and - instread of / and + */ + return string.replace('_', '/').replace('-', '+'); + } + + /** + * Internal: obtain the pkcs5 padding string for a given padded length; + */ + function filledBuffer(length: number, value: number) { + var result = Buffer.alloc(length); + result.fill(value); + return result; + } + var pkcs5Padding = [filledBuffer(16, 16)]; + for (var i = 1; i <= 16; i++) pkcs5Padding.push(filledBuffer(i, i)); + + /** + * A class encapsulating the client-specifiable parameters for + * the cipher. + * + * algorithm is the name of the algorithm in the default system provider, + * or the lower-cased version of it; eg "aes" or "AES". + * + * Clients may instance a CipherParams directly and populate it, or may + * query the implementation to obtain a default system CipherParams. + */ + class CipherParams implements API.CipherParams { + algorithm: string; + keyLength: number; + mode: string; + key: NodeCipherKey; + iv: unknown; + + constructor(algorithm: string, keyLength: number, mode: string, key: NodeCipherKey) { + this.algorithm = algorithm; + this.keyLength = keyLength; + this.mode = mode; + this.key = key; + this.iv = null; + } + } + + function isInstCipherParams(params: API.CipherParams | API.CipherParamOptions): params is API.CipherParams { + /* In node, can't use instanceof CipherParams due to the vm context problem (see + * https://github.com/nwjs/nw.js/wiki/Differences-of-JavaScript-contexts). + * So just test for presence of all necessary attributes */ + return !!(params.algorithm && params.key && params.keyLength && params.mode); + } + + /** + * Utility classes and interfaces for message payload encryption. + * + * This class supports AES/CBC/PKCS5 with a default keylength of 128 bits + * but supporting other keylengths. Other algorithms and chaining modes are + * not supported directly, but supportable by extending/implementing the base + * classes and interfaces here. + * + * Secure random data for creation of Initialization Vectors (IVs) and keys + * is obtained from the default system SecureRandom. Future extensions of this + * class might make the SecureRandom pluggable or at least seedable with + * client-provided entropy. + * + * Each message payload is encrypted with an IV in CBC mode, and the IV is + * concatenated with the resulting raw ciphertext to construct the "ciphertext" + * data passed to the recipient. + */ + class Crypto { + static CipherParams = CipherParams; + + /** + * Obtain a complete CipherParams instance from the provided params, filling + * in any not provided with default values, calculating a keyLength from + * the supplied key, and validating the result. + * @param params an object containing at a minimum a `key` key with value the + * key, as either a binary or a base64-encoded string. + * May optionally also contain: algorithm (defaults to AES), + * mode (defaults to 'cbc') + */ + static getDefaultParams(params: API.CipherParamOptions) { + var key: NodeCipherKey; + + if (!params.key) { + throw new Error('Crypto.getDefaultParams: a key is required'); + } + + if (typeof params.key === 'string') { + key = bufferUtils.base64Decode(normaliseBase64(params.key)); + } else if (params.key instanceof ArrayBuffer) { + key = Buffer.from(params.key); + } else { + key = params.key; + } + + var algorithm = params.algorithm || DEFAULT_ALGORITHM; + var keyLength = key.length * 8; + var mode = params.mode || DEFAULT_MODE; + var cipherParams = new CipherParams(algorithm, keyLength, mode, key); + + if (params.keyLength && params.keyLength !== cipherParams.keyLength) { + throw new Error( + 'Crypto.getDefaultParams: a keyLength of ' + + params.keyLength + + ' was specified, but the key actually has length ' + + cipherParams.keyLength, + ); + } + + validateCipherParams(cipherParams); + return cipherParams; + } + + /** + * Generate a random encryption key from the supplied keylength (or the + * default keyLength if none supplied) as a Buffer + * @param keyLength (optional) the required keyLength in bits + */ + static async generateRandomKey(keyLength?: number): Promise { + try { + return generateRandom((keyLength || DEFAULT_KEYLENGTH) / 8); + } catch (err) { + throw new ErrorInfo('Failed to generate random key: ' + (err as Error).message, 500, 50000, err as Error); + } + } + + /** + * Internal; get a ChannelCipher instance based on the given cipherParams + * @param params either a CipherParams instance or some subset of its + * fields that includes a key + */ + static getCipher(params: IGetCipherParams) { + var cipherParams = isInstCipherParams(params) ? (params as CipherParams) : this.getDefaultParams(params); + + return { + cipherParams: cipherParams, + cipher: new CBCCipher(cipherParams, params.iv ?? null), + }; + } + } + + Crypto satisfies ICryptoStatic; + + class CBCCipher implements ICipher { + algorithm: string; + key: NodeCipherKey; + iv: Buffer | null; + encryptCipher: NodeCipher | null = null; + + constructor(params: CipherParams, iv: Buffer | null) { + this.algorithm = params.algorithm + '-' + String(params.keyLength) + '-' + params.mode; + this.key = params.key; + this.iv = iv; + } + + async encrypt(plaintext: InputPlaintext): Promise { + Logger.logAction(Logger.LOG_MICRO, 'CBCCipher.encrypt()', ''); + + const iv = await this.getIv(); + if (!this.encryptCipher) { + this.encryptCipher = crypto.createCipheriv(this.algorithm, this.key, iv); + } + + var plaintextBuffer = bufferUtils.toBuffer(plaintext); + var plaintextLength = plaintextBuffer.length, + paddedLength = getPaddedLength(plaintextLength); + var cipherOut = this.encryptCipher.update( + Buffer.concat([plaintextBuffer, pkcs5Padding[paddedLength - plaintextLength]]), + ); + var ciphertext = Buffer.concat([iv, cipherOut]); + return ciphertext; + } + + async decrypt(ciphertext: InputCiphertext): Promise { + var decryptCipher = crypto.createDecipheriv(this.algorithm, this.key, ciphertext.slice(0, DEFAULT_BLOCKLENGTH)), + plaintext = decryptCipher.update(ciphertext.slice(DEFAULT_BLOCKLENGTH)), + final = decryptCipher.final(); + if (final && final.length) plaintext = Buffer.concat([plaintext, final]); + return plaintext; + } + + async getIv(): Promise { + if (this.iv) { + var iv = this.iv; + this.iv = null; + return iv; + } + + var randomBlock = await generateRandom(DEFAULT_BLOCKLENGTH); + + if (!this.encryptCipher) { + return randomBlock; + } else { + /* Since the iv for a new block is the ciphertext of the last, this + * sets a new iv (= aes(randomBlock XOR lastCipherText)) as well as + * returning it */ + return this.encryptCipher.update(randomBlock); + } + } + } + + return Crypto; +}; + +export { createCryptoClass }; diff --git a/src/platform/nodejs/lib/util/defaults.ts b/src/platform/nodejs/lib/util/defaults.ts index 5ec0f8139b..200d9e8fd6 100644 --- a/src/platform/nodejs/lib/util/defaults.ts +++ b/src/platform/nodejs/lib/util/defaults.ts @@ -1,16 +1,12 @@ import IDefaults from '../../../../common/types/IDefaults'; -import TransportNames from '../../../../common/constants/TransportNames'; +import { TransportNames } from '../../../../common/constants/TransportName'; const Defaults: IDefaults = { connectivityCheckUrl: 'https://internet-up.ably-realtime.com/is-the-internet-up.txt', + wsConnectivityUrl: 'wss://ws-up.ably-realtime.com', /* Note: order matters here: the base transport is the leftmost one in the - * intersection of baseTransportOrder and the transports clientOption that's supported. - * (For node this is the same as the transportPreferenceOrder, but for - * browsers it's different*/ + * intersection of baseTransportOrder and the transports clientOption that's supported. */ defaultTransports: [TransportNames.WebSocket], - baseTransportOrder: [TransportNames.Comet, TransportNames.WebSocket], - transportPreferenceOrder: [TransportNames.Comet, TransportNames.WebSocket], - upgradeTransports: [TransportNames.WebSocket], restAgentOptions: { maxSockets: 40, keepAlive: true }, }; diff --git a/src/platform/nodejs/lib/util/http.ts b/src/platform/nodejs/lib/util/http.ts index a6e757f37e..b78f89c286 100644 --- a/src/platform/nodejs/lib/util/http.ts +++ b/src/platform/nodejs/lib/util/http.ts @@ -1,16 +1,22 @@ import Platform from 'common/platform'; import Defaults from 'common/lib/util/defaults'; import ErrorInfo from 'common/lib/types/errorinfo'; -import { ErrnoException, IHttp, PathParameter, RequestCallback, RequestParams } from '../../../../common/types/http'; +import { + ErrnoException, + RequestBody, + IPlatformHttpStatic, + RequestResultError, + RequestParams, + RequestResult, +} from '../../../../common/types/http'; import HttpMethods from '../../../../common/constants/HttpMethods'; import got, { Response, Options, CancelableRequest, Agents } from 'got'; import http from 'http'; import https from 'https'; -import Rest from 'common/lib/client/rest'; -import Realtime from 'common/lib/client/realtime'; -import { NormalisedClientOptions, RestAgentOptions } from 'common/types/ClientOptions'; +import BaseClient from 'common/lib/client/baseclient'; +import { RestAgentOptions } from 'common/types/ClientOptions'; import { isSuccessCode } from 'common/constants/HttpStatusCodes'; -import { shallowEquals } from 'common/lib/util/utils'; +import { createMissingPluginError, shallowEquals } from 'common/lib/util/utils'; /*************************************************** * @@ -28,174 +34,31 @@ import { shallowEquals } from 'common/lib/util/utils'; const globalAgentPool: Array<{ options: RestAgentOptions; agents: Agents }> = []; -const handler = function (uri: string, params: unknown, callback?: RequestCallback) { - return function (err: ErrnoException | null, response?: Response, body?: unknown) { - if (err) { - callback?.(err); - return; - } - const statusCode = (response as Response).statusCode, - headers = (response as Response).headers; - if (statusCode >= 300) { - switch (headers['content-type']) { - case 'application/json': - body = JSON.parse(body as string); - break; - case 'application/x-msgpack': - body = Platform.Config.msgpack.decode(body as Buffer); - } - const error = (body as { error: ErrorInfo }).error - ? ErrorInfo.fromValues((body as { error: ErrorInfo }).error) - : new ErrorInfo( - (headers['x-ably-errormessage'] as string) || - 'Error response received from server: ' + statusCode + ' body was: ' + Platform.Config.inspect(body), - Number(headers['x-ably-errorcode']), - statusCode - ); - callback?.(error, body, headers, true, statusCode); - return; - } - callback?.(null, body, headers, false, statusCode); - }; -}; - -function shouldFallback(err: ErrnoException) { - const { code, statusCode } = err; - return ( - code === 'ENETUNREACH' || - code === 'EHOSTUNREACH' || - code === 'EHOSTDOWN' || - code === 'ETIMEDOUT' || - code === 'ESOCKETTIMEDOUT' || - code === 'ENOTFOUND' || - code === 'ECONNRESET' || - code === 'ECONNREFUSED' || - (statusCode >= 500 && statusCode <= 504) - ); -} - -function getHosts(client: Rest | Realtime): string[] { - /* If we're a connected realtime client, try the endpoint we're connected - * to first -- but still have fallbacks, being connected is not an absolute - * guarantee that a datacenter has free capacity to service REST requests. */ - const connection = (client as Realtime).connection; - const connectionHost = connection && connection.connectionManager.host; - - if (connectionHost) { - return [connectionHost].concat(Defaults.getFallbackHosts(client.options)); - } - - return Defaults.getHosts(client.options); -} - -const Http: typeof IHttp = class { +const Http: IPlatformHttpStatic = class { static methods = [HttpMethods.Get, HttpMethods.Delete, HttpMethods.Post, HttpMethods.Put, HttpMethods.Patch]; static methodsWithoutBody = [HttpMethods.Get, HttpMethods.Delete]; static methodsWithBody = [HttpMethods.Post, HttpMethods.Put, HttpMethods.Patch]; - agent: Agents | null = null; - _getHosts = getHosts; + private agent: Agents | null = null; supportsAuthHeaders = true; supportsLinkHeaders = true; - options: NormalisedClientOptions; + private client: BaseClient | null; - constructor(options: NormalisedClientOptions) { - this.options = options || {}; + constructor(client?: BaseClient) { + this.client = client ?? null; } - /* Unlike for doUri, the 'rest' param here is mandatory, as it's used to generate the hosts */ - do( + async doUri( method: HttpMethods, - rest: Rest, - path: PathParameter, - headers: Record | null, - body: unknown, - params: RequestParams, - callback: RequestCallback - ): void { - const uriFromHost = - typeof path === 'function' - ? path - : function (host: string) { - return rest.baseUri(host) + path; - }; - - const currentFallback = rest._currentFallback; - if (currentFallback) { - if (currentFallback.validUntil > Date.now()) { - /* Use stored fallback */ - this.doUri( - method, - rest, - uriFromHost(currentFallback.host), - headers, - body, - params, - (err?: ErrnoException | ErrorInfo | null, ...args: unknown[]) => { - if (err && shouldFallback(err as ErrnoException)) { - /* unstore the fallback and start from the top with the default sequence */ - rest._currentFallback = null; - this.do(method, rest, path, headers, body, params, callback); - return; - } - callback(err, ...args); - } - ); - return; - } else { - /* Fallback expired; remove it and fallthrough to normal sequence */ - rest._currentFallback = null; - } - } - - const hosts = getHosts(rest); - - /* see if we have one or more than one host */ - if (hosts.length === 1) { - this.doUri(method, rest, uriFromHost(hosts[0]), headers, body, params, callback); - return; - } - - const tryAHost = (candidateHosts: Array, persistOnSuccess?: boolean) => { - const host = candidateHosts.shift(); - this.doUri( - method, - rest, - uriFromHost(host as string), - headers, - body, - params, - function (err?: ErrnoException | ErrorInfo | null, ...args: unknown[]) { - if (err && shouldFallback(err as ErrnoException) && candidateHosts.length) { - tryAHost(candidateHosts, true); - return; - } - if (persistOnSuccess) { - /* RSC15f */ - rest._currentFallback = { - host: host as string, - validUntil: Date.now() + rest.options.timeouts.fallbackRetryTimeout, - }; - } - callback(err, ...args); - } - ); - }; - tryAHost(hosts); - } - - doUri( - method: HttpMethods, - rest: Rest, uri: string, headers: Record | null, - body: unknown, + body: RequestBody | null, params: RequestParams, - callback: RequestCallback - ): void { + ): Promise { /* Will generally be making requests to one or two servers exclusively * (Ably and perhaps an auth server), so for efficiency, use the * foreverAgent to keep the TCP stream alive between requests where possible */ - const agentOptions = (rest && rest.options.restAgentOptions) || (Defaults.restAgentOptions as RestAgentOptions); + const agentOptions = + (this.client && this.client.options.restAgentOptions) || (Defaults.restAgentOptions as RestAgentOptions); const doOptions: Options = { headers: headers || undefined, responseType: 'buffer' }; if (!this.agent) { @@ -222,66 +85,98 @@ const Http: typeof IHttp = class { doOptions.agent = this.agent; doOptions.url = uri; - doOptions.timeout = { request: ((rest && rest.options.timeouts) || Defaults.TIMEOUTS).httpRequestTimeout }; + doOptions.timeout = { + request: ((this.client && this.client.options.timeouts) || Defaults.TIMEOUTS).httpRequestTimeout, + }; // We have our own logic that retries appropriate statuscodes to fallback endpoints, // with timeouts constructed appropriately. Don't want `got` doing its own retries to // the same endpoint, inappropriately retrying 429s, etc doOptions.retry = { limit: 0 }; - (got[method](doOptions) as CancelableRequest) - .then((res: Response) => { - handler(uri, params, callback)(null, res, res.body); - }) - .catch((err: ErrnoException) => { - if (err instanceof got.HTTPError) { - handler(uri, params, callback)(null, err.response, err.response.body); - return; - } - handler(uri, params, callback)(err); - }); + try { + const res = await (got[method](doOptions) as CancelableRequest); + return this._handler(null, res, res.body); + } catch (err) { + if (err instanceof got.HTTPError) { + return this._handler(null, err.response, err.response.body); + } + return this._handler(err as ErrnoException); + } } - checkConnectivity = (callback: (errorInfo: ErrorInfo | null, connected?: boolean) => void): void => { - if (this.options.disableConnectivityCheck) { - callback(null, true); - return; + checkConnectivity = async (): Promise => { + if (this.client?.options.disableConnectivityCheck) { + return true; } - const connectivityCheckUrl = this.options.connectivityCheckUrl || Defaults.connectivityCheckUrl; - const connectivityCheckParams = this.options.connectivityCheckParams; - const connectivityUrlIsDefault = !this.options.connectivityCheckUrl; + const connectivityCheckUrl = this.client?.options.connectivityCheckUrl || Defaults.connectivityCheckUrl; + const connectivityCheckParams = this.client?.options.connectivityCheckParams ?? null; + const connectivityUrlIsDefault = !this.client?.options.connectivityCheckUrl; - this.doUri( + const { error, statusCode, body } = await this.doUri( HttpMethods.Get, - null as any, connectivityCheckUrl, null, null, connectivityCheckParams, - function ( - err?: ErrnoException | ErrorInfo | null, - responseText?: unknown, - headers?: any, - unpacked?: boolean, - statusCode?: number - ) { - if (!err && !connectivityUrlIsDefault) { - callback(null, isSuccessCode(statusCode as number)); - return; - } - callback(null, !err && (responseText as Buffer | string)?.toString().trim() === 'yes'); - } ); + + if (!error && !connectivityUrlIsDefault) { + return isSuccessCode(statusCode as number); + } + return !error && (body as Buffer | string)?.toString().trim() === 'yes'; }; - Request?: ( - method: HttpMethods, - rest: Rest | null, - uri: string, - headers: Record | null, - params: RequestParams, - body: unknown, - callback: RequestCallback - ) => void = undefined; + shouldFallback(err: RequestResultError) { + const { code, statusCode } = err as ErrnoException; + return ( + code === 'ENETUNREACH' || + code === 'EHOSTUNREACH' || + code === 'EHOSTDOWN' || + code === 'ETIMEDOUT' || + code === 'ESOCKETTIMEDOUT' || + code === 'ENOTFOUND' || + code === 'ECONNRESET' || + code === 'ECONNREFUSED' || + (statusCode >= 500 && statusCode <= 504) + ); + } + + private _handler(err: ErrnoException | null, response?: Response, body?: unknown) { + if (err) { + return { error: err }; + } + + const statusCode = (response as Response).statusCode, + headers = (response as Response).headers; + + if (statusCode >= 300) { + switch (headers['content-type']) { + case 'application/json': + body = JSON.parse(body as string); + break; + + case 'application/x-msgpack': + if (!this.client?._MsgPack) { + return { error: createMissingPluginError('MsgPack') }; + } + body = this.client._MsgPack.decode(body as Buffer); + break; + } + + const error = (body as { error: ErrorInfo }).error + ? ErrorInfo.fromValues((body as { error: ErrorInfo }).error) + : new ErrorInfo( + (headers['x-ably-errormessage'] as string) || + 'Error response received from server: ' + statusCode + ' body was: ' + Platform.Config.inspect(body), + Number(headers['x-ably-errorcode']), + statusCode, + ); + + return { error, body, headers, unpacked: true, statusCode }; + } + + return { error: null, body, headers, unpacked: false, statusCode }; + } }; export default Http; diff --git a/src/platform/react-hooks/res/package.react.json b/src/platform/react-hooks/res/package.react.json index 09e6f67a7b..d1abb09572 100644 --- a/src/platform/react-hooks/res/package.react.json +++ b/src/platform/react-hooks/res/package.react.json @@ -1,7 +1,3 @@ { - "main": "./cjs/index.js", - "exports": { - "require": "./cjs", - "import": "./mjs" - } + "main": "./cjs/index.js" } diff --git a/src/platform/react-hooks/sample-app/src/App.tsx b/src/platform/react-hooks/sample-app/src/App.tsx index b60121fe5f..2d547f51b3 100644 --- a/src/platform/react-hooks/sample-app/src/App.tsx +++ b/src/platform/react-hooks/sample-app/src/App.tsx @@ -1,8 +1,9 @@ -import { Types } from 'ably'; +import * as Ably from 'ably'; import React, { useState } from 'react'; import { useChannel, usePresence, + usePresenceListener, useConnectionStateListener, useChannelStateListener, useAbly, @@ -10,44 +11,43 @@ import { import './App.css'; function App() { - const [messages, updateMessages] = useState([]); - const [derivedChannelMessages, updateDerivedChannelMessages] = useState([]); - const [frontOficeOnlyMessages, updateFrontOfficeOnlyMessages] = useState([]); + const [messages, updateMessages] = useState([]); + const [derivedChannelMessages, updateDerivedChannelMessages] = useState([]); + const [frontOficeOnlyMessages, updateFrontOfficeOnlyMessages] = useState([]); const [skip, setSkip] = useState(false); - const { channel, ably } = useChannel({ channelName: 'your-channel-name', skip }, (message) => { + const { channel, publish, ably } = useChannel({ channelName: 'your-channel-name', skip }, (message) => { updateMessages((prev) => [...prev, message]); }); useChannel( { channelName: 'your-derived-channel-name', - deriveOptions: { filter: 'headers.email == `"rob.pike@domain.com"` || headers.company == `"domain"`' }, + ablyId: 'rob', }, (message) => { updateDerivedChannelMessages((prev) => [...prev, message]); - } + }, ); useChannel( { channelName: 'your-derived-channel-name', - deriveOptions: { filter: 'headers.role == `"front-office"` || headers.company == `"domain"`' }, + ablyId: 'frontOffice', }, (message) => { updateFrontOfficeOnlyMessages((prev) => [...prev, message]); - } + }, ); - const { channel: anotherChannelPublisher } = useChannel({ channelName: 'your-derived-channel-name' }); + const { publish: anotherChannelPublish } = useChannel({ + channelName: 'your-derived-channel-name', + }); - const { presenceData, updateStatus } = usePresence( - { channelName: 'your-channel-name', skip }, - { foo: 'bar' }, - (update) => { - console.log(update); - } - ); + const { updateStatus } = usePresence({ channelName: 'your-channel-name', skip }, { foo: 'bar' }); + const { presenceData } = usePresenceListener({ channelName: 'your-channel-name', skip }, (update) => { + console.log(update); + }); const [, setConnectionState] = useState(ably.connection.state); @@ -57,8 +57,8 @@ function App() { const [ablyErr, setAblyErr] = useState(''); const [channelState, setChannelState] = useState(channel.state); - const [previousChannelState, setPreviousChannelState] = useState(); - const [channelStateReason, setChannelStateReason] = useState(); + const [previousChannelState, setPreviousChannelState] = useState(); + const [channelStateReason, setChannelStateReason] = useState(); useChannelStateListener('your-channel-name', (stateChange) => { setAblyErr(JSON.stringify(stateChange.reason)); @@ -77,7 +77,8 @@ function App() { const presentClients = presenceData.map((msg, index) => (
  • - {msg.clientId}: {JSON.stringify(msg.data)} + {/* PresenceMessage type is not correctly resolved and is missing 'clientId' property due to fail to load 'ably' type declarations in this test file */} + {(msg as any).clientId}: {JSON.stringify(msg.data)}
  • )); @@ -87,7 +88,7 @@ function App() {
    -
      {presentUsers}
    ); }; const UsePresenceComponentMultipleClients = () => { - const { presenceData: val1, updateStatus: update1 } = usePresence({ channelName: testChannelName }, 'foo'); - const { updateStatus: update2 } = usePresence({ channelName: testChannelName, id: 'otherClient' }, 'bar'); - - const presentUsers = val1.map((presence, index) => { - return ( -
  • - {presence.clientId} - {JSON.stringify(presence)} -
  • - ); - }); + const { updateStatus: update1 } = usePresence({ channelName: testChannelName }, 'foo'); + const { updateStatus: update2 } = usePresence({ channelName: testChannelName, ablyId: 'otherClient' }, 'bar'); return ( <> @@ -237,14 +234,13 @@ const UsePresenceComponentMultipleClients = () => { > Update -
      {presentUsers}
    ); }; interface UsePresenceStateErrorsComponentProps { - onConnectionError?: (err: Types.ErrorInfo) => unknown; - onChannelError?: (err: Types.ErrorInfo) => unknown; + onConnectionError?: (err: Ably.ErrorInfo) => unknown; + onChannelError?: (err: Ably.ErrorInfo) => unknown; } const UsePresenceStateErrorsComponent = ({ @@ -270,11 +266,19 @@ interface MyPresenceType { } const TypedUsePresenceComponent = () => { - const { presenceData } = usePresence('testChannelName', { - foo: 'bar', - }); + const { updateStatus } = usePresence(testChannelName, { foo: 'bar' }); - return
    {JSON.stringify(presenceData)}
    ; + return ( +
    + +
    + ); }; const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); diff --git a/src/platform/react-hooks/src/hooks/usePresence.ts b/src/platform/react-hooks/src/hooks/usePresence.ts index add5055b84..a22d808ba7 100644 --- a/src/platform/react-hooks/src/hooks/usePresence.ts +++ b/src/platform/react-hooks/src/hooks/usePresence.ts @@ -1,117 +1,69 @@ -import { type Types } from 'ably'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { channelOptionsWithAgent, ChannelParameters } from '../AblyReactHooks.js'; +import type * as Ably from 'ably'; +import { useCallback, useEffect, useRef } from 'react'; +import { ChannelParameters } from '../AblyReactHooks.js'; import { useAbly } from './useAbly.js'; +import { useChannelInstance } from './useChannelInstance.js'; import { useStateErrors } from './useStateErrors.js'; export interface PresenceResult { - presenceData: PresenceMessage[]; updateStatus: (messageOrPresenceObject: T) => void; - connectionError: Types.ErrorInfo | null; - channelError: Types.ErrorInfo | null; + connectionError: Ably.ErrorInfo | null; + channelError: Ably.ErrorInfo | null; } -export type OnPresenceMessageReceived = (presenceData: PresenceMessage) => void; -export type UseStatePresenceUpdate = (presenceData: Types.PresenceMessage[]) => void; - -const INACTIVE_CONNECTION_STATES: Types.ConnectionState[] = ['suspended', 'closing', 'closed', 'failed']; +const INACTIVE_CONNECTION_STATES: Ably.ConnectionState[] = ['suspended', 'closing', 'closed', 'failed']; export function usePresence( channelNameOrNameAndOptions: ChannelParameters, messageOrPresenceObject?: T, - onPresenceUpdated?: OnPresenceMessageReceived ): PresenceResult { const params = typeof channelNameOrNameAndOptions === 'object' ? channelNameOrNameAndOptions : { channelName: channelNameOrNameAndOptions }; - - const ably = useAbly(params.id); - - const subscribeOnly = typeof channelNameOrNameAndOptions === 'string' ? false : params.subscribeOnly; - - const channelOptions = params.options; - const channelOptionsRef = useRef(channelOptions); - - const channel = useMemo( - () => ably.channels.get(params.channelName, channelOptionsWithAgent(channelOptionsRef.current)), - [ably, params.channelName] - ); const skip = params.skip; + const ably = useAbly(params.ablyId); + const { channel } = useChannelInstance(params.ablyId, params.channelName); const { connectionError, channelError } = useStateErrors(params); + // we can't simply add messageOrPresenceObject to dependency list in our useCallback/useEffect hooks, + // since it will most likely cause an infinite loop of updates in cases when user calls this hook + // with an object literal instead of a state or memoized object. + // to prevent this from happening we store messageOrPresenceObject in a ref, and use that instead. + // note that it still prevents us from automatically re-entering presence with new messageOrPresenceObject if it changes. + // one of the options to fix this, is to use deep equals to check if the object has actually changed. see https://github.com/ably/ably-js/issues/1688. + const messageOrPresenceObjectRef = useRef(messageOrPresenceObject); useEffect(() => { - if (channelOptionsRef.current !== channelOptions && channelOptions) { - channel.setOptions(channelOptionsWithAgent(channelOptions)); - } - channelOptionsRef.current = channelOptions; - }, [channel, channelOptions]); - - const [presenceData, updatePresenceData] = useState>>([]); - - const updatePresence = async (message?: Types.PresenceMessage) => { - const snapshot = await channel.presence.get(); - updatePresenceData(snapshot); + messageOrPresenceObjectRef.current = messageOrPresenceObject; + }, [messageOrPresenceObject]); - onPresenceUpdated?.call(this, message); - }; + const onMount = useCallback(async () => { + await channel.presence.enter(messageOrPresenceObjectRef.current); + }, [channel.presence]); - const onMount = async () => { - channel.presence.subscribe('enter', updatePresence); - channel.presence.subscribe('leave', updatePresence); - channel.presence.subscribe('update', updatePresence); - - if (!subscribeOnly) { - await channel.presence.enter(messageOrPresenceObject); - } - - const snapshot = await channel.presence.get(); - updatePresenceData(snapshot); - }; - - const onUnmount = () => { + const onUnmount = useCallback(() => { // if connection is in one of inactive states, leave call will produce exception if (channel.state === 'attached' && !INACTIVE_CONNECTION_STATES.includes(ably.connection.state)) { - if (!subscribeOnly) { - channel.presence.leave(); - } + channel.presence.leave(); } - channel.presence.unsubscribe('enter', updatePresence); - channel.presence.unsubscribe('leave', updatePresence); - channel.presence.unsubscribe('update', updatePresence); - }; + }, [channel, ably.connection.state]); - const useEffectHook = () => { - !skip && onMount(); + useEffect(() => { + if (skip) return; + + onMount(); return () => { onUnmount(); }; - }; - - // eslint-disable-next-line react-hooks/exhaustive-deps - useEffect(useEffectHook, [skip]); + }, [skip, onMount, onUnmount]); const updateStatus = useCallback( (messageOrPresenceObject: T) => { - if (!subscribeOnly) { - channel.presence.update(messageOrPresenceObject); - } else { - throw new Error('updateStatus can not be called while using the hook in subscribeOnly mode'); - } + channel.presence.update(messageOrPresenceObject); }, - [subscribeOnly, channel] + [channel], ); - return { presenceData, updateStatus, connectionError, channelError }; -} - -interface PresenceMessage { - action: Types.PresenceAction; - clientId: string; - connectionId: string; - data: T; - encoding: string; - id: string; - timestamp: number; + return { updateStatus, connectionError, channelError }; } diff --git a/src/platform/react-hooks/src/hooks/usePresenceListener.test.tsx b/src/platform/react-hooks/src/hooks/usePresenceListener.test.tsx new file mode 100644 index 0000000000..67f04f0c20 --- /dev/null +++ b/src/platform/react-hooks/src/hooks/usePresenceListener.test.tsx @@ -0,0 +1,396 @@ +import React from 'react'; +import type * as Ably from 'ably'; +import { it, beforeEach, describe, expect, vi } from 'vitest'; +import { OnPresenceMessageReceived, usePresenceListener } from './usePresenceListener.js'; +import { render, screen, act, waitFor } from '@testing-library/react'; +import { FakeAblySdk, FakeAblyChannels } from '../fakes/ably.js'; +import { AblyProvider } from '../AblyProvider.js'; +import { ChannelProvider } from '../ChannelProvider.js'; + +const testChannelName = 'testChannel'; + +function renderInCtxProvider(client: FakeAblySdk, children: React.ReactNode | React.ReactNode[]) { + const renderResult = render( + + {children} + , + ); + + const originalRerender = renderResult.rerender; + renderResult.rerender = (children: React.ReactNode | React.ReactNode[]) => { + return originalRerender( + + {children} + , + ); + }; + + return renderResult; +} + +describe('usePresenceListener', () => { + let channels: FakeAblyChannels; + let ablyClient: FakeAblySdk; + let otherClient: FakeAblySdk; + + beforeEach(() => { + channels = new FakeAblyChannels([testChannelName]); + ablyClient = new FakeAblySdk().connectTo(channels); + otherClient = new FakeAblySdk().connectTo(channels); + }); + + it('presence data is not visible on first render as it runs in an effect', async () => { + // enter presence before rendering the component + ablyClient.channels.get(testChannelName).presence.enter('bar'); + + renderInCtxProvider(ablyClient, ); + + const values = screen.getByRole('presence').innerHTML; + expect(values).toBe(''); + + await act(async () => { + await wait(2); + // To let react run its updates so we don't see warnings in the test output + }); + }); + + it('presence data available after effect runs', async () => { + // enter presence before rendering the component + ablyClient.channels.get(testChannelName).presence.enter('bar'); + + renderInCtxProvider(ablyClient, ); + + await act(async () => { + await wait(2); + }); + + const values = screen.getByRole('presence').innerHTML; + expect(values).toContain(`"bar"`); + }); + + it('presence data in component updates when presence was updated', async () => { + ablyClient.channels.get(testChannelName).presence.enter('bar'); + + renderInCtxProvider(ablyClient, ); + + await act(async () => { + ablyClient.channels.get(testChannelName).presence.update('baz'); + }); + + const values = screen.getByRole('presence').innerHTML; + expect(values).toContain(`"baz"`); + }); + + it('presence data respects updates made by other clients', async () => { + ablyClient.channels.get(testChannelName).presence.enter('bar'); + + renderInCtxProvider(ablyClient, ); + + await act(async () => { + otherClient.channels.get(testChannelName).presence.enter('baz'); + }); + + const presenceElement = screen.getByRole('presence'); + const values = presenceElement.innerHTML; + expect(presenceElement.children.length).toBe(2); + expect(values).toContain(`"bar"`); + expect(values).toContain(`"baz"`); + }); + + it('presence API works with type information provided', async () => { + const data: MyPresenceType = { foo: 'bar' }; + ablyClient.channels.get(testChannelName).presence.enter(data); + + renderInCtxProvider(ablyClient, ); + + await act(async () => { + await wait(2); + }); + + const values = screen.getByRole('presence').innerHTML; + expect(values).toContain(`"data":${JSON.stringify(data)}`); + }); + + it('`skip` param prevents mounting and subscribing to presence events', async () => { + // can't really test 'leave' event, since if 'skip' works as expected then we won't have any data available to check that it's gone + ablyClient.channels.get(testChannelName).presence.enter('bar'); + ablyClient.channels.get(testChannelName).presence.update('baz'); + + renderInCtxProvider(ablyClient, ); + + await act(async () => { + await wait(2); + }); + + const values = screen.getByRole('presence').innerHTML; + expect(values).to.not.contain(`"bar"`); + expect(values).to.not.contain(`"baz"`); + }); + + it('usePresenceListener works with multiple clients', async () => { + ablyClient.channels.get(testChannelName).presence.enter('bar1'); + otherClient.channels.get(testChannelName).presence.enter('bar2'); + + renderInCtxProvider( + ablyClient, + + + + + , + ); + + await act(async () => { + ablyClient.channels.get(testChannelName).presence.update('baz1'); + otherClient.channels.get(testChannelName).presence.update('baz2'); + }); + + const values1 = screen.getByRole('presence1').innerHTML; + expect(values1).toContain(`"data":"baz1"`); + expect(values1).toContain(`"data":"baz2"`); + + const values2 = screen.getByRole('presence2').innerHTML; + expect(values2).toContain(`"data":"baz1"`); + expect(values2).toContain(`"data":"baz2"`); + }); + + it('calls onPresenceMessageReceived callback on new messages', async () => { + const onPresenceMessageReceived = vi.fn(); + ablyClient.channels.get(testChannelName).presence.enter('bar'); + + renderInCtxProvider( + ablyClient, + , + ); + + await act(async () => { + await wait(2); + }); + + // should not have been called for already existing presence state + expect(onPresenceMessageReceived).toHaveBeenCalledTimes(0); + + await act(async () => { + ablyClient.channels.get(testChannelName).presence.update('baz'); + }); + + expect(onPresenceMessageReceived).toHaveBeenCalledTimes(1); + expect(onPresenceMessageReceived).toHaveBeenCalledWith(expect.objectContaining({ data: 'baz' })); + }); + + it('reacts to onPresenceMessageReceived callback changes', async () => { + let onPresenceMessageReceived = vi.fn(); + ablyClient.channels.get(testChannelName).presence.enter('foo'); + + const { rerender } = renderInCtxProvider( + ablyClient, + , + ); + + await act(async () => { + ablyClient.channels.get(testChannelName).presence.update('bar'); + }); + + expect(onPresenceMessageReceived).toHaveBeenCalledTimes(1); + expect(onPresenceMessageReceived).toHaveBeenCalledWith(expect.objectContaining({ data: 'bar' })); + + // change callback function and rerender + onPresenceMessageReceived = vi.fn(); + rerender( + , + ); + + await act(async () => { + ablyClient.channels.get(testChannelName).presence.update('baz'); + }); + + // new callback should be called once + expect(onPresenceMessageReceived).toHaveBeenCalledTimes(1); + expect(onPresenceMessageReceived).toHaveBeenCalledWith(expect.objectContaining({ data: 'baz' })); + }); + + it('handles channel errors', async () => { + const onChannelError = vi.fn(); + const reason = { message: 'foo' }; + + renderInCtxProvider( + ablyClient, + + + , + ); + + const channelErrorElem = screen.getByRole('channelError'); + expect(onChannelError).toHaveBeenCalledTimes(0); + expect(channelErrorElem.innerHTML).toEqual(''); + + await act(async () => { + ablyClient.channels.get('blah').emit('failed', { + reason, + }); + }); + + expect(channelErrorElem.innerHTML).toEqual(reason.message); + expect(onChannelError).toHaveBeenCalledTimes(1); + expect(onChannelError).toHaveBeenCalledWith(reason); + }); + + it('handles connection errors', async () => { + const onConnectionError = vi.fn(); + const reason = { message: 'foo' }; + + renderInCtxProvider( + ablyClient, + + + , + ); + + const connectionErrorElem = screen.getByRole('connectionError'); + expect(onConnectionError).toHaveBeenCalledTimes(0); + expect(connectionErrorElem.innerHTML).toEqual(''); + + await act(async () => { + ablyClient.connection.emit('failed', { + reason, + }); + }); + + expect(connectionErrorElem.innerHTML).toEqual(reason.message); + expect(onConnectionError).toHaveBeenCalledTimes(1); + expect(onConnectionError).toHaveBeenCalledWith(reason); + }); + + it('should not affect existing presence listeners when hook unmounts', async () => { + const enterListener = vi.fn(); + ablyClient.channels.get(testChannelName).presence.subscribe('enter', enterListener); + + // enter presence + ablyClient.channels.get(testChannelName).presence.enter('bar'); + + const { unmount } = renderInCtxProvider(ablyClient, ); + + // wait for 'usePresenceListener' to render + await act(async () => { + await wait(2); + }); + + // expect existing listener and component to have presence data + const values = screen.getByRole('presence').innerHTML; + expect(values).toContain(`"bar"`); + expect(enterListener).toHaveBeenCalledWith(expect.objectContaining({ data: 'bar' })); + + unmount(); + + // Wait for `usePresenceListener` to be fully unmounted and effect's clean-ups applied + await act(async () => { + await wait(2); + }); + + ablyClient.channels.get(testChannelName).presence.enter('baz'); + + // Check that listener still exists + await waitFor(() => { + expect(enterListener).toHaveBeenCalledWith(expect.objectContaining({ data: 'baz' })); + }); + }); +}); + +const UsePresenceListenerComponent = ({ + skip, + onPresenceMessageReceived, +}: { + skip?: boolean; + onPresenceMessageReceived?: OnPresenceMessageReceived; +}) => { + const { presenceData } = usePresenceListener({ channelName: testChannelName, skip }, onPresenceMessageReceived); + + const presentUsers = presenceData.map((presence, index) => { + return ( +
  • + {/* PresenceMessage type is not correctly resolved and is missing 'clientId' property due to fail to load 'ably' type declarations in this test file */} + {(presence as any).clientId} - {JSON.stringify(presence)} +
  • + ); + }); + + return ( + <> +
      {presentUsers}
    + + ); +}; + +const UsePresenceListenerComponentMultipleClients = () => { + const { presenceData: presenceData1 } = usePresenceListener({ channelName: testChannelName }); + const { presenceData: presenceData2 } = usePresenceListener({ channelName: testChannelName, ablyId: 'otherClient' }); + + const presentUsers1 = presenceData1.map((presence, index) => { + return ( +
  • + {/* PresenceMessage type is not correctly resolved and is missing 'clientId' property due to fail to load 'ably' type declarations in this test file */} + {(presence as any).clientId} - {JSON.stringify(presence)} +
  • + ); + }); + const presentUsers2 = presenceData2.map((presence, index) => { + return ( +
  • + {/* PresenceMessage type is not correctly resolved and is missing 'clientId' property due to fail to load 'ably' type declarations in this test file */} + {(presence as any).clientId} - {JSON.stringify(presence)} +
  • + ); + }); + + return ( + <> +
      {presentUsers1}
    +
      {presentUsers2}
    + + ); +}; + +interface UsePresenceListenerStateErrorsComponentProps { + onConnectionError?: (err: Ably.ErrorInfo) => unknown; + onChannelError?: (err: Ably.ErrorInfo) => unknown; +} + +const UsePresenceListenerStateErrorsComponent = ({ + onConnectionError, + onChannelError, +}: UsePresenceListenerStateErrorsComponentProps) => { + const { connectionError, channelError } = usePresenceListener({ + channelName: 'blah', + onConnectionError, + onChannelError, + }); + + return ( + <> +

    {connectionError?.message}

    +

    {channelError?.message}

    + + ); +}; + +interface MyPresenceType { + foo: string; +} + +const TypedUsePresenceListenerComponent = () => { + const { presenceData } = usePresenceListener(testChannelName); + + return
    {JSON.stringify(presenceData)}
    ; +}; + +const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); diff --git a/src/platform/react-hooks/src/hooks/usePresenceListener.ts b/src/platform/react-hooks/src/hooks/usePresenceListener.ts new file mode 100644 index 0000000000..656bcf3339 --- /dev/null +++ b/src/platform/react-hooks/src/hooks/usePresenceListener.ts @@ -0,0 +1,68 @@ +import type * as Ably from 'ably'; +import { useCallback, useEffect, useRef, useState } from 'react'; +import { ChannelParameters } from '../AblyReactHooks.js'; +import { useChannelInstance } from './useChannelInstance.js'; +import { useStateErrors } from './useStateErrors.js'; + +interface PresenceMessage extends Ably.PresenceMessage { + data: T; +} + +export interface PresenceListenerResult { + presenceData: PresenceMessage[]; + connectionError: Ably.ErrorInfo | null; + channelError: Ably.ErrorInfo | null; +} + +export type OnPresenceMessageReceived = (presenceData: PresenceMessage) => void; + +export function usePresenceListener( + channelNameOrNameAndOptions: ChannelParameters, + onPresenceMessageReceived?: OnPresenceMessageReceived, +): PresenceListenerResult { + const params = + typeof channelNameOrNameAndOptions === 'object' + ? channelNameOrNameAndOptions + : { channelName: channelNameOrNameAndOptions }; + const skip = params.skip; + + const { channel } = useChannelInstance(params.ablyId, params.channelName); + const { connectionError, channelError } = useStateErrors(params); + const [presenceData, updatePresenceData] = useState>>([]); + + const onPresenceMessageReceivedRef = useRef(onPresenceMessageReceived); + useEffect(() => { + onPresenceMessageReceivedRef.current = onPresenceMessageReceived; + }, [onPresenceMessageReceived]); + + const updatePresence = useCallback( + async (message?: Ably.PresenceMessage) => { + const snapshot = await channel.presence.get(); + updatePresenceData(snapshot); + + onPresenceMessageReceivedRef.current?.(message); + }, + [channel.presence], + ); + + const onMount = useCallback(async () => { + channel.presence.subscribe(['enter', 'leave', 'update'], updatePresence); + const snapshot = await channel.presence.get(); + updatePresenceData(snapshot); + }, [channel.presence, updatePresence]); + + const onUnmount = useCallback(async () => { + channel.presence.unsubscribe(['enter', 'leave', 'update'], updatePresence); + }, [channel.presence, updatePresence]); + + useEffect(() => { + if (skip) return; + + onMount(); + return () => { + onUnmount(); + }; + }, [skip, onMount, onUnmount]); + + return { presenceData, connectionError, channelError }; +} diff --git a/src/platform/react-hooks/src/hooks/useStateErrors.ts b/src/platform/react-hooks/src/hooks/useStateErrors.ts index 9921b6cac0..ebf6076d19 100644 --- a/src/platform/react-hooks/src/hooks/useStateErrors.ts +++ b/src/platform/react-hooks/src/hooks/useStateErrors.ts @@ -1,12 +1,12 @@ -import { Types } from 'ably'; +import { ErrorInfo } from 'ably'; import { useState } from 'react'; import { useConnectionStateListener } from './useConnectionStateListener.js'; import { useChannelStateListener } from './useChannelStateListener.js'; import { ChannelNameAndOptions } from '../AblyReactHooks.js'; export function useStateErrors(params: ChannelNameAndOptions) { - const [connectionError, setConnectionError] = useState(null); - const [channelError, setChannelError] = useState(null); + const [connectionError, setConnectionError] = useState(null); + const [channelError, setChannelError] = useState(null); useConnectionStateListener( ['suspended', 'failed', 'disconnected'], @@ -16,7 +16,7 @@ export function useStateErrors(params: ChannelNameAndOptions) { setConnectionError(stateChange.reason); } }, - params.id + params.ablyId, ); useConnectionStateListener( @@ -24,7 +24,7 @@ export function useStateErrors(params: ChannelNameAndOptions) { () => { setConnectionError(null); }, - params.id + params.ablyId, ); useChannelStateListener(params, ['suspended', 'failed', 'detached'], (stateChange) => { diff --git a/src/platform/react-hooks/src/index.ts b/src/platform/react-hooks/src/index.ts index 10454f0986..c064287468 100644 --- a/src/platform/react-hooks/src/index.ts +++ b/src/platform/react-hooks/src/index.ts @@ -1,7 +1,10 @@ export * from './AblyReactHooks.js'; export * from './hooks/useChannel.js'; export * from './hooks/usePresence.js'; +export * from './hooks/usePresenceListener.js'; export * from './hooks/useAbly.js'; export * from './AblyProvider.js'; export * from './hooks/useChannelStateListener.js'; export * from './hooks/useConnectionStateListener.js'; +export { ChannelProvider } from './ChannelProvider.js'; +export * from './AblyContext.js'; diff --git a/src/platform/react-hooks/tsconfig.json b/src/platform/react-hooks/tsconfig.json index ed956b2d1d..b4fcca7644 100644 --- a/src/platform/react-hooks/tsconfig.json +++ b/src/platform/react-hooks/tsconfig.json @@ -2,7 +2,7 @@ "include": ["./src/**/*.ts", "./ably.d.ts"], "exclude": ["./src/**/*.test.tsx", "./src/fakes/**/*.ts"], "compilerOptions": { - "target": "es6", + "target": "ES2017", "rootDir": "./src", "sourceMap": true, "strict": false, diff --git a/src/platform/react-native/config.ts b/src/platform/react-native/config.ts index 959bfbf23d..8a0be88127 100644 --- a/src/platform/react-native/config.ts +++ b/src/platform/react-native/config.ts @@ -1,49 +1,48 @@ -import msgpack from '../web/lib/util/msgpack'; -import { parse as parseBase64 } from 'crypto-js/build/enc-base64'; import { IPlatformConfig } from '../../common/types/IPlatformConfig'; +import BufferUtils from '../web/lib/util/bufferutils'; -const Platform: IPlatformConfig = { - agent: 'reactnative', - logTimestamps: true, - noUpgrade: false, - binaryType: 'arraybuffer', - WebSocket: WebSocket, - xhrSupported: true, - allowComet: true, - jsonpSupported: false, - streamingSupported: true, - useProtocolHeartbeats: true, - createHmac: null, - msgpack: msgpack, - supportsBinary: !!(typeof TextDecoder !== 'undefined' && TextDecoder), - preferBinary: false, // Motivation as on web; see `preferBinary` comment there. - ArrayBuffer: typeof ArrayBuffer !== 'undefined' && ArrayBuffer, - atob: global.atob, - nextTick: function (f: Function) { - setTimeout(f, 0); - }, - addEventListener: null, - inspect: JSON.stringify, - stringByteSize: function (str: string) { - /* str.length will be an underestimate for non-ascii strings. But if we're - * in a browser too old to support TextDecoder, not much we can do. Better - * to underestimate, so if we do go over-size, the server will reject the - * message */ - return (typeof TextDecoder !== 'undefined' && new TextEncoder().encode(str).length) || str.length; - }, - TextEncoder: global.TextEncoder, - TextDecoder: global.TextDecoder, - Promise: global.Promise, - getRandomWordArray: (function (RNRandomBytes) { - return function (byteLength: number, callback: Function) { - RNRandomBytes.randomBytes(byteLength, function (err: Error, base64String: string) { - callback(err, !err && parseBase64(base64String)); - }); - }; - // Installing @types/react-native would fix this but conflicts with @types/node - // See https://github.com/DefinitelyTyped/DefinitelyTyped/issues/15960 - // eslint-disable-next-line @typescript-eslint/no-var-requires - })(require('react-native').NativeModules.RNRandomBytes), +type RNRandomBytes = { + randomBytes: (byteLength: number, cb: (err: Error | null, base64String: string | null) => void) => void; }; -export default Platform; +export default function (bufferUtils: typeof BufferUtils): IPlatformConfig { + return { + agent: 'reactnative', + logTimestamps: true, + binaryType: 'arraybuffer', + WebSocket: WebSocket, + xhrSupported: true, + allowComet: true, + useProtocolHeartbeats: true, + supportsBinary: !!(typeof TextDecoder !== 'undefined' && TextDecoder), + preferBinary: false, // Motivation as on web; see `preferBinary` comment there. + ArrayBuffer: typeof ArrayBuffer !== 'undefined' && ArrayBuffer, + atob: global.atob, + nextTick: function (f: Function) { + setTimeout(f, 0); + }, + addEventListener: null, + inspect: JSON.stringify, + stringByteSize: function (str: string) { + /* str.length will be an underestimate for non-ascii strings. But if we're + * in a browser too old to support TextDecoder, not much we can do. Better + * to underestimate, so if we do go over-size, the server will reject the + * message */ + return (typeof TextDecoder !== 'undefined' && new TextEncoder().encode(str).length) || str.length; + }, + TextEncoder: global.TextEncoder, + TextDecoder: global.TextDecoder, + getRandomArrayBuffer: (function (RNRandomBytes: RNRandomBytes) { + return async function (byteLength: number) { + return new Promise((resolve, reject) => { + RNRandomBytes.randomBytes(byteLength, (err, base64String) => { + err ? reject(err) : resolve(bufferUtils.toArrayBuffer(bufferUtils.base64Decode(base64String!))); + }); + }); + }; + // Installing @types/react-native would fix this but conflicts with @types/node + // See https://github.com/DefinitelyTyped/DefinitelyTyped/issues/15960 + // eslint-disable-next-line @typescript-eslint/no-var-requires + })(require('react-native').NativeModules.RNRandomBytes), + }; +} diff --git a/src/platform/react-native/index.ts b/src/platform/react-native/index.ts index 1a7868174d..998c2dd19a 100644 --- a/src/platform/react-native/index.ts +++ b/src/platform/react-native/index.ts @@ -1,15 +1,16 @@ // Common -import Rest from '../../common/lib/client/rest'; -import Realtime from '../../common/lib/client/realtime'; +import { DefaultRest } from '../../common/lib/client/defaultrest'; +import { DefaultRealtime } from '../../common/lib/client/defaultrealtime'; import Platform from '../../common/platform'; import ErrorInfo from '../../common/lib/types/errorinfo'; +import { fromDeserializedIncludingDependencies as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage'; // Platform Specific import BufferUtils from '../web/lib/util/bufferutils'; // @ts-ignore -import CryptoFactory from '../web/lib/util/crypto'; -import Http from '../web/lib/util/http'; -import Config from './config'; +import { createCryptoClass } from '../web/lib/util/crypto'; +import Http from '../web/lib/http/http'; +import configFactory from './config'; // @ts-ignore import Transports from '../web/lib/transport'; import Logger from '../../common/lib/util/logger'; @@ -17,8 +18,11 @@ import { getDefaults } from '../../common/lib/util/defaults'; import WebStorage from '../web/lib/util/webstorage'; import PlatformDefaults from '../web/lib/util/defaults'; import msgpack from '../web/lib/util/msgpack'; +import { defaultBundledRequestImplementations } from '../web/lib/http/request'; -const Crypto = CryptoFactory(Config, BufferUtils); +const Config = configFactory(BufferUtils); + +const Crypto = createCryptoClass(Config, BufferUtils); Platform.Crypto = Crypto; Platform.BufferUtils = BufferUtils; @@ -27,8 +31,12 @@ Platform.Config = Config; Platform.Transports = Transports; Platform.WebStorage = WebStorage; -Rest.Crypto = Crypto; -Realtime.Crypto = Crypto; +for (const clientClass of [DefaultRest, DefaultRealtime]) { + clientClass.Crypto = Crypto; + clientClass._MsgPack = msgpack; +} + +Http.bundledRequestImplementations = defaultBundledRequestImplementations; Logger.initLogHandlers(); @@ -41,7 +49,8 @@ if (Platform.Config.agent) { export default { ErrorInfo, - Rest, - Realtime, + Rest: DefaultRest, + Realtime: DefaultRealtime, msgpack, + protocolMessageFromDeserialized, }; diff --git a/src/platform/web-noencryption/index.ts b/src/platform/web-noencryption/index.ts deleted file mode 100644 index 94723e9d9b..0000000000 --- a/src/platform/web-noencryption/index.ts +++ /dev/null @@ -1,44 +0,0 @@ -// Common -import Rest from '../../common/lib/client/rest'; -import Realtime from '../../common/lib/client/realtime'; -import Platform from '../../common/platform'; -import ErrorInfo from '../../common/lib/types/errorinfo'; - -// Platform Specific -import BufferUtils from '../web/lib/util/bufferutils'; -// @ts-ignore -import Http from '../web/lib/util/http'; -import Config from '../web/config'; -// @ts-ignore -import Transports from '../web/lib/transport'; -import Logger from '../../common/lib/util/logger'; -import { getDefaults } from '../../common/lib/util/defaults'; -import WebStorage from '../web/lib/util/webstorage'; -import PlatformDefaults from '../web/lib/util/defaults'; -import msgpack from '../web/lib/util/msgpack'; - -Platform.Crypto = null; -Platform.BufferUtils = BufferUtils; -Platform.Http = Http; -Platform.Config = Config; -Platform.Transports = Transports; -Platform.WebStorage = WebStorage; - -Rest.Crypto = null; -Realtime.Crypto = null; - -Logger.initLogHandlers(); - -Platform.Defaults = getDefaults(PlatformDefaults); - -if (Platform.Config.agent) { - // @ts-ignore - Platform.Defaults.agent += ' ' + Platform.Config.agent; -} - -export default { - ErrorInfo, - Rest, - Realtime, - msgpack, -}; diff --git a/src/platform/web/config-webworker.ts b/src/platform/web/config-webworker.ts deleted file mode 100644 index 45a1aa43ba..0000000000 --- a/src/platform/web/config-webworker.ts +++ /dev/null @@ -1,5 +0,0 @@ -import Config from './config'; - -Config.isWebworker = true; - -export default Config; diff --git a/src/platform/web/config.ts b/src/platform/web/config.ts index 4edd8db9aa..ec2c9b6362 100644 --- a/src/platform/web/config.ts +++ b/src/platform/web/config.ts @@ -1,15 +1,12 @@ -import msgpack from './lib/util/msgpack'; -import { IPlatformConfig, TypedArray } from '../../common/types/IPlatformConfig'; +import { IPlatformConfig } from '../../common/types/IPlatformConfig'; import * as Utils from 'common/lib/util/utils'; // Workaround for salesforce lightning locker compat const globalObject = Utils.getGlobalObject(); -declare var msCrypto: typeof crypto; // for IE11 - if (typeof Window === 'undefined' && typeof WorkerGlobalScope === 'undefined') { console.log( - "Warning: this distribution of Ably is intended for browsers. On nodejs, please use the 'ably' package on npm" + "Warning: this distribution of Ably is intended for browsers. On nodejs, please use the 'ably' package on npm", ); } @@ -21,6 +18,16 @@ function allowComet() { return !globalObject.WebSocket || !loc || !loc.origin || loc.origin.indexOf('http') > -1; } +// from: https://stackoverflow.com/a/18002694 +export function isWebWorkerContext(): boolean { + // run this in global scope of window or worker. since window.self = window, we're ok + if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) { + return true; + } else { + return false; + } +} + const userAgent = globalObject.navigator && globalObject.navigator.userAgent.toString(); const currentUrl = globalObject.location && globalObject.location.href; @@ -29,17 +36,12 @@ const Config: IPlatformConfig = { logTimestamps: true, userAgent: userAgent, currentUrl: currentUrl, - noUpgrade: userAgent && !!userAgent.match(/MSIE\s8\.0/), binaryType: 'arraybuffer', WebSocket: globalObject.WebSocket, fetchSupported: !!globalObject.fetch, xhrSupported: globalObject.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest(), - jsonpSupported: typeof document !== 'undefined', allowComet: allowComet(), - streamingSupported: true, useProtocolHeartbeats: true, - createHmac: null, - msgpack: msgpack, supportsBinary: !!globalObject.TextDecoder, /* Per Paddy (https://ably-real-time.slack.com/archives/CURL4U2FP/p1705674537763479) web intentionally prefers JSON to MessagePack: * @@ -65,18 +67,12 @@ const Config: IPlatformConfig = { }, TextEncoder: globalObject.TextEncoder, TextDecoder: globalObject.TextDecoder, - Promise: globalObject.Promise, - getRandomValues: (function (crypto) { - if (crypto === undefined) { - return undefined; - } - return function (arr: TypedArray, callback?: (error: Error | null) => void) { - crypto.getRandomValues(arr); - if (callback) { - callback(null); - } - }; - })(globalObject.crypto || msCrypto), + getRandomArrayBuffer: async function (byteLength: number): Promise { + const byteArray = new Uint8Array(byteLength); + globalObject.crypto.getRandomValues(byteArray); + return byteArray.buffer; + }, + isWebworker: isWebWorkerContext(), }; export default Config; diff --git a/src/platform/web/index-webworker.ts b/src/platform/web/index-webworker.ts deleted file mode 100644 index de3be9b971..0000000000 --- a/src/platform/web/index-webworker.ts +++ /dev/null @@ -1,4 +0,0 @@ -import './config-webworker'; -import Ably from './index'; - -export default Ably; diff --git a/src/platform/web/index.ts b/src/platform/web/index.ts index 9adc0acaff..23058f1885 100644 --- a/src/platform/web/index.ts +++ b/src/platform/web/index.ts @@ -1,14 +1,15 @@ // Common -import Rest from '../../common/lib/client/rest'; -import Realtime from '../../common/lib/client/realtime'; +import { DefaultRest } from '../../common/lib/client/defaultrest'; +import { DefaultRealtime } from '../../common/lib/client/defaultrealtime'; import Platform from '../../common/platform'; import ErrorInfo from '../../common/lib/types/errorinfo'; +import { fromDeserializedIncludingDependencies as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage'; // Platform Specific import BufferUtils from './lib/util/bufferutils'; // @ts-ignore -import CryptoFactory from './lib/util/crypto'; -import Http from './lib/util/http'; +import { createCryptoClass } from './lib/util/crypto'; +import Http from './lib/http/http'; import Config from './config'; // @ts-ignore import Transports from './lib/transport'; @@ -17,8 +18,9 @@ import { getDefaults } from '../../common/lib/util/defaults'; import WebStorage from './lib/util/webstorage'; import PlatformDefaults from './lib/util/defaults'; import msgpack from './lib/util/msgpack'; +import { defaultBundledRequestImplementations } from './lib/http/request'; -const Crypto = CryptoFactory(Config, BufferUtils); +const Crypto = createCryptoClass(Config, BufferUtils); Platform.Crypto = Crypto; Platform.BufferUtils = BufferUtils; @@ -27,8 +29,12 @@ Platform.Config = Config; Platform.Transports = Transports; Platform.WebStorage = WebStorage; -Rest.Crypto = Crypto; -Realtime.Crypto = Crypto; +for (const clientClass of [DefaultRest, DefaultRealtime]) { + clientClass.Crypto = Crypto; + clientClass._MsgPack = msgpack; +} + +Http.bundledRequestImplementations = defaultBundledRequestImplementations; Logger.initLogHandlers(); @@ -39,17 +45,11 @@ if (Platform.Config.agent) { Platform.Defaults.agent += ' ' + Platform.Config.agent; } -/* If using IE8, don't attempt to upgrade from xhr_polling to xhr_streaming - - * while it can do streaming, the low max http-connections-per-host limit means - * that the polling transport is crippled during the upgrade process. So just - * leave it at the base transport */ -if (Platform.Config.noUpgrade) { - Platform.Defaults.upgradeTransports = []; -} +export { DefaultRest as Rest, DefaultRealtime as Realtime, msgpack, protocolMessageFromDeserialized }; export default { ErrorInfo, - Rest, - Realtime, + Rest: DefaultRest, + Realtime: DefaultRealtime, msgpack, }; diff --git a/src/platform/web/lib/http/http.ts b/src/platform/web/lib/http/http.ts new file mode 100644 index 0000000000..ffe2761636 --- /dev/null +++ b/src/platform/web/lib/http/http.ts @@ -0,0 +1,174 @@ +import Platform from 'common/platform'; +import Defaults from 'common/lib/util/defaults'; +import ErrorInfo, { PartialErrorInfo } from 'common/lib/types/errorinfo'; +import { RequestBody, RequestResultError, RequestParams, RequestResult } from 'common/types/http'; +import HttpMethods from 'common/constants/HttpMethods'; +import BaseClient from 'common/lib/client/baseclient'; +import XHRStates from 'common/constants/XHRStates'; +import Logger from 'common/lib/util/logger'; +import { StandardCallback } from 'common/types/utils'; +import { isSuccessCode } from 'common/constants/HttpStatusCodes'; +import { ModularPlugins } from 'common/lib/client/modularplugins'; + +export type HTTPRequestImplementations = Pick; + +function createMissingImplementationError() { + return new ErrorInfo( + 'No HTTP request plugin provided. Provide at least one of the FetchRequest or XHRRequest plugins.', + 400, + 40000, + ); +} + +const Http = class { + static methods = [HttpMethods.Get, HttpMethods.Delete, HttpMethods.Post, HttpMethods.Put, HttpMethods.Patch]; + static methodsWithoutBody = [HttpMethods.Get, HttpMethods.Delete]; + static methodsWithBody = [HttpMethods.Post, HttpMethods.Put, HttpMethods.Patch]; + // HTTP request implementations that are available even without a BaseClient object (needed by some tests which directly instantiate `Http` without a client) + static bundledRequestImplementations: HTTPRequestImplementations; + checksInProgress: Array> | null = null; + private client: BaseClient | null; + + constructor(client?: BaseClient) { + this.client = client ?? null; + const connectivityCheckUrl = client?.options.connectivityCheckUrl || Defaults.connectivityCheckUrl; + const connectivityCheckParams = client?.options.connectivityCheckParams ?? null; + const connectivityUrlIsDefault = !client?.options.connectivityCheckUrl; + + const requestImplementations = { + ...Http.bundledRequestImplementations, + ...client?._additionalHTTPRequestImplementations, + }; + const xhrRequestImplementation = requestImplementations.XHRRequest; + const fetchRequestImplementation = requestImplementations.FetchRequest; + const hasImplementation = !!(xhrRequestImplementation || fetchRequestImplementation); + + if (!hasImplementation) { + throw createMissingImplementationError(); + } + + if (Platform.Config.xhrSupported && xhrRequestImplementation) { + this.supportsAuthHeaders = true; + this.Request = async function ( + method: HttpMethods, + uri: string, + headers: Record | null, + params: RequestParams, + body: RequestBody | null, + ) { + return new Promise((resolve) => { + const req = xhrRequestImplementation.createRequest( + uri, + headers, + params, + body, + XHRStates.REQ_SEND, + (client && client.options.timeouts) ?? null, + method, + ); + req.once( + 'complete', + ( + error: RequestResult['error'], + body: RequestResult['body'], + headers: RequestResult['headers'], + unpacked: RequestResult['unpacked'], + statusCode: RequestResult['statusCode'], + ) => resolve({ error, body, headers, unpacked, statusCode }), + ); + req.exec(); + }); + }; + if (client?.options.disableConnectivityCheck) { + this.checkConnectivity = async function () { + return true; + }; + } else { + this.checkConnectivity = async function () { + Logger.logAction( + Logger.LOG_MICRO, + '(XHRRequest)Http.checkConnectivity()', + 'Sending; ' + connectivityCheckUrl, + ); + + const requestResult = await this.doUri( + HttpMethods.Get, + connectivityCheckUrl, + null, + null, + connectivityCheckParams, + ); + + let result = false; + if (!connectivityUrlIsDefault) { + result = !requestResult.error && isSuccessCode(requestResult.statusCode as number); + } else { + result = !requestResult.error && (requestResult.body as string)?.replace(/\n/, '') == 'yes'; + } + + Logger.logAction(Logger.LOG_MICRO, '(XHRRequest)Http.checkConnectivity()', 'Result: ' + result); + return result; + }; + } + } else if (Platform.Config.fetchSupported && fetchRequestImplementation) { + this.supportsAuthHeaders = true; + this.Request = async (method, uri, headers, params, body) => { + return fetchRequestImplementation(method, client ?? null, uri, headers, params, body); + }; + this.checkConnectivity = async function () { + Logger.logAction(Logger.LOG_MICRO, '(Fetch)Http.checkConnectivity()', 'Sending; ' + connectivityCheckUrl); + const requestResult = await this.doUri(HttpMethods.Get, connectivityCheckUrl, null, null, null); + const result = !requestResult.error && (requestResult.body as string)?.replace(/\n/, '') == 'yes'; + Logger.logAction(Logger.LOG_MICRO, '(Fetch)Http.checkConnectivity()', 'Result: ' + result); + return result; + }; + } else { + this.Request = async () => { + const error = hasImplementation + ? new PartialErrorInfo('no supported HTTP transports available', null, 400) + : createMissingImplementationError(); + return { error }; + }; + } + } + + async doUri( + method: HttpMethods, + uri: string, + headers: Record | null, + body: RequestBody | null, + params: RequestParams, + ): Promise { + if (!this.Request) { + return { error: new PartialErrorInfo('Request invoked before assigned to', null, 500) }; + } + return this.Request(method, uri, headers, params, body); + } + + private Request?: ( + method: HttpMethods, + uri: string, + headers: Record | null, + params: RequestParams, + body: RequestBody | null, + ) => Promise; + + checkConnectivity?: () => Promise = undefined; + + supportsAuthHeaders = false; + supportsLinkHeaders = false; + + shouldFallback(errorInfo: RequestResultError) { + const statusCode = errorInfo.statusCode as number; + /* 400 + no code = a generic xhr onerror. Browser doesn't give us enough + * detail to know whether it's fallback-fixable, but it may be (eg if a + * network issue), so try just in case */ + return ( + (statusCode === 408 && !errorInfo.code) || + (statusCode === 400 && !errorInfo.code) || + (statusCode >= 500 && statusCode <= 504) + ); + } +}; + +export default Http; diff --git a/src/platform/web/lib/http/request/fetchrequest.ts b/src/platform/web/lib/http/request/fetchrequest.ts new file mode 100644 index 0000000000..1e6a0d073c --- /dev/null +++ b/src/platform/web/lib/http/request/fetchrequest.ts @@ -0,0 +1,102 @@ +import HttpMethods from 'common/constants/HttpMethods'; +import BaseClient from 'common/lib/client/baseclient'; +import ErrorInfo, { PartialErrorInfo } from 'common/lib/types/errorinfo'; +import { RequestBody, RequestResultError, ResponseHeaders, RequestParams, RequestResult } from 'common/types/http'; +import Platform from 'common/platform'; +import Defaults from 'common/lib/util/defaults'; +import * as Utils from 'common/lib/util/utils'; + +function isAblyError(responseBody: unknown, headers: Headers): responseBody is { error?: ErrorInfo } { + return !!headers.get('x-ably-errorcode'); +} + +function getAblyError(responseBody: unknown, headers: Headers) { + if (isAblyError(responseBody, headers)) { + return responseBody.error && ErrorInfo.fromValues(responseBody.error); + } +} + +function convertHeaders(headers: Headers) { + const result: ResponseHeaders = {}; + + headers.forEach((value, key) => { + result[key] = value; + }); + + return result; +} + +export default async function fetchRequest( + method: HttpMethods, + client: BaseClient | null, + uri: string, + headers: Record | null, + params: RequestParams, + body: RequestBody | null, +): Promise { + const fetchHeaders = new Headers(headers || {}); + const _method = method ? method.toUpperCase() : Utils.isNil(body) ? 'GET' : 'POST'; + + const controller = new AbortController(); + + let timeout: ReturnType; // This way we don’t have to worry about the fact that the TypeScript compiler is — for reasons I haven’t looked into — picking up the signature of the Node version of setTimeout, which has a different return type to the web one + const timeoutPromise: Promise = new Promise((resolve) => { + timeout = setTimeout( + () => { + controller.abort(); + resolve({ error: new PartialErrorInfo('Request timed out', null, 408) }); + }, + client ? client.options.timeouts.httpRequestTimeout : Defaults.TIMEOUTS.httpRequestTimeout, + ); + }); + + const requestInit: RequestInit = { + method: _method, + headers: fetchHeaders, + body: body as any, + }; + + if (!Platform.Config.isWebworker) { + requestInit.credentials = fetchHeaders.has('authorization') ? 'include' : 'same-origin'; + } + + const resultPromise = (async (): Promise => { + try { + const res = await Utils.getGlobalObject().fetch(uri + '?' + new URLSearchParams(params || {}), requestInit); + + clearTimeout(timeout!); + + const contentType = res.headers.get('Content-Type'); + let body; + if (contentType && contentType.indexOf('application/x-msgpack') > -1) { + body = await res.arrayBuffer(); + } else if (contentType && contentType.indexOf('application/json') > -1) { + body = await res.json(); + } else { + body = await res.text(); + } + + const unpacked = !!contentType && contentType.indexOf('application/x-msgpack') === -1; + const headers = convertHeaders(res.headers); + + if (!res.ok) { + const error = + getAblyError(body, res.headers) || + new PartialErrorInfo( + 'Error response received from server: ' + res.status + ' body was: ' + Platform.Config.inspect(body), + null, + res.status, + ); + + return { error, body, headers, unpacked, statusCode: res.status }; + } else { + return { error: null, body, headers, unpacked, statusCode: res.status }; + } + } catch (error) { + clearTimeout(timeout!); + return { error: error as RequestResultError }; + } + })(); + + return Promise.race([timeoutPromise, resultPromise]); +} diff --git a/src/platform/web/lib/http/request/index.ts b/src/platform/web/lib/http/request/index.ts new file mode 100644 index 0000000000..91012f68d8 --- /dev/null +++ b/src/platform/web/lib/http/request/index.ts @@ -0,0 +1,10 @@ +import { HTTPRequestImplementations } from '../http'; +import XHRRequest from './xhrrequest'; +import fetchRequest from './fetchrequest'; + +export const defaultBundledRequestImplementations: HTTPRequestImplementations = { + XHRRequest: XHRRequest, + FetchRequest: fetchRequest, +}; + +export const modularBundledRequestImplementations: HTTPRequestImplementations = {}; diff --git a/src/platform/web/lib/transport/xhrrequest.ts b/src/platform/web/lib/http/request/xhrrequest.ts similarity index 89% rename from src/platform/web/lib/transport/xhrrequest.ts rename to src/platform/web/lib/http/request/xhrrequest.ts index b2ff5141d9..942097d26e 100644 --- a/src/platform/web/lib/transport/xhrrequest.ts +++ b/src/platform/web/lib/http/request/xhrrequest.ts @@ -5,12 +5,12 @@ import Logger from 'common/lib/util/logger'; import Defaults from 'common/lib/util/defaults'; import HttpMethods from 'common/constants/HttpMethods'; import IXHRRequest from 'common/types/IXHRRequest'; -import { RequestParams } from 'common/types/http'; +import { RequestBody, RequestParams } from 'common/types/http'; import XHRStates from 'common/constants/XHRStates'; import Platform from 'common/platform'; function isAblyError(responseBody: unknown, headers: Record): responseBody is { error?: ErrorInfo } { - return Utils.arrIn(Utils.allToLowerCase(Utils.keysArray(headers)), 'x-ably-errorcode'); + return Utils.allToLowerCase(Utils.keysArray(headers)).includes('x-ably-errorcode'); } function getAblyError(responseBody: unknown, headers: Record) { @@ -19,27 +19,10 @@ function getAblyError(responseBody: unknown, headers: Record) { } } -declare const global: { - XDomainRequest: unknown; -}; - const noop = function () {}; let idCounter = 0; const pendingRequests: Record = {}; -const isIE = typeof global !== 'undefined' && global.XDomainRequest; - -function ieVersion() { - const match = navigator.userAgent.toString().match(/MSIE\s([\d.]+)/); - return match && Number(match[1]); -} - -function needJsonEnvelope() { - /* IE 10 xhr bug: http://stackoverflow.com/a/16320339 */ - let version; - return isIE && (version = ieVersion()) && version === 10; -} - function getHeader(xhr: XMLHttpRequest, header: string) { return xhr.getResponseHeader && xhr.getResponseHeader(header); } @@ -56,10 +39,10 @@ function isEncodingChunked(xhr: XMLHttpRequest) { } function getHeadersAsObject(xhr: XMLHttpRequest) { - const headerPairs = Utils.trim(xhr.getAllResponseHeaders()).split('\r\n'); + const headerPairs = xhr.getAllResponseHeaders().trim().split('\r\n'); const headers: Record = {}; for (let i = 0; i < headerPairs.length; i++) { - const parts = headerPairs[i].split(':').map(Utils.trim); + const parts = headerPairs[i].split(':').map((x) => x.trim()); headers[parts[0].toLowerCase()] = parts[1]; } return headers; @@ -68,7 +51,7 @@ function getHeadersAsObject(xhr: XMLHttpRequest) { class XHRRequest extends EventEmitter implements IXHRRequest { uri: string; headers: Record; - body: unknown; + body: RequestBody | null; method: string; requestMode: number; timeouts: Record; @@ -83,19 +66,18 @@ class XHRRequest extends EventEmitter implements IXHRRequest { uri: string, headers: Record | null, params: Record, - body: unknown, + body: RequestBody | null, requestMode: number, timeouts: Record, - method?: HttpMethods + method?: HttpMethods, ) { super(); params = params || {}; params.rnd = Utils.cheapRandStr(); - if (needJsonEnvelope() && !params.envelope) params.envelope = 'json'; this.uri = uri + Utils.toQueryString(params); this.headers = headers || {}; this.body = body; - this.method = method ? method.toUpperCase() : Utils.isEmptyArg(body) ? 'GET' : 'POST'; + this.method = method ? method.toUpperCase() : Utils.isNil(body) ? 'GET' : 'POST'; this.requestMode = requestMode; this.timeouts = timeouts; this.timedOut = false; @@ -108,10 +90,10 @@ class XHRRequest extends EventEmitter implements IXHRRequest { uri: string, headers: Record | null, params: RequestParams, - body: unknown, + body: RequestBody | null, requestMode: number, timeouts: Record | null, - method?: HttpMethods + method?: HttpMethods, ): XHRRequest { /* XHR requests are used either with the context being a realtime * transport, or with timeouts passed in (for when used by a rest client), @@ -124,7 +106,7 @@ class XHRRequest extends EventEmitter implements IXHRRequest { body, requestMode, _timeouts, - method + method, ); } @@ -133,7 +115,7 @@ class XHRRequest extends EventEmitter implements IXHRRequest { body?: unknown, headers?: Record | null, unpacked?: boolean | null, - statusCode?: number + statusCode?: number, ): void { if (!this.requestComplete) { this.requestComplete = true; @@ -191,7 +173,7 @@ class XHRRequest extends EventEmitter implements IXHRRequest { errorEvent: ProgressEvent, message: string, code: number | null, - statusCode: number + statusCode: number, ) => { let errorMessage = message + ' (event type: ' + errorEvent.type + ')'; if (this?.xhr?.statusText) errorMessage += ', current statusText is ' + this.xhr.statusText; @@ -273,7 +255,7 @@ class XHRRequest extends EventEmitter implements IXHRRequest { * is contains an error action (hence the nonsuccess statuscode), we can * consider the request to have succeeded, just pass it on to * onProtocolMessage to decide what to do */ - if (successResponse || Utils.isArray(parsedResponse)) { + if (successResponse || Array.isArray(parsedResponse)) { this.complete(null, parsedResponse, headers, unpacked, statusCode); return; } @@ -286,7 +268,7 @@ class XHRRequest extends EventEmitter implements IXHRRequest { ' body was: ' + Platform.Config.inspect(parsedResponse), null, - statusCode + statusCode, ); } this.complete(err, parsedResponse, headers, unpacked, statusCode); @@ -327,8 +309,6 @@ class XHRRequest extends EventEmitter implements IXHRRequest { if (xhr.status !== 0) { if (statusCode === undefined) { statusCode = xhr.status; - /* IE returns 1223 for 204: http://bugs.jquery.com/ticket/1450 */ - if (statusCode === 1223) statusCode = 204; onResponse(); } if (readyState == 3 && streaming) { diff --git a/src/platform/web/lib/transport/fetchrequest.ts b/src/platform/web/lib/transport/fetchrequest.ts deleted file mode 100644 index 9af6592628..0000000000 --- a/src/platform/web/lib/transport/fetchrequest.ts +++ /dev/null @@ -1,85 +0,0 @@ -import HttpMethods from 'common/constants/HttpMethods'; -import Rest from 'common/lib/client/rest'; -import ErrorInfo, { PartialErrorInfo } from 'common/lib/types/errorinfo'; -import { RequestCallback, RequestParams } from 'common/types/http'; -import Platform from 'common/platform'; -import Defaults from 'common/lib/util/defaults'; -import * as Utils from 'common/lib/util/utils'; -import { getGlobalObject } from 'common/lib/util/utils'; - -function isAblyError(responseBody: unknown, headers: Headers): responseBody is { error?: ErrorInfo } { - return !!headers.get('x-ably-errorcode'); -} - -function getAblyError(responseBody: unknown, headers: Headers) { - if (isAblyError(responseBody, headers)) { - return responseBody.error && ErrorInfo.fromValues(responseBody.error); - } -} - -export default function fetchRequest( - method: HttpMethods, - rest: Rest | null, - uri: string, - headers: Record | null, - params: RequestParams, - body: unknown, - callback: RequestCallback -) { - const fetchHeaders = new Headers(headers || {}); - const _method = method ? method.toUpperCase() : Utils.isEmptyArg(body) ? 'GET' : 'POST'; - - const controller = new AbortController(); - - const timeout = setTimeout( - () => { - controller.abort(); - callback(new PartialErrorInfo('Request timed out', null, 408)); - }, - rest ? rest.options.timeouts.httpRequestTimeout : Defaults.TIMEOUTS.httpRequestTimeout - ); - - const requestInit: RequestInit = { - method: _method, - headers: fetchHeaders, - body: body as any, - }; - - if (!Platform.Config.isWebworker) { - requestInit.credentials = fetchHeaders.has('authorization') ? 'include' : 'same-origin'; - } - - getGlobalObject() - .fetch(uri + '?' + new URLSearchParams(params || {}), requestInit) - .then((res) => { - clearTimeout(timeout); - const contentType = res.headers.get('Content-Type'); - let prom; - if (contentType && contentType.indexOf('application/x-msgpack') > -1) { - prom = res.arrayBuffer(); - } else if (contentType && contentType.indexOf('application/json') > -1) { - prom = res.json(); - } else { - prom = res.text(); - } - prom.then((body) => { - const unpacked = !!contentType && contentType.indexOf('application/x-msgpack') === -1; - if (!res.ok) { - const err = - getAblyError(body, res.headers) || - new PartialErrorInfo( - 'Error response received from server: ' + res.status + ' body was: ' + Platform.Config.inspect(body), - null, - res.status - ); - callback(err, body, res.headers, unpacked, res.status); - } else { - callback(null, body, res.headers, unpacked, res.status); - } - }); - }) - .catch((err) => { - clearTimeout(timeout); - callback(err); - }); -} diff --git a/src/platform/web/lib/transport/index.js b/src/platform/web/lib/transport/index.js deleted file mode 100644 index 2e53447e06..0000000000 --- a/src/platform/web/lib/transport/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import JSONPTransport from './jsonptransport'; -import XHRPollingTransport from './xhrpollingtransport'; -import XHRStreamingTransport from './xhrstreamingtransport'; - -export default [JSONPTransport, XHRPollingTransport, XHRStreamingTransport]; diff --git a/src/platform/web/lib/transport/index.ts b/src/platform/web/lib/transport/index.ts new file mode 100644 index 0000000000..25515a9e77 --- /dev/null +++ b/src/platform/web/lib/transport/index.ts @@ -0,0 +1,23 @@ +import TransportName from 'common/constants/TransportName'; +import Platform from 'common/platform'; +import XhrPollingTransport from './xhrpollingtransport'; +import WebSocketTransport from '../../../../common/lib/transport/websockettransport'; + +// For reasons that I don’t understand, if we use [TransportNames.XhrPolling] for the keys in defaultTransports’s, then defaultTransports does not get tree-shaken. Hence using literals instead. They’re still correctly type-checked. + +const order: TransportName[] = ['xhr_polling']; + +const defaultTransports: (typeof Platform)['Transports'] = { + order, + bundledImplementations: { + web_socket: WebSocketTransport, + xhr_polling: XhrPollingTransport, + }, +}; + +export default defaultTransports; + +export const ModularTransports: (typeof Platform)['Transports'] = { + order, + bundledImplementations: {}, +}; diff --git a/src/platform/web/lib/transport/jsonptransport.ts b/src/platform/web/lib/transport/jsonptransport.ts deleted file mode 100644 index dac890a147..0000000000 --- a/src/platform/web/lib/transport/jsonptransport.ts +++ /dev/null @@ -1,256 +0,0 @@ -import * as Utils from 'common/lib/util/utils'; -import CometTransport from 'common/lib/transport/comettransport'; -import Platform from 'common/platform'; -import EventEmitter from 'common/lib/util/eventemitter'; -import ErrorInfo, { IPartialErrorInfo, PartialErrorInfo } from 'common/lib/types/errorinfo'; -import Defaults from 'common/lib/util/defaults'; -import Logger from 'common/lib/util/logger'; -import Auth from 'common/lib/client/auth'; -import HttpMethods from 'common/constants/HttpMethods'; -import { RequestParams } from 'common/types/http'; -import ConnectionManager, { TransportParams } from 'common/lib/transport/connectionmanager'; -import XHRStates from 'common/constants/XHRStates'; - -// Workaround for salesforce lightning locker compatibility -let globalObject = Utils.getGlobalObject() as unknown as { - _ablyjs_jsonp: Record; - JSONPTransport: typeof JSONPTransport; -}; - -const noop = function () {}; -/* Can't just use window.Ably, as that won't exist if using the commonjs version. */ -const _: Record = (globalObject._ablyjs_jsonp = {}); - -/* express strips out parantheses from the callback! - * Kludge to still alow its responses to work, while not keeping the - * function form for normal use and not cluttering window.Ably - * https://github.com/expressjs/express/blob/5b4d4b4ab1324743534fbcd4709f4e75bb4b4e9d/lib/response.js#L305 - */ -_._ = function (id: string) { - return _['_' + id] || noop; -}; -let idCounter = 1; -const shortName = 'jsonp'; - -export function createRequest( - uri: string, - headers: Record | null, - params?: RequestParams, - body?: unknown, - requestMode?: number, - timeouts?: Record | null, - method?: HttpMethods -) { - /* JSONP requests are used either with the context being a realtime - * transport, or with timeouts passed in (for when used by a rest client), - * or completely standalone. Use the appropriate timeouts in each case */ - timeouts = timeouts || Defaults.TIMEOUTS; - return new Request( - undefined, - uri, - headers, - Utils.copy(params), - body, - requestMode as number, - timeouts as Record, - method - ); -} - -class JSONPTransport extends CometTransport { - shortName = shortName; - - constructor(connectionManager: ConnectionManager, auth: Auth, params: TransportParams) { - super(connectionManager, auth, params); - params.stream = false; - } - - static isAvailable() { - return Platform.Config.jsonpSupported && Platform.Config.allowComet; - } - - toString() { - return 'JSONPTransport; uri=' + this.baseUri + '; isConnected=' + this.isConnected; - } - - createRequest( - uri: string, - headers: Record | null, - params?: Record, - body?: unknown, - requestMode?: number, - timeouts?: Record, - method?: HttpMethods - ) { - /* JSONP requests are used either with the context being a realtime - * transport, or with timeouts passed in (for when used by a rest client), - * or completely standalone. Use the appropriate timeouts in each case */ - timeouts = this?.timeouts || timeouts || Defaults.TIMEOUTS; - return createRequest(uri, headers, params, body, requestMode, timeouts, method); - } -} - -export class Request extends EventEmitter { - id: string | number; - uri: string; - params: Record; - body: unknown; - requestMode: number; - timeouts: Record; - requestComplete: boolean; - method?: HttpMethods; - script?: HTMLScriptElement; - timer?: number | NodeJS.Timeout | null; - - constructor( - id: string | number | undefined, - uri: string, - headers: Record | null, - params: Record | null, - body: unknown | null, - requestMode: number, - timeouts: Record, - method?: HttpMethods - ) { - super(); - if (id === undefined) id = idCounter++; - this.id = id; - this.uri = uri; - this.params = params || {}; - this.params.rnd = Utils.cheapRandStr(); - if (headers) { - /* JSONP doesn't allow headers. Cherry-pick a couple to turn into qs params */ - if (headers['X-Ably-Version']) this.params.v = headers['X-Ably-Version']; - if (headers['X-Ably-Lib']) this.params.lib = headers['X-Ably-Lib']; - } - this.body = body; - this.method = method; - this.requestMode = requestMode; - this.timeouts = timeouts; - this.requestComplete = false; - } - - exec() { - const id = this.id, - body = this.body, - method = this.method, - uri = this.uri, - params = this.params; - - params.callback = '_ablyjs_jsonp._(' + id + ')'; - - params.envelope = 'jsonp'; - if (body) { - params.body = body as string; - } - if (method && method !== 'get') { - params.method = method; - } - - const script = (this.script = document.createElement('script')); - const src = uri + Utils.toQueryString(params); - script.src = src; - if (script.src.split('/').slice(-1)[0] !== src.split('/').slice(-1)[0]) { - /* The src has been truncated. Can't abort, but can at least emit an - * error so the user knows what's gone wrong. (Can't compare strings - * directly as src may have a port, script.src won't) */ - Logger.logAction( - Logger.LOG_ERROR, - 'JSONP Request.exec()', - 'Warning: the browser appears to have truncated the script URI. This will likely result in the request failing due to an unparseable body param' - ); - } - script.async = true; - script.type = 'text/javascript'; - script.charset = 'UTF-8'; - script.onerror = (err: string | Event) => { - this.complete( - new PartialErrorInfo('JSONP script error (event: ' + Platform.Config.inspect(err) + ')', null, 400) - ); - }; - - type JSONPResponse = { - statusCode?: number; - response: { - error?: ErrorInfo; - }; - headers?: Record; - }; - - _['_' + id] = (message: JSONPResponse) => { - if (message.statusCode) { - /* Handle as enveloped jsonp, as all jsonp transport uses should be */ - const response = message.response; - if (message.statusCode == 204) { - this.complete(null, null, null, message.statusCode); - } else if (!response) { - this.complete(new PartialErrorInfo('Invalid server response: no envelope detected', null, 500)); - } else if (message.statusCode < 400 || Utils.isArray(response)) { - /* If response is an array, it's an array of protocol messages -- even if - * it contains an error action (hence the nonsuccess statuscode), we can - * consider the request to have succeeded, just pass it on to - * onProtocolMessage to decide what to do */ - this.complete(null, response, message.headers, message.statusCode); - } else { - const err = - response.error || new PartialErrorInfo('Error response received from server', null, message.statusCode); - this.complete(err); - } - } else { - /* Handle as non-enveloped -- as will be eg from a customer's authUrl server */ - this.complete(null, message); - } - }; - - const timeout = - this.requestMode == XHRStates.REQ_SEND ? this.timeouts.httpRequestTimeout : this.timeouts.recvTimeout; - this.timer = setTimeout(this.abort.bind(this), timeout); - let head = document.getElementsByTagName('head')[0]; - (head as HTMLHeadElement).insertBefore(script, (head as HTMLHeadElement).firstChild); - } - - complete( - err?: IPartialErrorInfo | null, - body?: unknown, - headers?: Record | null, - statusCode?: number - ) { - headers = headers || {}; - if (!this.requestComplete) { - this.requestComplete = true; - let contentType; - if (body) { - contentType = typeof body == 'string' ? 'text/plain' : 'application/json'; - headers['content-type'] = contentType; - this.emit('data', body); - } - - this.emit('complete', err, body, headers, /* unpacked: */ true, statusCode); - this.dispose(); - } - } - - abort() { - this.dispose(); - } - - dispose() { - const timer = this.timer; - if (timer) { - clearTimeout(timer as NodeJS.Timeout); - this.timer = null; - } - const script = this.script as HTMLScriptElement; - if (script.parentNode) script.parentNode.removeChild(script); - delete _[this.id]; - this.emit('disposed'); - } -} - -export default function (connectionManager: typeof ConnectionManager): typeof JSONPTransport { - globalObject.JSONPTransport = JSONPTransport; - if (JSONPTransport.isAvailable()) { - connectionManager.supportedTransports[shortName] = JSONPTransport; - } - return JSONPTransport; -} diff --git a/src/platform/web/lib/transport/withoutjsonp.js b/src/platform/web/lib/transport/withoutjsonp.js deleted file mode 100644 index 983f0cf0f4..0000000000 --- a/src/platform/web/lib/transport/withoutjsonp.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * This file exists for React Native and Nativescript in order to exclude the unsupported JSONP transport from these platforms. - */ -import XHRPollingTransport from './xhrpollingtransport'; -import XHRStreamingTransport from './xhrstreamingtransport'; - -export default [XHRPollingTransport, XHRStreamingTransport]; diff --git a/src/platform/web/lib/transport/xhrpollingtransport.js b/src/platform/web/lib/transport/xhrpollingtransport.js deleted file mode 100644 index af75d43abb..0000000000 --- a/src/platform/web/lib/transport/xhrpollingtransport.js +++ /dev/null @@ -1,35 +0,0 @@ -import * as Utils from '../../../../common/lib/util/utils'; -import Platform from '../../../../common/platform'; -import CometTransport from '../../../../common/lib/transport/comettransport'; -import XHRRequest from './xhrrequest'; - -var XHRPollingTransport = function (connectionManager) { - var shortName = 'xhr_polling'; - - function XHRPollingTransport(connectionManager, auth, params) { - params.stream = false; - CometTransport.call(this, connectionManager, auth, params); - this.shortName = shortName; - } - Utils.inherits(XHRPollingTransport, CometTransport); - - XHRPollingTransport.isAvailable = function () { - return Platform.Config.xhrSupported && Platform.Config.allowComet; - }; - - XHRPollingTransport.prototype.toString = function () { - return 'XHRPollingTransport; uri=' + this.baseUri + '; isConnected=' + this.isConnected; - }; - - XHRPollingTransport.prototype.createRequest = function (uri, headers, params, body, requestMode) { - return XHRRequest.createRequest(uri, headers, params, body, requestMode, this.timeouts); - }; - - if (typeof connectionManager !== 'undefined' && XHRPollingTransport.isAvailable()) { - connectionManager.supportedTransports[shortName] = XHRPollingTransport; - } - - return XHRPollingTransport; -}; - -export default XHRPollingTransport; diff --git a/src/platform/web/lib/transport/xhrpollingtransport.ts b/src/platform/web/lib/transport/xhrpollingtransport.ts new file mode 100644 index 0000000000..12268fed86 --- /dev/null +++ b/src/platform/web/lib/transport/xhrpollingtransport.ts @@ -0,0 +1,37 @@ +import Platform from '../../../../common/platform'; +import CometTransport from '../../../../common/lib/transport/comettransport'; +import XHRRequest from '../http/request/xhrrequest'; +import ConnectionManager, { TransportParams } from 'common/lib/transport/connectionmanager'; +import Auth from 'common/lib/client/auth'; +import { RequestBody, RequestParams } from 'common/types/http'; +import { TransportNames } from 'common/constants/TransportName'; + +var shortName = TransportNames.XhrPolling; +class XHRPollingTransport extends CometTransport { + shortName = shortName; + constructor(connectionManager: ConnectionManager, auth: Auth, params: TransportParams) { + super(connectionManager, auth, params); + params.stream = false; + this.shortName = shortName; + } + + static isAvailable() { + return !!(Platform.Config.xhrSupported && Platform.Config.allowComet); + } + + toString() { + return 'XHRPollingTransport; uri=' + this.baseUri + '; isConnected=' + this.isConnected; + } + + createRequest( + uri: string, + headers: Record, + params: RequestParams, + body: RequestBody | null, + requestMode: number, + ) { + return XHRRequest.createRequest(uri, headers, params, body, requestMode, this.timeouts); + } +} + +export default XHRPollingTransport; diff --git a/src/platform/web/lib/transport/xhrstreamingtransport.js b/src/platform/web/lib/transport/xhrstreamingtransport.js deleted file mode 100644 index faf40f15ff..0000000000 --- a/src/platform/web/lib/transport/xhrstreamingtransport.js +++ /dev/null @@ -1,35 +0,0 @@ -import * as Utils from '../../../../common/lib/util/utils'; -import CometTransport from '../../../../common/lib/transport/comettransport'; -import Platform from '../../../../common/platform'; -import XHRRequest from './xhrrequest'; - -var XHRStreamingTransport = function (connectionManager) { - var shortName = 'xhr_streaming'; - - /* public constructor */ - function XHRStreamingTransport(connectionManager, auth, params) { - CometTransport.call(this, connectionManager, auth, params); - this.shortName = shortName; - } - Utils.inherits(XHRStreamingTransport, CometTransport); - - XHRStreamingTransport.isAvailable = function () { - return Platform.Config.xhrSupported && Platform.Config.streamingSupported && Platform.Config.allowComet; - }; - - XHRStreamingTransport.prototype.toString = function () { - return 'XHRStreamingTransport; uri=' + this.baseUri + '; isConnected=' + this.isConnected; - }; - - XHRStreamingTransport.prototype.createRequest = function (uri, headers, params, body, requestMode) { - return XHRRequest.createRequest(uri, headers, params, body, requestMode, this.timeouts); - }; - - if (typeof connectionManager !== 'undefined' && XHRStreamingTransport.isAvailable()) { - connectionManager.supportedTransports[shortName] = XHRStreamingTransport; - } - - return XHRStreamingTransport; -}; - -export default XHRStreamingTransport; diff --git a/src/platform/web/lib/util/bufferutils.ts b/src/platform/web/lib/util/bufferutils.ts index 2210f281cb..279112ed95 100644 --- a/src/platform/web/lib/util/bufferutils.ts +++ b/src/platform/web/lib/util/bufferutils.ts @@ -1,36 +1,19 @@ -import { parse as parseHex, stringify as stringifyHex } from 'crypto-js/build/enc-hex'; -import { parse as parseUtf8, stringify as stringifyUtf8 } from 'crypto-js/build/enc-utf8'; -import { parse as parseBase64, stringify as stringifyBase64 } from 'crypto-js/build/enc-base64'; -import WordArray from 'crypto-js/build/lib-typedarrays'; import Platform from 'common/platform'; -import { TypedArray } from 'common/types/IPlatformConfig'; import IBufferUtils from 'common/types/IBufferUtils'; +import { hmac as hmacSha256 } from './hmac-sha256'; /* Most BufferUtils methods that return a binary object return an ArrayBuffer - * if supported, else a CryptoJS WordArray. The exception is toBuffer, which - * returns a Uint8Array (and won't work on browsers too old to support it) */ + * The exception is toBuffer, which returns a Uint8Array (and won't work on + * browsers too old to support it) */ -export type Bufferlike = WordArray | ArrayBuffer | TypedArray; +export type Bufferlike = BufferSource; export type Output = Bufferlike; export type ToBufferOutput = Uint8Array; -export type ComparableBuffer = ArrayBuffer; -class BufferUtils implements IBufferUtils { +class BufferUtils implements IBufferUtils { base64CharSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; hexCharSet = '0123456789abcdef'; - isWordArray(ob: unknown): ob is WordArray { - return ob !== null && ob !== undefined && (ob as WordArray).sigBytes !== undefined; - } - - isArrayBuffer(ob: unknown): ob is ArrayBuffer { - return ob !== null && ob !== undefined && (ob as ArrayBuffer).constructor === ArrayBuffer; - } - - isTypedArray(ob: unknown): ob is TypedArray { - return !!ArrayBuffer && ArrayBuffer.isView && ArrayBuffer.isView(ob); - } - // // https://gist.githubusercontent.com/jonleighton/958841/raw/f200e30dfe95212c0165ccf1ae000ca51e9de803/gistfile1.js uint8ViewToBase64(bytes: Uint8Array) { let base64 = ''; @@ -95,7 +78,7 @@ class BufferUtils implements IBufferUtils>> 2] >>> (24 - (i % 4) * 8)) & 0xff; - } - - return uint8View; - } - - throw new Error('BufferUtils.toBuffer expected an arraybuffer, typed array, or CryptoJS wordarray'); + throw new Error('BufferUtils.toBuffer expected an ArrayBuffer or a view onto one'); } toArrayBuffer(buffer: Bufferlike): ArrayBuffer { - if (this.isArrayBuffer(buffer)) { + if (buffer instanceof ArrayBuffer) { return buffer; } return this.toBuffer(buffer).buffer; } - toWordArray(buffer: Bufferlike | number[]) { - if (this.isTypedArray(buffer)) { - buffer = buffer.buffer; - } - return this.isWordArray(buffer) ? buffer : WordArray.create(buffer as number[]); - } - base64Encode(buffer: Bufferlike) { - if (this.isWordArray(buffer)) { - return stringifyBase64(buffer); - } return this.uint8ViewToBase64(this.toBuffer(buffer)); } base64Decode(str: string): Output { if (ArrayBuffer && Platform.Config.atob) { return this.base64ToArrayBuffer(str); + } else { + throw new Error('Expected ArrayBuffer to exist and Platform.Config.atob to be configured'); } - return parseBase64(str); } hexEncode(buffer: Bufferlike) { - return stringifyHex(this.toWordArray(buffer)); + const arrayBuffer = + buffer instanceof ArrayBuffer + ? buffer + : buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength); + const uint8Array = new Uint8Array(arrayBuffer); + return uint8Array.reduce((accum, byte) => accum + byte.toString(16).padStart(2, '0'), ''); } - hexDecode(string: string) { - var wordArray = parseHex(string); - return ArrayBuffer ? this.toArrayBuffer(wordArray) : wordArray; + hexDecode(hexEncodedBytes: string) { + if (hexEncodedBytes.length % 2 !== 0) { + throw new Error("Can't create a byte array from a hex string of odd length"); + } + + const uint8Array = new Uint8Array(hexEncodedBytes.length / 2); + + for (let i = 0; i < uint8Array.length; i++) { + uint8Array[i] = parseInt(hexEncodedBytes.slice(2 * i, 2 * (i + 1)), 16); + } + + return uint8Array.buffer.slice(uint8Array.byteOffset, uint8Array.byteOffset + uint8Array.byteLength); } utf8Encode(string: string) { if (Platform.Config.TextEncoder) { return new Platform.Config.TextEncoder().encode(string).buffer; + } else { + throw new Error('Expected TextEncoder to be configured'); } - return parseUtf8(string); } /* For utf8 decoding we apply slightly stricter input validation than to @@ -179,46 +155,45 @@ class BufferUtils implements IBufferUtils; +type InputPlaintext = CryptoDataTypes.InputPlaintext; +type OutputCiphertext = ArrayBuffer; +type InputCiphertext = CryptoDataTypes.InputCiphertext; +type OutputPlaintext = ArrayBuffer; + +var createCryptoClass = function (config: IPlatformConfig, bufferUtils: typeof BufferUtils) { + var DEFAULT_ALGORITHM = 'aes'; + var DEFAULT_KEYLENGTH = 256; // bits + var DEFAULT_MODE = 'cbc'; + var DEFAULT_BLOCKLENGTH = 16; // bytes + + /** + * Internal: checks that the cipherParams are a valid combination. Currently + * just checks that the calculated keyLength is a valid one for aes-cbc + */ + function validateCipherParams(params: API.CipherParams) { + if (params.algorithm === 'aes' && params.mode === 'cbc') { + if (params.keyLength === 128 || params.keyLength === 256) { + return; + } + throw new Error( + 'Unsupported key length ' + + params.keyLength + + ' for aes-cbc encryption. Encryption key must be 128 or 256 bits (16 or 32 ASCII characters)', + ); + } + } + + function normaliseBase64(string: string) { + /* url-safe base64 strings use _ and - instread of / and + */ + return string.replace('_', '/').replace('-', '+'); + } + + function isCipherParams(params: API.CipherParams | API.CipherParamOptions): params is API.CipherParams { + // Although API.CipherParams is an interface, the documentation for its `key` property makes it clear that the only valid way to form one is by using getDefaultParams. The implementation of getDefaultParams returns an instance of CipherParams. + return params instanceof CipherParams; + } + + /** + * A class encapsulating the client-specifiable parameters for + * the cipher. + * + * algorithm is the name of the algorithm in the default system provider, + * or the lower-cased version of it; eg "aes" or "AES". + * + * Clients are recommended to not call this directly, but instead to use the + * Crypto.getDefaultParams helper, which will fill in any fields not supplied + * with default values and validation the result. + */ + class CipherParams implements API.CipherParams { + algorithm: string; + keyLength: number; + mode: string; + key: ArrayBuffer; + + constructor(algorithm: string, keyLength: number, mode: string, key: ArrayBuffer) { + this.algorithm = algorithm; + this.keyLength = keyLength; + this.mode = mode; + this.key = key; + } + } + + /** + * Utility classes and interfaces for message payload encryption. + * + * This class supports AES/CBC/PKCS5 with a default keylength of 128 bits + * but supporting other keylengths. Other algorithms and chaining modes are + * not supported directly, but supportable by extending/implementing the base + * classes and interfaces here. + * + * Secure random data for creation of Initialization Vectors (IVs) and keys + * is obtained from window.crypto.getRandomValues. + * + * Each message payload is encrypted with an IV in CBC mode, and the IV is + * concatenated with the resulting raw ciphertext to construct the "ciphertext" + * data passed to the recipient. + */ + class Crypto { + static CipherParams = CipherParams; + + /** + * Obtain a complete CipherParams instance from the provided params, filling + * in any not provided with default values, calculating a keyLength from + * the supplied key, and validating the result. + * @param params an object containing at a minimum a `key` key with value the + * key, as either a binary or a base64-encoded string. + * May optionally also contain: algorithm (defaults to AES), + * mode (defaults to 'cbc') + */ + static getDefaultParams(params: API.CipherParamOptions) { + var key: ArrayBuffer; + + if (!params.key) { + throw new Error('Crypto.getDefaultParams: a key is required'); + } + + if (typeof params.key === 'string') { + key = bufferUtils.toArrayBuffer(bufferUtils.base64Decode(normaliseBase64(params.key))); + } else if (params.key instanceof ArrayBuffer) { + key = params.key; + } else { + key = bufferUtils.toArrayBuffer(params.key); + } + + var algorithm = params.algorithm || DEFAULT_ALGORITHM; + var keyLength = key.byteLength * 8; + var mode = params.mode || DEFAULT_MODE; + var cipherParams = new CipherParams(algorithm, keyLength, mode, key); + + if (params.keyLength && params.keyLength !== cipherParams.keyLength) { + throw new Error( + 'Crypto.getDefaultParams: a keyLength of ' + + params.keyLength + + ' was specified, but the key actually has length ' + + cipherParams.keyLength, + ); + } + + validateCipherParams(cipherParams); + return cipherParams; + } + + /** + * Generate a random encryption key from the supplied keylength (or the + * default keyLength if none supplied) as an ArrayBuffer + * @param keyLength (optional) the required keyLength in bits + */ + static async generateRandomKey(keyLength?: number): Promise { + try { + return config.getRandomArrayBuffer((keyLength || DEFAULT_KEYLENGTH) / 8); + } catch (err) { + throw new ErrorInfo('Failed to generate random key: ' + (err as Error).message, 400, 50000, err as Error); + } + } + + /** + * Internal; get a ChannelCipher instance based on the given cipherParams + * @param params either a CipherParams instance or some subset of its + * fields that includes a key + */ + static getCipher(params: IGetCipherParams) { + var cipherParams = isCipherParams(params) ? (params as CipherParams) : this.getDefaultParams(params); + + return { + cipherParams: cipherParams, + cipher: new CBCCipher(cipherParams, params.iv ?? null), + }; + } + } + + Crypto satisfies ICryptoStatic; + + class CBCCipher implements ICipher { + algorithm: string; + webCryptoAlgorithm: string; + key: ArrayBuffer; + iv: ArrayBuffer | null; + + constructor(params: CipherParams, iv: IV | null) { + if (!crypto.subtle) { + if (isSecureContext) { + throw new Error( + 'Crypto operations are not possible since the browser’s SubtleCrypto class is unavailable (reason unknown).', + ); + } else { + throw new Error( + 'Crypto operations are is not possible since the current environment is a non-secure context and hence the browser’s SubtleCrypto class is not available.', + ); + } + } + + this.algorithm = params.algorithm + '-' + String(params.keyLength) + '-' + params.mode; + this.webCryptoAlgorithm = params.algorithm + '-' + params.mode; + this.key = bufferUtils.toArrayBuffer(params.key); + this.iv = iv ? bufferUtils.toArrayBuffer(iv) : null; + } + + private concat(buffer1: Bufferlike, buffer2: Bufferlike) { + const output = new ArrayBuffer(buffer1.byteLength + buffer2.byteLength); + const outputView = new DataView(output); + + const buffer1View = new DataView(bufferUtils.toArrayBuffer(buffer1)); + for (let i = 0; i < buffer1View.byteLength; i++) { + outputView.setInt8(i, buffer1View.getInt8(i)); + } + + const buffer2View = new DataView(bufferUtils.toArrayBuffer(buffer2)); + for (let i = 0; i < buffer2View.byteLength; i++) { + outputView.setInt8(buffer1View.byteLength + i, buffer2View.getInt8(i)); + } + + return output; + } + + async encrypt(plaintext: InputPlaintext): Promise { + Logger.logAction(Logger.LOG_MICRO, 'CBCCipher.encrypt()', ''); + + const iv = await this.getIv(); + const cryptoKey = await crypto.subtle.importKey('raw', this.key, this.webCryptoAlgorithm, false, ['encrypt']); + const ciphertext = await crypto.subtle.encrypt({ name: this.webCryptoAlgorithm, iv }, cryptoKey, plaintext); + + return this.concat(iv, ciphertext); + } + + async decrypt(ciphertext: InputCiphertext): Promise { + Logger.logAction(Logger.LOG_MICRO, 'CBCCipher.decrypt()', ''); + + const ciphertextArrayBuffer = bufferUtils.toArrayBuffer(ciphertext); + const iv = ciphertextArrayBuffer.slice(0, DEFAULT_BLOCKLENGTH); + const ciphertextBody = ciphertextArrayBuffer.slice(DEFAULT_BLOCKLENGTH); + + const cryptoKey = await crypto.subtle.importKey('raw', this.key, this.webCryptoAlgorithm, false, ['decrypt']); + return crypto.subtle.decrypt({ name: this.webCryptoAlgorithm, iv }, cryptoKey, ciphertextBody); + } + + async getIv(): Promise { + if (this.iv) { + var iv = this.iv; + this.iv = null; + return iv; + } + + const randomBlock = await config.getRandomArrayBuffer(DEFAULT_BLOCKLENGTH); + return bufferUtils.toArrayBuffer(randomBlock); + } + } + + return Crypto; +}; + +export { createCryptoClass }; diff --git a/src/platform/web/lib/util/defaults.ts b/src/platform/web/lib/util/defaults.ts index d5c26535b0..c2bfd52ecc 100644 --- a/src/platform/web/lib/util/defaults.ts +++ b/src/platform/web/lib/util/defaults.ts @@ -1,33 +1,13 @@ import IDefaults from 'common/types/IDefaults'; -import TransportNames from 'common/constants/TransportNames'; +import { TransportNames } from 'common/constants/TransportName'; const Defaults: IDefaults = { connectivityCheckUrl: 'https://internet-up.ably-realtime.com/is-the-internet-up.txt', - jsonpInternetUpUrl: 'https://internet-up.ably-realtime.com/is-the-internet-up-0-9.js', + wsConnectivityUrl: 'wss://ws-up.ably-realtime.com', /* Order matters here: the base transport is the leftmost one in the * intersection of baseTransportOrder and the transports clientOption that's - * supported. This is not quite the same as the preference order -- e.g. - * xhr_polling is preferred to jsonp, but for browsers that support it we want - * the base transport to be xhr_polling, not jsonp */ - defaultTransports: [ - TransportNames.XhrPolling, - TransportNames.XhrStreaming, - TransportNames.JsonP, - TransportNames.WebSocket, - ], - baseTransportOrder: [ - TransportNames.XhrPolling, - TransportNames.XhrStreaming, - TransportNames.JsonP, - TransportNames.WebSocket, - ], - transportPreferenceOrder: [ - TransportNames.JsonP, - TransportNames.XhrPolling, - TransportNames.XhrStreaming, - TransportNames.WebSocket, - ], - upgradeTransports: [TransportNames.XhrStreaming, TransportNames.WebSocket], + * supported. */ + defaultTransports: [TransportNames.XhrPolling, TransportNames.WebSocket], }; export default Defaults; diff --git a/src/platform/web/lib/util/hmac-sha256.ts b/src/platform/web/lib/util/hmac-sha256.ts new file mode 100644 index 0000000000..dd2ac76711 --- /dev/null +++ b/src/platform/web/lib/util/hmac-sha256.ts @@ -0,0 +1,217 @@ +/** + * Copied from https://gist.github.com/stevendesu/2d52f7b5e1f1184af3b667c0b5e054b8 + * + * "A simple, open-source, HMAC-SHA256 implementation in pure JavaScript. Designed for efficient minification." + * + * I asked about licensing, and the author said: + * + * > Feel free to use it however you'd like 😄 As the gist title indicates, + * > this is "a simple open source implementation". Feel free to choose whatever + * > license you find most permissible, but I offer no warranty for the code. + * > It's 100% free to do with as you please. + */ + +// To ensure cross-browser support even without a proper SubtleCrypto +// impelmentation (or without access to the impelmentation, as is the case with +// Chrome loaded over HTTP instead of HTTPS), this library can create SHA-256 +// HMAC signatures using nothing but raw JavaScript + +/* eslint-disable no-magic-numbers, id-length, no-param-reassign, new-cap */ + +// By giving internal functions names that we can mangle, future calls to +// them are reduced to a single byte (minor space savings in minified file) +var uint8Array = Uint8Array; +var uint32Array = Uint32Array; +var pow = Math.pow; + +// Will be initialized below +// Using a Uint32Array instead of a simple array makes the minified code +// a bit bigger (we lose our `unshift()` hack), but comes with huge +// performance gains +var DEFAULT_STATE = new uint32Array(8); +var ROUND_CONSTANTS: number[] = []; + +// Reusable object for expanded message +// Using a Uint32Array instead of a simple array makes the minified code +// 7 bytes larger, but comes with huge performance gains +var M = new uint32Array(64); + +// After minification the code to compute the default state and round +// constants is smaller than the output. More importantly, this serves as a +// good educational aide for anyone wondering where the magic numbers come +// from. No magic numbers FTW! +function getFractionalBits(n: number) { + return ((n - (n | 0)) * pow(2, 32)) | 0; +} + +var n = 2, + nPrime = 0; +while (nPrime < 64) { + // isPrime() was in-lined from its original function form to save + // a few bytes + var isPrime = true; + // Math.sqrt() was replaced with pow(n, 1/2) to save a few bytes + // var sqrtN = pow(n, 1 / 2); + // So technically to determine if a number is prime you only need to + // check numbers up to the square root. However this function only runs + // once and we're only computing the first 64 primes (up to 311), so on + // any modern CPU this whole function runs in a couple milliseconds. + // By going to n / 2 instead of sqrt(n) we net 8 byte savings and no + // scaling performance cost + for (var factor = 2; factor <= n / 2; factor++) { + if (n % factor === 0) { + isPrime = false; + } + } + if (isPrime) { + if (nPrime < 8) { + DEFAULT_STATE[nPrime] = getFractionalBits(pow(n, 1 / 2)); + } + ROUND_CONSTANTS[nPrime] = getFractionalBits(pow(n, 1 / 3)); + + nPrime++; + } + + n++; +} + +// For cross-platform support we need to ensure that all 32-bit words are +// in the same endianness. A UTF-8 TextEncoder will return BigEndian data, +// so upon reading or writing to our ArrayBuffer we'll only swap the bytes +// if our system is LittleEndian (which is about 99% of CPUs) +var LittleEndian = !!new uint8Array(new uint32Array([1]).buffer)[0]; + +function convertEndian(word: number) { + if (LittleEndian) { + return ( + // byte 1 -> byte 4 + (word >>> 24) | + // byte 2 -> byte 3 + (((word >>> 16) & 0xff) << 8) | + // byte 3 -> byte 2 + ((word & 0xff00) << 8) | + // byte 4 -> byte 1 + (word << 24) + ); + } else { + return word; + } +} + +function rightRotate(word: number, bits: number) { + return (word >>> bits) | (word << (32 - bits)); +} + +function sha256(data: Uint8Array) { + // Copy default state + var STATE = DEFAULT_STATE.slice(); + + // Caching this reduces occurrences of ".length" in minified JavaScript + // 3 more byte savings! :D + var legth = data.length; + + // Pad data + var bitLength = legth * 8; + var newBitLength = 512 - ((bitLength + 64) % 512) - 1 + bitLength + 65; + + // "bytes" and "words" are stored BigEndian + var bytes = new uint8Array(newBitLength / 8); + var words = new uint32Array(bytes.buffer); + + bytes.set(data, 0); + // Append a 1 + bytes[legth] = 0b10000000; + // Store length in BigEndian + words[words.length - 1] = convertEndian(bitLength); + + // Loop iterator (avoid two instances of "var") -- saves 2 bytes + var round; + + // Process blocks (512 bits / 64 bytes / 16 words at a time) + for (var block = 0; block < newBitLength / 32; block += 16) { + var workingState = STATE.slice(); + + // Rounds + for (round = 0; round < 64; round++) { + var MRound; + // Expand message + if (round < 16) { + // Convert to platform Endianness for later math + MRound = convertEndian(words[block + round]); + } else { + var gamma0x = M[round - 15]; + var gamma1x = M[round - 2]; + MRound = + M[round - 7] + + M[round - 16] + + (rightRotate(gamma0x, 7) ^ rightRotate(gamma0x, 18) ^ (gamma0x >>> 3)) + + (rightRotate(gamma1x, 17) ^ rightRotate(gamma1x, 19) ^ (gamma1x >>> 10)); + } + + // M array matches platform endianness + M[round] = MRound |= 0; + + // Computation + var t1 = + (rightRotate(workingState[4], 6) ^ rightRotate(workingState[4], 11) ^ rightRotate(workingState[4], 25)) + + ((workingState[4] & workingState[5]) ^ (~workingState[4] & workingState[6])) + + workingState[7] + + MRound + + ROUND_CONSTANTS[round]; + var t2 = + (rightRotate(workingState[0], 2) ^ rightRotate(workingState[0], 13) ^ rightRotate(workingState[0], 22)) + + ((workingState[0] & workingState[1]) ^ (workingState[2] & (workingState[0] ^ workingState[1]))); + for (var i = 7; i > 0; i--) { + workingState[i] = workingState[i - 1]; + } + workingState[0] = (t1 + t2) | 0; + workingState[4] = (workingState[4] + t1) | 0; + } + + // Update state + for (round = 0; round < 8; round++) { + STATE[round] = (STATE[round] + workingState[round]) | 0; + } + } + + // Finally the state needs to be converted to BigEndian for output + // And we want to return a Uint8Array, not a Uint32Array + return new uint8Array( + new uint32Array( + STATE.map(function (val) { + return convertEndian(val); + }), + ).buffer, + ); +} + +export function hmac(key: Uint8Array, data: Uint8Array) { + if (key.length > 64) key = sha256(key); + + if (key.length < 64) { + const tmp = new Uint8Array(64); + tmp.set(key, 0); + key = tmp; + } + + // Generate inner and outer keys + var innerKey = new Uint8Array(64); + var outerKey = new Uint8Array(64); + for (var i = 0; i < 64; i++) { + innerKey[i] = 0x36 ^ key[i]; + outerKey[i] = 0x5c ^ key[i]; + } + + // Append the innerKey + var msg = new Uint8Array(data.length + 64); + msg.set(innerKey, 0); + msg.set(data, 64); + + // Has the previous message and append the outerKey + var result = new Uint8Array(64 + 32); + result.set(outerKey, 0); + result.set(sha256(msg), 64); + + // Hash the previous message + return sha256(result); +} diff --git a/src/platform/web/lib/util/http.ts b/src/platform/web/lib/util/http.ts deleted file mode 100644 index 2be9519de7..0000000000 --- a/src/platform/web/lib/util/http.ts +++ /dev/null @@ -1,328 +0,0 @@ -import Platform from 'common/platform'; -import * as Utils from 'common/lib/util/utils'; -import Defaults from 'common/lib/util/defaults'; -import ErrorInfo, { PartialErrorInfo } from 'common/lib/types/errorinfo'; -import { ErrnoException, IHttp, RequestCallback, RequestParams } from 'common/types/http'; -import HttpMethods from 'common/constants/HttpMethods'; -import Rest from 'common/lib/client/rest'; -import Realtime from 'common/lib/client/realtime'; -import XHRRequest from '../transport/xhrrequest'; -import XHRStates from 'common/constants/XHRStates'; -import Logger from 'common/lib/util/logger'; -import { StandardCallback } from 'common/types/utils'; -import { createRequest, Request } from '../transport/jsonptransport'; -import fetchRequest from '../transport/fetchrequest'; -import { NormalisedClientOptions } from 'common/types/ClientOptions'; -import { isSuccessCode } from 'common/constants/HttpStatusCodes'; - -function shouldFallback(errorInfo: ErrorInfo) { - const statusCode = errorInfo.statusCode as number; - /* 400 + no code = a generic xhr onerror. Browser doesn't give us enough - * detail to know whether it's fallback-fixable, but it may be (eg if a - * network issue), so try just in case */ - return ( - (statusCode === 408 && !errorInfo.code) || - (statusCode === 400 && !errorInfo.code) || - (statusCode >= 500 && statusCode <= 504) - ); -} - -function getHosts(client: Rest | Realtime): string[] { - /* If we're a connected realtime client, try the endpoint we're connected - * to first -- but still have fallbacks, being connected is not an absolute - * guarantee that a datacenter has free capacity to service REST requests. */ - const connection = (client as Realtime).connection, - connectionHost = connection && connection.connectionManager.host; - - if (connectionHost) { - return [connectionHost].concat(Defaults.getFallbackHosts(client.options)); - } - - return Defaults.getHosts(client.options); -} - -const Http: typeof IHttp = class { - static methods = [HttpMethods.Get, HttpMethods.Delete, HttpMethods.Post, HttpMethods.Put, HttpMethods.Patch]; - static methodsWithoutBody = [HttpMethods.Get, HttpMethods.Delete]; - static methodsWithBody = [HttpMethods.Post, HttpMethods.Put, HttpMethods.Patch]; - checksInProgress: Array> | null = null; - options: NormalisedClientOptions; - - constructor(options: NormalisedClientOptions) { - this.options = options || {}; - - const connectivityCheckUrl = this.options.connectivityCheckUrl || Defaults.connectivityCheckUrl; - const connectivityCheckParams = this.options.connectivityCheckParams; - const connectivityUrlIsDefault = !this.options.connectivityCheckUrl; - if (Platform.Config.xhrSupported) { - this.supportsAuthHeaders = true; - this.Request = function ( - method: HttpMethods, - rest: Rest | null, - uri: string, - headers: Record | null, - params: RequestParams, - body: unknown, - callback: RequestCallback - ) { - const req = XHRRequest.createRequest( - uri, - headers, - params, - body, - XHRStates.REQ_SEND, - rest && rest.options.timeouts, - method - ); - req.once('complete', callback); - req.exec(); - return req; - }; - if (this.options.disableConnectivityCheck) { - this.checkConnectivity = function (callback: (err: null, connectivity: true) => void) { - callback(null, true); - }; - } else { - this.checkConnectivity = function (callback: (err: ErrorInfo | null, connectivity: boolean) => void) { - Logger.logAction( - Logger.LOG_MICRO, - '(XHRRequest)Http.checkConnectivity()', - 'Sending; ' + connectivityCheckUrl - ); - this.doUri( - HttpMethods.Get, - null as any, - connectivityCheckUrl, - null, - null, - connectivityCheckParams, - function ( - err?: ErrorInfo | ErrnoException | null, - responseText?: unknown, - headers?: any, - unpacked?: boolean, - statusCode?: number - ) { - let result = false; - if (!connectivityUrlIsDefault) { - result = !err && isSuccessCode(statusCode as number); - } else { - result = !err && (responseText as string)?.replace(/\n/, '') == 'yes'; - } - Logger.logAction(Logger.LOG_MICRO, '(XHRRequest)Http.checkConnectivity()', 'Result: ' + result); - callback(null, result); - } - ); - }; - } - } else if (Platform.Config.jsonpSupported) { - this.Request = function ( - method: HttpMethods, - rest: Rest | null, - uri: string, - headers: Record | null, - params: RequestParams, - body: unknown, - callback: RequestCallback - ) { - const req = createRequest( - uri, - headers, - params, - body, - XHRStates.REQ_SEND, - rest && rest.options.timeouts, - method - ); - req.once('complete', callback); - Platform.Config.nextTick(function () { - req.exec(); - }); - return req; - }; - - if (this.options.disableConnectivityCheck) { - this.checkConnectivity = function (callback: (err: null, connectivity: true) => void) { - callback(null, true); - }; - } else { - this.checkConnectivity = function (callback: (err: ErrorInfo | null, connectivity?: boolean) => void) { - const upUrl = Defaults.jsonpInternetUpUrl; - - if (this.checksInProgress) { - this.checksInProgress.push(callback); - return; - } - this.checksInProgress = [callback]; - Logger.logAction(Logger.LOG_MICRO, '(JSONP)Http.checkConnectivity()', 'Sending; ' + upUrl); - - const req = new Request( - 'isTheInternetUp', - upUrl as string, - null, - null, - null, - XHRStates.REQ_SEND, - Defaults.TIMEOUTS - ); - req.once('complete', (err: Error, response: string) => { - const result = !err && response; - Logger.logAction(Logger.LOG_MICRO, '(JSONP)Http.checkConnectivity()', 'Result: ' + result); - for (let i = 0; i < (this.checksInProgress as Array>).length; i++) - (this.checksInProgress as Array>)[i](null, result); - this.checksInProgress = null; - }); - Platform.Config.nextTick(function () { - req.exec(); - }); - }; - } - } else if (Platform.Config.fetchSupported) { - this.supportsAuthHeaders = true; - this.Request = fetchRequest; - this.checkConnectivity = function (callback: (err: ErrorInfo | null, connectivity: boolean) => void) { - Logger.logAction(Logger.LOG_MICRO, '(Fetch)Http.checkConnectivity()', 'Sending; ' + connectivityCheckUrl); - this.doUri( - HttpMethods.Get, - null as any, - connectivityCheckUrl, - null, - null, - null, - function (err?: ErrorInfo | ErrnoException | null, responseText?: unknown) { - const result = !err && (responseText as string)?.replace(/\n/, '') == 'yes'; - Logger.logAction(Logger.LOG_MICRO, '(Fetch)Http.checkConnectivity()', 'Result: ' + result); - callback(null, result); - } - ); - }; - } else { - this.Request = (method, rest, uri, headers, params, body, callback) => { - callback(new PartialErrorInfo('no supported HTTP transports available', null, 400), null); - }; - } - } - - /* Unlike for doUri, the 'rest' param here is mandatory, as it's used to generate the hosts */ - do( - method: HttpMethods, - rest: Rest, - path: string, - headers: Record | null, - body: unknown, - params: RequestParams, - callback?: RequestCallback - ): void { - const uriFromHost = - typeof path == 'function' - ? path - : function (host: string) { - return rest.baseUri(host) + path; - }; - - const currentFallback = rest._currentFallback; - if (currentFallback) { - if (currentFallback.validUntil > Utils.now()) { - /* Use stored fallback */ - if (!this.Request) { - callback?.(new PartialErrorInfo('Request invoked before assigned to', null, 500)); - return; - } - this.Request( - method, - rest, - uriFromHost(currentFallback.host), - headers, - params, - body, - (err?: ErrnoException | ErrorInfo | null, ...args: unknown[]) => { - // This typecast is safe because ErrnoExceptions are only thrown in NodeJS - if (err && shouldFallback(err as ErrorInfo)) { - /* unstore the fallback and start from the top with the default sequence */ - rest._currentFallback = null; - this.do(method, rest, path, headers, body, params, callback); - return; - } - callback?.(err, ...args); - } - ); - return; - } else { - /* Fallback expired; remove it and fallthrough to normal sequence */ - rest._currentFallback = null; - } - } - - const hosts = getHosts(rest); - - /* if there is only one host do it */ - if (hosts.length === 1) { - this.doUri(method, rest, uriFromHost(hosts[0]), headers, body, params, callback as RequestCallback); - return; - } - - /* hosts is an array with preferred host plus at least one fallback */ - const tryAHost = (candidateHosts: Array, persistOnSuccess?: boolean) => { - const host = candidateHosts.shift(); - this.doUri( - method, - rest, - uriFromHost(host as string), - headers, - body, - params, - function (err?: ErrnoException | ErrorInfo | null, ...args: unknown[]) { - // This typecast is safe because ErrnoExceptions are only thrown in NodeJS - if (err && shouldFallback(err as ErrorInfo) && candidateHosts.length) { - tryAHost(candidateHosts, true); - return; - } - if (persistOnSuccess) { - /* RSC15f */ - rest._currentFallback = { - host: host as string, - validUntil: Utils.now() + rest.options.timeouts.fallbackRetryTimeout, - }; - } - callback?.(err, ...args); - } - ); - }; - tryAHost(hosts); - } - - doUri( - method: HttpMethods, - rest: Rest | null, - uri: string, - headers: Record | null, - body: unknown, - params: RequestParams, - callback: RequestCallback - ): void { - if (!this.Request) { - callback(new PartialErrorInfo('Request invoked before assigned to', null, 500)); - return; - } - this.Request(method, rest, uri, headers, params, body, callback); - } - - Request?: ( - method: HttpMethods, - rest: Rest | null, - uri: string, - headers: Record | null, - params: RequestParams, - body: unknown, - callback: RequestCallback - ) => void; - - checkConnectivity?: (callback: (err: ErrorInfo | null, connectivity?: boolean) => void) => void = undefined; - - supportsAuthHeaders = false; - supportsLinkHeaders = false; - - _getHosts = getHosts; -}; - -export default Http; diff --git a/src/platform/web/lib/util/msgpack.ts b/src/platform/web/lib/util/msgpack.ts index 46d4c0781b..e61dd3497f 100644 --- a/src/platform/web/lib/util/msgpack.ts +++ b/src/platform/web/lib/util/msgpack.ts @@ -78,7 +78,7 @@ function utf8Read(view: DataView, offset: number, length: number) { // Three byte character if ((byte_ & 0xf0) === 0xe0) { string += String.fromCharCode( - ((byte_ & 0x0f) << 12) | ((view.getUint8(++i) & 0x3f) << 6) | ((view.getUint8(++i) & 0x3f) << 0) + ((byte_ & 0x0f) << 12) | ((view.getUint8(++i) & 0x3f) << 6) | ((view.getUint8(++i) & 0x3f) << 0), ); continue; } @@ -88,7 +88,7 @@ function utf8Read(view: DataView, offset: number, length: number) { ((byte_ & 0x07) << 18) | ((view.getUint8(++i) & 0x3f) << 12) | ((view.getUint8(++i) & 0x3f) << 6) | - ((view.getUint8(++i) & 0x3f) << 0) + ((view.getUint8(++i) & 0x3f) << 0), ); continue; } diff --git a/src/platform/web/lib/util/webstorage.ts b/src/platform/web/lib/util/webstorage.ts index d1ac7e4ec2..a87300417a 100644 --- a/src/platform/web/lib/util/webstorage.ts +++ b/src/platform/web/lib/util/webstorage.ts @@ -1,8 +1,9 @@ -import * as Utils from 'common/lib/util/utils'; import IWebStorage from 'common/types/IWebStorage'; const test = 'ablyjs-storage-test'; +let globalObject = typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : self; + class Webstorage implements IWebStorage { sessionSupported: boolean; localSupported: boolean; @@ -14,16 +15,16 @@ class Webstorage implements IWebStorage { * somewhat roundabout way. (If unsupported or no global object, * will throw on accessing a property of undefined) */ try { - global.sessionStorage.setItem(test, test); - global.sessionStorage.removeItem(test); + globalObject.sessionStorage.setItem(test, test); + globalObject.sessionStorage.removeItem(test); this.sessionSupported = true; } catch (e) { this.sessionSupported = false; } try { - global.localStorage.setItem(test, test); - global.localStorage.removeItem(test); + globalObject.localStorage.setItem(test, test); + globalObject.localStorage.removeItem(test); this.localSupported = true; } catch (e) { this.localSupported = false; @@ -57,7 +58,7 @@ class Webstorage implements IWebStorage { private _set(name: string, value: string, ttl: number | undefined, session: any) { const wrappedValue: Record = { value: value }; if (ttl) { - wrappedValue.expires = Utils.now() + ttl; + wrappedValue.expires = Date.now() + ttl; } return this.storageInterface(session).setItem(name, JSON.stringify(wrappedValue)); } @@ -68,7 +69,7 @@ class Webstorage implements IWebStorage { const rawItem = this.storageInterface(session).getItem(name); if (!rawItem) return null; const wrappedValue = JSON.parse(rawItem); - if (wrappedValue.expires && wrappedValue.expires < Utils.now()) { + if (wrappedValue.expires && wrappedValue.expires < Date.now()) { this.storageInterface(session).removeItem(name); return null; } @@ -80,7 +81,7 @@ class Webstorage implements IWebStorage { } private storageInterface(session?: boolean) { - return session ? global.sessionStorage : global.localStorage; + return session ? globalObject.sessionStorage : globalObject.localStorage; } } diff --git a/src/platform/web/modular.ts b/src/platform/web/modular.ts new file mode 100644 index 0000000000..19a9b513a5 --- /dev/null +++ b/src/platform/web/modular.ts @@ -0,0 +1,46 @@ +// Common +import { BaseRest } from '../../common/lib/client/baserest'; +import BaseRealtime from '../../common/lib/client/baserealtime'; +import Platform from '../../common/platform'; +import ErrorInfo from '../../common/lib/types/errorinfo'; + +// Platform Specific +import BufferUtils from './lib/util/bufferutils'; +// @ts-ignore +import Http from './lib/http/http'; +import Config from './config'; +// @ts-ignore +import { ModularTransports } from './lib/transport'; +import Logger from '../../common/lib/util/logger'; +import { getDefaults } from '../../common/lib/util/defaults'; +import WebStorage from './lib/util/webstorage'; +import PlatformDefaults from './lib/util/defaults'; +import { modularBundledRequestImplementations } from './lib/http/request'; + +Platform.BufferUtils = BufferUtils; +Platform.Http = Http; +Platform.Config = Config; +Platform.Transports = ModularTransports; +Platform.WebStorage = WebStorage; + +Http.bundledRequestImplementations = modularBundledRequestImplementations; + +Logger.initLogHandlers(); + +Platform.Defaults = getDefaults(PlatformDefaults); + +if (Platform.Config.agent) { + // @ts-ignore + Platform.Defaults.agent += ' ' + Platform.Config.agent; +} + +export * from './modular/crypto'; +export * from './modular/message'; +export * from './modular/presencemessage'; +export * from './modular/msgpack'; +export * from './modular/realtimepresence'; +export * from './modular/transports'; +export * from './modular/http'; +export { Rest } from '../../common/lib/client/rest'; +export { FilteredSubscriptions as MessageInteractions } from '../../common/lib/client/filteredsubscriptions'; +export { BaseRest, BaseRealtime, ErrorInfo }; diff --git a/src/platform/web/modular/crypto.ts b/src/platform/web/modular/crypto.ts new file mode 100644 index 0000000000..9f9704f7ee --- /dev/null +++ b/src/platform/web/modular/crypto.ts @@ -0,0 +1,14 @@ +import BufferUtils from '../lib/util/bufferutils'; +import { createCryptoClass } from '../lib/util/crypto'; +import Config from '../config'; +import * as API from '../../../../ably'; + +export const Crypto = /* @__PURE__@ */ createCryptoClass(Config, BufferUtils); + +export const generateRandomKey: API.Crypto['generateRandomKey'] = (keyLength) => { + return Crypto.generateRandomKey(keyLength); +}; + +export const getDefaultCryptoParams: API.Crypto['getDefaultParams'] = (params) => { + return Crypto.getDefaultParams(params); +}; diff --git a/src/platform/web/modular/http.ts b/src/platform/web/modular/http.ts new file mode 100644 index 0000000000..24b664f30d --- /dev/null +++ b/src/platform/web/modular/http.ts @@ -0,0 +1,2 @@ +export { default as XHRRequest } from '../lib/http/request/xhrrequest'; +export { default as FetchRequest } from '../lib/http/request/fetchrequest'; diff --git a/src/platform/web/modular/message.ts b/src/platform/web/modular/message.ts new file mode 100644 index 0000000000..18c5d1396b --- /dev/null +++ b/src/platform/web/modular/message.ts @@ -0,0 +1,21 @@ +import * as API from '../../../../ably'; +import { Crypto } from './crypto'; +import { fromEncoded, fromEncodedArray } from '../../../common/lib/types/message'; + +// The type assertions for the decode* functions below are due to https://github.com/ably/ably-js/issues/1421 + +export const decodeMessage = ((obj, options) => { + return fromEncoded(null, obj, options); +}) as API.MessageStatic['fromEncoded']; + +export const decodeEncryptedMessage = ((obj, options) => { + return fromEncoded(Crypto, obj, options); +}) as API.MessageStatic['fromEncoded']; + +export const decodeMessages = ((obj, options) => { + return fromEncodedArray(null, obj, options); +}) as API.MessageStatic['fromEncodedArray']; + +export const decodeEncryptedMessages = ((obj, options) => { + return fromEncodedArray(Crypto, obj, options); +}) as API.MessageStatic['fromEncodedArray']; diff --git a/src/platform/web/modular/msgpack.ts b/src/platform/web/modular/msgpack.ts new file mode 100644 index 0000000000..698b09e34c --- /dev/null +++ b/src/platform/web/modular/msgpack.ts @@ -0,0 +1 @@ +export { default as MsgPack } from '../lib/util/msgpack'; diff --git a/src/platform/web/modular/presencemessage.ts b/src/platform/web/modular/presencemessage.ts new file mode 100644 index 0000000000..60e5351e9c --- /dev/null +++ b/src/platform/web/modular/presencemessage.ts @@ -0,0 +1,8 @@ +import * as API from '../../../../ably'; +import { fromEncoded, fromEncodedArray, fromValues } from '../../../common/lib/types/presencemessage'; + +// The type assertions for the functions below are due to https://github.com/ably/ably-js/issues/1421 + +export const decodePresenceMessage = fromEncoded as API.PresenceMessageStatic['fromEncoded']; +export const decodePresenceMessages = fromEncodedArray as API.PresenceMessageStatic['fromEncodedArray']; +export const constructPresenceMessage = fromValues as API.PresenceMessageStatic['fromValues']; diff --git a/src/platform/web/modular/realtimepresence.ts b/src/platform/web/modular/realtimepresence.ts new file mode 100644 index 0000000000..f4f138d720 --- /dev/null +++ b/src/platform/web/modular/realtimepresence.ts @@ -0,0 +1,14 @@ +import { RealtimePresencePlugin } from 'common/lib/client/modularplugins'; +import { default as realtimePresenceClass } from '../../../common/lib/client/realtimepresence'; +import { + fromValues as presenceMessageFromValues, + fromValuesArray as presenceMessagesFromValuesArray, +} from '../../../common/lib/types/presencemessage'; + +const RealtimePresence: RealtimePresencePlugin = { + RealtimePresence: realtimePresenceClass, + presenceMessageFromValues, + presenceMessagesFromValuesArray, +}; + +export { RealtimePresence }; diff --git a/src/platform/web/modular/transports.ts b/src/platform/web/modular/transports.ts new file mode 100644 index 0000000000..86301dfc3d --- /dev/null +++ b/src/platform/web/modular/transports.ts @@ -0,0 +1,2 @@ +export { default as XHRPolling } from '../lib/transport/xhrpollingtransport'; +export { default as WebSocketTransport } from '../../../common/lib/transport/websockettransport'; diff --git a/test/all.js b/test/all.js deleted file mode 100644 index b94d0a8acd..0000000000 --- a/test/all.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -var fs = require('fs'), - path = require('path'), - specDir = __dirname; - -require('./support/modules_helper'); -require('./support/test_helper'); - -function findAll(dir, pattern) { - var searchDir = path.resolve(specDir, dir), - result = {}; - - fs.readdirSync(searchDir) - .filter(function (file) { - return pattern.test(file); - }) - .forEach(function (file) { - result[file.match(pattern)[1]] = require(path.resolve(searchDir, file)); - }); - - return result; -} - -exports.rest = findAll('rest', /(\w+)\.test\.js/); -exports.realtime = findAll('realtime', /(\w+)\.test\.js/); - -exports.tear_down = require('./support/tear_down'); diff --git a/test/browser/connection.test.js b/test/browser/connection.test.js index d615c133c8..870b0a9f78 100644 --- a/test/browser/connection.test.js +++ b/test/browser/connection.test.js @@ -18,14 +18,6 @@ define(['shared_helper', 'chai'], function (helper, chai) { return false; } - // IE doesn't support creating your own events with new - try { - var testEvent = new Event('foo'); - } catch (e) { - console.log('On IE; skipping connection.test.js'); - return false; - } - return true; } @@ -64,7 +56,7 @@ define(['shared_helper', 'chai'], function (helper, chai) { try { expect( disconnectedAt - connectedAt < 1500, - 'Offline event caused connection to move to the disconnected state' + 'Offline event caused connection to move to the disconnected state', ).to.be.ok; } catch (err) { closeAndFinish(done, realtime, err); @@ -75,7 +67,7 @@ define(['shared_helper', 'chai'], function (helper, chai) { try { expect( reconnectingAt - disconnectedAt < 1500, - 'Client automatically reattempts connection without waiting for disconnect timeout, even if the state is still offline' + 'Client automatically reattempts connection without waiting for disconnect timeout, even if the state is still offline', ).to.be.ok; } catch (err) { closeAndFinish(done, realtime, err); @@ -111,7 +103,7 @@ define(['shared_helper', 'chai'], function (helper, chai) { try { expect( connection.state == 'disconnected', - 'Connection should still be disconnected before we trigger it to connect' + 'Connection should still be disconnected before we trigger it to connect', ).to.be.ok; } catch (err) { closeAndFinish(done, realtime, err); @@ -121,7 +113,7 @@ define(['shared_helper', 'chai'], function (helper, chai) { try { expect( new Date() - disconnectedAt < 1500, - 'Online event should have caused the connection to enter the connecting state without waiting for disconnect timeout' + 'Online event should have caused the connection to enter the connecting state without waiting for disconnect timeout', ).to.be.ok; } catch (err) { closeAndFinish(done, realtime, err); @@ -159,7 +151,7 @@ define(['shared_helper', 'chai'], function (helper, chai) { try { expect( connection.state == 'suspended', - 'Connection should still be suspended before we trigger it to connect' + 'Connection should still be suspended before we trigger it to connect', ).to.be.ok; } catch (err) { closeAndFinish(done, realtime, err); @@ -169,7 +161,7 @@ define(['shared_helper', 'chai'], function (helper, chai) { try { expect( new Date() - suspendedAt < 1500, - 'Online event should have caused the connection to enter the connecting state without waiting for suspended timeout' + 'Online event should have caused the connection to enter the connecting state without waiting for suspended timeout', ).to.be.ok; } catch (err) { closeAndFinish(done, realtime, err); @@ -234,7 +226,7 @@ define(['shared_helper', 'chai'], function (helper, chai) { try { expect(realtime.connection.state).to.equal( 'connected', - 'check connection state initially unaffected by page refresh' + 'check connection state initially unaffected by page refresh', ); simulateDroppedConnection(realtime); } catch (err) { @@ -247,7 +239,7 @@ define(['shared_helper', 'chai'], function (helper, chai) { try { expect( sameConnection(connectionKey, newRealtime.connection.key), - 'Check new realtime recovered the connection from the cookie' + 'Check new realtime recovered the connection from the cookie', ).to.be.ok; } catch (err) { closeAndFinish(done, [realtime, newRealtime], err); @@ -275,7 +267,7 @@ define(['shared_helper', 'chai'], function (helper, chai) { try { expect(realtime.connection.state).to.equal( 'connected', - 'check connection state initially unaffected by page refresh' + 'check connection state initially unaffected by page refresh', ); simulateDroppedConnection(realtime); } catch (err) { @@ -288,7 +280,7 @@ define(['shared_helper', 'chai'], function (helper, chai) { try { expect( !sameConnection(connectionKey, newRealtime.connection.key), - 'Check new realtime created a new connection' + 'Check new realtime created a new connection', ).to.be.ok; } catch (err) { closeAndFinish(done, [realtime, newRealtime], err); @@ -334,7 +326,7 @@ define(['shared_helper', 'chai'], function (helper, chai) { try { expect(realtime.connection.state).to.equal( 'connected', - 'check connection state initially unaffected by page refresh' + 'check connection state initially unaffected by page refresh', ); simulateDroppedConnection(realtime); } catch (err) { @@ -347,7 +339,7 @@ define(['shared_helper', 'chai'], function (helper, chai) { try { expect( sameConnection(connectionKey, newRealtime.connection.key), - 'Check new realtime recovered the old' + 'Check new realtime recovered the old', ).to.be.ok; } catch (err) { closeAndFinish(done, [realtime, newRealtime], err); @@ -365,7 +357,7 @@ define(['shared_helper', 'chai'], function (helper, chai) { if (this.event === 'transport.active' && transport.shortName === 'web_socket') { try { expect(window.localStorage.getItem(transportPreferenceName)).to.equal( - JSON.stringify({ value: 'web_socket' }) + JSON.stringify({ value: 'web_socket' }), ); } catch (err) { closeAndFinish(done, realtime, err); @@ -377,91 +369,17 @@ define(['shared_helper', 'chai'], function (helper, chai) { monitorConnection(done, realtime); }); - it('use_persisted_transport0', function (done) { - var transportPreferenceName = 'ably-transport-preference'; - window.localStorage.setItem(transportPreferenceName, JSON.stringify({ value: 'web_socket' })); - - var realtime = helper.AblyRealtime(); - - realtime.connection.connectionManager.on(function (transport) { - if (this.event === 'transport.active') { - try { - expect(transport.shortName).to.equal('web_socket'); - } catch (err) { - closeAndFinish(done, realtime, err); - return; - } - closeAndFinish(done, realtime); - } - }); - monitorConnection(done, realtime); - }); - - it('use_persisted_transport1', function (done) { - window.localStorage.setItem(transportPreferenceName, JSON.stringify({ value: 'xhr_streaming' })); - - var realtime = helper.AblyRealtime(); - - realtime.connection.connectionManager.on(function (transport) { - if (this.event === 'transport.active') { - try { - expect(transport.shortName).to.equal('xhr_streaming'); - } catch (err) { - closeAndFinish(done, realtime, err); - return; - } - closeAndFinish(done, realtime); - } - }); - monitorConnection(done, realtime); - }); - it('browser_transports', function (done) { var realtime = helper.AblyRealtime(); try { expect(realtime.connection.connectionManager.baseTransport).to.equal('xhr_polling'); - expect(realtime.connection.connectionManager.upgradeTransports).to.deep.equal([ - 'xhr_streaming', - 'web_socket', - ]); + expect(realtime.connection.connectionManager.webSocketTransportAvailable).to.be.ok; } catch (err) { closeAndFinish(done, realtime, err); return; } closeAndFinish(done, realtime); }); - - /* - * Check behaviour when going through a proxy which doesn't send any xhr streaming - * responses until the stream closes (simulated by a transportparam interpreted by - * realtime) - */ - it('connection behaviour with a proxy through which streaming is broken', function (done) { - const realtime = helper.AblyRealtime({ - transportParams: { - simulateNoStreamingProxy: true, - maxStreamDuration: 7500, - }, - realtimeRequestTimeout: 5000, - transports: ['xhr_polling', 'xhr_streaming'], - }); - realtime.connection.once('connected', function () { - const connectionEvents = []; - realtime.connection.on(function () { - connectionEvents.push(this.event); - }); - // After 10s, we should have remained connected, and still be on xhr_polling; - // xhr_streaming should have failed to connect after 5s, and in particular - // should not have completed the upgrade after 7.5s. - setTimeout(function () { - expect(realtime.connection.state).to.equal('connected'); - const transport = realtime.connection.connectionManager.activeProtocol.getTransport(); - expect(transport.shortName).to.equal('xhr_polling'); - expect(connectionEvents.length).to.equal(0); - closeAndFinish(done, realtime); - }, 10000); - }); - }); }); } }); diff --git a/test/browser/http.test.js b/test/browser/http.test.js index c9822b3918..5db5ea7894 100644 --- a/test/browser/http.test.js +++ b/test/browser/http.test.js @@ -3,15 +3,14 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { var rest; var expect = chai.expect; + var whenPromiseSettles = helper.whenPromiseSettles; describe('rest/http/fetch', function () { this.timeout(60 * 1000); - let initialXhrSupported, initialJsonpSupported; + let initialXhrSupported; before(function (done) { initialXhrSupported = Ably.Rest.Platform.Config.xhrSupported; - initialJsonpSupported = Ably.Rest.Platform.Config.jsonpSupported; Ably.Rest.Platform.Config.xhrSupported = false; - Ably.Rest.Platform.Config.jsonpSupported = false; helper.setupApp(function () { rest = helper.AblyRest(); done(); @@ -20,11 +19,10 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { after((done) => { Ably.Rest.Platform.Config.xhrSupported = initialXhrSupported; - Ably.Rest.Platform.Config.jsonpSupported = initialJsonpSupported; done(); }); - it('Should use fetch when XHR and JSONP are not supported', function (done) { + it('Should use fetch when XHR is not supported', function (done) { let oldFetch = window.fetch; window.fetch = () => { done(); @@ -36,7 +34,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { it('Should succeed in using fetch to publish a message', function (done) { const channel = rest.channels.get('http_test_channel'); - channel.publish('test', 'Testing fetch support', (err) => { + whenPromiseSettles(channel.publish('test', 'Testing fetch support'), (err) => { expect(err).to.not.exist; done(); }); @@ -44,7 +42,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { it('Should pass errors correctly', function (done) { const channel = rest.channels.get(''); - channel.publish('test', 'Invalid message', (err) => { + whenPromiseSettles(channel.publish('test', 'Invalid message'), (err) => { expect(err).to.exist; done(); }); diff --git a/test/browser/modular.test.js b/test/browser/modular.test.js new file mode 100644 index 0000000000..82e5b0698a --- /dev/null +++ b/test/browser/modular.test.js @@ -0,0 +1,824 @@ +import { + BaseRest, + BaseRealtime, + Rest, + generateRandomKey, + getDefaultCryptoParams, + decodeMessage, + decodeEncryptedMessage, + decodeMessages, + decodeEncryptedMessages, + Crypto, + MsgPack, + RealtimePresence, + decodePresenceMessage, + decodePresenceMessages, + constructPresenceMessage, + XHRPolling, + WebSocketTransport, + FetchRequest, + XHRRequest, + MessageInteractions, +} from '../../build/modular/index.mjs'; + +function registerAblyModularTests(helper) { + describe('browser/modular', function () { + this.timeout(10 * 1000); + const expect = chai.expect; + const BufferUtils = BaseRest.Platform.BufferUtils; + const ablyClientOptions = helper.ablyClientOptions; + const testResourcesPath = helper.testResourcesPath; + const testMessageEquality = helper.testMessageEquality; + const randomString = helper.randomString; + const getTestApp = helper.getTestApp; + const loadTestData = async (dataPath) => { + return new Promise((resolve, reject) => { + helper.loadTestData(dataPath, (err, testData) => (err ? reject(err) : resolve(testData))); + }); + }; + + before((done) => { + helper.setupApp(done); + }); + + describe('attempting to initialize with no client options', () => { + for (const clientClass of [BaseRest, BaseRealtime]) { + describe(clientClass.name, () => { + it('throws an error', () => { + expect(() => new clientClass()).to.throw('must be initialized with a client options object'); + }); + }); + } + }); + + describe('attempting to initialize with just an API key', () => { + for (const clientClass of [BaseRest, BaseRealtime]) { + describe(clientClass.name, () => { + it('throws an error', () => { + expect(() => new clientClass('foo:bar')).to.throw( + 'cannot be initialized with just an Ably API key; you must provide a client options object with a `plugins` property', + ); + }); + }); + } + }); + + describe('attempting to initialize with just a token', () => { + for (const clientClass of [BaseRest, BaseRealtime]) { + describe(clientClass.name, () => { + it('throws an error', () => { + expect(() => new clientClass('foo')).to.throw( + 'cannot be initialized with just an Ably Token; you must provide a client options object with a `plugins` property', + ); + }); + }); + } + }); + + describe('without any plugins', () => { + for (const clientClass of [BaseRest, BaseRealtime]) { + describe(clientClass.name, () => { + it('throws an error due to the absence of an HTTP plugin', () => { + expect(() => new clientClass(ablyClientOptions())).to.throw( + 'No HTTP request plugin provided. Provide at least one of the FetchRequest or XHRRequest plugins.', + ); + }); + }); + } + }); + + describe('Rest', () => { + const restScenarios = [ + { + description: 'use push admin functionality', + action: (client) => client.push.admin.publish({ clientId: 'foo' }, { data: { bar: 'baz' } }), + }, + { description: 'call `time()`', action: (client) => client.time() }, + { + description: 'call `auth.createTokenRequest()` with `queryTime` option enabled', + action: (client) => + client.auth.createTokenRequest(undefined, { + key: getTestApp().keys[0].keyStr /* if passing authOptions you have to explicitly pass the key */, + queryTime: true, + }), + }, + { description: 'call `stats()`', action: (client) => client.stats() }, + { description: 'call `request(...)`', action: (client) => client.request('get', '/channels/channel', 2) }, + { + description: 'call `batchPublish(...)`', + action: (client) => client.batchPublish({ channels: ['channel'], messages: { data: { foo: 'bar' } } }), + }, + { + description: 'call `batchPresence(...)`', + action: (client) => client.batchPresence(['channel']), + }, + { + description: 'call `auth.revokeTokens(...)`', + getAdditionalClientOptions: () => { + const testApp = getTestApp(); + return { key: testApp.keys[4].keyStr /* this key has revocableTokens enabled */ }; + }, + action: (client) => client.auth.revokeTokens([{ type: 'clientId', value: 'foo' }]), + }, + { + description: 'call channel’s `history()`', + action: (client) => client.channels.get('channel').history(), + }, + { + description: 'call channel’s `presence.history()`', + additionalRealtimePlugins: { RealtimePresence }, + action: (client) => client.channels.get('channel').presence.history(), + }, + { + description: 'call channel’s `status()`', + action: (client) => client.channels.get('channel').status(), + }, + ]; + + describe('BaseRest without explicit Rest', () => { + for (const scenario of restScenarios) { + it(`allows you to ${scenario.description}`, async () => { + const client = new BaseRest( + ablyClientOptions({ ...scenario.getAdditionalClientOptions?.(), plugins: { FetchRequest } }), + ); + + let thrownError = null; + try { + await scenario.action(client); + } catch (error) { + thrownError = error; + } + + expect(thrownError).to.be.null; + }); + } + }); + + describe('BaseRealtime with Rest', () => { + for (const scenario of restScenarios) { + it(`allows you to ${scenario.description}`, async () => { + const client = new BaseRealtime( + ablyClientOptions({ + ...scenario.getAdditionalClientOptions?.(), + plugins: { + WebSocketTransport, + FetchRequest, + Rest, + ...scenario.additionalRealtimePlugins, + }, + }), + ); + + let thrownError = null; + try { + await scenario.action(client); + } catch (error) { + thrownError = error; + } + + expect(thrownError).to.be.null; + }); + } + }); + + describe('BaseRealtime without Rest', () => { + it('still allows publishing and subscribing', async () => { + const client = new BaseRealtime(ablyClientOptions({ plugins: { WebSocketTransport, FetchRequest } })); + + const channel = client.channels.get('channel'); + await channel.attach(); + + const recievedMessagePromise = new Promise((resolve) => { + channel.subscribe((message) => { + resolve(message); + }); + }); + + await channel.publish({ data: { foo: 'bar' } }); + + const receivedMessage = await recievedMessagePromise; + expect(receivedMessage.data).to.eql({ foo: 'bar' }); + }); + + it('allows `auth.createTokenRequest()` without `queryTime` option enabled', async () => { + const client = new BaseRealtime(ablyClientOptions({ plugins: { WebSocketTransport, FetchRequest } })); + + const tokenRequest = await client.auth.createTokenRequest(); + expect(tokenRequest).to.be.an('object'); + }); + + for (const scenario of restScenarios) { + it(`throws an error when attempting to ${scenario.description}`, async () => { + const client = new BaseRealtime( + ablyClientOptions({ + ...scenario.getAdditionalClientOptions?.(), + plugins: { + WebSocketTransport, + FetchRequest, + ...scenario.additionalRealtimePlugins, + }, + }), + ); + + let thrownError = null; + try { + await scenario.action(client); + } catch (error) { + thrownError = error; + } + + expect(thrownError).not.to.be.null; + expect(thrownError.message).to.equal('Rest plugin not provided'); + }); + } + }); + }); + + describe('Crypto standalone functions', () => { + it('generateRandomKey', async () => { + const key = await generateRandomKey(); + expect(key).to.be.an('ArrayBuffer'); + }); + + it('getDefaultCryptoParams', async () => { + const key = await generateRandomKey(); + const params = getDefaultCryptoParams({ key }); + expect(params).to.be.an('object'); + }); + }); + + describe('Message standalone functions', () => { + async function testDecodesMessageData(functionUnderTest) { + const testData = await loadTestData(testResourcesPath + 'crypto-data-128.json'); + + const item = testData.items[1]; + const decoded = await functionUnderTest(item.encoded); + + expect(decoded.data).to.be.an('ArrayBuffer'); + } + + describe('decodeMessage', () => { + it('decodes a message’s data', async () => { + testDecodesMessageData(decodeMessage); + }); + + it('throws an error when given channel options with a cipher', async () => { + const testData = await loadTestData(testResourcesPath + 'crypto-data-128.json'); + const key = BufferUtils.base64Decode(testData.key); + const iv = BufferUtils.base64Decode(testData.iv); + + let thrownError = null; + try { + await decodeMessage(testData.items[0].encrypted, { cipher: { key, iv } }); + } catch (error) { + thrownError = error; + } + + expect(thrownError).not.to.be.null; + expect(thrownError.message).to.equal('Crypto plugin not provided'); + }); + }); + + describe('decodeEncryptedMessage', async () => { + it('decodes a message’s data', async () => { + testDecodesMessageData(decodeEncryptedMessage); + }); + + it('decrypts a message', async () => { + const testData = await loadTestData(testResourcesPath + 'crypto-data-128.json'); + + const key = BufferUtils.base64Decode(testData.key); + const iv = BufferUtils.base64Decode(testData.iv); + + for (const item of testData.items) { + const [decodedFromEncoded, decodedFromEncrypted] = await Promise.all([ + decodeMessage(item.encoded), + decodeEncryptedMessage(item.encrypted, { cipher: { key, iv } }), + ]); + + testMessageEquality(decodedFromEncoded, decodedFromEncrypted); + } + }); + }); + + async function testDecodesMessagesData(functionUnderTest) { + const testData = await loadTestData(testResourcesPath + 'crypto-data-128.json'); + + const items = [testData.items[1], testData.items[3]]; + const decoded = await functionUnderTest(items.map((item) => item.encoded)); + + expect(decoded[0].data).to.be.an('ArrayBuffer'); + expect(decoded[1].data).to.be.an('array'); + } + + describe('decodeMessages', () => { + it('decodes messages’ data', async () => { + testDecodesMessagesData(decodeMessages); + }); + + it('throws an error when given channel options with a cipher', async () => { + const testData = await loadTestData(testResourcesPath + 'crypto-data-128.json'); + const key = BufferUtils.base64Decode(testData.key); + const iv = BufferUtils.base64Decode(testData.iv); + + let thrownError = null; + try { + await decodeMessages( + testData.items.map((item) => item.encrypted), + { cipher: { key, iv } }, + ); + } catch (error) { + thrownError = error; + } + + expect(thrownError).not.to.be.null; + expect(thrownError.message).to.equal('Crypto plugin not provided'); + }); + }); + + describe('decodeEncryptedMessages', () => { + it('decodes messages’ data', async () => { + testDecodesMessagesData(decodeEncryptedMessages); + }); + + it('decrypts messages', async () => { + const testData = await loadTestData(testResourcesPath + 'crypto-data-128.json'); + + const key = BufferUtils.base64Decode(testData.key); + const iv = BufferUtils.base64Decode(testData.iv); + + const [decodedFromEncoded, decodedFromEncrypted] = await Promise.all([ + decodeMessages(testData.items.map((item) => item.encoded)), + decodeEncryptedMessages( + testData.items.map((item) => item.encrypted), + { cipher: { key, iv } }, + ), + ]); + + for (let i = 0; i < decodedFromEncoded.length; i++) { + testMessageEquality(decodedFromEncoded[i], decodedFromEncrypted[i]); + } + }); + }); + }); + + describe('Crypto', () => { + describe('without Crypto', () => { + async function testThrowsAnErrorWhenGivenChannelOptionsWithACipher(clientClassConfig) { + const client = new clientClassConfig.clientClass( + ablyClientOptions({ + plugins: { + ...clientClassConfig.additionalPlugins, + FetchRequest, + }, + }), + ); + const key = await generateRandomKey(); + expect(() => client.channels.get('channel', { cipher: { key } })).to.throw('Crypto plugin not provided'); + } + + for (const clientClassConfig of [ + { clientClass: BaseRest }, + { clientClass: BaseRealtime, additionalPlugins: { WebSocketTransport } }, + ]) { + describe(clientClassConfig.clientClass.name, () => { + it('throws an error when given channel options with a cipher', async () => { + await testThrowsAnErrorWhenGivenChannelOptionsWithACipher(clientClassConfig); + }); + }); + } + }); + + describe('with Crypto', () => { + async function testIsAbleToPublishEncryptedMessages(clientClassConfig) { + const clientOptions = ablyClientOptions(); + + const key = await generateRandomKey(); + + // Publish the message on a channel configured to use encryption, and receive it on one not configured to use encryption + + const rxClient = new BaseRealtime({ ...clientOptions, plugins: { WebSocketTransport, FetchRequest } }); + const rxChannel = rxClient.channels.get('channel'); + await rxChannel.attach(); + + const rxMessagePromise = new Promise((resolve, _) => rxChannel.subscribe((message) => resolve(message))); + + const encryptionChannelOptions = { cipher: { key } }; + + const txMessage = { name: 'message', data: 'data' }; + const txClient = new clientClassConfig.clientClass({ + ...clientOptions, + plugins: { + ...clientClassConfig.additionalPlugins, + FetchRequest, + Crypto, + }, + }); + const txChannel = txClient.channels.get('channel', encryptionChannelOptions); + await txChannel.publish(txMessage); + + const rxMessage = await rxMessagePromise; + + // Verify that the message was published with encryption + expect(rxMessage.encoding).to.equal('utf-8/cipher+aes-256-cbc'); + + // Verify that the message was correctly encrypted + const rxMessageDecrypted = await decodeEncryptedMessage(rxMessage, encryptionChannelOptions); + testMessageEquality(rxMessageDecrypted, txMessage); + } + + for (const clientClassConfig of [ + { clientClass: BaseRest }, + { clientClass: BaseRealtime, additionalPlugins: { WebSocketTransport } }, + ]) { + describe(clientClassConfig.clientClass.name, () => { + it('is able to publish encrypted messages', async () => { + await testIsAbleToPublishEncryptedMessages(clientClassConfig); + }); + }); + } + }); + }); + + describe('MsgPack', () => { + async function testRestUsesContentType(rest, expectedContentType) { + const channelName = 'channel'; + const channel = rest.channels.get(channelName); + const contentTypeUsedForPublishPromise = new Promise((resolve, reject) => { + rest.http.do = async (method, path, headers, body, params) => { + if (!(method == 'post' && path == `/channels/${channelName}/messages`)) { + return new Promise(() => {}); + } + resolve(headers['content-type']); + return { error: null }; + }; + }); + + await channel.publish('message', 'body'); + + const contentTypeUsedForPublish = await contentTypeUsedForPublishPromise; + expect(contentTypeUsedForPublish).to.equal(expectedContentType); + } + + async function testRealtimeUsesFormat(realtime, expectedFormat) { + const formatUsedForConnectionPromise = new Promise((resolve, reject) => { + realtime.connection.connectionManager.connectImpl = (transportParams) => { + resolve(transportParams.format); + }; + }); + realtime.connect(); + + const formatUsedForConnection = await formatUsedForConnectionPromise; + expect(formatUsedForConnection).to.equal(expectedFormat); + } + + // TODO once https://github.com/ably/ably-js/issues/1424 is fixed, this should also test the case where the useBinaryProtocol option is not specified + describe('with useBinaryProtocol client option', () => { + describe('without MsgPack', () => { + describe('BaseRest', () => { + it('uses JSON', async () => { + const client = new BaseRest(ablyClientOptions({ useBinaryProtocol: true, plugins: { FetchRequest } })); + await testRestUsesContentType(client, 'application/json'); + }); + }); + + describe('BaseRealtime', () => { + it('uses JSON', async () => { + const client = new BaseRealtime( + ablyClientOptions({ + useBinaryProtocol: true, + autoConnect: false, + plugins: { + WebSocketTransport, + FetchRequest, + }, + }), + ); + await testRealtimeUsesFormat(client, 'json'); + }); + }); + }); + + describe('with MsgPack', () => { + describe('BaseRest', () => { + it('uses MessagePack', async () => { + const client = new BaseRest( + ablyClientOptions({ + useBinaryProtocol: true, + plugins: { + FetchRequest, + MsgPack, + }, + }), + ); + await testRestUsesContentType(client, 'application/x-msgpack'); + }); + }); + + describe('BaseRealtime', () => { + it('uses MessagePack', async () => { + const client = new BaseRealtime( + ablyClientOptions({ + useBinaryProtocol: true, + autoConnect: false, + plugins: { + WebSocketTransport, + FetchRequest, + MsgPack, + }, + }), + ); + await testRealtimeUsesFormat(client, 'msgpack'); + }); + }); + }); + }); + }); + + describe('RealtimePresence', () => { + describe('BaseRealtime without RealtimePresence', () => { + it('throws an error when attempting to access the `presence` property', () => { + const client = new BaseRealtime(ablyClientOptions({ plugins: { WebSocketTransport, FetchRequest } })); + const channel = client.channels.get('channel'); + + expect(() => channel.presence).to.throw('RealtimePresence plugin not provided'); + }); + + it('doesn’t break when it receives a PRESENCE ProtocolMessage', async () => { + const rxClient = new BaseRealtime(ablyClientOptions({ plugins: { WebSocketTransport, FetchRequest } })); + const rxChannel = rxClient.channels.get('channel'); + + await rxChannel.attach(); + + const receivedMessagePromise = new Promise((resolve) => rxChannel.subscribe(resolve)); + + const txClient = new BaseRealtime( + ablyClientOptions({ + clientId: randomString(), + plugins: { + WebSocketTransport, + FetchRequest, + RealtimePresence, + }, + }), + ); + const txChannel = txClient.channels.get('channel'); + + await txChannel.publish('message', 'body'); + await txChannel.presence.enter(); + + // The idea being here that in order for receivedMessagePromise to resolve, rxClient must have first processed the PRESENCE ProtocolMessage that resulted from txChannel.presence.enter() + + await receivedMessagePromise; + }); + }); + + describe('BaseRealtime with RealtimePresence', () => { + it('offers realtime presence functionality', async () => { + const rxChannel = new BaseRealtime( + ablyClientOptions({ + plugins: { + WebSocketTransport, + FetchRequest, + RealtimePresence, + }, + }), + ).channels.get('channel'); + const txClientId = randomString(); + const txChannel = new BaseRealtime( + ablyClientOptions({ + clientId: txClientId, + plugins: { + WebSocketTransport, + FetchRequest, + RealtimePresence, + }, + }), + ).channels.get('channel'); + + let resolveRxPresenceMessagePromise; + const rxPresenceMessagePromise = new Promise((resolve, reject) => { + resolveRxPresenceMessagePromise = resolve; + }); + await rxChannel.presence.subscribe('enter', resolveRxPresenceMessagePromise); + await txChannel.presence.enter(); + + const rxPresenceMessage = await rxPresenceMessagePromise; + expect(rxPresenceMessage.clientId).to.equal(txClientId); + }); + }); + }); + + describe('PresenceMessage standalone functions', () => { + describe('decodePresenceMessage', () => { + it('decodes a presence message’s data', async () => { + const buffer = BufferUtils.utf8Encode('foo'); + const encodedMessage = { data: BufferUtils.base64Encode(buffer), encoding: 'base64' }; + + const decodedMessage = await decodePresenceMessage(encodedMessage); + + expect(BufferUtils.areBuffersEqual(decodedMessage.data, buffer)).to.be.true; + expect(decodedMessage.encoding).to.be.null; + }); + }); + + describe('decodeMessages', () => { + it('decodes presence messages’ data', async () => { + const buffers = ['foo', 'bar'].map((data) => BufferUtils.utf8Encode(data)); + const encodedMessages = buffers.map((buffer) => ({ + data: BufferUtils.base64Encode(buffer), + encoding: 'base64', + })); + + const decodedMessages = await decodePresenceMessages(encodedMessages); + + for (let i = 0; i < decodedMessages.length; i++) { + const decodedMessage = decodedMessages[i]; + + expect(BufferUtils.areBuffersEqual(decodedMessage.data, buffers[i])).to.be.true; + expect(decodedMessage.encoding).to.be.null; + } + }); + }); + + describe('constructPresenceMessage', () => { + it('creates a PresenceMessage instance', async () => { + const extras = { foo: 'bar' }; + const presenceMessage = constructPresenceMessage({ extras }); + + expect(presenceMessage.constructor.name).to.contain('PresenceMessage'); + expect(presenceMessage.extras).to.equal(extras); + }); + }); + }); + + describe('Transports', () => { + describe('BaseRealtime', () => { + describe('without a transport plugin', () => { + it('throws an error due to absence of a transport plugin', () => { + expect(() => new BaseRealtime(ablyClientOptions({ plugins: { FetchRequest } }))).to.throw( + 'no requested transports available', + ); + }); + }); + + for (const scenario of [ + { pluginsKey: 'WebSocketTransport', transportPlugin: WebSocketTransport, transportName: 'web_socket' }, + { pluginsKey: 'XHRPolling', transportPlugin: XHRPolling, transportName: 'xhr_polling' }, + ]) { + describe(`with the ${scenario.pluginsKey} plugin`, () => { + it(`is able to use the ${scenario.transportName} transport`, async () => { + const realtime = new BaseRealtime( + ablyClientOptions({ + autoConnect: false, + transports: [scenario.transportName], + plugins: { + FetchRequest, + [scenario.pluginsKey]: scenario.transportPlugin, + }, + }), + ); + + let firstTransportCandidate; + const connectionManager = realtime.connection.connectionManager; + const originalTryATransport = connectionManager.tryATransport; + realtime.connection.connectionManager.tryATransport = (transportParams, candidate, callback) => { + if (!firstTransportCandidate) { + firstTransportCandidate = candidate; + } + originalTryATransport.bind(connectionManager)(transportParams, candidate, callback); + }; + + realtime.connect(); + + await realtime.connection.once('connected'); + expect(firstTransportCandidate).to.equal(scenario.transportName); + }); + }); + } + }); + }); + + describe('HTTP request implementations', () => { + describe('with multiple HTTP request implementations', () => { + it('prefers XHR', async () => { + let usedXHR = false; + + const XHRRequestSpy = class XHRRequestSpy extends XHRRequest { + static createRequest(...args) { + usedXHR = true; + return super.createRequest(...args); + } + }; + + const rest = new BaseRest(ablyClientOptions({ plugins: { FetchRequest, XHRRequest: XHRRequestSpy } })); + await rest.time(); + + expect(usedXHR).to.be.true; + }); + }); + }); + + describe('MessageInteractions', () => { + describe('BaseRealtime', () => { + describe('without MessageInteractions', () => { + it('is able to subscribe to and unsubscribe from channel events, as long as a MessageFilter isn’t passed', async () => { + const realtime = new BaseRealtime(ablyClientOptions({ plugins: { WebSocketTransport, FetchRequest } })); + const channel = realtime.channels.get('channel'); + await channel.attach(); + + const subscribeReceivedMessagePromise = new Promise((resolve) => channel.subscribe(resolve)); + + await channel.publish('message', 'body'); + + const subscribeReceivedMessage = await subscribeReceivedMessagePromise; + expect(subscribeReceivedMessage.data).to.equal('body'); + }); + + it('throws an error when attempting to subscribe to channel events using a MessageFilter', async () => { + const realtime = new BaseRealtime(ablyClientOptions({ plugins: { WebSocketTransport, FetchRequest } })); + const channel = realtime.channels.get('channel'); + + let thrownError = null; + try { + await channel.subscribe({ clientId: 'someClientId' }, () => {}); + } catch (error) { + thrownError = error; + } + + expect(thrownError).not.to.be.null; + expect(thrownError.message).to.equal('MessageInteractions plugin not provided'); + }); + }); + + describe('with MessageInteractions', () => { + it('can take a MessageFilter argument when subscribing to and unsubscribing from channel events', async () => { + const realtime = new BaseRealtime( + ablyClientOptions({ + plugins: { + WebSocketTransport, + FetchRequest, + MessageInteractions, + }, + }), + ); + const channel = realtime.channels.get('channel'); + + await channel.attach(); + + // Test `subscribe` with a filter: send two messages with different clientIds, and check that unfiltered subscription receives both messages but clientId-filtered subscription only receives the matching one. + const messageFilter = { clientId: 'someClientId' }; // note that `unsubscribe` compares filter by reference, I found that a bit surprising + + const filteredSubscriptionReceivedMessages = []; + channel.subscribe(messageFilter, (message) => { + filteredSubscriptionReceivedMessages.push(message); + }); + + const unfilteredSubscriptionReceivedFirstTwoMessagesPromise = new Promise((resolve) => { + const receivedMessages = []; + channel.subscribe(function listener(message) { + receivedMessages.push(message); + if (receivedMessages.length === 2) { + channel.unsubscribe(listener); + resolve(); + } + }); + }); + + await channel.publish(await decodeMessage({ clientId: 'someClientId' })); + await channel.publish(await decodeMessage({ clientId: 'someOtherClientId' })); + await unfilteredSubscriptionReceivedFirstTwoMessagesPromise; + + expect(filteredSubscriptionReceivedMessages.length).to.equal(1); + expect(filteredSubscriptionReceivedMessages[0].clientId).to.equal('someClientId'); + + // Test `unsubscribe` with a filter: call `unsubscribe` with the clientId filter, publish a message matching the filter, check that only the unfiltered listener recieves it + channel.unsubscribe(messageFilter); + + const unfilteredSubscriptionReceivedNextMessagePromise = new Promise((resolve) => { + channel.subscribe(function listener() { + channel.unsubscribe(listener); + resolve(); + }); + }); + + await channel.publish(await decodeMessage({ clientId: 'someClientId' })); + await unfilteredSubscriptionReceivedNextMessagePromise; + + expect(filteredSubscriptionReceivedMessages.length).to./* (still) */ equal(1); + }); + }); + }); + }); + }); +} + +// This function is called by browser_setup.js once `require` is available +window.registerAblyModularTests = async () => { + return new Promise((resolve) => { + require(['shared_helper'], (helper) => { + registerAblyModularTests(helper); + resolve(); + }); + }); +}; diff --git a/test/browser/simple.test.js b/test/browser/simple.test.js index 03245bfbac..5eac642bfb 100644 --- a/test/browser/simple.test.js +++ b/test/browser/simple.test.js @@ -2,6 +2,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { var expect = chai.expect; + var whenPromiseSettles = helper.whenPromiseSettles; describe('browser/simple', function () { this.timeout(60 * 1000); @@ -16,7 +17,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { }); function isTransportAvailable(transport) { - return transport in Ably.Realtime.ConnectionManager.supportedTransports; + return transport in Ably.Realtime.ConnectionManager.supportedTransports(Ably.Realtime._transports); } function realtimeConnection(transports) { @@ -71,7 +72,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { ably.connection.on('connected', function () { connectionTimeout.stop(); heartbeatTimeout = failWithin(25, done, ably, 'wait for heartbeat'); - ably.connection.ping(function (err) { + whenPromiseSettles(ably.connection.ping(), function (err) { heartbeatTimeout.stop(); done(err); ably.close(); @@ -115,7 +116,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { receiveMessagesTimeout = failWithin(15, done, ably, 'wait for published messages to be received'); timer = setInterval(function () { - channel.publish('event0', 'Hello world at: ' + new Date(), function (err) { + whenPromiseSettles(channel.publish('event0', 'Hello world at: ' + new Date()), function (err) { sentCbCount++; checkFinish(); }); @@ -179,27 +180,6 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { }); } - var xhrStreamingTransport = 'xhr_streaming'; - if (isTransportAvailable(xhrStreamingTransport)) { - it('xhrstreamingbase0', function (done) { - connectionWithTransport(done, xhrStreamingTransport); - }); - - /* - * Publish and subscribe, json transport - */ - it('xhrstreamingpublish0', function (done) { - publishWithTransport(done, xhrStreamingTransport); - }); - - /* - * Check heartbeat - */ - it('xhrstreamingheartbeat0', function (done) { - heartbeatWithTransport(done, xhrStreamingTransport); - }); - } - var xhrPollingTransport = 'xhr_polling'; if (isTransportAvailable(xhrPollingTransport)) { it('xhrpollingbase0', function (done) { @@ -221,27 +201,6 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { }); } - var jsonpTransport = 'jsonp'; - if (isTransportAvailable(jsonpTransport)) { - it('jsonpbase0', function (done) { - connectionWithTransport(done, jsonpTransport); - }); - - /* - * Publish and subscribe, json transport - */ - it('jsonppublish0', function (done) { - publishWithTransport(done, jsonpTransport); - }); - - /* - * Check heartbeat - */ - it('jsonpheartbeat0', function (done) { - heartbeatWithTransport(done, jsonpTransport); - }); - } - it('auto_transport_base0', function (done) { connectionWithTransport(done); }); diff --git a/test/common/globals/environment.js b/test/common/globals/environment.js index 9a75be7273..f1515222f8 100644 --- a/test/common/globals/environment.js +++ b/test/common/globals/environment.js @@ -1,7 +1,7 @@ /* Assumes process.env defined, or window.__env__ or populated via globals.env.js and karam-env-preprocessor plugin */ define(function (require) { - var defaultLogLevel = 2, + var defaultLogLevel = 4, environment = isBrowser ? window.__env__ || {} : process.env, ablyEnvironment = environment.ABLY_ENV || 'sandbox', realtimeHost = environment.ABLY_REALTIME_HOST, @@ -11,6 +11,8 @@ define(function (require) { tls = 'ABLY_USE_TLS' in environment ? environment.ABLY_USE_TLS.toLowerCase() !== 'false' : true, logLevel = environment.ABLY_LOG_LEVEL || defaultLogLevel; + let logLevelSet = environment.ABLY_LOG_LEVEL !== undefined; + if (isBrowser) { var url = window.location.href, keysValues = url.split(/[\?&]+/), @@ -27,13 +29,39 @@ define(function (require) { if (query['port']) port = query['port']; if (query['tls_port']) tlsPort = query['tls_port']; if (query['tls']) tls = query['tls'].toLowerCase() !== 'false'; - if (query['log_level']) logLevel = Number(query['log_level']) || defaultLogLevel; + if (query['log_level']) { + logLevel = Number(query['log_level']); + logLevelSet = true; + } } else if (process) { process.on('uncaughtException', function (err) { console.error(err.stack); }); } + function getLogTimestamp() { + const time = new Date(); + return ( + time.getHours().toString().padStart(2, '0') + + ':' + + time.getMinutes().toString().padStart(2, '0') + + ':' + + time.getSeconds().toString().padStart(2, '0') + + '.' + + time.getMilliseconds().toString().padStart(3, '0') + ); + } + + let clientLogs = []; + + function getLogs() { + return clientLogs; + } + + function flushLogs() { + clientLogs = []; + } + return (module.exports = { environment: ablyEnvironment, realtimeHost: realtimeHost, @@ -42,12 +70,15 @@ define(function (require) { tlsPort: tlsPort, tls: tls, logLevel: logLevel, + getLogs, + flushLogs, + logHandler: function (msg) { - var time = new Date(); - console.log( - time.getHours() + ':' + time.getMinutes() + ':' + time.getSeconds() + '.' + time.getMilliseconds(), - msg - ); + if (logLevelSet) { + console.log(getLogTimestamp(), msg); + } else { + clientLogs.push([getLogTimestamp(), msg]); + } }, }); }); diff --git a/test/common/globals/named_dependencies.js b/test/common/globals/named_dependencies.js index 3228c893c6..0558b575e7 100644 --- a/test/common/globals/named_dependencies.js +++ b/test/common/globals/named_dependencies.js @@ -3,9 +3,6 @@ define(function () { return (module.exports = { // Ably modules ably: { browser: 'build/ably', node: 'build/ably-node' }, - 'ably.noencryption': { browser: 'build/ably.noencryption' }, - base64: { browser: 'node_modules/crypto-js/build/enc-base64', node: 'skip' }, - utf8: { browser: 'node_modules/crypto-js/build/enc-utf8', node: 'skip' }, 'vcdiff-decoder': { browser: 'node_modules/@ably/vcdiff-decoder/dist/vcdiff-decoder', node: 'node_modules/@ably/vcdiff-decoder', diff --git a/test/common/modules/client_module.js b/test/common/modules/client_module.js index 7f2c990ea0..5ea3c37ba6 100644 --- a/test/common/modules/client_module.js +++ b/test/common/modules/client_module.js @@ -12,7 +12,7 @@ define(['ably', 'globals', 'test/common/modules/testapp_module'], function (Ably /* Use a default api key if no auth methods provided */ if ( - utils.arrEvery(authMethods, function (method) { + authMethods.every(function (method) { return !(method in clientOptions); }) ) { @@ -34,5 +34,6 @@ define(['ably', 'globals', 'test/common/modules/testapp_module'], function (Ably Ably: Ably, AblyRest: ablyRest, AblyRealtime: ablyRealtime, + ablyClientOptions, }); }); diff --git a/test/common/modules/shared_helper.js b/test/common/modules/shared_helper.js index d6b6812910..0cfd8ce1d1 100644 --- a/test/common/modules/shared_helper.js +++ b/test/common/modules/shared_helper.js @@ -7,19 +7,17 @@ define([ 'test/common/modules/testapp_module', 'test/common/modules/client_module', 'test/common/modules/testapp_manager', + 'globals', 'async', -], function (testAppModule, clientModule, testAppManager, async) { + 'chai', +], function (testAppModule, clientModule, testAppManager, globals, async, chai) { var utils = clientModule.Ably.Realtime.Utils; var platform = clientModule.Ably.Realtime.Platform; - clientModule.Ably.Realtime.ConnectionManager.initTransports(); - var supportedTransports = utils.keysArray(clientModule.Ably.Realtime.ConnectionManager.supportedTransports), - /* Don't include jsonp in availableTransports if xhr works. Why? Because - * you can't abort requests. So recv's stick around for 90s till realtime - * ends them. So in a test, the browsers max-connections-per-host limit - * fills up quickly, which messes up other comet transports too */ - availableTransports = utils.arrIn(supportedTransports, 'xhr_polling') - ? utils.arrWithoutValue(supportedTransports, 'jsonp') - : supportedTransports, + var BufferUtils = platform.BufferUtils; + var expect = chai.expect; + var availableTransports = utils.keysArray( + clientModule.Ably.Realtime.ConnectionManager.supportedTransports(clientModule.Ably.Realtime._transports), + ), bestTransport = availableTransports[0], /* IANA reserved; requests to it will hang forever */ unroutableHost = '10.255.255.1', @@ -37,7 +35,7 @@ define([ } function monitorConnection(done, realtime, states) { - utils.arrForEach(states || ['failed', 'suspended'], function (state) { + (states || ['failed', 'suspended']).forEach(function (state) { realtime.connection.on(state, function () { done(new Error('Connection monitoring: state changed to ' + state + ', aborting test')); realtime.close(); @@ -45,6 +43,15 @@ define([ }); } + async function monitorConnectionAsync(action, realtime, states) { + const monitoringResultPromise = new Promise((resolve, reject) => { + monitorConnection((err) => (err ? reject(err) : resolve()), realtime, states); + }); + const actionResultPromise = Promise.resolve(action()); + + return Promise.race([monitoringResultPromise, actionResultPromise]); + } + function closeAndFinish(done, realtime, err) { if (typeof realtime === 'undefined') { // Likely called in a catch block for an exception @@ -53,7 +60,7 @@ define([ return; } if (Object.prototype.toString.call(realtime) == '[object Array]') { - var realtimes = utils.arrFilter(realtime, function (rt) { + var realtimes = realtime.filter(function (rt) { return rt !== undefined; }); closeAndFinishSeveral(done, realtimes, err); @@ -64,6 +71,25 @@ define([ }); } + async function closeAndFinishAsync(realtime, err) { + return new Promise((resolve, reject) => { + closeAndFinish((err) => (err ? reject(err) : resolve()), realtime, err); + }); + } + + /** + * Uses a callback to communicate the result of a `Promise`. The first argument passed to the callback will be either an error (when the promise is rejected) or `null` (when the promise is fulfilled). In the case where the promise is fulfilled, the resulting value will be passed to the callback as a second argument. + */ + function whenPromiseSettles(promise, callback) { + promise + .then((result) => { + callback(null, result); + }) + .catch((err) => { + callback(err); + }); + } + function simulateDroppedConnection(realtime) { // Go into the 'disconnected' state before actually disconnecting the transports // to avoid the instantaneous reconnect attempt that would be triggered in @@ -123,45 +149,43 @@ define([ } done(e); }); - } + }, ); } /* testFn is assumed to be a function of realtimeOptions that returns a mocha test */ - function testOnAllTransports(name, testFn, excludeUpgrade, skip) { + function testOnAllTransports(name, testFn, skip) { var itFn = skip ? it.skip : it; let transports = availableTransports; - utils.arrForEach(transports, function (transport) { + transports.forEach(function (transport) { itFn( name + '_with_' + transport + '_binary_transport', - testFn({ transports: [transport], useBinaryProtocol: true }) + testFn({ transports: [transport], useBinaryProtocol: true }), ); itFn( name + '_with_' + transport + '_text_transport', - testFn({ transports: [transport], useBinaryProtocol: false }) + testFn({ transports: [transport], useBinaryProtocol: false }), ); }); - /* Plus one for no transport specified (ie use upgrade mechanism if + /* Plus one for no transport specified (ie use websocket/base mechanism if * present). (we explicitly specify all transports since node only does - * nodecomet+upgrade if comet is explicitly requested + * websocket+nodecomet if comet is explicitly requested) * */ - if (!excludeUpgrade) { - itFn(name + '_with_binary_transport', testFn({ transports, useBinaryProtocol: true })); - itFn(name + '_with_text_transport', testFn({ transports, useBinaryProtocol: false })); - } + itFn(name + '_with_binary_transport', testFn({ transports, useBinaryProtocol: true })); + itFn(name + '_with_text_transport', testFn({ transports, useBinaryProtocol: false })); } - testOnAllTransports.skip = function (name, testFn, excludeUpgrade) { - testOnAllTransports(name, testFn, excludeUpgrade, true); + testOnAllTransports.skip = function (name, testFn) { + testOnAllTransports(name, testFn, true); }; function restTestOnJsonMsgpack(name, testFn, skip) { var itFn = skip ? it.skip : it; - itFn(name + ' with binary protocol', function (done) { - testFn(done, new clientModule.AblyRest({ useBinaryProtocol: true }), name + '_binary'); + itFn(name + ' with binary protocol', async function () { + await testFn(new clientModule.AblyRest({ useBinaryProtocol: true }), name + '_binary'); }); - itFn(name + ' with text protocol', function (done) { - testFn(done, new clientModule.AblyRest({ useBinaryProtocol: false }), name + '_text'); + itFn(name + ' with text protocol', async function () { + await testFn(new clientModule.AblyRest({ useBinaryProtocol: false }), name + '_text'); }); } @@ -183,39 +207,66 @@ define([ return !!transport.toString().match(/wss?\:/); } - var arrFind = Array.prototype.find - ? function (arr, predicate) { - return arr.find(predicate); - } - : function (arr, predicate) { - var value; - for (var i = 0; i < arr.length; i++) { - value = arr[i]; - if (predicate(value)) { - return value; - } - } - return undefined; - }; - - var arrFilter = Array.prototype.filter - ? function (arr, predicate) { - return arr.filter(predicate); - } - : function (arr, predicate) { - var res = []; - for (var i = 0; i < arr.length; i++) { - if (predicate(arr[i])) { - res.push(arr[i]); - } - } - return res; - }; - function randomString() { return Math.random().toString().slice(2); } + function testMessageEquality(one, two) { + // treat `null` same as `undefined` (using ==, rather than ===) + expect(one.encoding == two.encoding, "Encoding mismatch ('" + one.encoding + "' != '" + two.encoding + "').").to.be + .ok; + + if (typeof one.data === 'string' && typeof two.data === 'string') { + expect(one.data === two.data, 'String data contents mismatch.').to.be.ok; + return; + } + + if (BufferUtils.isBuffer(one.data) && BufferUtils.isBuffer(two.data)) { + expect(BufferUtils.areBuffersEqual(one.data, two.data), 'Buffer data contents mismatch.').to.equal(true); + return; + } + + var json1 = JSON.stringify(one.data); + var json2 = JSON.stringify(two.data); + if (null === json1 || undefined === json1 || null === json2 || undefined === json2) { + expect(false, 'JSON stringify failed.').to.be.ok; + return; + } + expect(json1 === json2, 'JSON data contents mismatch.').to.be.ok; + } + + let activeClients = []; + + function AblyRealtime(options) { + const client = clientModule.AblyRealtime(options); + activeClients.push(client); + return client; + } + + /* Slightly crude catch-all hook to close any dangling realtime clients left open + * after a test fails without calling closeAndFinish */ + function closeActiveClients() { + activeClients.forEach((client) => { + client.close(); + }); + activeClients = []; + } + + function logTestResults() { + if (this.currentTest.isFailed()) { + const logs = globals.getLogs(); + if (logs.length > 0) { + // empty console.logs are for vertical spacing + console.log(); + console.log('Logs for failing test: \n'); + logs.forEach(([timestamp, log]) => { + console.log(timestamp, log); + }); + console.log(); + } + } + } + return (module.exports = { setupApp: testAppModule.setup, tearDownApp: testAppModule.tearDown, @@ -224,7 +275,8 @@ define([ Ably: clientModule.Ably, AblyRest: clientModule.AblyRest, - AblyRealtime: clientModule.AblyRealtime, + AblyRealtime: AblyRealtime, + ablyClientOptions: clientModule.ablyClientOptions, Utils: utils, loadTestData: testAppManager.loadJsonData, @@ -232,7 +284,9 @@ define([ displayError: displayError, monitorConnection: monitorConnection, + monitorConnectionAsync: monitorConnectionAsync, closeAndFinish: closeAndFinish, + closeAndFinishAsync: closeAndFinishAsync, simulateDroppedConnection: simulateDroppedConnection, becomeSuspended: becomeSuspended, testOnAllTransports: testOnAllTransports, @@ -244,8 +298,10 @@ define([ isWebsocket: isWebsocket, unroutableHost: unroutableHost, unroutableAddress: unroutableAddress, - arrFind: arrFind, - arrFilter: arrFilter, + whenPromiseSettles: whenPromiseSettles, randomString: randomString, + testMessageEquality: testMessageEquality, + closeActiveClients, + logTestResults, }); }); diff --git a/test/common/modules/testapp_manager.js b/test/common/modules/testapp_manager.js index b1da1db152..de8fb32661 100644 --- a/test/common/modules/testapp_manager.js +++ b/test/common/modules/testapp_manager.js @@ -2,14 +2,13 @@ /* global define, isNativescript, fetch */ /* testapp module is responsible for setting up and tearing down apps in the test environment */ -define(['globals', 'base64', 'utf8', 'ably'], function (ablyGlobals, Base64, UTF8, ably) { +define(['globals', 'ably'], function (ablyGlobals, ably) { var restHost = ablyGlobals.restHost || prefixDomainWithEnvironment('rest.ably.io', ablyGlobals.environment), tlsPort = ablyGlobals.tlsPort; var isBrowser = typeof window === 'object', isNativescript = typeof global === 'object' && global.isNativescript, httpReq = httpReqFunction(), - toBase64 = base64Function(), loadJsonData = loadJsonDataNode, testResourcesPath = 'test/common/ably-common/test-resources/'; @@ -31,35 +30,10 @@ define(['globals', 'base64', 'utf8', 'ably'], function (ablyGlobals, Base64, UTF } } - function createXHR() { - var result = new XMLHttpRequest(); - if ('withCredentials' in result) return result; - if (typeof XDomainRequest !== 'undefined') { - var xdr = new XDomainRequest(); /* Use IE-specific "CORS" code with XDR */ - xdr.isXDR = true; - return xdr; - } - return null; - } - - function NSbase64Function(d) { - return Base64.stringify(d); - } - - function base64Function() { - if (isBrowser) { - return function (str) { - return Base64.stringify(UTF8.parse(str)); - }; - } else { - return function (str) { - return Buffer.from(str, 'ascii').toString('base64'); - }; - } - } - - function schemeMatchesCurrent(scheme) { - return scheme === window.location.protocol.slice(0, -1); + function toBase64(str) { + var bufferUtils = ably.Realtime.Platform.BufferUtils; + var buffer = bufferUtils.utf8Encode(str); + return bufferUtils.base64Encode(buffer); } function httpReqFunction() { @@ -85,37 +59,11 @@ define(['globals', 'base64', 'utf8', 'ably'], function (ablyGlobals, Base64, UTF }; } else if (isBrowser) { return function (options, callback) { - var xhr = createXHR(); + var xhr = new XMLHttpRequest(); var uri; uri = options.scheme + '://' + options.host + ':' + options.port + options.path; - if (xhr.isXDR && !schemeMatchesCurrent(options.scheme)) { - /* Can't use XDR for cross-scheme. For some requests could just force - * the same scheme and be done with it, but not for authenticated - * requests to ably, can't use basic auth for non-tls endpoints. - * Luckily ably can handle jsonp, so just use the ably Http method, - * which will use the jsonp transport. Can't just do this all the time - * as the local express webserver serves files statically, so can't do - * jsonp. */ - if (options.method === 'DELETE') { - /* Ignore DELETEs -- can't be done with jsonp at the moment, and - * simulation apps self-delete after a while */ - callback(); - } else { - new ably.Rest.Platform.Http().doUri( - options.method, - null, - uri, - options.headers, - options.body, - options.paramsIfNoHeaders || {}, - callback - ); - } - return; - } - xhr.open(options.method, uri); if (options.headers && !xhr.isXDR) { for (var h in options.headers) if (h !== 'Content-Length') xhr.setRequestHeader(h, options.headers[h]); diff --git a/test/common/modules/testapp_module.js b/test/common/modules/testapp_module.js index f05dac54b9..3e2e7db1b0 100644 --- a/test/common/modules/testapp_module.js +++ b/test/common/modules/testapp_module.js @@ -40,7 +40,7 @@ define(['test/common/modules/testapp_manager', 'globals'], function (testAppMana configuredTestApp().appId + ' in environment ' + (ablyGlobals.environment || 'production') + - ' has been set up' + ' has been set up', ); done(); } diff --git a/test/mocha.html b/test/mocha.html index 376e39478e..490230b487 100644 --- a/test/mocha.html +++ b/test/mocha.html @@ -22,6 +22,7 @@ + diff --git a/test/package/browser/template/.gitignore b/test/package/browser/template/.gitignore new file mode 100644 index 0000000000..2911203326 --- /dev/null +++ b/test/package/browser/template/.gitignore @@ -0,0 +1,6 @@ +dist +node_modules/ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/test/package/browser/template/README.md b/test/package/browser/template/README.md new file mode 100644 index 0000000000..8bf44061be --- /dev/null +++ b/test/package/browser/template/README.md @@ -0,0 +1,30 @@ +# ably-js NPM package test (for browser) + +This directory is intended to be used for testing the following aspects of the ably-js NPM package when used in a browser-based app: + +- that its exports are correctly configured and provide access to ably-js’s functionality +- that its TypeScript typings are correctly configured and can be successfully used from a TypeScript-based app that imports the package + +It contains three files, each of which import ably-js in different manners, and provide a way to briefly exercise its functionality: + +- `src/index-default.ts` imports the default ably-js package (`import { Realtime } from 'ably'`). +- `src/index-modular.ts` imports the tree-shakable ably-js package (`import { BaseRealtime, WebSocketTransport, FetchRequest } from 'ably/modular'`). +- `src/ReactApp.tsx` imports React hooks from the ably-js package (`import { useChannel } from 'ably/react'`). + +## Why is `ably` not in `package.json`? + +The `ably` dependency gets added when we run the repository’s `test:package` package script. That script copies the contents of this `template` directory to a new temporary directory, and then adds the `ably` dependency to the copy. We do this so that we can check this directory’s `package-lock.json` into Git, without needing to modify it whenever ably-js’s dependencies change. + +## React hooks tests + +To test hooks imported from `ably/react` in React components, we used [Playwright for components](https://playwright.dev/docs/test-components). The main logic sits in `src/ReactApp.tsx`, and `AblyProvider` is configured in `playwright/index.tsx` file based on [this guide](https://playwright.dev/docs/test-components#hooks). + +## Package scripts + +This directory exposes three package scripts that are to be used for testing: + +- `build`: Uses esbuild to create: + 1. a bundle containing `src/index-default.ts` and ably-js; + 2. a bundle containing `src/index-modular.ts` and ably-js. +- `test`: Using the bundles created by `build` and playwright components setup, tests that the code that exercises ably-js’s functionality is working correctly in a browser. +- `typecheck`: Type-checks the code that imports ably-js. diff --git a/test/package/browser/template/package-lock.json b/test/package/browser/template/package-lock.json new file mode 100644 index 0000000000..701b9122a7 --- /dev/null +++ b/test/package/browser/template/package-lock.json @@ -0,0 +1,3162 @@ +{ + "name": "template", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "template", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@playwright/experimental-ct-react": "^1.39.0", + "@playwright/test": "^1.39.0", + "@tsconfig/node16": "^16.1.1", + "@types/express": "^4.17.20", + "@types/node": "^20.11.19", + "esbuild": "^0.18.20", + "express": "^4.18.2", + "ts-node": "^10.9.1", + "typescript": "^5.2.2" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", + "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.9", + "@babel/parser": "^7.23.9", + "@babel/template": "^7.23.9", + "@babel/traverse": "^7.23.9", + "@babel/types": "^7.23.9", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.6", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.9.tgz", + "integrity": "sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.23.9", + "@babel/traverse": "^7.23.9", + "@babel/types": "^7.23.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", + "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz", + "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz", + "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz", + "integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.23.9", + "@babel/types": "^7.23.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.9.tgz", + "integrity": "sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.9", + "@babel/types": "^7.23.9", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", + "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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_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/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", + "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@playwright/experimental-ct-core": { + "version": "1.41.2", + "resolved": "https://registry.npmjs.org/@playwright/experimental-ct-core/-/experimental-ct-core-1.41.2.tgz", + "integrity": "sha512-JtW7gjmBCeHWKmZcOPuoJ15i2ergVX9PnyYUrAvBbWL8l4X9B7Sblro8nE49hO9baANfEGl4BbRUaj+pA67F6w==", + "dev": true, + "dependencies": { + "playwright": "1.41.2", + "playwright-core": "1.41.2", + "vite": "^4.4.12" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/rollup": { + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/vite": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz", + "integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==", + "dev": true, + "dependencies": { + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/@playwright/experimental-ct-react": { + "version": "1.41.2", + "resolved": "https://registry.npmjs.org/@playwright/experimental-ct-react/-/experimental-ct-react-1.41.2.tgz", + "integrity": "sha512-BMcxh2DM3t8nMgUJKWMWAXGIzcYY/4w1ibckGP48edHU/cmZCbI5W7mUXfxMhYCt4mQJ1NuYcFIg5Jt+QVIuRQ==", + "dev": true, + "dependencies": { + "@playwright/experimental-ct-core": "1.41.2", + "@vitejs/plugin-react": "^4.0.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@playwright/test": { + "version": "1.41.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.2.tgz", + "integrity": "sha512-qQB9h7KbibJzrDpkXkYvsmiDJK14FULCCZgEcoe2AvFAS64oCirWTwzTlAYEbKaRxWs5TFesE1Na6izMv3HfGg==", + "dev": true, + "dependencies": { + "playwright": "1.41.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz", + "integrity": "sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "peer": true + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.0.tgz", + "integrity": "sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "peer": true + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz", + "integrity": "sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "peer": true + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.0.tgz", + "integrity": "sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.0.tgz", + "integrity": "sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.0.tgz", + "integrity": "sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.0.tgz", + "integrity": "sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.0.tgz", + "integrity": "sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.0.tgz", + "integrity": "sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.0.tgz", + "integrity": "sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.0.tgz", + "integrity": "sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.0.tgz", + "integrity": "sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.0.tgz", + "integrity": "sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-16.1.1.tgz", + "integrity": "sha512-+pio93ejHN4nINX4pXqfnR/fPLRtJBaT4ORaa5RH0Oc1zoYmo2B2koG+M328CQhHKn1Wj6FcOxCDFXAot9NhvA==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true, + "peer": true + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.43", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz", + "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.11.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz", + "integrity": "sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/qs": { + "version": "6.9.11", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", + "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", + "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz", + "integrity": "sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.5", + "@babel/plugin-transform-react-jsx-self": "^7.23.3", + "@babel/plugin-transform-react-jsx-source": "^7.23.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001588", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001588.tgz", + "integrity": "sha512-+hVY9jE44uKLkH0SrUTqxjxqNTOWHsbnQDIKjwkZ3lNTzUUVdBLBGXtj/q5Mp5u98r3droaZAewQuEDzjQdZlQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "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/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.4.677", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.677.tgz", + "integrity": "sha512-erDa3CaDzwJOpyvfKhOiJjBVNnMM0qxHq47RheVVwsSQrgBA9ZSGV9kdaOfZDPXcHzhG7lBxhj6A7KvfLJBd6Q==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "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/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/playwright": { + "version": "1.41.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.2.tgz", + "integrity": "sha512-v0bOa6H2GJChDL8pAeLa/LZC4feoAMbSQm1/jF/ySsWWoaNItvrMP7GEkvEEFyCTUYKMxjQKaTSg5up7nR6/8A==", + "dev": true, + "dependencies": { + "playwright-core": "1.41.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.41.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.2.tgz", + "integrity": "sha512-VaTvwCA4Y8kxEe+kfm2+uUUw5Lubf38RxF7FpBxLPmGe5sdNkSg5e3ChEigaGrX7qdqT3pt2m/98LiyvU2x6CA==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/postcss": { + "version": "8.4.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", + "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react-refresh": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", + "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.12.0.tgz", + "integrity": "sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==", + "dev": true, + "peer": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.12.0", + "@rollup/rollup-android-arm64": "4.12.0", + "@rollup/rollup-darwin-arm64": "4.12.0", + "@rollup/rollup-darwin-x64": "4.12.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.12.0", + "@rollup/rollup-linux-arm64-gnu": "4.12.0", + "@rollup/rollup-linux-arm64-musl": "4.12.0", + "@rollup/rollup-linux-riscv64-gnu": "4.12.0", + "@rollup/rollup-linux-x64-gnu": "4.12.0", + "@rollup/rollup-linux-x64-musl": "4.12.0", + "@rollup/rollup-win32-arm64-msvc": "4.12.0", + "@rollup/rollup-win32-ia32-msvc": "4.12.0", + "@rollup/rollup-win32-x64-msvc": "4.12.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "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/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/send/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/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", + "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.2", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/side-channel": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", + "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-node": { + "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.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "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" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "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/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.3.tgz", + "integrity": "sha512-UfmUD36DKkqhi/F75RrxvPpry+9+tTkrXfMNZD+SboZqBCMsxKtO52XeGzzuh7ioz+Eo/SYDBbdb0Z7vgcDJew==", + "dev": true, + "peer": true, + "dependencies": { + "esbuild": "^0.19.3", + "postcss": "^8.4.35", + "rollup": "^4.2.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "dev": true, + "hasInstallScript": true, + "peer": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, + "node_modules/vite/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/test/package/browser/template/package.json b/test/package/browser/template/package.json new file mode 100644 index 0000000000..a05aa04977 --- /dev/null +++ b/test/package/browser/template/package.json @@ -0,0 +1,28 @@ +{ + "name": "template", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "build": "esbuild --bundle src/index-default.ts --outdir=dist && esbuild --bundle src/index-modular.ts --outdir=dist", + "typecheck": "tsc --project src -noEmit", + "test-support:server": "ts-node server/server.ts", + "test": "npm run test:lib && npm run test:hooks", + "test:lib": "playwright test -c playwright-lib.config.js", + "test:hooks": "playwright test -c playwright-hooks.config.ts", + "test:install-deps": "playwright install chromium" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@playwright/experimental-ct-react": "^1.39.0", + "@playwright/test": "^1.39.0", + "@tsconfig/node16": "^16.1.1", + "@types/express": "^4.17.20", + "@types/node": "^20.11.19", + "esbuild": "^0.18.20", + "express": "^4.18.2", + "ts-node": "^10.9.1", + "typescript": "^5.2.2" + } +} diff --git a/test/package/browser/template/playwright-hooks.config.ts b/test/package/browser/template/playwright-hooks.config.ts new file mode 100644 index 0000000000..afe7f537fa --- /dev/null +++ b/test/package/browser/template/playwright-hooks.config.ts @@ -0,0 +1,41 @@ +import { defineConfig, devices } from '@playwright/experimental-ct-react'; + +/** + * Playwright config for running ably-js react hooks NPM package tests. + * Based on https://playwright.dev/docs/test-components. + * + * See config options: https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './test/hooks', + /* The base directory, relative to the config file, for snapshot files created with toMatchSnapshot and toHaveScreenshot. */ + snapshotDir: './__snapshots__', + /* Maximum time one test can run for. */ + timeout: 10 * 1000, + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + + /* Port to use for Playwright component endpoint. */ + ctPort: 3100, + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], +}); diff --git a/test/package/browser/template/playwright-lib.config.js b/test/package/browser/template/playwright-lib.config.js new file mode 100644 index 0000000000..4e12fe01b7 --- /dev/null +++ b/test/package/browser/template/playwright-lib.config.js @@ -0,0 +1,17 @@ +import { defineConfig } from '@playwright/test'; + +/** + * Playwright config for running ably-js lib NPM package tests. + * + * See config options: https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './test/lib', + webServer: { + command: 'npm run test-support:server', + url: 'http://localhost:4567', + }, + use: { + baseURL: 'http://localhost:4567', + }, +}); diff --git a/test/package/browser/template/playwright/index.html b/test/package/browser/template/playwright/index.html new file mode 100644 index 0000000000..5d009b1f30 --- /dev/null +++ b/test/package/browser/template/playwright/index.html @@ -0,0 +1,12 @@ + + + + + + Ably NPM package test (react export) + + +
    + + + diff --git a/test/package/browser/template/playwright/index.tsx b/test/package/browser/template/playwright/index.tsx new file mode 100644 index 0000000000..5b17f5d566 --- /dev/null +++ b/test/package/browser/template/playwright/index.tsx @@ -0,0 +1,22 @@ +import { beforeMount } from '@playwright/experimental-ct-react/hooks'; +import * as Ably from 'ably'; +import { AblyProvider, ChannelProvider } from 'ably/react'; + +import { createSandboxAblyAPIKey } from '../src/sandbox'; + +beforeMount(async ({ App }) => { + const key = await createSandboxAblyAPIKey(); + + const client = new Ably.Realtime({ + key, + environment: 'sandbox', + }); + + return ( + + + + + + ); +}); diff --git a/test/package/browser/template/server/resources/index-default.html b/test/package/browser/template/server/resources/index-default.html new file mode 100644 index 0000000000..f71012bf99 --- /dev/null +++ b/test/package/browser/template/server/resources/index-default.html @@ -0,0 +1,11 @@ + + + + + Ably NPM package test (default export) + + + + + + diff --git a/test/package/browser/template/server/resources/index-modular.html b/test/package/browser/template/server/resources/index-modular.html new file mode 100644 index 0000000000..1a0ad35d14 --- /dev/null +++ b/test/package/browser/template/server/resources/index-modular.html @@ -0,0 +1,11 @@ + + + + + Ably NPM package test (tree-shakable export) + + + + + + diff --git a/test/package/browser/template/server/resources/runTest.js b/test/package/browser/template/server/resources/runTest.js new file mode 100644 index 0000000000..ccdc2d18e7 --- /dev/null +++ b/test/package/browser/template/server/resources/runTest.js @@ -0,0 +1,9 @@ +(async () => { + try { + await testAblyPackage(); + onResult(null); + } catch (error) { + console.log('Caught error', error); + onResult(error); + } +})(); diff --git a/test/package/browser/template/server/server.ts b/test/package/browser/template/server/server.ts new file mode 100644 index 0000000000..2409fc30c5 --- /dev/null +++ b/test/package/browser/template/server/server.ts @@ -0,0 +1,15 @@ +import express from 'express'; +import path from 'node:path'; + +async function startWebServer(listenPort: number) { + const server = express(); + server.get('/', (req, res) => res.send('OK')); + server.use(express.static(path.join(__dirname, '/resources'))); + for (const filename of ['index-default.js', 'index-modular.js']) { + server.use(`/${filename}`, express.static(path.join(__dirname, '..', 'dist', filename))); + } + + server.listen(listenPort); +} + +startWebServer(4567); diff --git a/test/package/browser/template/src/ReactApp.tsx b/test/package/browser/template/src/ReactApp.tsx new file mode 100644 index 0000000000..e3d83d2f68 --- /dev/null +++ b/test/package/browser/template/src/ReactApp.tsx @@ -0,0 +1,50 @@ +import * as Ably from 'ably'; +import { useChannel } from 'ably/react'; +import { useEffect, useState } from 'react'; + +export function App() { + // check that we can refer to the types exported by Ably. + const [messages, updateMessages] = useState([]); + + // check that we can use ably/react exported members + const { channel, ably } = useChannel({ channelName: 'channel' }, (message) => { + updateMessages((prev) => [...prev, message]); + }); + + const messagePreviews = messages.map((message, idx) => ); + + useEffect(() => { + async function publishMessages() { + try { + // Check that we can use the TypeScript overload that accepts name and data as separate arguments + await channel.publish('message', { foo: 'bar' }); + + // Check that we can use the TypeScript overload that accepts a Message object + await channel.publish({ name: 'message', data: { foo: 'baz' } }); + (window as any).onResult(); + } catch (error) { + (window as any).onResult(error); + } + } + + publishMessages(); + }, [channel]); + + return ( +
    +
    Ably NPM package test (react export)
    +
    +

    Messages

    +
      {messagePreviews}
    +
    +
    + ); +} + +function MessagePreview({ message }: { message: Ably.Message }) { + return ( +
  • + {message.name}: {message.data.foo} +
  • + ); +} diff --git a/test/package/browser/template/src/index-default.ts b/test/package/browser/template/src/index-default.ts new file mode 100644 index 0000000000..cda822d21b --- /dev/null +++ b/test/package/browser/template/src/index-default.ts @@ -0,0 +1,32 @@ +import * as Ably from 'ably'; +import { createSandboxAblyAPIKey } from './sandbox'; + +// This function exists to check that we can refer to the types exported by Ably. +async function attachChannel(channel: Ably.RealtimeChannel) { + await channel.attach(); +} + +globalThis.testAblyPackage = async function () { + const key = await createSandboxAblyAPIKey(); + + const realtime = new Ably.Realtime({ key, environment: 'sandbox' }); + + const channel = realtime.channels.get('channel'); + await attachChannel(channel); + + const receivedMessagePromise = new Promise((resolve) => { + channel.subscribe(resolve); + }); + + // Check that we can use the TypeScript overload that accepts name and data as separate arguments + await channel.publish('message', { foo: 'bar' }); + const receivedMessage = await receivedMessagePromise; + + // Check that id and timestamp of a message received from Ably can be assigned to non-optional types + const { id: string, timestamp: number } = receivedMessage; + + channel.unsubscribe(); + + // Check that we can use the TypeScript overload that accepts a Message object + await channel.publish({ name: 'message', data: { foo: 'bar' } }); +}; diff --git a/test/package/browser/template/src/index-modular.ts b/test/package/browser/template/src/index-modular.ts new file mode 100644 index 0000000000..a9186f56dd --- /dev/null +++ b/test/package/browser/template/src/index-modular.ts @@ -0,0 +1,43 @@ +import { BaseRealtime, WebSocketTransport, FetchRequest, generateRandomKey } from 'ably/modular'; +import { InboundMessage, RealtimeChannel } from 'ably'; +import { createSandboxAblyAPIKey } from './sandbox'; + +// This function exists to check that we can refer to the types exported by Ably. +async function attachChannel(channel: RealtimeChannel) { + await channel.attach(); +} + +// This function exists to check that one of the free-standing functions (arbitrarily chosen) can be imported and does something vaguely sensible. +async function checkStandaloneFunction() { + const generatedKey = await generateRandomKey(); + if (!(generatedKey instanceof ArrayBuffer)) { + throw new Error('Expected to get an ArrayBuffer from generateRandomKey'); + } +} + +globalThis.testAblyPackage = async function () { + const key = await createSandboxAblyAPIKey(); + + const realtime = new BaseRealtime({ key, environment: 'sandbox', plugins: { WebSocketTransport, FetchRequest } }); + + const channel = realtime.channels.get('channel'); + await attachChannel(channel); + + const receivedMessagePromise = new Promise((resolve) => { + channel.subscribe(resolve); + }); + + // Check that we can use the TypeScript overload that accepts name and data as separate arguments + await channel.publish('message', { foo: 'bar' }); + const receivedMessage = await receivedMessagePromise; + + // Check that id and timestamp of a message received from Ably can be assigned to non-optional types + const { id: string, timestamp: number } = receivedMessage; + + await checkStandaloneFunction(); + + channel.unsubscribe(); + + // Check that we can use the TypeScript overload that accepts a Message object + await channel.publish({ name: 'message', data: { foo: 'bar' } }); +}; diff --git a/test/package/browser/template/src/sandbox.ts b/test/package/browser/template/src/sandbox.ts new file mode 100644 index 0000000000..100ab22f00 --- /dev/null +++ b/test/package/browser/template/src/sandbox.ts @@ -0,0 +1,16 @@ +import testAppSetup from '../../../../common/ably-common/test-resources/test-app-setup.json'; + +export async function createSandboxAblyAPIKey() { + const response = await fetch('https://sandbox-rest.ably.io/apps', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(testAppSetup.post_apps), + }); + + if (!response.ok) { + throw new Error(`Response not OK (${response.status})`); + } + + const testApp = await response.json(); + return testApp.keys[0].keyStr; +} diff --git a/test/package/browser/template/src/tsconfig.json b/test/package/browser/template/src/tsconfig.json new file mode 100644 index 0000000000..e280baa6de --- /dev/null +++ b/test/package/browser/template/src/tsconfig.json @@ -0,0 +1,10 @@ +{ + "include": ["**/*.ts", "**/*.tsx"], + "compilerOptions": { + "resolveJsonModule": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "jsx": "react-jsx" + } +} diff --git a/test/package/browser/template/test/hooks/ReactApp.spec.tsx b/test/package/browser/template/test/hooks/ReactApp.spec.tsx new file mode 100644 index 0000000000..6a734da0a1 --- /dev/null +++ b/test/package/browser/template/test/hooks/ReactApp.spec.tsx @@ -0,0 +1,35 @@ +import { expect, test } from '@playwright/experimental-ct-react'; + +import { App } from '../../src/ReactApp'; + +test.describe('NPM package', () => { + for (const scenario of [{ name: 'react export' }]) { + test.describe(scenario.name, () => { + test('can be imported and provides access to Ably functionality', async ({ mount, page }) => { + page.on('console', (message) => { + if (['error', 'warning'].includes(message.type())) { + console.log(`Console ${message.type()}:`, message); + } + }); + + page.on('pageerror', (err) => { + console.log('Uncaught exception:', err); + }); + + const pageResultPromise = new Promise((resolve, reject) => { + page.exposeFunction('onResult', (error: Error | null) => { + if (error) { + reject(error); + } else { + resolve(); + } + }); + }); + + const component = await mount(); + await expect(pageResultPromise).resolves.not.toThrow(); + await expect(component).toContainText('Ably NPM package test (react export)'); + }); + }); + } +}); diff --git a/test/package/browser/template/test/lib/package.test.ts b/test/package/browser/template/test/lib/package.test.ts new file mode 100644 index 0000000000..7397533af3 --- /dev/null +++ b/test/package/browser/template/test/lib/package.test.ts @@ -0,0 +1,25 @@ +import { test, expect } from '@playwright/test'; + +test.describe('NPM package', () => { + for (const scenario of [ + { name: 'default export', path: '/index-default.html' }, + { name: 'modular export', path: '/index-modular.html' }, + ]) { + test.describe(scenario.name, () => { + test('can be imported and provides access to Ably functionality', async ({ page }) => { + const pageResultPromise = new Promise((resolve, reject) => { + page.exposeFunction('onResult', (error: Error | null) => { + if (error) { + reject(error); + } else { + resolve(); + } + }); + }); + + await page.goto(scenario.path); + await pageResultPromise; + }); + }); + } +}); diff --git a/test/package/browser/template/tsconfig.json b/test/package/browser/template/tsconfig.json new file mode 100644 index 0000000000..b72f565555 --- /dev/null +++ b/test/package/browser/template/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "jsx": "react-jsx" + } +} diff --git a/test/playwright.html b/test/playwright.html index c91d41a5bd..22a0bf0ca1 100644 --- a/test/playwright.html +++ b/test/playwright.html @@ -21,6 +21,7 @@ + diff --git a/test/realtime/api.test.js b/test/realtime/api.test.js index 1a4dae77be..ab5e3695e4 100644 --- a/test/realtime/api.test.js +++ b/test/realtime/api.test.js @@ -6,9 +6,12 @@ define(['ably', 'chai'], function (Ably, chai) { describe('realtime/api', function () { it('Client constructors', function () { expect(typeof Ably.Realtime).to.equal('function'); - expect(typeof Ably.Realtime.Promise).to.equal('function'); - expect(typeof Ably.Realtime.Callbacks).to.equal('function'); - expect(Ably.Realtime.Callbacks).to.equal(Ably.Realtime); + }); + + it('constructor without any arguments', function () { + expect(() => new Ably.Realtime()).to.throw( + 'must be initialized with either a client options object, an Ably API key, or an Ably Token', + ); }); it('Crypto', function () { diff --git a/test/realtime/auth.test.js b/test/realtime/auth.test.js index c8b36c6ea6..eacae1f9a3 100644 --- a/test/realtime/auth.test.js +++ b/test/realtime/auth.test.js @@ -12,20 +12,21 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var monitorConnection = helper.monitorConnection; var testOnAllTransports = helper.testOnAllTransports; var mixin = helper.Utils.mixin; - var http = new Ably.Realtime.Platform.Http(); + var http = new Ably.Realtime._Http(); var jwtTestChannelName = 'JWT_test' + String(Math.floor(Math.random() * 10000) + 1); var echoServer = 'https://echo.ably.io'; + var whenPromiseSettles = helper.whenPromiseSettles; /* * Helper function to fetch JWT tokens from the echo server */ function getJWT(params, callback) { var authUrl = echoServer + '/createJWT'; - http.doUri('get', null, authUrl, null, null, params, function (err, body) { - if (err) { - callback(err, null); + whenPromiseSettles(http.doUri('get', authUrl, null, null, params), function (err, result) { + if (result.error) { + callback(result.error, null); } - callback(null, body.toString()); + callback(null, result.body.toString()); }); } @@ -40,13 +41,13 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async } var rest = helper.AblyRest({ queryTime: true }); - rest.time(function (err, time) { + whenPromiseSettles(rest.time(), function (err, time) { if (err) { done(err); return; } else { currentTime = time; - rest.auth.requestToken({}, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken({}), function (err, tokenDetails) { try { expect(!err, err && displayError(err)).to.be.ok; done(); @@ -64,7 +65,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async */ it('authbase0', function (done) { var realtime = helper.AblyRealtime(); - realtime.auth.requestToken(function (err, tokenDetails) { + whenPromiseSettles(realtime.auth.requestToken(), function (err, tokenDetails) { if (err) { closeAndFinish(done, realtime, err); return; @@ -88,7 +89,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async it('auth_useAuthUrl_json', function (done) { var realtime, rest = helper.AblyRest(); - rest.auth.requestToken(null, null, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken(null, null), function (err, tokenDetails) { if (err) { closeAndFinish(done, realtime, err); return; @@ -113,7 +114,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async it('auth_useAuthUrl_post_json', function (done) { var realtime, rest = helper.AblyRest(); - rest.auth.requestToken(null, null, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken(null, null), function (err, tokenDetails) { if (err) { closeAndFinish(done, realtime, err); return; @@ -138,7 +139,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async it('auth_useAuthUrl_plainText', function (done) { var realtime, rest = helper.AblyRest(); - rest.auth.requestToken(null, null, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken(null, null), function (err, tokenDetails) { if (err) { closeAndFinish(done, realtime, err); return; @@ -164,7 +165,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var realtime, rest = helper.AblyRest(); var authCallback = function (tokenParams, callback) { - rest.auth.createTokenRequest(tokenParams, null, function (err, tokenRequest) { + whenPromiseSettles(rest.auth.createTokenRequest(tokenParams, null), function (err, tokenRequest) { if (err) { closeAndFinish(done, realtime, err); return; @@ -202,7 +203,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async rest = helper.AblyRest(); var clientId = 'test clientid'; var authCallback = function (tokenParams, callback) { - rest.auth.requestToken(tokenParams, null, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken(tokenParams, null), function (err, tokenDetails) { if (err) { closeAndFinish(done, realtime, err); return; @@ -238,7 +239,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var realtime, rest = helper.AblyRest(); var authCallback = function (tokenParams, callback) { - rest.auth.requestToken(tokenParams, null, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken(tokenParams, null), function (err, tokenDetails) { if (err) { closeAndFinish(done, realtime, err); return; @@ -275,7 +276,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async it('auth_useAuthUrl_mixed_authParams_qsParams', function (done) { var realtime, rest = helper.AblyRest(); - rest.auth.createTokenRequest(null, null, function (err, tokenRequest) { + whenPromiseSettles(rest.auth.createTokenRequest(null, null), function (err, tokenRequest) { if (err) { closeAndFinish(done, realtime, err); return; @@ -312,7 +313,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var rest = helper.AblyRest(), testClientId = 'testClientId'; var authCallback = function (tokenParams, callback) { - rest.auth.requestToken({ clientId: testClientId }, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken({ clientId: testClientId }), function (err, tokenDetails) { if (err) { done(err); return; @@ -350,7 +351,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var clientRealtime, testClientId = 'test client id'; var rest = helper.AblyRest(); - rest.auth.requestToken({ clientId: testClientId }, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken({ clientId: testClientId }), function (err, tokenDetails) { if (err) { done(err); return; @@ -376,7 +377,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var realtime, testClientId = 'test client id'; var rest = helper.AblyRest(); - rest.auth.requestToken({ clientId: '*' }, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken({ clientId: '*' }), function (err, tokenDetails) { if (err) { done(err); return; @@ -404,7 +405,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var realtime, testClientId = 'test client id'; var rest = helper.AblyRest(); - rest.auth.requestToken({ clientId: '*' }, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken({ clientId: '*' }), function (err, tokenDetails) { if (err) { done(err); return; @@ -432,7 +433,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var clientRealtime, testClientId = 'test client id'; var rest = helper.AblyRest(); - rest.auth.requestToken({ clientId: testClientId }, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken({ clientId: testClientId }), function (err, tokenDetails) { if (err) { done(err); return; @@ -459,28 +460,17 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var realtime = helper.AblyRealtime(realtimeOptions); realtime.connection.on(function (stateChange) { if (stateChange.previous !== 'initialized') { - if (helper.bestTransport === 'jsonp') { - try { - // auth endpoints don't envelope, so we assume the 'least harmful' option, which is a disconnection with concomitant retry - expect(stateChange.current).to.equal('disconnected', 'Check connection goes to the expected state'); - // jsonp doesn't let you examine the statuscode - expect(stateChange.reason.statusCode).to.equal(401, 'Check correct cause error code'); - } catch (err) { - done(err); - } - } else { - try { - expect(stateChange.current).to.equal( - expectFailure ? 'failed' : 'disconnected', - 'Check connection goes to the expected state' - ); - expect(stateChange.reason.statusCode).to.equal( - expectFailure ? 403 : 401, - 'Check correct cause error code' - ); - } catch (err) { - done(err); - } + try { + expect(stateChange.current).to.equal( + expectFailure ? 'failed' : 'disconnected', + 'Check connection goes to the expected state', + ); + expect(stateChange.reason.statusCode).to.equal( + expectFailure ? 403 : 401, + 'Check correct cause error code', + ); + } catch (err) { + done(err); } try { expect(stateChange.reason.code).to.equal(80019, 'Check correct error code'); @@ -500,7 +490,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async authCallback: function (tokenParams, callback) { callback(new Error('An error from client code that the authCallback might return')); }, - }) + }), ); it( @@ -510,7 +500,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async /* (^._.^)ノ */ }, realtimeRequestTimeout: 100, - }) + }), ); it( @@ -519,7 +509,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async authCallback: function (tokenParams, callback) { callback(); }, - }) + }), ); it( @@ -528,7 +518,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async authCallback: function (tokenParams, callback) { callback(null, { horse: 'ebooks' }); }, - }) + }), ); it( @@ -541,7 +531,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async } callback(null, token); }, - }) + }), ); it( @@ -550,7 +540,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async authCallback: function (tokenParams, callback) { callback(null, ''); }, - }) + }), ); it( @@ -558,28 +548,28 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async authCallback_failures({ authUrl: helper.unroutableAddress, realtimeRequestTimeout: 100, - }) + }), ); it( 'authUrl_404', authCallback_failures({ authUrl: 'http://example.com/404', - }) + }), ); it( 'authUrl_wrong_content_type', authCallback_failures({ authUrl: 'http://example.com/', - }) + }), ); it( 'authUrl_401', authCallback_failures({ authUrl: echoServer + '/respondwith?status=401', - }) + }), ); it( @@ -587,7 +577,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async authCallback_failures({ authUrl: echoServer + '/?type=json&body=' + encodeURIComponent(JSON.stringify(JSON.stringify({ keyName: 'foo.bar' }))), - }) + }), ); /* 403 should cause the connection to go to failed, unlike the others */ @@ -597,8 +587,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async { authUrl: echoServer + '/respondwith?status=403', }, - true - ) + true, + ), ); /* expectFailed: */ /* 403 should cause connection to fail even with an external error response */ @@ -611,50 +601,46 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async '/?status=403&type=json&body=' + encodeURIComponent(JSON.stringify({ error: { some_custom: 'error' } })), }, - true - ) + true, + ), ); - /* auth endpoints don't envelope, so this won't work with jsonp */ - if (helper.bestTransport !== 'jsonp') { - it('authUrl_403_previously_active', function (done) { - var realtime, - rest = helper.AblyRest(); - rest.auth.requestToken(null, null, function (err, tokenDetails) { - if (err) { - closeAndFinish(done, realtime, err); - return; - } + it('authUrl_403_previously_active', function (done) { + var realtime, + rest = helper.AblyRest(); + whenPromiseSettles(rest.auth.requestToken(null, null), function (err, tokenDetails) { + if (err) { + closeAndFinish(done, realtime, err); + return; + } - var authPath = echoServer + '/?type=json&body=' + encodeURIComponent(JSON.stringify(tokenDetails)); + var authPath = echoServer + '/?type=json&body=' + encodeURIComponent(JSON.stringify(tokenDetails)); - realtime = helper.AblyRealtime({ authUrl: authPath }); + realtime = helper.AblyRealtime({ authUrl: authPath }); - realtime.connection.on('connected', function () { - /* replace the authUrl and reauth */ - realtime.auth.authorize( - null, - { authUrl: echoServer + '/respondwith?status=403' }, - function (err, tokenDetails) { - try { - expect(err && err.statusCode).to.equal(403, 'Check err statusCode'); - expect(err && err.code).to.equal(40300, 'Check err code'); - expect(realtime.connection.state).to.equal('failed', 'Check connection goes to the failed state'); - expect(realtime.connection.errorReason && realtime.connection.errorReason.statusCode).to.equal( - 403, - 'Check correct cause error code' - ); - expect(realtime.connection.errorReason.code).to.equal(80019, 'Check correct connection error code'); - closeAndFinish(done, realtime); - } catch (err) { - closeAndFinish(done, realtime, err); - } + realtime.connection.on('connected', function () { + /* replace the authUrl and reauth */ + whenPromiseSettles( + realtime.auth.authorize(null, { authUrl: echoServer + '/respondwith?status=403' }), + function (err, tokenDetails) { + try { + expect(err && err.statusCode).to.equal(403, 'Check err statusCode'); + expect(err && err.code).to.equal(40300, 'Check err code'); + expect(realtime.connection.state).to.equal('failed', 'Check connection goes to the failed state'); + expect(realtime.connection.errorReason && realtime.connection.errorReason.statusCode).to.equal( + 403, + 'Check correct cause error code', + ); + expect(realtime.connection.errorReason.code).to.equal(80019, 'Check correct connection error code'); + closeAndFinish(done, realtime); + } catch (err) { + closeAndFinish(done, realtime, err); } - ); - }); + }, + ); }); }); - } + }); /* * Check state change reason is propogated during a disconnect @@ -665,7 +651,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var clientRealtime, rest = helper.AblyRest(); - rest.auth.requestToken({ ttl: 5000 }, null, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken({ ttl: 5000 }, null), function (err, tokenDetails) { if (err) { done(err); return; @@ -702,15 +688,15 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async originalTime = rest.time; /* stub time */ - rest.time = function (callback) { + rest.time = async function () { timeRequestCount += 1; - originalTime.call(rest, callback); + return originalTime.call(rest); }; try { expect( isNaN(parseInt(rest.serverTimeOffset)) && !rest.serverTimeOffset, - 'Server time offset is empty and falsey until a time request has been made' + 'Server time offset is empty and falsey until a time request has been made', ).to.be.ok; } catch (err) { done(err); @@ -720,13 +706,13 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var asyncFns = []; for (var i = 0; i < 10; i++) { asyncFns.push(function (callback) { - rest.auth.createTokenRequest({}, null, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.createTokenRequest({}, null), function (err, tokenDetails) { if (err) { return callback(err); } expect( !isNaN(parseInt(rest.serverTimeOffset)), - 'Server time offset is configured when time is requested' + 'Server time offset is configured when time is requested', ).to.be.ok; callback(); }); @@ -759,7 +745,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var clientId = 'test clientid'; var authCallback = function (tokenParams, callback) { tokenParams.ttl = 5000; - rest.auth.requestToken(tokenParams, null, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken(tokenParams, null), function (err, tokenDetails) { if (err) { closeAndFinish(done, realtime, err); return; @@ -800,7 +786,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var clientId = 'test clientid'; var authCallback = function (tokenParams, callback) { tokenParams.ttl = 5000; - rest.auth.requestToken(tokenParams, null, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken(tokenParams, null), function (err, tokenDetails) { if (err) { closeAndFinish(done, realtime, err); return; @@ -838,33 +824,36 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var realtime, rest = helper.AblyRest(); var clientId = 'test clientid'; - rest.auth.requestToken({ ttl: 5000, clientId: clientId }, null, function (err, tokenDetails) { - if (err) { - closeAndFinish(done, realtime, err); - return; - } - realtime = helper.AblyRealtime(mixin(realtimeOpts, { token: tokenDetails.token, clientId: clientId })); - realtime.connection.once('connected', function () { - realtime.connection.once('disconnected', function (stateChange) { - try { - expect(stateChange.reason.code).to.equal(40142, 'Verify correct disconnect code'); - } catch (err) { - done(err); - return; - } - realtime.connection.once('failed', function (stateChange) { - /* Library has no way to generate a new token, so should fail */ + whenPromiseSettles( + rest.auth.requestToken({ ttl: 5000, clientId: clientId }, null), + function (err, tokenDetails) { + if (err) { + closeAndFinish(done, realtime, err); + return; + } + realtime = helper.AblyRealtime(mixin(realtimeOpts, { token: tokenDetails.token, clientId: clientId })); + realtime.connection.once('connected', function () { + realtime.connection.once('disconnected', function (stateChange) { try { - expect(stateChange.reason.code).to.equal(40171, 'Verify correct cause failure code'); - realtime.close(); - done(); + expect(stateChange.reason.code).to.equal(40142, 'Verify correct disconnect code'); } catch (err) { done(err); + return; } + realtime.connection.once('failed', function (stateChange) { + /* Library has no way to generate a new token, so should fail */ + try { + expect(stateChange.reason.code).to.equal(40171, 'Verify correct cause failure code'); + realtime.close(); + done(); + } catch (err) { + done(err); + } + }); }); }); - }); - }); + }, + ); }; }); @@ -876,7 +865,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var realtime, rest = helper.AblyRest(); var clientId = 'test clientid'; - rest.auth.requestToken({ ttl: 1, clientId: clientId }, null, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken({ ttl: 1, clientId: clientId }, null), function (err, tokenDetails) { if (err) { closeAndFinish(done, realtime, err); return; @@ -896,7 +885,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async * established, before realtime sends error response. So token error * goes through the same path as a connected transport, so goes to * disconnected first */ - utils.arrForEach(['connected', 'suspended'], function (state) { + ['connected', 'suspended'].forEach(function (state) { realtime.connection.on(state, function () { done(new Error('State changed to ' + state + ', should have gone to failed')); realtime.close(); @@ -919,7 +908,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async tokenParams.clientId = '*'; tokenParams.capability = firstTime ? { wrong: ['*'] } : { right: ['*'] }; firstTime = false; - rest.auth.requestToken(tokenParams, null, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken(tokenParams, null), function (err, tokenDetails) { if (err) { closeAndFinish(done, realtime, err); return; @@ -931,7 +920,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async realtime = helper.AblyRealtime(mixin(realtimeOpts, { authCallback: authCallback })); realtime.connection.once('connected', function () { var channel = realtime.channels.get('right'); - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { try { expect(err, 'Check using first token, without channel attach capability').to.be.ok; expect(err.code).to.equal(40160, 'Check expected error code'); @@ -941,14 +930,14 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async } /* soon after connected, reauth */ - realtime.auth.authorize(null, null, function (err) { + whenPromiseSettles(realtime.auth.authorize(null, null), function (err) { try { expect(!err, err && displayError(err)).to.be.ok; } catch (err) { done(err); return; } - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { try { expect(!err, 'Check using second token, with channel attach capability').to.be.ok; closeAndFinish(done, realtime); @@ -983,15 +972,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async realtime.auth.authorize(null, { token: '3' }); expect(realtime.auth.authOptions.authUrl).to.equal( undefined, - 'Check authorize completely replaces stored authOptions with passed in ones' - ); - - /* TODO remove for lib version 1.0 */ - realtime.auth.authorize(null, { authUrl: 'http://invalid' }); - realtime.auth.authorize(null, { force: true }); - expect(realtime.auth.authOptions.authUrl).to.equal( - 'http://invalid', - 'Check authorize does *not* replace stored authOptions when the only option is "force" in 0.9, for compatibility with 0.8' + 'Check authorize completely replaces stored authOptions with passed in ones', ); closeAndFinish(done, realtime); @@ -1007,7 +988,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var rest = helper.AblyRest(), authCallback = function (tokenParams, callback) { // Request a token (should happen twice) - rest.auth.requestToken(tokenParams, null, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken(tokenParams, null), function (err, tokenDetails) { if (err) { closeAndFinish(done, realtime, err); return; @@ -1118,7 +1099,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var realtime = helper.AblyRealtime({ authCallback: authCallback }); realtime.connection.once('connected', function () { var channel = realtime.channels.get(jwtTestChannelName); - channel.publish('greeting', 'Hello World!', function (err) { + whenPromiseSettles(channel.publish('greeting', 'Hello World!'), function (err) { try { expect(err.code).to.equal(40160, 'Verify publish denied code'); expect(err.statusCode).to.equal(401, 'Verify publish denied status code'); @@ -1242,7 +1223,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async it('reauth_consistently_expired_token', function (done) { var realtime, rest = helper.AblyRest(); - rest.auth.requestToken({ ttl: 1 }, function (err, token) { + whenPromiseSettles(rest.auth.requestToken({ ttl: 1 }), function (err, token) { if (err) { done(err); return; @@ -1275,7 +1256,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async it('expired_token_no_autoremove_when_dont_have_servertime', function (done) { var realtime, rest = helper.AblyRest(); - rest.auth.requestToken(function (err, token) { + whenPromiseSettles(rest.auth.requestToken(), function (err, token) { if (err) { done(err); return; @@ -1303,7 +1284,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async it('expired_token_autoremove_when_have_servertime', function (done) { var realtime, rest = helper.AblyRest(); - rest.auth.requestToken(function (err, token) { + whenPromiseSettles(rest.auth.requestToken(), function (err, token) { if (err) { done(err); return; @@ -1317,13 +1298,13 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }; realtime = helper.AblyRealtime({ authCallback: authCallback, autoConnect: false }); /* Set the server time offset */ - realtime.time(function () { + whenPromiseSettles(realtime.time(), function () { realtime.connect(); realtime.connection.on('connected', function () { try { expect(authCallbackCallCount).to.equal( 2, - 'Check we did autoremove the expired token ourselves, so authCallback is called a second time' + 'Check we did autoremove the expired token ourselves, so authCallback is called a second time', ); closeAndFinish(done, realtime); } catch (err) { @@ -1337,37 +1318,36 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async /* Check that only the last authorize matters */ it('multiple_concurrent_authorize', function (done) { var realtime = helper.AblyRealtime({ - logLevel: 4, useTokenAuth: true, defaultTokenParams: { capability: { wrong: ['*'] } }, }); realtime.connection.once('connected', function () { - realtime.auth.authorize({ capability: { stillWrong: ['*'] } }, function (err) { + whenPromiseSettles(realtime.auth.authorize({ capability: { stillWrong: ['*'] } }), function (err) { try { expect(!err, 'Check first authorize cb was called').to.be.ok; } catch (err) { done(err); } }); - realtime.auth.authorize({ capability: { alsoNope: ['*'] } }, function (err) { + whenPromiseSettles(realtime.auth.authorize({ capability: { alsoNope: ['*'] } }), function (err) { try { expect(!err, 'Check second authorize cb was called').to.be.ok; } catch (err) { done(err); } }); - realtime.auth.authorize({ capability: { wtfAreYouThinking: ['*'] } }, function (err) { + whenPromiseSettles(realtime.auth.authorize({ capability: { wtfAreYouThinking: ['*'] } }), function (err) { try { expect(!err, 'Check third authorize one cb was called').to.be.ok; } catch (err) { done(err); } }); - realtime.auth.authorize({ capability: { right: ['*'] } }, function (err) { + whenPromiseSettles(realtime.auth.authorize({ capability: { right: ['*'] } }), function (err) { if (err) { closeAndFinish(done, realtime, err); } - realtime.channels.get('right').attach(function (err) { + whenPromiseSettles(realtime.channels.get('right').attach(), function (err) { try { expect(!err, (err && displayError(err)) || 'Successfully attached').to.be.ok; closeAndFinish(done, realtime); @@ -1390,7 +1370,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async closeAndFinish(done, realtime, err); }); realtime.connection.once('connected', function () { - realtime.channels.get('right').attach(function (err) { + whenPromiseSettles(realtime.channels.get('right').attach(), function (err) { try { expect(!err, (err && displayError(err)) || 'Successfully attached').to.be.ok; closeAndFinish(done, realtime); diff --git a/test/realtime/channel.test.js b/test/realtime/channel.test.js index 5e83d2706b..c339edef3d 100644 --- a/test/realtime/channel.test.js +++ b/test/realtime/channel.test.js @@ -6,9 +6,12 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var expect = chai.expect; var displayError = helper.displayError; var closeAndFinish = helper.closeAndFinish; + var closeAndFinishAsync = helper.closeAndFinishAsync; var monitorConnection = helper.monitorConnection; - var createPM = Ably.Realtime.ProtocolMessage.fromDeserialized; + var monitorConnectionAsync = helper.monitorConnectionAsync; + var createPM = Ably.protocolMessageFromDeserialized; var testOnAllTransports = helper.testOnAllTransports; + var whenPromiseSettles = helper.whenPromiseSettles; var randomString = helper.randomString; function checkCanSubscribe(channel, testChannel) { @@ -24,7 +27,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async callback(); }); - testChannel.publish(eventName, null, function (err) { + whenPromiseSettles(testChannel.publish(eventName, null), function (err) { if (received) return; if (err) callback(err); timeout = setTimeout(function () { @@ -48,7 +51,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async callback('checkCantSubscribe: unexpectedly received message'); }); - testChannel.publish(eventName, null, function (err) { + whenPromiseSettles(testChannel.publish(eventName, null), function (err) { if (received) return; if (err) callback(err); timeout = setTimeout(function () { @@ -61,13 +64,13 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async function checkCanPublish(channel) { return function (callback) { - channel.publish(null, null, callback); + whenPromiseSettles(channel.publish(null, null), callback); }; } function checkCantPublish(channel) { return function (callback) { - channel.publish(null, null, function (err) { + whenPromiseSettles(channel.publish(null, null), function (err) { if (err && err.code === 40160) { callback(); } else { @@ -80,7 +83,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async function checkCanEnterPresence(channel) { return function (callback) { var clientId = randomString(); - channel.presence.enterClient(clientId, null, function (err) { + whenPromiseSettles(channel.presence.enterClient(clientId, null), function (err) { channel.presence.leaveClient(clientId); callback(err); }); @@ -89,7 +92,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async function checkCantEnterPresence(channel) { return function (callback) { - channel.presence.enterClient(randomString(), null, function (err) { + whenPromiseSettles(channel.presence.enterClient(randomString(), null), function (err) { if (err && err.code === 40160) { callback(); } else { @@ -113,7 +116,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async callback(); }); - testChannel.presence.enterClient(clientId, null, function (err) { + whenPromiseSettles(testChannel.presence.enterClient(clientId, null), function (err) { if (received) return; if (err) callback(err); timeout = setTimeout(function () { @@ -139,7 +142,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async callback('checkCantPresenceSubscribe: unexpectedly received message'); }); - testChannel.presence.enterClient(clientId, null, function (err) { + whenPromiseSettles(testChannel.presence.enterClient(clientId, null), function (err) { if (received) return; if (err) callback(err); timeout = setTimeout(function () { @@ -208,7 +211,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var realtime = helper.AblyRealtime(realtimeOpts); realtime.connection.on('connected', function () { var channel0 = realtime.channels.get('channelattach0'); - channel0.attach(function (err) { + whenPromiseSettles(channel0.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); } @@ -230,7 +233,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async try { var realtime = helper.AblyRealtime(realtimeOpts); var channel2 = realtime.channels.get('channelattach2'); - channel2.attach(function (err) { + whenPromiseSettles(channel2.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; @@ -255,11 +258,11 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var realtime = helper.AblyRealtime(realtimeOpts); realtime.connection.on('connected', function () { var channel0 = realtime.channels.get('channelattach3'); - channel0.attach(function (err) { + whenPromiseSettles(channel0.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); } - channel0.detach(function (err) { + whenPromiseSettles(channel0.detach(), function (err) { if (err) { closeAndFinish(done, realtime, err); } @@ -277,11 +280,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async } }; }, - true + true, ); - /* NB upgrade is excluded because realtime now sends an ATTACHED - * post-upgrade, which can race with the DETACHED if the DETACH is only sent - * just after upgrade. Re-include it with 1.1 spec which has IDs in ATTACHs */ /* * Attach with an empty channel and expect a channel error @@ -293,7 +293,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var realtime = helper.AblyRealtime(realtimeOpts); realtime.connection.once('connected', function () { var channel0 = realtime.channels.get(''); - channel0.attach(function (err) { + whenPromiseSettles(channel0.attach(), function (err) { if (err) { setTimeout(function () { try { @@ -325,7 +325,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var realtime = helper.AblyRealtime(realtimeOpts); realtime.connection.once('connected', function () { var channel = realtime.channels.get(':hell'); - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { try { expect(channel.errorReason.code).to.equal(40010, 'Attach error was set as the channel errorReason'); @@ -362,7 +362,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async try { var realtime = helper.AblyRealtime(realtimeOpts); realtime.connection.once('connected', function () { - realtime.channels.get('publish_no_attach').publish(function (err) { + whenPromiseSettles(realtime.channels.get('publish_no_attach').publish(), function (err) { if (err) { closeAndFinish(done, realtime, new Error('Unexpected attach failure: ' + helper.displayError(err))); return; @@ -385,7 +385,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async try { var realtime = helper.AblyRealtime(realtimeOpts); realtime.connection.once('connected', function () { - realtime.channels.get(':hell').publish(function (err) { + whenPromiseSettles(realtime.channels.get(':hell').publish(), function (err) { if (err) { try { expect(err.code).to.equal(40010, 'correct error code'); @@ -414,10 +414,10 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async try { var realtime = helper.AblyRealtime(realtimeOpts); realtime.connection.once('connected', function () { - realtime.channels.get(':hell').attach(function (err) { + whenPromiseSettles(realtime.channels.get(':hell').attach(), function (err) { if (err) { /* attempt second attach */ - realtime.channels.get(':hell').attach(function (err) { + whenPromiseSettles(realtime.channels.get(':hell').attach(), function (err) { if (err) { setTimeout(function () { try { @@ -446,22 +446,15 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async /* * Attach then later call whenState which fires immediately */ - it('channelattachOnceOrIfAfter', function (done) { + it('channelattachWhenState', function (done) { try { var realtime = helper.AblyRealtime(), - channel = realtime.channels.get('channelattachOnceOrIf'), - firedImmediately = false; + channel = realtime.channels.get('channelattachWhenState'); - channel.attach(function (err) { - channel.whenState('attached', function () { - firedImmediately = true; - }); - try { - expect(firedImmediately, 'whenState fired immediately as attached').to.be.ok; - closeAndFinish(done, realtime); - } catch (err) { + whenPromiseSettles(channel.attach(), function (err) { + whenPromiseSettles(channel.whenState('attached'), function () { closeAndFinish(done, realtime, err); - } + }); }); monitorConnection(done, realtime); } catch (err) { @@ -479,7 +472,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async firedImmediately = false; channel.attach(); - channel.whenState('attached', function () { + whenPromiseSettles(channel.whenState('attached'), function () { firedImmediately = true; try { expect(channel.state).to.equal('attached', 'whenState fired when attached'); @@ -509,7 +502,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async params: params, }; var channel = realtime.channels.get(testName, channelOptions); - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; @@ -536,7 +529,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async function (err) { testRealtime.close(); closeAndFinish(done, realtime, err); - } + }, ); }); }); @@ -563,7 +556,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }; var channel = realtime.channels.get(testName); channel.setOptions(channelOptions); - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; @@ -585,7 +578,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async function (err) { testRealtime.close(); closeAndFinish(done, realtime, err); - } + }, ); }); }); @@ -639,7 +632,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var channel = realtime.channels.get(testName, { params: params, }); - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; @@ -685,58 +678,32 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async async.series( [ function (cb) { - var setOptionsReturned = false; - channel.setOptions( - { - params: params, - modes: modes, - }, - function () { - expect( - !setOptionsReturned, - 'setOptions failed to call back immediately, when no reattach is required' - ).to.be.ok; - cb(); - } - ); - setOptionsReturned = true; - }, - function (cb) { - channel.attach(cb); + whenPromiseSettles(channel.attach(), cb); }, function (cb) { var channelUpdated = false; - // attached or update: in case this is happening in parallel with - // a transport upgrade, we might flip to attaching, meaning it'll come - // through as attached not update - channel._allChannelChanges.on(['attached', 'update'], function () { + channel._allChannelChanges.on(['update'], function () { channelUpdated = true; }); - var setOptionsReturned = false; - channel.setOptions( - { + whenPromiseSettles( + channel.setOptions({ params: { modes: 'publish', }, - }, + }), function () { /* Wait a tick so we don' depend on whether the update event runs the * channelUpdated listener or the setOptions listener first */ Ably.Realtime.Platform.Config.nextTick(function () { - expect( - setOptionsReturned, - 'setOptions should return immediately and call back after the reattach' - ).to.be.ok; expect( channelUpdated, - 'Check channel went to the server to update the channel params' + 'Check channel went to the server to update the channel params', ).to.be.ok; cb(); }); - } + }, ); - setOptionsReturned = true; }, function (cb) { var channelUpdated = false; @@ -744,39 +711,22 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async channelUpdated = true; }); - var setOptionsReturned = false; - channel.setOptions( - { + whenPromiseSettles( + channel.setOptions({ modes: ['subscribe'], - }, + }), function () { Ably.Realtime.Platform.Config.nextTick(function () { - expect( - setOptionsReturned, - 'setOptions should return immediately and call back after the reattach' - ).to.be.ok; expect(channelUpdated, 'Check channel went to the server to update the channel mode').to.be.ok; cb(); }); - } + }, ); - setOptionsReturned = true; - }, - function (cb) { - var setOptionsReturned = false; - channel.setOptions({}, function () { - expect( - !setOptionsReturned, - 'setOptions failed to call back immediately, when no reattach is required' - ).to.be.ok; - cb(); - }); - setOptionsReturned = true; }, ], function (err) { closeAndFinish(done, realtime, err); - } + }, ); }); monitorConnection(done, realtime); @@ -802,7 +752,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async modes: ['publish', 'presence_subscribe'], }; var channel = realtime.channels.get(testName, channelOptions); - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; @@ -829,7 +779,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async function (err) { testRealtime.close(); closeAndFinish(done, realtime, err); - } + }, ); }); }); @@ -852,7 +802,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async modes: modes, }; var channel = realtime.channels.get(testName, channelOptions); - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; @@ -878,7 +828,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async function (err) { testRealtime.close(); closeAndFinish(done, realtime, err); - } + }, ); }); }); @@ -902,7 +852,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async params: { delta: 'vcdiff' }, }; var channel = realtime.channels.get(testName, channelOptions); - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; @@ -929,7 +879,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async function (err) { testRealtime.close(); closeAndFinish(done, realtime, err); - } + }, ); }); }); @@ -951,7 +901,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async async.series( [ function (cb) { - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { cb(err); }); }, @@ -959,12 +909,12 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var channelOptions = { modes: 'subscribe', }; - channel.setOptions(channelOptions, function (err) { + whenPromiseSettles(channel.setOptions(channelOptions), function (err) { expect(err.code).to.equal(40000, 'Check channelOptions validation error code'); expect(err.statusCode).to.equal(400, 'Check channelOptions validation error statusCode'); expect(channel.modes).to.deep.equal( defaultChannelModes.split(','), - 'Check channel options modes result' + 'Check channel options modes result', ); cb(); }); @@ -973,12 +923,12 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var channelOptions = { modes: [1, 'subscribe'], }; - channel.setOptions(channelOptions, function (err) { + whenPromiseSettles(channel.setOptions(channelOptions), function (err) { expect(err.code).to.equal(40000, 'Check channelOptions validation error code'); expect(err.statusCode).to.equal(400, 'Check channelOptions validation error statusCode'); expect(channel.modes).to.deep.equal( defaultChannelModes.split(','), - 'Check channel options modes result' + 'Check channel options modes result', ); cb(); }); @@ -987,7 +937,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var channelOptions = { params: 'test', }; - channel.setOptions(channelOptions, function (err) { + whenPromiseSettles(channel.setOptions(channelOptions), function (err) { expect(err.code).to.equal(40000, 'Check channelOptions validation error code'); expect(err.statusCode).to.equal(400, 'Check channelOptions validation error statusCode'); expect(channel.params).to.deep.equal({}, 'Check channel options params'); @@ -999,7 +949,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var channelOptions = { params: { nonexistent: 'foo' }, }; - channel.setOptions(channelOptions, function () { + whenPromiseSettles(channel.setOptions(channelOptions), function () { expect(channel.params).to.deep.equal({}, 'Check channel params'); cb(); }); @@ -1008,13 +958,13 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var channelOptions = { modes: undefined, }; - channel.setOptions(channelOptions, function (err) { + whenPromiseSettles(channel.setOptions(channelOptions), function (err) { expect(err.code).to.equal(40000, 'Check channelOptions validation error code'); expect(err.statusCode).to.equal(400, 'Check channelOptions validation error statusCode'); expect(channel.params).to.deep.equal({}, 'Check channel options params result'); expect(channel.modes).to.deep.equal( defaultChannelModes.split(','), - 'Check channel options modes result' + 'Check channel options modes result', ); cb(); }); @@ -1023,13 +973,13 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var channelOptions = { modes: ['susribe'], }; - channel.setOptions(channelOptions, function (err) { + whenPromiseSettles(channel.setOptions(channelOptions), function (err) { expect(err.code).to.equal(40000, 'Check channelOptions validation error code'); expect(err.statusCode).to.equal(400, 'Check channelOptions validation error statusCode'); expect(channel.params).to.deep.equal({}, 'Check channel options params result'); expect(channel.modes).to.deep.equal( defaultChannelModes.split(','), - 'Check channel options modes result' + 'Check channel options modes result', ); cb(); }); @@ -1037,7 +987,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], function (err) { closeAndFinish(done, realtime, err); - } + }, ); }); monitorConnection(done, realtime); @@ -1054,7 +1004,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var realtime = helper.AblyRealtime({ useBinaryProtocol: true }); realtime.connection.on('connected', function () { var channel6 = realtime.channels.get('channelsubscribe0'); - channel6.attach(function (err) { + whenPromiseSettles(channel6.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; @@ -1094,21 +1044,21 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async channelByEvent.unsubscribe('event', listenerByEvent); channelByListener.unsubscribe(listenerNoEvent); channelAll.unsubscribe(); - channelByEvent.publish('event', 'data', function (err) { + whenPromiseSettles(channelByEvent.publish('event', 'data'), function (err) { try { expect(!err, 'Error publishing single event: ' + err).to.be.ok; } catch (err) { closeAndFinish(done, realtime, err); return; } - channelByListener.publish(null, 'data', function (err) { + whenPromiseSettles(channelByListener.publish(null, 'data'), function (err) { try { expect(!err, 'Error publishing any event: ' + err).to.be.ok; } catch (err) { closeAndFinish(done, realtime, err); return; } - channelAll.publish(null, 'data', function (err) { + whenPromiseSettles(channelAll.publish(null, 'data'), function (err) { try { expect(!err, 'Error publishing any event: ' + err).to.be.ok; expect(messagesReceived).to.equal(3, 'Only three messages should be received by the listeners'); @@ -1139,13 +1089,13 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async realtime.connection.on('connected', function () { channelByEvent = realtime.channels.get('channelsubscribe1-event'); - channelByEvent.subscribe('event', listenerByEvent, function () { + whenPromiseSettles(channelByEvent.subscribe('event', listenerByEvent), function () { channelByEvent.publish('event', 'data'); channelByListener = realtime.channels.get('channelsubscribe1-listener'); - channelByListener.subscribe(null, listenerNoEvent, function () { + whenPromiseSettles(channelByListener.subscribe(null, listenerNoEvent), function () { channelByListener.publish(null, 'data'); channelAll = realtime.channels.get('channelsubscribe1-all'); - channelAll.subscribe(listenerAllEvents, function () { + whenPromiseSettles(channelAll.subscribe(listenerAllEvents), function () { channelAll.publish(null, 'data'); }); }); @@ -1174,7 +1124,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (cb) { - channel.attach(cb); + whenPromiseSettles(channel.attach(), cb); }, function (cb) { /* Sabotage the reattach attempt, then simulate a server-sent detach */ @@ -1191,7 +1141,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async action: 13, channel: channelName, error: { statusCode: 500, code: 50000, message: 'generic serverside failure' }, - }) + }), ); }, function (cb) { @@ -1204,7 +1154,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], function (err) { closeAndFinish(done, realtime, err); - } + }, ); }); @@ -1233,11 +1183,11 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async action: 13, channel: channelName, error: { statusCode: 500, code: 50000, message: 'generic serverside failure' }, - }) + }), ); }); }; - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { try { expect(err.code).to.equal(50000, 'check error is propogated to the attach callback'); expect(channel.state).to.equal('suspended', 'check channel goes into suspended'); @@ -1258,7 +1208,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async channel = realtime.channels.get(channelName); realtime.connection.once('connected', function () { - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; @@ -1278,7 +1228,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async action: 9, channel: channelName, error: { statusCode: 500, code: 50000, message: 'generic serverside failure' }, - }) + }), ); }); }); @@ -1302,7 +1252,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (cb) { - channel.attach(cb); + whenPromiseSettles(channel.attach(), cb); }, function (cb) { channel.once(function (stateChange) { @@ -1320,13 +1270,13 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async action: 11, channel: channelName, error: { statusCode: 500, code: 50000, message: 'generic serverside failure' }, - }) + }), ); }, ], function (err) { closeAndFinish(done, realtime, err); - } + }, ); }); @@ -1338,7 +1288,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async channel = realtime.channels.get('publish_no_queueing'); /* try a publish while not yet connected */ - channel.publish('foo', 'bar', function (err) { + whenPromiseSettles(channel.publish('foo', 'bar'), function (err) { try { expect(err, 'Check publish while disconnected/connecting is rejected').to.be.ok; closeAndFinish(done, realtime); @@ -1372,7 +1322,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (cb) { - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { expect(err, 'Channel attach timed out as expected').to.be.ok; expect(err && err.code).to.equal(90007, 'Attach timeout err passed to attach callback'); expect(channel.state).to.equal('suspended', 'Check channel state goes to suspended'); @@ -1391,7 +1341,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], function (err) { closeAndFinish(done, realtime, err); - } + }, ); }); @@ -1417,7 +1367,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (cb) { - channel.attach(cb); + whenPromiseSettles(channel.attach(), cb); }, function (cb) { /* Have the connection go into the suspended state, and check that the @@ -1454,7 +1404,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async channel.once(function (stateChange) { expect(stateChange.current).to.equal( 'suspended', - 'Check that the channel goes back into suspended after attach fails' + 'Check that the channel goes back into suspended after attach fails', ); expect(stateChange.reason && stateChange.reason.code).to.equal(90007, 'Check correct error code'); cb(); @@ -1463,7 +1413,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], function (err) { closeAndFinish(done, realtime, err); - } + }, ); }); @@ -1481,7 +1431,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (cb) { - channel.attach(cb); + whenPromiseSettles(channel.attach(), cb); }, function (cb) { /* Sabotage the detach attempt, detach, then simulate a server-sent attached while @@ -1506,7 +1456,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], function (err) { closeAndFinish(done, realtime, err); - } + }, ); }); @@ -1517,12 +1467,17 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var channel = realtime.channels.get(channelName); channel.state = 'suspended'; - channel.detach(function () { - expect(channel.state).to.equal( - 'detached', - 'Check that detach on suspended channel results in detached channel' - ); - done(); + whenPromiseSettles(channel.detach(), function () { + try { + expect(channel.state).to.equal( + 'detached', + 'Check that detach on suspended channel results in detached channel', + ); + + closeAndFinish(done, realtime); + } catch (err) { + closeAndFinish(done, realtime, err); + } }); }); @@ -1534,12 +1489,12 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async channel.state = 'failed'; - channel.detach(function (err) { + whenPromiseSettles(channel.detach(), function (err) { if (!err) { - done(new Error('expected detach to return error response')); + closeAndFinish(done, realtime, new Error('expected detach to return error response')); return; } - done(); + closeAndFinish(done, realtime); }); }); @@ -1553,7 +1508,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var subscriber = function (message) { expect(message.data).to.equal('message'); channel.unsubscribe(subscriber); - channel.detach(function (err) { + whenPromiseSettles(channel.detach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; @@ -1574,7 +1529,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var realtime = helper.AblyRealtime(); var channelName = 'attach_returns_state_chnage'; var channel = realtime.channels.get(channelName); - channel.attach(function (err, stateChange) { + whenPromiseSettles(channel.attach(), function (err, stateChange) { if (err) { closeAndFinish(done, realtime, err); return; @@ -1589,7 +1544,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async } // for an already-attached channel, null is returned - channel.attach(function (err, stateChange) { + whenPromiseSettles(channel.attach(), function (err, stateChange) { if (err) { closeAndFinish(done, realtime, err); return; @@ -1610,9 +1565,10 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var realtime = helper.AblyRealtime(); var channelName = 'subscribe_returns_state_chnage'; var channel = realtime.channels.get(channelName); - channel.subscribe( - function () {}, // message listener - // attach callback + whenPromiseSettles( + channel.subscribe( + function () {}, // message listener + ), function (err, stateChange) { if (err) { closeAndFinish(done, realtime, err); @@ -1627,7 +1583,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async return; } closeAndFinish(done, realtime); - } + }, ); }); @@ -1638,7 +1594,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var channel = realtime.channels.get(channelName, channelOpts); // attach with rewind but no channel history - hasBacklog should be false - channel.attach(function (err, stateChange) { + whenPromiseSettles(channel.attach(), function (err, stateChange) { if (err) { closeAndFinish(done, realtime, err); return; @@ -1663,12 +1619,12 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var restChannel = rest.channels.get(channelName); // attach with rewind after publishing - hasBacklog should be true - restChannel.publish('foo', 'bar', function (err) { + whenPromiseSettles(restChannel.publish('foo', 'bar'), function (err) { if (err) { closeAndFinish(done, realtime, err); return; } - rtChannel.attach(function (err, stateChange) { + whenPromiseSettles(rtChannel.attach(), function (err, stateChange) { if (err) { closeAndFinish(done, realtime, err); return; @@ -1689,7 +1645,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async const realtime = helper.AblyRealtime(); const channel = realtime.channels.get('channel-with-options', { modes: ['PRESENCE'] }); channel.attach(); - channel.whenState('attaching', function () { + whenPromiseSettles(channel.whenState('attaching'), function () { try { realtime.channels.get('channel-with-options', { modes: ['PRESENCE'] }); closeAndFinish(done, realtime); @@ -1698,5 +1654,27 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async } }); }); + + it('whenState', async () => { + const realtime = helper.AblyRealtime(); + + await monitorConnectionAsync(async () => { + const channel = realtime.channels.get('channel'); + + // RTL25a - when already in given state, returns null + const initializedStateChange = await channel.whenState('initialized'); + expect(initializedStateChange).to.be.null; + + // RTL25b — when not in given state, calls #once + const attachedStateChangePromise = channel.whenState('attached'); + channel.attach(); + const attachedStateChange = await attachedStateChangePromise; + expect(attachedStateChange).not.to.be.null; + expect(attachedStateChange.previous).to.equal('attaching'); + expect(attachedStateChange.current).to.equal('attached'); + }, realtime); + + await closeAndFinishAsync(realtime); + }); }); }); diff --git a/test/realtime/connection.test.js b/test/realtime/connection.test.js index e1c0754e33..058b6a646e 100644 --- a/test/realtime/connection.test.js +++ b/test/realtime/connection.test.js @@ -3,9 +3,12 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async, chai) { var expect = chai.expect; var closeAndFinish = helper.closeAndFinish; - var createPM = Ably.Realtime.ProtocolMessage.fromDeserialized; + var closeAndFinishAsync = helper.closeAndFinishAsync; + var createPM = Ably.protocolMessageFromDeserialized; var displayError = helper.displayError; var monitorConnection = helper.monitorConnection; + var monitorConnectionAsync = helper.monitorConnectionAsync; + var whenPromiseSettles = helper.whenPromiseSettles; describe('realtime/connection', function () { this.timeout(60 * 1000); @@ -42,7 +45,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async try { realtime = helper.AblyRealtime(); realtime.connection.on('connected', function () { - realtime.connection.ping(function (err, responseTime) { + whenPromiseSettles(realtime.connection.ping(), function (err, responseTime) { if (err) { closeAndFinish(done, realtime, err); return; @@ -65,7 +68,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async it('connectionAttributes', function (done) { var realtime; try { - realtime = helper.AblyRealtime({ logLevel: 4 }); + realtime = helper.AblyRealtime(); realtime.connection.on('connected', function () { try { const recoveryContext = JSON.parse(realtime.connection.recoveryKey); @@ -77,7 +80,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async } var channel = realtime.channels.get('connectionattributes'); - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; @@ -95,7 +98,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (cb) { - channel.publish('name', 'data', cb); + whenPromiseSettles(channel.publish('name', 'data'), cb); }, ], function (err) { @@ -104,7 +107,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async return; } realtime.connection.close(); - realtime.connection.whenState('closed', function () { + whenPromiseSettles(realtime.connection.whenState('closed'), function () { try { expect(realtime.connection.recoveryKey).to.equal(null, 'verify recovery key null after close'); closeAndFinish(done, realtime); @@ -112,7 +115,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async closeAndFinish(done, realtime, err); } }); - } + }, ); }); }); @@ -135,19 +138,19 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async try { expect(stateChange.reason.code).to.equal( 80018, - 'verify unrecoverable-connection error set in stateChange.reason' + 'verify unrecoverable-connection error set in stateChange.reason', ); expect(realtime.connection.errorReason.code).to.equal( 80018, - 'verify unrecoverable-connection error set in connection.errorReason' + 'verify unrecoverable-connection error set in connection.errorReason', ); expect(realtime.connection.connectionManager.msgSerial).to.equal( 0, - 'verify msgSerial is 0 (new connection), not 3' + 'verify msgSerial is 0 (new connection), not 3', ); expect(realtime.connection.key.indexOf('ablyjs_test_fake')).to.equal( -1, - 'verify connection using a new connectionkey' + 'verify connection using a new connectionkey', ); closeAndFinish(done, realtime); } catch (err) { @@ -162,7 +165,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async /* * Check that a message published on one transport that has not yet been * acked will be republished with the same msgSerial on a new transport (eg - * after a resume or an upgrade), before any new messages are send (and + * after a resume), before any new messages are send (and * without being merged with new messages) */ it('connectionQueuing', function (done) { @@ -172,72 +175,105 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async realtime.connection.once('connected', function () { var transport = connectionManager.activeProtocol.transport; - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; } + + let transportSendCallback; + /* Sabotage sending the message */ transport.send = function (msg) { if (msg.action == 15) { expect(msg.msgSerial).to.equal(0, 'Expect msgSerial to be 0'); + + if (!transportSendCallback) { + done(new Error('transport.send override called before transportSendCallback populated')); + } + + transportSendCallback(null); } }; - async.parallel( + let publishCallback; + + async.series( [ function (cb) { + transportSendCallback = cb; + /* Sabotaged publish */ - channel.publish('first', null, function (err) { - try { - expect(!err, 'Check publish happened (eventually) without err').to.be.ok; - } catch (err) { - cb(err); - return; + whenPromiseSettles(channel.publish('first', null), function (err) { + if (!publishCallback) { + done(new Error('publish completed before publishCallback populated')); } - cb(); + publishCallback(err); }); }, - function (cb) { - /* After the disconnect, on reconnect, spy on transport.send again */ - connectionManager.once('transport.pending', function (transport) { - var oldSend = transport.send; - transport.send = function (msg, msgCb) { - if (msg.action === 15) { - if (msg.messages[0].name === 'first') { - try { - expect(msg.msgSerial).to.equal(0, 'Expect msgSerial of original message to still be 0'); - expect(msg.messages.length).to.equal( - 1, - 'Expect second message to not have been merged with the attempted message' - ); - } catch (err) { - cb(err); - return; - } - } else if (msg.messages[0].name === 'second') { + // We wait for transport.send to recieve the message that we just + // published before we proceed to disconnecting the transport, to + // make sure that the message got marked as `sendAttempted`. + + function (cb) { + async.parallel( + [ + function (cb) { + publishCallback = function (err) { try { - expect(msg.msgSerial).to.equal(1, 'Expect msgSerial of new message to be 1'); + expect(!err, 'Check publish happened (eventually) without err').to.be.ok; } catch (err) { cb(err); return; } cb(); - } - } - oldSend.call(transport, msg, msgCb); - }; - channel.publish('second', null); - }); + }; + }, + function (cb) { + /* After the disconnect, on reconnect, spy on transport.send again */ + connectionManager.once('transport.pending', function (transport) { + var oldSend = transport.send; - /* Disconnect the transport (will automatically reconnect and resume) () */ - connectionManager.disconnectAllTransports(); + transport.send = function (msg, msgCb) { + if (msg.action === 15) { + if (msg.messages[0].name === 'first') { + try { + expect(msg.msgSerial).to.equal(0, 'Expect msgSerial of original message to still be 0'); + expect(msg.messages.length).to.equal( + 1, + 'Expect second message to not have been merged with the attempted message', + ); + } catch (err) { + cb(err); + return; + } + } else if (msg.messages[0].name === 'second') { + try { + expect(msg.msgSerial).to.equal(1, 'Expect msgSerial of new message to be 1'); + } catch (err) { + cb(err); + return; + } + cb(); + } + } + oldSend.call(transport, msg, msgCb); + }; + channel.publish('second', null); + }); + + /* Disconnect the transport (will automatically reconnect and resume) () */ + connectionManager.disconnectAllTransports(); + }, + ], + cb, + ); }, ], function (err) { closeAndFinish(done, realtime, err); - } + }, ); }); }); @@ -255,7 +291,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async expect(details.connectionStateTtl).to.equal(12345, 'Check connectionStateTtl in event'); expect(connectionManager.connectionStateTtl).to.equal( 12345, - 'Check connectionStateTtl set in connectionManager' + 'Check connectionStateTtl set in connectionManager', ); expect(details.clientId).to.equal('foo', 'Check clientId in event'); expect(realtime.auth.clientId).to.equal('foo', 'Check clientId set in auth'); @@ -277,36 +313,30 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async maxMessageSize: 98765, connectionStateTtl: 12345, }, - }) + }), ); }); monitorConnection(done, realtime); }); - if (typeof Promise !== 'undefined') { - describe('connection_promise', function () { - it('ping', function (done) { - var client = helper.AblyRealtime({ promises: true }); + it('whenState', async () => { + const realtime = helper.AblyRealtime({ autoConnect: false }); - client.connection - .once('connected') - .then(function () { - client.connection - .ping() - .then(function (responseTime) { - expect(typeof responseTime).to.equal('number', 'check that a responseTime returned'); - expect(responseTime > 0, 'check that responseTime was positive').to.be.ok; - closeAndFinish(done, client); - }) - ['catch'](function (err) { - closeAndFinish(done, client, err); - }); - }) - ['catch'](function (err) { - closeAndFinish(done, client, err); - }); - }); - }); - } + await monitorConnectionAsync(async () => { + // RTN26a - when already in given state, returns null + const initializedStateChange = await realtime.connection.whenState('initialized'); + expect(initializedStateChange).to.be.null; + + // RTN26b — when not in given state, calls #once + const connectedStateChangePromise = realtime.connection.whenState('connected'); + realtime.connection.connect(); + const connectedStateChange = await connectedStateChangePromise; + expect(connectedStateChange).not.to.be.null; + expect(connectedStateChange.previous).to.equal('connecting'); + expect(connectedStateChange.current).to.equal('connected'); + }, realtime); + + await closeAndFinishAsync(realtime); + }); }); }); diff --git a/test/realtime/connectivity.test.js b/test/realtime/connectivity.test.js index ea4edc9177..ba2c408323 100644 --- a/test/realtime/connectivity.test.js +++ b/test/realtime/connectivity.test.js @@ -5,6 +5,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { var closeAndFinish = helper.closeAndFinish; var monitorConnection = helper.monitorConnection; var utils = helper.Utils; + var whenPromiseSettles = helper.whenPromiseSettles; describe('realtime/connectivity', function () { this.timeout(60 * 1000); @@ -22,7 +23,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { * Connect with available http transports; internet connectivity check should work */ it('http_connectivity_check', function (done) { - new Ably.Realtime.Platform.Http().checkConnectivity(function (err, res) { + whenPromiseSettles(new Ably.Realtime._Http().checkConnectivity(), function (err, res) { try { expect(res && !err, 'Connectivity check completed ' + (err && utils.inspectError(err))).to.be.ok; } catch (err) { @@ -33,44 +34,51 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { }); }); + function options(connectivityCheckUrl, disableConnectivityCheck) { + return { + connectivityCheckUrl, + disableConnectivityCheck, + autoConnect: false, + }; + } + describe('configured_connectivity_check_url', function () { var urlScheme = 'https://'; var echoServer = 'echo.ably.io'; var successUrl = echoServer + '/respondwith?status=200'; var failUrl = echoServer + '/respondwith?status=500'; - function options(connectivityCheckUrl) { - return { - connectivityCheckUrl: connectivityCheckUrl, - autoConnect: false, - }; - } - it('succeeds with scheme', function (done) { - new helper.AblyRealtime(options(urlScheme + successUrl)).http.checkConnectivity(function (err, res) { - try { - expect(res && !err, 'Connectivity check completed ' + (err && utils.inspectError(err))).to.be.ok; - } catch (err) { - done(err); - return; - } - done(); - }); + whenPromiseSettles( + new helper.AblyRealtime(options(urlScheme + successUrl)).http.checkConnectivity(), + function (err, res) { + try { + expect(res && !err, 'Connectivity check completed ' + (err && utils.inspectError(err))).to.be.ok; + } catch (err) { + done(err); + return; + } + done(); + }, + ); }); it('fails with scheme', function (done) { - new helper.AblyRealtime(options(urlScheme + failUrl)).http.checkConnectivity(function (err, res) { - try { - expect(!res, 'Connectivity check expected to return false').to.be.ok; - done(); - } catch (err) { - done(err); - } - }); + whenPromiseSettles( + new helper.AblyRealtime(options(urlScheme + failUrl)).http.checkConnectivity(), + function (err, res) { + try { + expect(!res, 'Connectivity check expected to return false').to.be.ok; + done(); + } catch (err) { + done(err); + } + }, + ); }); it('succeeds with querystring', function (done) { - new helper.AblyRealtime(options(successUrl)).http.checkConnectivity(function (err, res) { + whenPromiseSettles(new helper.AblyRealtime(options(successUrl)).http.checkConnectivity(), function (err, res) { try { expect(res && !err, 'Connectivity check completed ' + (err && utils.inspectError(err))).to.be.ok; done(); @@ -81,7 +89,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { }); it('fails with querystring', function (done) { - new helper.AblyRealtime(options(failUrl)).http.checkConnectivity(function (err, res) { + whenPromiseSettles(new helper.AblyRealtime(options(failUrl)).http.checkConnectivity(), function (err, res) { try { expect(!res, 'Connectivity check expected to return false').to.be.ok; done(); @@ -92,40 +100,46 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { }); it('succeeds with plain url', function (done) { - new helper.AblyRealtime(options('sandbox-rest.ably.io/time')).http.checkConnectivity(function (err, res) { - try { - expect(res && !err, 'Connectivity check completed ' + (err && utils.inspectError(err))).to.be.ok; - done(); - } catch (err) { - done(err); - } - }); + whenPromiseSettles( + new helper.AblyRealtime(options('sandbox-rest.ably.io/time')).http.checkConnectivity(), + function (err, res) { + try { + expect(res && !err, 'Connectivity check completed ' + (err && utils.inspectError(err))).to.be.ok; + done(); + } catch (err) { + done(err); + } + }, + ); }); it('fails with plain url', function (done) { - new helper.AblyRealtime(options('echo.ably.io')).http.checkConnectivity(function (err, res) { + whenPromiseSettles( + new helper.AblyRealtime(options('echo.ably.io')).http.checkConnectivity(), + function (err, res) { + try { + expect(!res, 'Connectivity check expected to return false').to.be.ok; + done(); + } catch (err) { + done(err); + } + }, + ); + }); + }); + + it('disable_connectivity_check', function (done) { + whenPromiseSettles( + new helper.AblyRealtime(options('notarealhost', true)).http.checkConnectivity(), + function (err, res) { try { - expect(!res, 'Connectivity check expected to return false').to.be.ok; + expect(res && !err, 'Connectivity check completed ' + (err && utils.inspectError(err))).to.be.ok; done(); } catch (err) { done(err); } - }); - }); - }); - - it('disable_connectivity_check', function (done) { - new helper.AblyRealtime({ - connectivityCheckUrl: 'notarealhost', - disableConnectivityCheck: true, - }).http.checkConnectivity(function (err, res) { - try { - expect(res && !err, 'Connectivity check completed ' + (err && utils.inspectError(err))).to.be.ok; - done(); - } catch (err) { - done(err); - } - }); + }, + ); }); }); }); diff --git a/test/realtime/crypto.test.js b/test/realtime/crypto.test.js index 042699be23..140047aeca 100644 --- a/test/realtime/crypto.test.js +++ b/test/realtime/crypto.test.js @@ -11,40 +11,21 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var msgpack = typeof window == 'object' ? Ably.msgpack : require('@ably/msgpack-js'); var testOnAllTransports = helper.testOnAllTransports; var closeAndFinish = helper.closeAndFinish; + var whenPromiseSettles = helper.whenPromiseSettles; function attachChannels(channels, callback) { async.map( channels, function (channel, cb) { - channel.attach(cb); + whenPromiseSettles(channel.attach(), cb); }, - callback + callback, ); } function testMessageEquality(done, one, two) { try { - // treat `null` same as `undefined` (using ==, rather than ===) - expect(one.encoding == two.encoding, "Encoding mismatch ('" + one.encoding + "' != '" + two.encoding + "').").to - .be.ok; - - if (typeof one.data === 'string' && typeof two.data === 'string') { - expect(one.data === two.data, 'String data contents mismatch.').to.be.ok; - return; - } - - if (BufferUtils.isBuffer(one.data) && BufferUtils.isBuffer(two.data)) { - expect(BufferUtils.bufferCompare(one.data, two.data) === 0, 'Buffer data contents mismatch.').to.be.ok; - return; - } - - var json1 = JSON.stringify(one.data); - var json2 = JSON.stringify(two.data); - if (null === json1 || undefined === json1 || null === json2 || undefined === json2) { - expect(false, 'JSON stringify failed.').to.be.ok; - return; - } - expect(json1 === json2, 'JSON data contents mismatch.').to.be.ok; + helper.testMessageEquality(one, two); } catch (err) { done(err); } @@ -56,7 +37,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async return; } - loadTestData(testResourcesPath + filename, function (err, testData) { + loadTestData(testResourcesPath + filename, async function (err, testData) { if (err) { done(new Error('Unable to get test assets; err = ' + displayError(err))); return; @@ -71,11 +52,11 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var item = testData.items[i]; /* read messages from test data and decode (ie remove any base64 encoding). */ - var createTestMessage = function () { - return Message.fromEncoded(item.encoded); + var createTestMessage = async function () { + return await Message.fromEncoded(item.encoded); }; - var encryptedMessage = Message.fromEncoded(item.encrypted); + var encryptedMessage = await Message.fromEncoded(item.encrypted); var runTest = function (testMessage) { /* reset channel cipher, to ensure it uses the given iv */ @@ -84,13 +65,13 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }; // Run the test with the message’s data as-is. - runTest(createTestMessage()); + runTest(await createTestMessage()); if (testPlaintextVariants) { - var testMessage = createTestMessage(); + var testMessage = await createTestMessage(); if (BufferUtils.isBuffer(testMessage.data) && !(testMessage.data instanceof ArrayBuffer)) { // Now, check that we can also handle an ArrayBuffer plaintext. - var testMessageWithArrayBufferData = createTestMessage(); + var testMessageWithArrayBufferData = await createTestMessage(); testMessageWithArrayBufferData.data = BufferUtils.toArrayBuffer(testMessageWithArrayBufferData.data); runTest(testMessageWithArrayBufferData); } @@ -118,14 +99,14 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async /* generateRandomKey with an explicit keyLength */ it('generateRandomKey0', function (done) { - Crypto.generateRandomKey(64, function (err, key) { + whenPromiseSettles(Crypto.generateRandomKey(64), function (err, key) { if (err) { done(err); return; } try { - /* .length for a nodejs buffer, .sigbytes for a browser CryptoJS WordArray */ - expect(key.length || key.sigBytes).to.equal(8, 'generated key is the correct length'); + /* .length for a nodejs buffer, .byteLength for a browser ArrayBuffer */ + expect(key.length || key.byteLength).to.equal(8, 'generated key is the correct length'); done(); } catch (err) { done(err); @@ -135,13 +116,13 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async /* generateRandomKey with no keyLength should generate 256-bit keys */ it('generateRandomKey1', function (done) { - Crypto.generateRandomKey(function (err, key) { + whenPromiseSettles(Crypto.generateRandomKey(), function (err, key) { if (err) { done(err); return; } try { - expect(key.length || key.sigBytes).to.equal(32, 'generated key is the default length'); + expect(key.length || key.byteLength).to.equal(32, 'generated key is the default length'); done(); } catch (err) { done(err); @@ -149,8 +130,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }); - it('getDefaultParams_wordArray_key', function (done) { - Crypto.generateRandomKey(function (err, key) { + it('getDefaultParams_withResultOfGenerateRandomKey', function (done) { + whenPromiseSettles(Crypto.generateRandomKey(), function (err, key) { if (err) { done(err); } @@ -167,14 +148,14 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); it('getDefaultParams_ArrayBuffer_key', function (done) { - Crypto.generateRandomKey(function (err, key) { + whenPromiseSettles(Crypto.generateRandomKey(), function (err, key) { if (err) { done(err); } var arrayBufferKey = Ably.Realtime.Platform.BufferUtils.toArrayBuffer(key); var params = Crypto.getDefaultParams({ key: arrayBufferKey }); try { - expect(BufferUtils.bufferCompare(params.key, key)).to.equal(0); + expect(BufferUtils.areBuffersEqual(params.key, key)).to.equal(true); done(); } catch (err) { done(err); @@ -183,7 +164,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); it('getDefaultParams_base64_key', function (done) { - Crypto.generateRandomKey(function (err, key) { + whenPromiseSettles(Crypto.generateRandomKey(), function (err, key) { if (err) { done(err); return; @@ -191,7 +172,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var b64key = Ably.Realtime.Platform.BufferUtils.base64Encode(key); var params = Crypto.getDefaultParams({ key: b64key }); try { - expect(BufferUtils.bufferCompare(params.key, key)).to.equal(0); + expect(BufferUtils.areBuffersEqual(params.key, key)).to.equal(true); done(); } catch (err) { done(err); @@ -200,7 +181,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); it('getDefaultParams_check_keylength', function (done) { - Crypto.generateRandomKey(64, function (err, key) { + whenPromiseSettles(Crypto.generateRandomKey(64), function (err, key) { if (err) { done(err); return; @@ -215,7 +196,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); it('getDefaultParams_preserves_custom_algorithms', function (done) { - Crypto.generateRandomKey(64, function (err, key) { + whenPromiseSettles(Crypto.generateRandomKey(64), function (err, key) { if (err) { done(err); return; @@ -242,11 +223,11 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async true, function (channelOpts, testMessage, encryptedMessage) { /* encrypt plaintext message; encode() also to handle data that is not already string or buffer */ - Message.encode(testMessage, channelOpts, function () { + whenPromiseSettles(Message.encode(testMessage, channelOpts), function () { /* compare */ testMessageEquality(done, testMessage, encryptedMessage); }); - } + }, ); }); @@ -259,11 +240,11 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async true, function (channelOpts, testMessage, encryptedMessage) { /* encrypt plaintext message; encode() also to handle data that is not already string or buffer */ - Message.encode(testMessage, channelOpts, function () { + whenPromiseSettles(Message.encode(testMessage, channelOpts), function () { /* compare */ testMessageEquality(done, testMessage, encryptedMessage); }); - } + }, ); }); @@ -274,12 +255,12 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async 'decrypt_message_128', 2, false, - function (channelOpts, testMessage, encryptedMessage) { + async function (channelOpts, testMessage, encryptedMessage) { /* decrypt encrypted message; decode() also to handle data that is not string or buffer */ - Message.decode(encryptedMessage, channelOpts); + await Message.decode(encryptedMessage, channelOpts); /* compare */ testMessageEquality(done, testMessage, encryptedMessage); - } + }, ); }); @@ -290,12 +271,12 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async 'decrypt_message_256', 2, false, - function (channelOpts, testMessage, encryptedMessage) { + async function (channelOpts, testMessage, encryptedMessage) { /* decrypt encrypted message; decode() also to handle data that is not string or buffer */ - Message.decode(encryptedMessage, channelOpts); + await Message.decode(encryptedMessage, channelOpts); /* compare */ testMessageEquality(done, testMessage, encryptedMessage); - } + }, ); }); @@ -305,7 +286,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async return; } - loadTestData(testResourcesPath + 'crypto-data-256.json', function (err, testData) { + loadTestData(testResourcesPath + 'crypto-data-256.json', async function (err, testData) { if (err) { done(err); return; @@ -315,8 +296,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async for (var i = 0; i < testData.items.length; i++) { var item = testData.items[i]; - var testMessage = Message.fromEncoded(item.encoded); - var decryptedMessage = Message.fromEncoded(item.encrypted, { cipher: { key: key, iv: iv } }); + var testMessage = await Message.fromEncoded(item.encoded); + var decryptedMessage = await Message.fromEncoded(item.encrypted, { cipher: { key: key, iv: iv } }); testMessageEquality(done, testMessage, decryptedMessage); } done(); @@ -333,19 +314,19 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async 2, false, function (channelOpts, testMessage, encryptedMessage, msgpackEncodedMessage) { - Message.encode(testMessage, channelOpts, function () { + whenPromiseSettles(Message.encode(testMessage, channelOpts), function () { var msgpackFromEncoded = msgpack.encode(testMessage); var msgpackFromEncrypted = msgpack.encode(encryptedMessage); var messageFromMsgpack = Message.fromValues( - msgpack.decode(BufferUtils.base64Decode(msgpackEncodedMessage)) + msgpack.decode(BufferUtils.base64Decode(msgpackEncodedMessage)), ); try { /* Mainly testing that we're correctly encoding the direct output from - * CryptoJS (a wordArray) into the msgpack binary type */ - expect(BufferUtils.bufferCompare(msgpackFromEncoded, msgpackFromEncrypted)).to.equal( - 0, - 'verify msgpack encodings of newly-encrypted and preencrypted messages identical using bufferCompare' + * the platform's ICipher implementation into the msgpack binary type */ + expect(BufferUtils.areBuffersEqual(msgpackFromEncoded, msgpackFromEncrypted)).to.equal( + true, + 'verify msgpack encodings of newly-encrypted and preencrypted messages identical using areBuffersEqual', ); /* Can't compare msgpackFromEncoded with fixture data because can't @@ -355,7 +336,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async done(err); } }); - } + }, ); }); @@ -367,19 +348,19 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async 2, false, function (channelOpts, testMessage, encryptedMessage, msgpackEncodedMessage) { - Message.encode(testMessage, channelOpts, function () { + whenPromiseSettles(Message.encode(testMessage, channelOpts), function () { var msgpackFromEncoded = msgpack.encode(testMessage); var msgpackFromEncrypted = msgpack.encode(encryptedMessage); var messageFromMsgpack = Message.fromValues( - msgpack.decode(BufferUtils.base64Decode(msgpackEncodedMessage)) + msgpack.decode(BufferUtils.base64Decode(msgpackEncodedMessage)), ); try { /* Mainly testing that we're correctly encoding the direct output from - * CryptoJS (a wordArray) into the msgpack binary type */ - expect(BufferUtils.bufferCompare(msgpackFromEncoded, msgpackFromEncrypted)).to.equal( - 0, - 'verify msgpack encodings of newly-encrypted and preencrypted messages identical using bufferCompare' + * the platform's ICipher implementation into the msgpack binary type */ + expect(BufferUtils.areBuffersEqual(msgpackFromEncoded, msgpackFromEncrypted)).to.equal( + true, + 'verify msgpack encodings of newly-encrypted and preencrypted messages identical using areBuffersEqual', ); /* Can't compare msgpackFromEncoded with fixture data because can't @@ -389,7 +370,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async done(err); } }); - } + }, ); }); } @@ -400,7 +381,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async return; } - Crypto.generateRandomKey(keyLength, function (err, key) { + whenPromiseSettles(Crypto.generateRandomKey(keyLength), function (err, key) { if (err) { closeAndFinish(done, realtime, err); return; @@ -412,7 +393,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async channel = realtime.channels.get('single_send', { cipher: { key: key } }), messageText = 'Test message for single_send - ' + JSON.stringify(realtimeOpts); - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; @@ -463,7 +444,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async channel = realtime.channels.get(channelName), messageText = 'Test message (' + channelName + ')'; - Crypto.generateRandomKey(128, function (err, key) { + whenPromiseSettles(Crypto.generateRandomKey(128), function (err, key) { channel.setOptions({ cipher: { key: key } }); try { expect(channel.channelOptions.cipher.algorithm).to.equal('aes'); @@ -487,12 +468,16 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async function recvAll(recvCb) { var received = 0; channel.subscribe('event0', function (msg) { - expect(msg.data == messageText).to.be.ok; + try { + expect(msg.data == messageText).to.be.ok; + } catch (error) { + recvCb(error); + } if (++received == iterations) recvCb(null); }); } - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; @@ -520,6 +505,14 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async _multiple_send(done, true, 20, 100); }); + it('multiple_send_binary_10_10', function (done) { + _multiple_send(done, false, 10, 10); + }); + + it('multiple_send_text_10_10', function (done) { + _multiple_send(done, true, 10, 10); + }); + function _single_send_separate_realtimes(done, txOpts, rxOpts) { if (!Crypto) { done(new Error('Encryption not supported')); @@ -533,7 +526,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async txChannel = txRealtime.channels.get(channelName), rxChannel = rxRealtime.channels.get(channelName); - Crypto.generateRandomKey(function (err, key) { + whenPromiseSettles(Crypto.generateRandomKey(), function (err, key) { if (err) { closeAndFinish(done, realtime, err); return; @@ -573,7 +566,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); txChannel.publish('event0', messageText); }); - } + }, ); }); } @@ -607,15 +600,14 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async channelName = 'publish_immediately', messageText = 'Test message'; - Crypto.generateRandomKey(function (err, key) { + whenPromiseSettles(Crypto.generateRandomKey(), function (err, key) { if (err) { closeAndFinish(done, [txRealtime, rxRealtime], err); return; } var rxChannel = rxRealtime.channels.get(channelName, { cipher: { key: key } }); - rxChannel.subscribe( - 'event0', - function (msg) { + whenPromiseSettles( + rxChannel.subscribe('event0', function (msg) { try { expect(msg.data == messageText).to.be.ok; } catch (err) { @@ -623,11 +615,11 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async return; } closeAndFinish(done, [txRealtime, rxRealtime]); - }, + }), function () { var txChannel = txRealtime.channels.get(channelName, { cipher: { key: key } }); txChannel.publish('event0', messageText); - } + }, ); }); }); @@ -653,8 +645,12 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async async.parallel( [ - Crypto.generateRandomKey, - Crypto.generateRandomKey, + function (cb) { + whenPromiseSettles(Crypto.generateRandomKey(), cb); + }, + function (cb) { + whenPromiseSettles(Crypto.generateRandomKey(), cb); + }, function (cb) { attachChannels([txChannel, rxChannel], cb); }, @@ -689,9 +685,9 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async closeAndFinish(done, [txRealtime, rxRealtime]); }); txChannel.publish('event0', messageText); - } + }, ); - } + }, ); }); @@ -718,7 +714,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async closeAndFinish(done, [txRealtime, rxRealtime], err); return; } - Crypto.generateRandomKey(function (err, rxKey) { + whenPromiseSettles(Crypto.generateRandomKey(), function (err, rxKey) { if (err) { closeAndFinish(done, [txRealtime, rxRealtime], err); return; @@ -761,7 +757,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async closeAndFinish(done, [txRealtime, rxRealtime], err); return; } - Crypto.generateRandomKey(function (err, txKey) { + whenPromiseSettles(Crypto.generateRandomKey(), function (err, txKey) { if (err) { closeAndFinish(done, [txRealtime, rxRealtime], err); return; @@ -806,7 +802,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async attachChannels([txChannel, rxChannel], cb); }; var setInitialOptions = function (cb) { - Crypto.generateRandomKey(function (err, key) { + whenPromiseSettles(Crypto.generateRandomKey(), function (err, key) { if (err) { closeAndFinish(done, [txRealtime, rxRealtime], err); return; @@ -823,7 +819,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async innercb(); }, ], - cb + cb, ); }); }; @@ -839,7 +835,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }; var createSecondKey = function (cb) { - Crypto.generateRandomKey(function (err, key) { + whenPromiseSettles(Crypto.generateRandomKey(), function (err, key) { if (err) { closeAndFinish(done, [txRealtime, rxRealtime], err); return; @@ -856,7 +852,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async innercb(); }, ], - cb + cb, ); }); }; @@ -898,7 +894,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], function (err) { closeAndFinish(done, [txRealtime, rxRealtime], err); - } + }, ); }); }); diff --git a/test/realtime/delta.test.js b/test/realtime/delta.test.js index 9b42c428ee..fb8f5edb72 100644 --- a/test/realtime/delta.test.js +++ b/test/realtime/delta.test.js @@ -5,6 +5,7 @@ define(['shared_helper', 'vcdiff-decoder', 'async', 'chai'], function (helper, v var displayError = helper.displayError; var closeAndFinish = helper.closeAndFinish; var monitorConnection = helper.monitorConnection; + var whenPromiseSettles = helper.whenPromiseSettles; var testData = [ { foo: 'bar', count: 1, status: 'active' }, { foo: 'bar', count: 2, status: 'active' }, @@ -50,7 +51,7 @@ define(['shared_helper', 'vcdiff-decoder', 'async', 'chai'], function (helper, v }); var channel = realtime.channels.get(testName, { params: { delta: 'vcdiff' } }); - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); } @@ -58,8 +59,8 @@ define(['shared_helper', 'vcdiff-decoder', 'async', 'chai'], function (helper, v channel.on('attaching', function (stateChange) { done( new Error( - 'Channel reattaching, presumably due to decode failure; reason =' + displayError(stateChange.reason) - ) + 'Channel reattaching, presumably due to decode failure; reason =' + displayError(stateChange.reason), + ), ); }); @@ -78,7 +79,7 @@ define(['shared_helper', 'vcdiff-decoder', 'async', 'chai'], function (helper, v }); async.timesSeries(testData.length, function (i, cb) { - channel.publish(i.toString(), testData[i], cb); + whenPromiseSettles(channel.publish(i.toString(), testData[i]), cb); }); }); @@ -99,9 +100,9 @@ define(['shared_helper', 'vcdiff-decoder', 'async', 'chai'], function (helper, v }); var channel = realtime.channels.get(testName); - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { - closeAndFinish(doner, realtime, err); + closeAndFinish(done, realtime, err); } channel.subscribe(function (message) { try { @@ -118,7 +119,7 @@ define(['shared_helper', 'vcdiff-decoder', 'async', 'chai'], function (helper, v }); async.timesSeries(testData.length, function (i, cb) { - channel.publish(i.toString(), testData[i], cb); + whenPromiseSettles(channel.publish(i.toString(), testData[i]), cb); }); }); @@ -139,7 +140,7 @@ define(['shared_helper', 'vcdiff-decoder', 'async', 'chai'], function (helper, v }); var channel = realtime.channels.get(testName, { params: { delta: 'vcdiff' } }); - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); } @@ -165,7 +166,7 @@ define(['shared_helper', 'vcdiff-decoder', 'async', 'chai'], function (helper, v closeAndFinish( done, realtime, - new Error('Check no further decode failures; reason =' + displayError(stateChange.reason)) + new Error('Check no further decode failures; reason =' + displayError(stateChange.reason)), ); }); }); @@ -181,7 +182,7 @@ define(['shared_helper', 'vcdiff-decoder', 'async', 'chai'], function (helper, v }); async.timesSeries(testData.length, function (i, cb) { - channel.publish(i.toString(), testData[i], cb); + whenPromiseSettles(channel.publish(i.toString(), testData[i]), cb); }); }); @@ -207,7 +208,7 @@ define(['shared_helper', 'vcdiff-decoder', 'async', 'chai'], function (helper, v }); var channel = realtime.channels.get(testName, { params: { delta: 'vcdiff' } }); - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); } @@ -232,7 +233,7 @@ define(['shared_helper', 'vcdiff-decoder', 'async', 'chai'], function (helper, v }); async.timesSeries(testData.length, function (i, cb) { - channel.publish(i.toString(), testData[i], cb); + whenPromiseSettles(channel.publish(i.toString(), testData[i]), cb); }); }); @@ -248,7 +249,7 @@ define(['shared_helper', 'vcdiff-decoder', 'async', 'chai'], function (helper, v var realtime = helper.AblyRealtime(); var channel = realtime.channels.get('noPlugin', { params: { delta: 'vcdiff' } }); - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); } @@ -262,7 +263,7 @@ define(['shared_helper', 'vcdiff-decoder', 'async', 'chai'], function (helper, v closeAndFinish(done, realtime); }); async.timesSeries(testData.length, function (i, cb) { - channel.publish(i.toString(), testData[i], cb); + whenPromiseSettles(channel.publish(i.toString(), testData[i]), cb); }); }); diff --git a/test/realtime/encoding.test.js b/test/realtime/encoding.test.js index 7c50152acb..5f27673e03 100644 --- a/test/realtime/encoding.test.js +++ b/test/realtime/encoding.test.js @@ -7,6 +7,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var displayError = helper.displayError; var encodingFixturesPath = helper.testResourcesPath + 'messages-encoding.json'; var closeAndFinish = helper.closeAndFinish; + var Defaults = Ably.Rest.Platform.Defaults; + var whenPromiseSettles = helper.whenPromiseSettles; describe('realtime/encoding', function () { this.timeout(60 * 1000); @@ -40,10 +42,10 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async async.parallel( [ function (attachCb) { - channel.attach(attachCb); + whenPromiseSettles(channel.attach(), attachCb); }, function (attachCb) { - binarychannel.attach(attachCb); + whenPromiseSettles(binarychannel.attach(), attachCb); }, ], function (err) { @@ -64,7 +66,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async if (encodingSpec.expectedHexValue) { expect(BufferUtils.hexEncode(msg.data)).to.equal( encodingSpec.expectedHexValue, - 'Check data matches' + 'Check data matches', ); } else { expect(msg.data).to.deep.equal(encodingSpec.expectedValue, 'Check data matches'); @@ -82,7 +84,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async if (encodingSpec.expectedHexValue) { expect(BufferUtils.hexEncode(msg.data)).to.equal( encodingSpec.expectedHexValue, - 'Check data matches' + 'Check data matches', ); } else { expect(msg.data).to.deep.equal(encodingSpec.expectedValue, 'Check data matches'); @@ -95,26 +97,29 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (parallelCb) { - realtime.request( - 'post', - channelPath, - null, - { name: name, data: encodingSpec.data, encoding: encodingSpec.encoding }, - null, + whenPromiseSettles( + realtime.request( + 'post', + channelPath, + Defaults.protocolVersion, + null, + { name: name, data: encodingSpec.data, encoding: encodingSpec.encoding }, + null, + ), function (err) { parallelCb(err); - } + }, ); }, ], - eachOfCb + eachOfCb, ); }, function (err) { closeAndFinish(done, [realtime, binaryrealtime], err); - } + }, ); - } + }, ); }); }); @@ -138,10 +143,10 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async async.parallel( [ function (attachCb) { - channel.attach(attachCb); + whenPromiseSettles(channel.attach(), attachCb); }, function (attachCb) { - binarychannel.attach(attachCb); + whenPromiseSettles(binarychannel.attach(), attachCb); }, ], function (err) { @@ -163,10 +168,10 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async async.parallel( [ function (parallelCb) { - channel.publish(name, data, parallelCb); + whenPromiseSettles(channel.publish(name, data), parallelCb); }, function (parallelCb) { - binarychannel.publish(name, data, parallelCb); + whenPromiseSettles(binarychannel.publish(name, data), parallelCb); }, ], function (err) { @@ -174,47 +179,50 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async eachOfCb(err); return; } - realtime.request('get', channelPath, null, null, null, function (err, resultPage) { - if (err) { - eachOfCb(err); - return; - } - try { - var msgs = helper.arrFilter(resultPage.items, function (m) { - return m.name === name; - }); - expect(msgs.length).to.equal( - 2, - 'Check expected number of results (one from json rt, one from binary rt)' - ); - expect(msgs[0].encoding == encodingSpec.encoding, 'Check encodings match').to.be.ok; - expect(msgs[1].encoding == encodingSpec.encoding, 'Check encodings match').to.be.ok; - if (msgs[0].encoding === 'json') { - expect(JSON.parse(encodingSpec.data)).to.deep.equal( - JSON.parse(msgs[0].data), - 'Check data matches' - ); - expect(JSON.parse(encodingSpec.data)).to.deep.equal( - JSON.parse(msgs[1].data), - 'Check data matches' + whenPromiseSettles( + realtime.request('get', channelPath, Defaults.protocolVersion, null, null, null), + function (err, resultPage) { + if (err) { + eachOfCb(err); + return; + } + try { + var msgs = resultPage.items.filter(function (m) { + return m.name === name; + }); + expect(msgs.length).to.equal( + 2, + 'Check expected number of results (one from json rt, one from binary rt)', ); - } else { - expect(encodingSpec.data).to.equal(msgs[0].data, 'Check data matches'); - expect(encodingSpec.data).to.equal(msgs[1].data, 'Check data matches'); + expect(msgs[0].encoding == encodingSpec.encoding, 'Check encodings match').to.be.ok; + expect(msgs[1].encoding == encodingSpec.encoding, 'Check encodings match').to.be.ok; + if (msgs[0].encoding === 'json') { + expect(JSON.parse(encodingSpec.data)).to.deep.equal( + JSON.parse(msgs[0].data), + 'Check data matches', + ); + expect(JSON.parse(encodingSpec.data)).to.deep.equal( + JSON.parse(msgs[1].data), + 'Check data matches', + ); + } else { + expect(encodingSpec.data).to.equal(msgs[0].data, 'Check data matches'); + expect(encodingSpec.data).to.equal(msgs[1].data, 'Check data matches'); + } + eachOfCb(); + } catch (err) { + eachOfCb(err); } - eachOfCb(); - } catch (err) { - eachOfCb(err); - } - }); - } + }, + ); + }, ); }, function (err) { closeAndFinish(done, [realtime, binaryrealtime], err); - } + }, ); - } + }, ); }); }); diff --git a/test/realtime/event_emitter.test.js b/test/realtime/event_emitter.test.js index eeb2d91851..435a7e1ac4 100644 --- a/test/realtime/event_emitter.test.js +++ b/test/realtime/event_emitter.test.js @@ -25,15 +25,12 @@ define(['shared_helper', 'chai'], function (helper, chai) { */ it('attachdetach0', function (done) { try { - /* Note: realtime now sends an ATTACHED post-upgrade, which can race with - * the DETACHED if the DETACH is only sent just after upgrade. Remove - * bestTransport with 1.1 spec which has IDs in ATTACHs */ - var realtime = helper.AblyRealtime({ transports: [helper.bestTransport] }), + var realtime = helper.AblyRealtime(), index, expectedConnectionEvents = ['connecting', 'connected', 'closing', 'closed'], expectedChannelEvents = ['attaching', 'attached', 'detaching', 'detached']; realtime.connection.on(function () { - if ((index = utils.arrIndexOf(expectedConnectionEvents, this.event)) > -1) { + if ((index = expectedConnectionEvents.indexOf(this.event)) > -1) { delete expectedConnectionEvents[index]; if (this.event == 'closed') { done(); @@ -45,7 +42,7 @@ define(['shared_helper', 'chai'], function (helper, chai) { realtime.connection.on('connected', function () { var channel = realtime.channels.get('channel'); channel.on(function () { - if ((index = utils.arrIndexOf(expectedChannelEvents, this.event)) > -1) { + if ((index = expectedChannelEvents.indexOf(this.event)) > -1) { delete expectedChannelEvents[index]; switch (this.event) { case 'detached': @@ -439,62 +436,60 @@ define(['shared_helper', 'chai'], function (helper, chai) { closeAndFinish(done, realtime); }); - if (typeof Promise !== 'undefined') { - describe('event_emitter_promise', function () { - it('whenState', function (done) { - var realtime = helper.AblyRealtime({ promises: true }); - var eventEmitter = realtime.connection; - - eventEmitter - .whenState('connected') - .then(function () { - closeAndFinish(done, realtime); - }) - ['catch'](function (err) { - closeAndFinish(done, realtime, err); - }); - }); - - it('once', function (done) { - var realtime = helper.AblyRealtime({ promises: true }); - var eventEmitter = realtime.connection; - - eventEmitter - .once('connected') - .then(function () { - closeAndFinish(done, realtime); - }) - ['catch'](function (err) { - closeAndFinish(done, realtime, err); - }); - }); - - it('anyEventsWithOnce', function (done) { - var realtime = helper.AblyRealtime({ autoConnect: false }), - eventEmitter = realtime.connection; + describe('event_emitter_promise', function () { + it('whenState', function (done) { + var realtime = helper.AblyRealtime(); + var eventEmitter = realtime.connection; - const p = eventEmitter.once(); - eventEmitter.emit('b'); - p.then(function () { + eventEmitter + .whenState('connected') + .then(function () { closeAndFinish(done, realtime); - }).catch(function (err) { + }) + ['catch'](function (err) { closeAndFinish(done, realtime, err); }); - }); + }); - it('arrayOfEventsWithOnce', function (done) { - var realtime = helper.AblyRealtime({ autoConnect: false }), - eventEmitter = realtime.connection; + it('once', function (done) { + var realtime = helper.AblyRealtime(); + var eventEmitter = realtime.connection; - const p = eventEmitter.once(['a', 'b', 'c']); - eventEmitter.emit('b'); - p.then(function () { + eventEmitter + .once('connected') + .then(function () { closeAndFinish(done, realtime); - }).catch(function (err) { + }) + ['catch'](function (err) { closeAndFinish(done, realtime, err); }); + }); + + it('anyEventsWithOnce', function (done) { + var realtime = helper.AblyRealtime({ autoConnect: false }), + eventEmitter = realtime.connection; + + const p = eventEmitter.once(); + eventEmitter.emit('b'); + p.then(function () { + closeAndFinish(done, realtime); + }).catch(function (err) { + closeAndFinish(done, realtime, err); }); }); - } + + it('arrayOfEventsWithOnce', function (done) { + var realtime = helper.AblyRealtime({ autoConnect: false }), + eventEmitter = realtime.connection; + + const p = eventEmitter.once(['a', 'b', 'c']); + eventEmitter.emit('b'); + p.then(function () { + closeAndFinish(done, realtime); + }).catch(function (err) { + closeAndFinish(done, realtime, err); + }); + }); + }); }); }); diff --git a/test/realtime/failure.test.js b/test/realtime/failure.test.js index d3906ca705..9bace8d5bb 100644 --- a/test/realtime/failure.test.js +++ b/test/realtime/failure.test.js @@ -6,8 +6,9 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var utils = helper.Utils; var noop = function () {}; var simulateDroppedConnection = helper.simulateDroppedConnection; - var createPM = Ably.Realtime.ProtocolMessage.fromDeserialized; + var createPM = Ably.protocolMessageFromDeserialized; var availableTransports = helper.availableTransports; + var whenPromiseSettles = helper.whenPromiseSettles; describe('realtime/failure', function () { this.timeout(60 * 1000); @@ -34,11 +35,11 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async expect(realtime.connection.errorReason.code).to.equal(40400, 'wrong error reason code on connection.'); expect(connectionStateChange.reason.code).to.equal( 40400, - 'wrong error reason code on connectionStateChange' + 'wrong error reason code on connectionStateChange', ); expect(connectionStateChange.reason).to.deep.equal( realtime.connection.errorReason, - 'error reason was not equally set on connection and connectionStateChange' + 'error reason was not equally set on connection and connectionStateChange', ); } catch (err) { cb(err, realtime); @@ -49,20 +50,20 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async realtime.connection.on('disconnected', function () { cb( new Error('connection state for transports ' + transports + ' should be failed, not disconnected'), - realtime + realtime, ); }); }; }; async.parallel( - utils - .arrMap(availableTransports, function (transport) { + availableTransports + .map(function (transport) { return failure_test([transport]); }) - .concat(failure_test(null)), // to test not specifying a transport (so will use upgrade mechanism) + .concat(failure_test(null)), // to test not specifying a transport (so will use websocket/base mechanism) function (err, realtimes) { closeAndFinish(done, realtimes, err); - } + }, ); } catch (err) { done(err); @@ -85,7 +86,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async realtime.connection.on('failed', function () { cb( new Error('connection state for transports ' + transports + ' should be disconnected, not failed'), - realtime + realtime, ); }); simulateDroppedConnection(realtime); @@ -93,14 +94,14 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }; }; async.parallel( - utils - .arrMap(availableTransports, function (transport) { + availableTransports + .map(function (transport) { return break_test([transport]); }) - .concat(break_test(null)), // to test not specifying a transport (so will use upgrade mechanism) + .concat(break_test(null)), // to test not specifying a transport (so will use websocket/base mechanism) function (err, realtimes) { closeAndFinish(done, realtimes, err); - } + }, ); } catch (err) { closeAndFinish(done, realtime, err); @@ -126,7 +127,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async * the suspended timeout trips before three connection cycles */ disconnectedRetryTimeout: 1000, realtimeRequestTimeout: 50, - preferenceConnectTimeout: 50, + webSocketConnectTimeout: 50, suspendedRetryTimeout: 1000, connectionStateTtl: 2900, }); @@ -156,7 +157,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ' was ' + connectionEvents + ', expected ' + - expectedConnectionEvents + expectedConnectionEvents, ); realtime.close(); cb(null, realtime); @@ -168,17 +169,17 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }; }; async.parallel( - utils - .arrMap(availableTransports, function (transport) { + availableTransports + .map(function (transport) { return lifecycleTest([transport]); }) - .concat(lifecycleTest(null)), // to test not specifying a transport (so will use upgrade mechanism) + .concat(lifecycleTest(null)), // to test not specifying a transport (so will use websocket/base mechanism) function (err) { if (err) { done(err); } done(); - } + }, ); } catch (err) { closeAndFinish(done, realtime, err); @@ -190,7 +191,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async expect(value).to.be.below(max); } - utils.arrForEach(availableTransports, function (transport) { + availableTransports.forEach(function (transport) { it('disconnected_backoff_' + transport, function (done) { var disconnectedRetryTimeout = 150; var realtime = helper.AblyRealtime({ @@ -239,7 +240,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var tests = [ function (callback) { - failChan.publish('event', 'data', function (err) { + whenPromiseSettles(failChan.publish('event', 'data'), function (err) { try { expect(err, 'publish failed').to.be.ok; expect(err.code).to.equal(channelFailedCode, 'publish failure code'); @@ -250,7 +251,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (callback) { - failChan.subscribe('event', noop, function (err) { + whenPromiseSettles(failChan.subscribe('event', noop), function (err) { try { expect(err, 'subscribe failed').to.be.ok; expect(err.code).to.equal(channelFailedCode, 'subscribe failure code'); @@ -261,7 +262,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (callback) { - failChan.presence.enterClient('clientId', function (err) { + whenPromiseSettles(failChan.presence.enterClient('clientId'), function (err) { try { expect(err, 'presence enter failed').to.be.ok; expect(err.code).to.equal(channelFailedCode, 'presence enter failure code'); @@ -272,7 +273,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (callback) { - failChan.presence.leaveClient('clientId', function (err) { + whenPromiseSettles(failChan.presence.leaveClient('clientId'), function (err) { try { expect(err, 'presence leave failed').to.be.ok; expect(err.code).to.equal(channelFailedCode, 'presence leave failure code'); @@ -283,7 +284,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (callback) { - failChan.presence.subscribe('event', noop, function (err) { + whenPromiseSettles(failChan.presence.subscribe('event', noop), function (err) { try { expect(err, 'presence subscribe failed').to.be.ok; expect(err.code).to.equal(channelFailedCode, 'subscribe failure code'); @@ -294,7 +295,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (callback) { - failChan.presence.subscribe('event', noop, function (err) { + whenPromiseSettles(failChan.presence.subscribe('event', noop), function (err) { try { expect(err, 'presence unsubscribe failed').to.be.ok; expect(err.code).to.equal(channelFailedCode, 'subscribe failure code'); @@ -305,7 +306,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (callback) { - failChan.presence.get(function (err) { + whenPromiseSettles(failChan.presence.get(), function (err) { try { expect(err, 'presence get failed').to.be.ok; expect(err.code).to.equal(channelFailedCode, 'presence get failure code'); @@ -320,7 +321,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async try { realtime.connection.once('connected', function () { failChan = realtime.channels.get('::'); - failChan.attach(function (err) { + whenPromiseSettles(failChan.attach(), function (err) { try { expect(err, 'channel attach failed').to.be.ok; expect(failChan.state).to.equal('failed', 'channel in failed state'); @@ -341,17 +342,17 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async it('attach_timeout', function (done) { var realtime = helper.AblyRealtime({ realtimeRequestTimeout: 2000, channelRetryTimeout: 1000 }), channel = realtime.channels.get('failed_attach'), - originalOnMessage = channel.onMessage.bind(channel); + originalProcessMessage = channel.processMessage.bind(channel); - channel.onMessage = function (message) { + channel.processMessage = async function (message) { if (message.action === 11) { return; } - originalOnMessage(message); + await originalProcessMessage(message); }; realtime.connection.once('connected', function () { - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { try { expect(err.code).to.equal(90007, 'check channel error code'); expect(err.statusCode).to.equal(408, 'check timeout statusCode'); @@ -372,7 +373,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }); - utils.arrForEach(availableTransports, function (transport) { + availableTransports.forEach(function (transport) { it('channel_backoff_' + transport, function (done) { var channelRetryTimeout = 150; var realtime = helper.AblyRealtime({ @@ -380,24 +381,24 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async transports: [transport], }), channel = realtime.channels.get('failed_attach'), - originalOnMessage = channel.onMessage.bind(channel), + originalProcessMessage = channel.processMessage.bind(channel), retryCount = 0; var performance = isBrowser ? window.performance : require('perf_hooks').performance; - channel.onMessage = function (message) { + channel.processMessage = async function (message) { // Ignore ATTACHED messages if (message.action === 11) { return; } - originalOnMessage(message); + await originalProcessMessage(message); }; var retryTimeouts = []; realtime.connection.on('connected', function () { realtime.options.timeouts.realtimeRequestTimeout = 1; - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { var lastSuspended = performance.now(); channel.on(function (stateChange) { @@ -453,7 +454,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (cb) { - channel.attach(cb); + whenPromiseSettles(channel.attach(), cb); }, function (cb) { var transport = realtime.connection.connectionManager.activeProtocol.transport, @@ -465,12 +466,12 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async originalOnProtocolMessage.apply(this, arguments); } }; - channel.publish('foo', 'bar', function (err) { + whenPromiseSettles(channel.publish('foo', 'bar'), function (err) { try { expect(err, 'Publish failed as expected').to.be.ok; expect(realtime.connection.state).to.equal( expectedRealtimeState, - 'check realtime state is ' + expectedRealtimeState + 'check realtime state is ' + expectedRealtimeState, ); expect(err.code).to.equal(expectedNackCode, 'Check error code was ' + expectedNackCode); cb(); @@ -485,7 +486,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], function (err) { closeAndFinish(done, realtime, err); - } + }, ); }; } @@ -497,8 +498,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async helper.becomeSuspended(realtime); }, 'suspended', - 80002 - ) + 80002, + ), ); it( @@ -511,8 +512,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, 'failed', - 40100 - ) + 40100, + ), ); it( @@ -522,8 +523,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async realtime.close(); }, 'closed', - 80017 - ) + 80017, + ), ); it('idle_transport_timeout', function (done) { @@ -546,7 +547,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async try { expect( statechange.current === 'disconnected' || statechange.current === 'connecting', - 'check connection goes to disconnected/connecting' + 'check connection goes to disconnected/connecting', ).to.be.ok; expect(statechange.reason.code).to.equal(80003, 'check code'); expect(statechange.reason.statusCode).to.equal(408, 'check statusCode'); @@ -575,7 +576,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async try { expect(stateChange.current).to.equal( 'disconnected', - 'expect next connection attempt to fail due to using the (bad) fallback host' + 'expect next connection attempt to fail due to using the (bad) fallback host', ); } catch (err) { closeAndFinish(done, realtime, err); @@ -592,7 +593,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async code: 50320, statusCode: 503, }, - }) + }), ); }); }; diff --git a/test/realtime/history.test.js b/test/realtime/history.test.js index 1017749c00..0f5783ca7d 100644 --- a/test/realtime/history.test.js +++ b/test/realtime/history.test.js @@ -3,19 +3,21 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { var expect = chai.expect; var utils = helper.Utils; - var preAttachMessages = utils.arrMap([1, 2, 3, 4, 5], function (i) { + var indexes = [1, 2, 3, 4, 5]; + var preAttachMessages = indexes.map(function (i) { return { name: 'pre-attach-' + i, data: 'some data' }; }); - var postAttachMessages = utils.arrMap([1, 2, 3, 4, 5], function (i) { + var postAttachMessages = indexes.map(function (i) { return { name: 'post-attach-' + i, data: 'some data' }; }); var closeAndFinish = helper.closeAndFinish; var monitorConnection = helper.monitorConnection; + var whenPromiseSettles = helper.whenPromiseSettles; var parallelPublishMessages = function (done, channel, messages, callback) { - var publishTasks = utils.arrMap(messages, function (event) { + var publishTasks = messages.map(function (event) { return function (publishCb) { - channel.publish(event.name, event.data, publishCb); + whenPromiseSettles(channel.publish(event.name, event.data), publishCb); }; }); @@ -54,9 +56,9 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { parallelPublishMessages(done, restChannel, preAttachMessages, function () { /* second, connect and attach to the channel */ try { - realtime.connection.whenState('connected', function () { + whenPromiseSettles(realtime.connection.whenState('connected'), function () { var rtChannel = realtime.channels.get('persisted:history_until_attach'); - rtChannel.attach(function (err) { + whenPromiseSettles(rtChannel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; @@ -72,7 +74,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { var tests = [ function (callback) { - rtChannel.history(function (err, resultPage) { + whenPromiseSettles(rtChannel.history(), function (err, resultPage) { if (err) { callback(err); } @@ -80,7 +82,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { var expectedLength = preAttachMessages.length + postAttachMessages.length; expect(resultPage.items.length).to.equal( expectedLength, - 'Verify all messages returned when no params' + 'Verify all messages returned when no params', ); callback(); } catch (err) { @@ -89,7 +91,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { }); }, function (callback) { - rtChannel.history({ untilAttach: false }, function (err, resultPage) { + whenPromiseSettles(rtChannel.history({ untilAttach: false }), function (err, resultPage) { if (err) { callback(err); } @@ -97,7 +99,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { var expectedLength = preAttachMessages.length + postAttachMessages.length; expect(resultPage.items.length).to.equal( expectedLength, - 'Verify all messages returned when untilAttached is false' + 'Verify all messages returned when untilAttached is false', ); callback(); } catch (err) { @@ -106,7 +108,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { }); }, function (callback) { - rtChannel.history({ untilAttach: true }, function (err, resultPage) { + whenPromiseSettles(rtChannel.history({ untilAttach: true }), function (err, resultPage) { if (err) { callback(err); } @@ -116,13 +118,13 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { var messages = resultPage.items; expect(messages.length).to.equal( preAttachMessages.length, - 'Verify right number of messages returned when untilAttached is true' + 'Verify right number of messages returned when untilAttached is true', ); expect( - utils.arrEvery(messages, function (message) { + messages.every(function (message) { return message.name.substring(0, 10) == 'pre-attach'; }), - 'Verify all returned messages were pre-attach ones' + 'Verify all returned messages were pre-attach ones', ).to.be.ok; callback(); } catch (err) { diff --git a/test/realtime/init.test.js b/test/realtime/init.test.js index cc3557b1ce..bca2f44f81 100644 --- a/test/realtime/init.test.js +++ b/test/realtime/init.test.js @@ -4,6 +4,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { var expect = chai.expect; var closeAndFinish = helper.closeAndFinish; var monitorConnection = helper.monitorConnection; + var whenPromiseSettles = helper.whenPromiseSettles; describe('realtime/init', function () { this.timeout(60 * 1000); @@ -17,24 +18,22 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { }); }); - /* Restrict to websocket or xhr streaming for the v= test as if stream=false the - * recvRequest may not be the connectRequest by the time we check it. All comet - * transports share the same connect uri generation code so should be adequately - * tested by testing xhr_streaming */ - if (helper.bestTransport === 'web_socket' || helper.bestTransport === 'xhr_streaming') { + /* Restrict to websocket or xhr polling for the v= test as if stream=false the + * recvRequest may not be the connectRequest by the time we check it. */ + if (helper.bestTransport === 'web_socket' || helper.bestTransport === 'xhr_polling') { /* * Base init case */ it('initbase0', function (done) { var realtime; try { - realtime = helper.AblyRealtime({ transports: ['web_socket', 'xhr_streaming'] }); + realtime = helper.AblyRealtime({ transports: ['web_socket', 'xhr_polling'] }); realtime.connection.on('connected', function () { /* check api version */ var transport = realtime.connection.connectionManager.activeProtocol.transport; - var connectUri = helper.isWebsocket(transport) ? transport.uri : transport.recvRequest.uri; + var connectUri = helper.isWebsocket(transport) ? transport.uri : transport.recvRequest.recvUri; try { - expect(connectUri.indexOf('v=2') > -1, 'Check uri includes v=2').to.be.ok; + expect(connectUri.indexOf('v=3') > -1, 'Check uri includes v=3').to.be.ok; } catch (err) { closeAndFinish(done, realtime, err); return; @@ -75,7 +74,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { var rest = helper.AblyRest(); var testKeyOpts = { key: helper.getTestApp().keys[1].keyStr }; - rest.auth.requestToken(null, testKeyOpts, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken(null, testKeyOpts), function (err, tokenDetails) { if (err) { done(err); return; @@ -155,7 +154,11 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { var realtime; try { var keyStr = helper.getTestApp().keys[0].keyStr; - realtime = helper.AblyRealtime({ key: keyStr, useTokenAuth: true, defaultTokenParams: { clientId: 'test' } }); + realtime = helper.AblyRealtime({ + key: keyStr, + useTokenAuth: true, + defaultTokenParams: { clientId: 'test' }, + }); expect(realtime.auth.clientId).to.equal(undefined); realtime.connection.on('connected', function () { try { @@ -240,15 +243,15 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { try { expect(realtime.connection.connectionManager.states.disconnected.retryDelay).to.equal( 123, - 'Verify disconnected retry frequency is settable' + 'Verify disconnected retry frequency is settable', ); expect(realtime.connection.connectionManager.states.suspended.retryDelay).to.equal( 456, - 'Verify suspended retry frequency is settable' + 'Verify suspended retry frequency is settable', ); expect(realtime.connection.connectionManager.options.timeouts.httpRequestTimeout).to.equal( 789, - 'Verify suspended retry frequency is settable' + 'Verify suspended retry frequency is settable', ); } catch (err) { closeAndFinish(done, realtime, err); @@ -273,7 +276,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { /* Note: uses internal knowledge of connectionManager */ expect(realtime.connection.connectionManager.httpHosts.length).to.equal( 3, - 'Verify hosts list is the expected length' + 'Verify hosts list is the expected length', ); expect(realtime.connection.connectionManager.httpHosts[0]).to.equal('a', 'Verify given restHost is first'); /* Replace chooseTransportForHost with a spy, then try calling @@ -306,14 +309,14 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { } }); - /* Check base and upgrade transports (nodejs only; browser tests in their own section) */ + /* Check base and websocket transports (nodejs only; browser tests in their own section) */ if (!isBrowser) { it('node_transports', function (done) { var realtime; try { realtime = helper.AblyRealtime({ transports: helper.availableTransports }); expect(realtime.connection.connectionManager.baseTransport).to.equal('comet'); - expect(realtime.connection.connectionManager.upgradeTransports).to.deep.equal(['web_socket']); + expect(realtime.connection.connectionManager.webSocketTransportAvailable).to.be.ok; closeAndFinish(done, realtime); } catch (err) { closeAndFinish(done, realtime, err); @@ -328,9 +331,9 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { var keyStr = helper.getTestApp().keys[0].keyStr; var realtime = helper.AblyRealtime({ key: keyStr, useTokenAuth: true }); realtime.connection.connectionManager.once('transport.pending', function (state) { - var transport = realtime.connection.connectionManager.pendingTransports[0], + var transport = realtime.connection.connectionManager.pendingTransport, originalOnProtocolMessage = transport.onProtocolMessage; - realtime.connection.connectionManager.pendingTransports[0].onProtocolMessage = function (message) { + realtime.connection.connectionManager.pendingTransport.onProtocolMessage = function (message) { try { if (message.action === 4) { expect(message.connectionDetails.connectionKey).to.be.ok; @@ -347,11 +350,11 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { try { expect(realtime.auth.clientId).to.equal( 'customClientId', - 'clientId should be set on the Auth object from connectionDetails' + 'clientId should be set on the Auth object from connectionDetails', ); expect(realtime.connection.key).to.equal( 'importantConnectionKey', - 'connection key from connectionDetails should be used' + 'connection key from connectionDetails should be used', ); } catch (err) { closeAndFinish(done, realtime, err); @@ -368,13 +371,14 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { var realtime = helper.AblyRealtime({ httpMaxRetryCount: 3, fallbackHosts: ['a', 'b', 'c'], + transport: ['web_socket'], }); realtime.connection.once('connected', function () { try { - var hosts = new Ably.Rest.Platform.Http()._getHosts(realtime); + var hosts = new Ably.Rest._Http()._getHosts(realtime); /* restHost rather than realtimeHost as that's what connectionManager * knows about; converted to realtimeHost by the websocketTransport */ - expect(hosts[0]).to.equal(realtime.options.restHost, 'Check connected realtime host is the first option'); + expect(hosts[0]).to.equal(realtime.options.realtimeHost, 'Check connected realtime host is the first option'); expect(hosts.length).to.equal(4, 'Check also have three fallbacks'); } catch (err) { closeAndFinish(done, realtime, err); @@ -392,7 +396,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { fallbackHosts: [goodHost, 'b', 'c'], }); realtime.connection.once('connected', function () { - var hosts = new Ably.Realtime.Platform.Http()._getHosts(realtime); + var hosts = new Ably.Realtime._Http()._getHosts(realtime); /* restHost rather than realtimeHost as that's what connectionManager * knows about; converted to realtimeHost by the websocketTransport */ try { @@ -404,34 +408,5 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { closeAndFinish(done, realtime); }); }); - - if (typeof Promise === 'undefined') { - it('init_callbacks_promises', function (done) { - try { - var realtime, - keyStr = helper.getTestApp().keys[0].keyStr, - getOptions = function () { - return { key: keyStr, autoConnect: false }; - }; - - realtime = new Ably.Realtime(getOptions()); - expect(!realtime.options.promises, 'Check promises defaults to false').to.be.ok; - - realtime = new Ably.Realtime.Promise(getOptions()); - expect(realtime.options.promises, 'Check promises default to true with promise constructor').to.be.ok; - - if (!isBrowser && typeof require == 'function') { - realtime = new require('../../promises').Realtime(getOptions()); - expect(realtime.options.promises, 'Check promises default to true with promise require target').to.be.ok; - - realtime = new require('../../callbacks').Realtime(getOptions()); - expect(!realtime.options.promises, 'Check promises default to false with callback require target').to.be.ok; - } - done(); - } catch (err) { - done(err); - } - }); - } }); }); diff --git a/test/realtime/message.test.js b/test/realtime/message.test.js index 84e63d2135..1a2c85331b 100644 --- a/test/realtime/message.test.js +++ b/test/realtime/message.test.js @@ -6,13 +6,14 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var utils = helper.Utils; let config = Ably.Realtime.Platform.Config; var closeAndFinish = helper.closeAndFinish; - var createPM = Ably.Realtime.ProtocolMessage.fromDeserialized; + var createPM = Ably.protocolMessageFromDeserialized; var monitorConnection = helper.monitorConnection; var testOnAllTransports = helper.testOnAllTransports; + var whenPromiseSettles = helper.whenPromiseSettles; var publishIntervalHelper = function (currentMessageNum, channel, dataFn, onPublish) { return function () { - channel.publish('event0', dataFn(), function () { + whenPromiseSettles(channel.publish('event0', dataFn()), function () { onPublish(); }); }; @@ -45,7 +46,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async realtime.connection.on('connected', function () { var testMsg = 'Hello world'; var rtChannel = realtime.channels.get('publishonce'); - rtChannel.attach(function (err) { + whenPromiseSettles(rtChannel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; @@ -82,7 +83,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var realtime = helper.AblyRealtime(realtimeOpts); realtime.connection.once('connected', function () { var channel = realtime.channels.get('publishfast_' + String(Math.random()).substr(2)); - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; @@ -100,11 +101,11 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async function (cb) { var ackd = 0; var publish = function (i) { - channel.publish('event', i.toString(), function (err) { + whenPromiseSettles(channel.publish('event', i.toString()), function (err) { try { expect( !err, - 'successfully published ' + i + (err ? ' err was ' + displayError(err) : '') + 'successfully published ' + i + (err ? ' err was ' + displayError(err) : ''), ).to.be.ok; } catch (err) { cb(err); @@ -124,7 +125,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], function (err) { closeAndFinish(done, realtime, err); - } + }, ); }); }); @@ -156,7 +157,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (cb) { - rxChannel.attach(function (err) { + whenPromiseSettles(rxChannel.attach(), function (err) { cb(err); }); }, @@ -180,11 +181,11 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async function (parCb) { var ackd = 0; var publish = function (i) { - txChannel.publish('event', { num: i }, function (err) { + whenPromiseSettles(txChannel.publish('event', { num: i }), function (err) { try { expect( !err, - 'successfully published ' + i + (err ? ' err was ' + displayError(err) : '') + 'successfully published ' + i + (err ? ' err was ' + displayError(err) : ''), ).to.be.ok; } catch (err) { parCb(err); @@ -208,13 +209,13 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async txRealtime.connection.connect(); }, ], - cb + cb, ); }, ], function (err) { closeAndFinish(done, [rxRealtime, txRealtime], err); - } + }, ); } catch (err) { closeAndFinish(done, [rxRealtime, txRealtime], err); @@ -259,7 +260,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }; // attach rtNoEchoChannel - rtNoEchoChannel.attach(function (err) { + whenPromiseSettles(rtNoEchoChannel.attach(), function (err) { try { expect(!err, 'Attached to rtNoEchoChannel with no error').to.be.ok; } catch (err) { @@ -275,7 +276,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); // attach rtEchoChannel - rtEchoChannel.attach(function (err) { + whenPromiseSettles(rtEchoChannel.attach(), function (err) { try { expect(!err, 'Attached to rtEchoChannel with no error').to.be.ok; } catch (err) { @@ -291,7 +292,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); // publish testMsg1 via rtNoEcho - rtNoEchoChannel.publish('event0', testMsg1, function () { + whenPromiseSettles(rtNoEchoChannel.publish('event0', testMsg1), function () { // publish testMsg2 via rtEcho rtEchoChannel.publish('event0', testMsg2); }); @@ -328,7 +329,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async /* connect and attach */ realtime.connection.on('connected', function () { var rtChannel = realtime.channels.get('publishVariations'); - rtChannel.attach(function (err) { + whenPromiseSettles(rtChannel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; @@ -352,14 +353,14 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async case 'objectWithNameAndEmptyStringData': expect(msg.data).to.equal( '', - 'Msg data received was a ' + typeof msg.data + ' when should have been an empty string' + 'Msg data received was a ' + typeof msg.data + ' when should have been an empty string', ); break; case 'objectWithNameAndFalseData': case 'nameAndFalseData': expect(msg.data).to.equal( false, - 'Msg data received was a ' + typeof msg.data + ' when should have been a bool false' + 'Msg data received was a ' + typeof msg.data + ' when should have been a bool false', ); break; case 'nameAndData': @@ -398,15 +399,14 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async async.eachSeries( testArguments, function iterator(args, callback) { - args.push(callback); - restChannel.publish.apply(restChannel, args); + whenPromiseSettles(restChannel.publish.apply(restChannel, args), callback); }, function (err) { if (err) { closeAndFinish(done, realtime, err); return; } - } + }, ); }); }); @@ -434,28 +434,31 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async /* connect and attach */ realtime.connection.on('connected', function () { var rtChannel = realtime.channels.get('publishDisallowed'); - rtChannel.attach(function (err) { + whenPromiseSettles(rtChannel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; } - /* publish events */ - var restChannel = rest.channels.get('publishDisallowed'); - for (var i = 0; i < testArguments.length; i++) { - try { - restChannel.publish.apply(restChannel, testArguments[i]); - closeAndFinish(done, realtime, new Error('Exception was not raised')); - } catch (err) { + (async function () { + /* publish events */ + var restChannel = rest.channels.get('publishDisallowed'); + for (var i = 0; i < testArguments.length; i++) { try { - expect(err.code).to.equal(40013, 'Invalid data type exception raised'); + await restChannel.publish.apply(restChannel, testArguments[i]); + closeAndFinish(done, realtime, new Error('Exception was not raised')); } catch (err) { - closeAndFinish(done, realtime, err); - return; + try { + expect(err.code).to.equal(40013, 'Invalid data type exception raised'); + } catch (err) { + closeAndFinish(done, realtime, err); + return; + } } } - } - closeAndFinish(done, realtime); + })().then(() => { + closeAndFinish(done, realtime); + }); }); }); monitorConnection(done, realtime); @@ -485,7 +488,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async /* connect and attach */ realtime.connection.on('connected', function () { var rtChannel = realtime.channels.get('publishEncodings'); - rtChannel.attach(function (err) { + whenPromiseSettles(rtChannel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; @@ -532,7 +535,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async testArguments, function iterator(item, callback) { try { - restChannel.publish(item, function (err) { + whenPromiseSettles(restChannel.publish(item), function (err) { try { expect(!err, 'Successfully published').to.be.ok; } catch (err) { @@ -544,7 +547,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async callback(err); } }, - cb + cb, ); }; @@ -569,7 +572,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async /* subscribe to event */ recvchannel.subscribe('event0', function (msg) { try { - expect(-1).to.not.equal(utils.arrIndexOf(messagesSent, msg.data), 'Received unexpected message text'); + expect(-1).to.not.equal(messagesSent.indexOf(msg.data), 'Received unexpected message text'); } catch (err) { closeAndFinish(done, realtime, err); return; @@ -603,18 +606,17 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var realtime = helper.AblyRealtime(realtimeOpts); var channel = realtime.channels.get('publish ' + JSON.stringify(realtimeOpts)); /* subscribe to event */ - channel.subscribe( - 'event0', - function () { + whenPromiseSettles( + channel.subscribe('event0', function () { --count; checkFinish(); - }, + }), function () { var dataFn = function () { return 'Hello world at: ' + new Date(); }; publishAtIntervals(count, channel, dataFn, onPublish); - } + }, ); }; }); @@ -682,7 +684,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var channel = realtime.channels.get('explicit_client_id_0'); /* subscribe to event */ - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { try { expect(!err, err && helper.displayError(err)).to.be.ok; @@ -706,14 +708,14 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (cb) { - channel.publish({ name: 'event0', clientId: clientId }, function (err) { + whenPromiseSettles(channel.publish({ name: 'event0', clientId: clientId }), function (err) { cb(err); }); }, ], function (err) { closeAndFinish(done, realtime, err); - } + }, ); }); }); @@ -726,7 +728,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async invalidClientId = 'invalid', rest = helper.AblyRest(); - rest.auth.requestToken({ clientId: clientId }, function (err, token) { + whenPromiseSettles(rest.auth.requestToken({ clientId: clientId }), function (err, token) { if (err) { done(err); return; @@ -743,7 +745,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async channel = realtime.channels.get('explicit_client_id_1'); // Publish before authentication to ensure the client library does not reject the message as the clientId is not known - channel.publish({ name: 'event0', clientId: invalidClientId }, function (err) { + whenPromiseSettles(channel.publish({ name: 'event0', clientId: invalidClientId }), function (err) { try { expect(err, 'Message was not published').to.be.ok; } catch (err) { @@ -790,7 +792,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (cb) { - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { cb(err); }); }, @@ -816,18 +818,21 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (innercb) { - channel.publish([{ name: 'a' }, { name: 'b' }, { name: 'c' }, { name: 'd' }], function (err) { - innercb(err); - }); + whenPromiseSettles( + channel.publish([{ name: 'a' }, { name: 'b' }, { name: 'c' }, { name: 'd' }]), + function (err) { + innercb(err); + }, + ); }, ], - outercb + outercb, ); }, ], function (err) { closeAndFinish(done, realtime, err); - } + }, ); }); @@ -836,8 +841,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async const channel = realtime.channels.get('subscribe_with_filter_object'); function send(cb) { - channel.publish( - [ + whenPromiseSettles( + channel.publish([ { name: 'correct', extras: { @@ -868,8 +873,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }, }, }, - ], - cb + ]), + cb, ); } @@ -887,7 +892,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async } // Wait for any errant messages to arrive before continuing config.nextTick(cb); - } + }, ); } @@ -899,7 +904,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (cb) { - return channel.attach(cb); + return whenPromiseSettles(channel.attach(), cb); }, function (cb) { return async.parallel([subscribe, send], cb); @@ -907,7 +912,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], function (err) { return closeAndFinish(done, realtime, err); - } + }, ); }); @@ -916,8 +921,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async const channel = realtime.channels.get('unsubscribe_with_filter_object'); function send(cb) { - channel.publish( - [ + whenPromiseSettles( + channel.publish([ { name: 'incorrect', extras: { @@ -927,8 +932,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }, }, }, - ], - cb + ]), + cb, ); } @@ -942,7 +947,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async channel.unsubscribe(listener); expect( channel.filteredSubscriptions.has(listener), - 'Listener should no longer be present after unsubscribing' + 'Listener should no longer be present after unsubscribing', ).to.be.false; config.nextTick(cb); } catch (e) { @@ -958,14 +963,14 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (cb) { - return channel.attach(cb); + return whenPromiseSettles(channel.attach(), cb); }, unsubscribe, send, ], function (err) { return closeAndFinish(done, realtime, err); - } + }, ); }); @@ -981,7 +986,9 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async cb(); }); }, - channel.attach.bind(channel), + function (cb) { + whenPromiseSettles(channel.attach(), cb); + }, function (outercb) { async.parallel( [ @@ -998,16 +1005,16 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (innercb) { - channel.publish([{ name: 'a', extras: extras }], innercb); + whenPromiseSettles(channel.publish([{ name: 'a', extras: extras }]), innercb); }, ], - outercb + outercb, ); }, ], function (err) { closeAndFinish(done, realtime, err); - } + }, ); }); @@ -1019,16 +1026,19 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async realtime.connection.once('connected', function () { connectionManager.once('connectiondetails', function (details) { - channel.publish('foo', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', function (err) { - try { - expect(err, 'Check publish refused').to.be.ok; - expect(err.code).to.equal(40009); - } catch (err) { - closeAndFinish(done, realtime, err); - return; - } - closeAndFinish(done, realtime); - }); + whenPromiseSettles( + channel.publish('foo', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'), + function (err) { + try { + expect(err, 'Check publish refused').to.be.ok; + expect(err.code).to.equal(40009); + } catch (err) { + closeAndFinish(done, realtime, err); + return; + } + closeAndFinish(done, realtime); + }, + ); }); var connectionDetails = connectionManager.connectionDetails; connectionDetails.maxMessageSize = 64; @@ -1036,7 +1046,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async createPM({ action: 4, connectionDetails: connectionDetails, - }) + }), ); }); }); @@ -1109,7 +1119,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var realtime = helper.AblyRealtime(), channel = realtime.channels.get('idempotentRealtimePublishing'); - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; @@ -1139,51 +1149,6 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }); - if (typeof Promise !== 'undefined') { - it('publishpromise', function (done) { - var realtime = helper.AblyRealtime({ promises: true }); - var channel = realtime.channels.get('publishpromise'); - - var publishPromise = realtime.connection - .once('connected') - .then(function (connectionStateChange) { - expect(connectionStateChange.current).to.equal( - 'connected', - 'Check promise is resolved with a connectionStateChange' - ); - return channel.attach(); - }) - .then(function () { - return channel.publish('name', 'data'); - }) - ['catch'](function (err) { - closeAndFinish(done, realtime, err); - }); - - var subscribePromise; - var messagePromise = new Promise(function (msgResolve) { - subscribePromise = channel.subscribe('name', function (msg) { - msgResolve(); - }); - }); - - var channelFailedPromise = realtime.channels - .get(':invalid') - .attach() - ['catch'](function (err) { - expect(err.code).to.equal(40010, 'Check err passed through correctly'); - }); - - Promise.all([publishPromise, subscribePromise, messagePromise, channelFailedPromise]) - .then(function () { - closeAndFinish(done, realtime); - }) - ['catch'](function (err) { - closeAndFinish(done, realtime, err); - }); - }); - } - it('subscribes to filtered channel', function (done) { var testData = [ { @@ -1249,7 +1214,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var filteredMessages = []; var unFilteredMessages = []; /* subscribe to event */ - rtFilteredChannel.attach(function (err) { + whenPromiseSettles(rtFilteredChannel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; @@ -1264,7 +1229,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async } }); - rtUnfilteredChannel.attach(function (err) { + whenPromiseSettles(rtUnfilteredChannel.attach(), function (err) { if (err) { closeAndFinish(done, realtime, err); return; @@ -1291,11 +1256,11 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async expect(filteredMessages[1].data).to.equal(testData[2].data, 'Unexpected data received'); expect(filteredMessages[0].extras.headers.name).to.equal( testData[0].extras.headers.name, - 'Unexpected header value received' + 'Unexpected header value received', ); expect(filteredMessages[1].extras.headers.name).to.equal( testData[2].extras.headers.name, - 'Unexpected header value received' + 'Unexpected header value received', ); // Check that message with header that doesn't meet filtering condition is not received. for (msg of filteredMessages) { diff --git a/test/realtime/presence.test.js b/test/realtime/presence.test.js index 8b5f8ff9a2..688d4b6b07 100644 --- a/test/realtime/presence.test.js +++ b/test/realtime/presence.test.js @@ -3,21 +3,22 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async, chai) { var expect = chai.expect; var utils = helper.Utils; - var createPM = Ably.Realtime.ProtocolMessage.fromDeserialized; + var createPM = Ably.protocolMessageFromDeserialized; var closeAndFinish = helper.closeAndFinish; var monitorConnection = helper.monitorConnection; var PresenceMessage = Ably.Realtime.PresenceMessage; + var whenPromiseSettles = helper.whenPromiseSettles; function extractClientIds(presenceSet) { - return utils - .arrMap(presenceSet, function (presmsg) { + return presenceSet + .map(function (presmsg) { return presmsg.clientId; }) .sort(); } function extractMember(presenceSet, clientId) { - return helper.arrFind(presenceSet, function (member) { + return presenceSet.find(function (member) { return member.clientId === clientId; }); } @@ -32,7 +33,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async realtime = helper.AblyRealtime(); realtime.connection.on('connected', function () { channel = realtime.channels.get(channelName); - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { callback(err, realtime, channel); }); }); @@ -83,7 +84,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async // testRunner might or might not call back with an open realtime var openConnections = res[1] && res[1].close ? [listenerRealtime, res[1]] : listenerRealtime; closeAndFinish(done, openConnections); - } + }, ); }); } catch (err) { @@ -102,7 +103,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async // Create authTokens associated with specific clientIds try { rest = helper.AblyRest(); - rest.auth.requestToken({ clientId: testClientId }, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken({ clientId: testClientId }), function (err, tokenDetails) { if (err) { done(err); return; @@ -115,7 +116,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async return; } - rest.auth.requestToken({ clientId: testClientId2 }, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken({ clientId: testClientId2 }), function (err, tokenDetails) { if (err) { done(err); return; @@ -147,12 +148,12 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async clientRealtime.connection.on('connected', function () { /* get channel, attach, and enter */ var clientChannel = clientRealtime.channels.get(channelName); - clientChannel.attach(function (err) { + whenPromiseSettles(clientChannel.attach(), function (err) { if (err) { cb(err, clientRealtime); return; } - clientChannel.presence.enter('Test client data (enter0)', function (err) { + whenPromiseSettles(clientChannel.presence.enter('Test client data (enter0)'), function (err) { cb(err, clientRealtime); }); }); @@ -173,7 +174,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async clientRealtime.connection.on('connected', function () { /* get channel, attach, and enter */ var clientChannel = clientRealtime.channels.get(channelName); - clientChannel.presence.enter('Test client data (enterWithoutAttach)', function (err) { + whenPromiseSettles(clientChannel.presence.enter('Test client data (enterWithoutAttach)'), function (err) { cb(err, clientRealtime); }); }); @@ -191,7 +192,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var enterWithoutConnect = function (cb) { var clientRealtime = helper.AblyRealtime({ clientId: testClientId, tokenDetails: authToken }); var clientChannel = clientRealtime.channels.get(channelName); - clientChannel.presence.enter('Test client data (enterWithoutConnect)', function (err) { + whenPromiseSettles(clientChannel.presence.enter('Test client data (enterWithoutConnect)'), function (err) { cb(err, clientRealtime); }); monitorConnection(done, clientRealtime); @@ -231,19 +232,19 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async clientRealtime.connection.on('connected', function () { /* get channel, attach, and enter */ var clientChannel = clientRealtime.channels.get(channelName); - clientChannel.attach(function (err) { + whenPromiseSettles(clientChannel.attach(), function (err) { if (err) { closeAndFinish(done, [listenerRealtime, clientRealtime], err); return; } - clientChannel.detach(function (err) { + whenPromiseSettles(clientChannel.detach(), function (err) { if (err) { closeAndFinish(done, [listenerRealtime, clientRealtime], err); return; } }); }); - clientChannel.presence.enter('Test client data (enter3)', function (err) { + whenPromiseSettles(clientChannel.presence.enter('Test client data (enter3)'), function (err) { // Note: either an error (pending messages failed to send due to detach) // or a success (pending messages were pushed out before the detach) // is an acceptable result. Throwing an uncaught exception (the behaviour @@ -275,12 +276,12 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async clientRealtime.connection.on('connected', function () { /* get channel, attach, and enter */ var clientChannel = clientRealtime.channels.get(channelName); - clientChannel.attach(function (err) { + whenPromiseSettles(clientChannel.attach(), function (err) { if (err) { cb(err, clientRealtime); return; } - clientChannel.presence.enter(function (err) { + whenPromiseSettles(clientChannel.presence.enter(), function (err) { cb(err, clientRealtime); }); }); @@ -301,7 +302,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async clientRealtime.connection.on('connected', function () { /* get channel, attach, and enter */ var clientChannel = clientRealtime.channels.get(channelName); - clientChannel.attach(function (err) { + whenPromiseSettles(clientChannel.attach(), function (err) { if (err) { cb(err, clientRealtime); return; @@ -326,7 +327,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async clientRealtime.connection.on('connected', function () { /* get channel, attach, and enter */ var clientChannel = clientRealtime.channels.get(channelName); - clientChannel.attach(function (err) { + whenPromiseSettles(clientChannel.attach(), function (err) { if (err) { cb(err, clientRealtime); return; @@ -350,8 +351,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var channelName = 'presenceMessageAction'; var clientChannel = clientRealtime.channels.get(channelName); var presence = clientChannel.presence; - presence.subscribe( - function (presenceMessage) { + whenPromiseSettles( + presence.subscribe(function (presenceMessage) { try { expect(presenceMessage.action).to.equal('enter', 'Action should contain string "enter"'); } catch (err) { @@ -359,14 +360,14 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async return; } closeAndFinish(done, clientRealtime); - }, + }), function onPresenceSubscribe(err) { if (err) { closeAndFinish(done, clientRealtime, err); return; } clientChannel.presence.enter(); - } + }, ); monitorConnection(done, clientRealtime); }); @@ -384,7 +385,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async async.series( [ function (cb) { - clientChannel.attach(cb); + whenPromiseSettles(clientChannel.attach(), cb); }, // Test entering with extras function (cb) { @@ -392,7 +393,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async try { expect(presenceMessage.extras).to.deep.equal( { headers: { key: 'value' } }, - 'extras should have headers "key=value"' + 'extras should have headers "key=value"', ); } catch (err) { cb(err); @@ -403,7 +404,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async presence.enter( PresenceMessage.fromValues({ extras: { headers: { key: 'value' } }, - }) + }), ); }, // Test leaving with extras @@ -412,7 +413,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async try { expect(presenceMessage.extras).to.deep.equal( { headers: { otherKey: 'otherValue' } }, - 'extras should have headers "otherKey=otherValue"' + 'extras should have headers "otherKey=otherValue"', ); } catch (err) { cb(err); @@ -423,7 +424,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async presence.leave( PresenceMessage.fromValues({ extras: { headers: { otherKey: 'otherValue' } }, - }) + }), ); }, ], @@ -433,7 +434,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async return; } closeAndFinish(done, clientRealtime); - } + }, ); monitorConnection(done, clientRealtime); @@ -461,17 +462,17 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); // NB remove besttransport in 1.1 spec, see attachdetach0 var clientChannel = clientRealtime.channels.get(channelName); clientRealtime.connection.once('connected', function () { - clientChannel.presence.enter('first', function (err) { + whenPromiseSettles(clientChannel.presence.enter('first'), function (err) { if (err) { cb(err, clientRealtime); return; } - clientChannel.detach(function (err) { + whenPromiseSettles(clientChannel.detach(), function (err) { if (err) { cb(err, clientRealtime); return; } - clientChannel.presence.enter('second', function (err) { + whenPromiseSettles(clientChannel.presence.enter('second'), function (err) { cb(err, clientRealtime); }); }); @@ -492,7 +493,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async clientRealtime = helper.AblyRealtime({ clientId: testClientId, tokenDetails: authToken }); var clientChannel = clientRealtime.channels.get(''); clientRealtime.connection.once('connected', function () { - clientChannel.presence.enter('clientId', function (err) { + whenPromiseSettles(clientChannel.presence.enter('clientId'), function (err) { if (err) { try { expect(err.code).to.equal(40010, 'Correct error code'); @@ -522,18 +523,18 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async clientRealtime.connection.on('connected', function () { /* get channel, attach, and enter */ var clientChannel = clientRealtime.channels.get(channelName); - clientChannel.attach(function (err) { + whenPromiseSettles(clientChannel.attach(), function (err) { if (err) { cb(err, clientRealtime); return; } - clientChannel.presence.enter('Test client data (leave0)', function (err) { + whenPromiseSettles(clientChannel.presence.enter('Test client data (leave0)'), function (err) { if (err) { cb(err, clientRealtime); return; } }); - clientChannel.presence.leave(function (err) { + whenPromiseSettles(clientChannel.presence.leave(), function (err) { cb(err, clientRealtime); }); }); @@ -570,17 +571,17 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var clientRealtime = helper.AblyRealtime({ clientId: testClientId, tokenDetails: authToken }); clientRealtime.connection.on('connected', function () { var clientChannel = clientRealtime.channels.get(channelName); - clientChannel.attach(function (err) { + whenPromiseSettles(clientChannel.attach(), function (err) { if (err) { cb(err, clientRealtime); return; } - clientChannel.presence.enter('Original data', function (err) { + whenPromiseSettles(clientChannel.presence.enter('Original data'), function (err) { if (err) { cb(err, clientRealtime); return; } - clientChannel.presence.update(newData, function (err) { + whenPromiseSettles(clientChannel.presence.update(newData), function (err) { cb(err, clientRealtime); }); }); @@ -601,7 +602,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var eventListener = function (channel, callback) { var presenceHandler = function () { /* Should be ENTER, but may be PRESENT in a race */ - channel.presence.get(function (err, presenceMembers) { + whenPromiseSettles(channel.presence.get(), function (err, presenceMembers) { if (err) { callback(err); return; @@ -624,12 +625,12 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async clientRealtime.connection.on('connected', function () { /* get channel, attach, and enter */ var clientChannel = clientRealtime.channels.get(channelName); - clientChannel.attach(function (err) { + whenPromiseSettles(clientChannel.attach(), function (err) { if (err) { cb(err, clientRealtime); return; } - clientChannel.presence.enter(testData, function (err) { + whenPromiseSettles(clientChannel.presence.enter(testData), function (err) { cb(err, clientRealtime); }); }); @@ -678,7 +679,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async clientRealtime.connection.on('connected', function () { /* get channel, attach, and enter */ var clientChannel = clientRealtime.channels.get(channelName); - clientChannel.presence.enter(testData, function (err) { + whenPromiseSettles(clientChannel.presence.enter(testData), function (err) { if (err) { closeAndFinish(done, clientRealtime, err); return; @@ -687,7 +688,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async clientRealtime2.connection.on('connected', function () { var clientChannel2 = clientRealtime2.channels.get(channelName); /* GET without attaching */ - clientChannel2.presence.get(function (err, presenceMembers) { + whenPromiseSettles(clientChannel2.presence.get(), function (err, presenceMembers) { if (err) { closeAndFinish(done, [clientRealtime, clientRealtime2], err); return; @@ -718,7 +719,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var presenceHandler = function () { // Ignore the first (enter) event if (this.event == 'leave') { - channel.presence.get(function (err, presenceMembers) { + whenPromiseSettles(channel.presence.get(), function (err, presenceMembers) { if (err) { callback(err); return; @@ -741,17 +742,17 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async clientRealtime.connection.on('connected', function () { /* get channel, attach, and enter */ var clientChannel = clientRealtime.channels.get(channelName); - clientChannel.attach(function (err) { + whenPromiseSettles(clientChannel.attach(), function (err) { if (err) { cb(err, clientRealtime); return; } - clientChannel.presence.enter('testClientData', function (err) { + whenPromiseSettles(clientChannel.presence.enter('testClientData'), function (err) { if (err) { cb(err, clientRealtime); return; } - clientChannel.presence.leave(function (err) { + whenPromiseSettles(clientChannel.presence.leave(), function (err) { cb(err, clientRealtime); }); }); @@ -771,7 +772,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var channelName = 'history'; var testClientData = 'Test client data (history0)'; var queryPresenceHistory = function (channel) { - channel.presence.history(function (err, resultPage) { + whenPromiseSettles(channel.presence.history(), function (err, resultPage) { if (err) { closeAndFinish(done, clientRealtime, err); return; @@ -779,15 +780,15 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var presenceMessages = resultPage.items; expect(presenceMessages.length).to.equal(2, 'Verify correct number of presence messages found'); - var actions = utils - .arrMap(presenceMessages, function (msg) { + var actions = presenceMessages + .map(function (msg) { return msg.action; }) .sort(); expect(actions).to.deep.equal(['enter', 'leave'], 'Verify presenceMessages have correct actions'); expect(presenceMessages[0].data || presenceMessages[1].data).to.equal( testClientData, - 'Verify correct data (from whichever message was the "enter")' + 'Verify correct data (from whichever message was the "enter")', ); closeAndFinish(done, clientRealtime); }); @@ -798,22 +799,22 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async clientRealtime.connection.on('connected', function () { /* get channel, attach, and enter */ var clientChannel = clientRealtime.channels.get(channelName); - clientChannel.attach(function (err) { + whenPromiseSettles(clientChannel.attach(), function (err) { if (err) { closeAndFinish(done, clientRealtime, err); return; } - clientChannel.presence.enter(testClientData, function (err) { + whenPromiseSettles(clientChannel.presence.enter(testClientData), function (err) { if (err) { closeAndFinish(done, clientRealtime, err); return; } - clientChannel.presence.leave(function (err) { + whenPromiseSettles(clientChannel.presence.leave(), function (err) { if (err) { closeAndFinish(done, clientRealtime, err); return; } - clientChannel.detach(function (err) { + whenPromiseSettles(clientChannel.detach(), function (err) { if (err) { closeAndFinish(done, clientRealtime, err); return; @@ -847,19 +848,19 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async clientRealtime1.connection.on('connected', function () { /* get channel, attach, and enter */ var clientChannel1 = clientRealtime1.channels.get(channelName); - clientChannel1.attach(function (err) { + whenPromiseSettles(clientChannel1.attach(), function (err) { if (err) { closeAndFinish(done, clientRealtime1, err); return; } - clientChannel1.presence.enter('Test client data (attach0)', function (err) { + whenPromiseSettles(clientChannel1.presence.enter('Test client data (attach0)'), function (err) { if (err) { closeAndFinish(done, clientRealtime1, err); return; } }); clientChannel1.presence.subscribe('enter', function () { - clientChannel1.presence.get(function (err, presenceMembers1) { + whenPromiseSettles(clientChannel1.presence.get(), function (err, presenceMembers1) { if (err) { closeAndFinish(done, clientRealtime1, err); return; @@ -876,14 +877,14 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async clientRealtime2.connection.on('connected', function () { /* get channel, attach */ var clientChannel2 = clientRealtime2.channels.get(channelName); - clientChannel2.attach(function (err) { + whenPromiseSettles(clientChannel2.attach(), function (err) { if (err) { closeAndFinish(done, [clientRealtime1, clientRealtime2], err); return; } clientChannel2.presence.subscribe('present', function () { /* get the channel members and verify testclient is there */ - clientChannel2.presence.get(function (err, presenceMembers2) { + whenPromiseSettles(clientChannel2.presence.get(), function (err, presenceMembers2) { if (err) { closeAndFinish(done, [clientRealtime1, clientRealtime2], err); return; @@ -891,7 +892,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async try { expect(presenceMembers1).to.deep.equal( presenceMembers2, - 'Verify member presence is indicated after attach' + 'Verify member presence is indicated after attach', ); } catch (err) { closeAndFinish(done, [clientRealtime1, clientRealtime2], err); @@ -931,12 +932,12 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async clientRealtime1.connection.on('connected', function () { /* get channel, attach, and enter */ clientChannel1 = clientRealtime1.channels.get(channelName); - clientChannel1.attach(function (err) { + whenPromiseSettles(clientChannel1.attach(), function (err) { if (err) { cb1(err); return; } - clientChannel1.presence.enter(data, function (err) { + whenPromiseSettles(clientChannel1.presence.enter(data), function (err) { if (err) { cb1(err); return; @@ -953,13 +954,13 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async clientRealtime2.connection.on('connected', function () { /* get channel, attach */ clientChannel2 = clientRealtime2.channels.get(channelName); - clientChannel2.attach(function (err) { + whenPromiseSettles(clientChannel2.attach(), function (err) { if (err) { cb2(err); return; } var enterPresence = function (onEnterCB) { - clientChannel2.presence.enter(data, function (err) { + whenPromiseSettles(clientChannel2.presence.enter(data), function (err) { if (err) { cb2(err); return; @@ -987,7 +988,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async [waitForClient(testClientId), waitForClient(testClientId2), enterPresence], function () { cb2(); - } + }, ); }); }); @@ -1003,7 +1004,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async [ /* First test: no filters */ function (cb) { - clientChannel2.presence.get(function (err, members) { + whenPromiseSettles(clientChannel2.presence.get(), function (err, members) { if (err) { return cb(err); } @@ -1011,7 +1012,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async expect(members.length).to.equal(2, 'Verify both members present'); expect(members[0].connectionId).to.not.equal( members[1].connectionId, - 'Verify members have distinct connectionIds' + 'Verify members have distinct connectionIds', ); } catch (err) { cb(err); @@ -1022,7 +1023,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }, /* Second test: filter by clientId */ function (cb) { - clientChannel2.presence.get({ clientId: testClientId }, function (err, members) { + whenPromiseSettles(clientChannel2.presence.get({ clientId: testClientId }), function (err, members) { if (err) { return cb(err); } @@ -1038,25 +1039,28 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }, /* Third test: filter by connectionId */ function (cb) { - clientChannel2.presence.get({ connectionId: clientRealtime1.connection.id }, function (err, members) { - if (err) { - return cb(err); - } - try { - expect(members.length).to.equal( - 1, - 'Verify only one member present when filtered by connectionId' - ); - expect(members[0].connectionId).to.equal( - clientRealtime1.connection.id, - 'Verify connectionId filter works' - ); - } catch (err) { - cb(err); - return; - } - cb(); - }); + whenPromiseSettles( + clientChannel2.presence.get({ connectionId: clientRealtime1.connection.id }), + function (err, members) { + if (err) { + return cb(err); + } + try { + expect(members.length).to.equal( + 1, + 'Verify only one member present when filtered by connectionId', + ); + expect(members[0].connectionId).to.equal( + clientRealtime1.connection.id, + 'Verify connectionId filter works', + ); + } catch (err) { + cb(err); + return; + } + cb(); + }, + ); }, ], function (err) { @@ -1065,9 +1069,9 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async return; } closeAndFinish(done, [clientRealtime1, clientRealtime2]); - } + }, ); - } + }, ); } catch (err) { closeAndFinish(done, [clientRealtime1, clientRealtime2], err); @@ -1094,16 +1098,16 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var clientChannel = clientRealtime.channels.get(channelName); clientRealtime.connection.once('connected', function () { /* get channel and enter (should automatically attach) */ - clientChannel.presence.enter('first', function (err) { + whenPromiseSettles(clientChannel.presence.enter('first'), function (err) { if (err) { cb(err, clientRealtime); return; } clientRealtime.close(); - clientRealtime.connection.whenState('closed', function () { + whenPromiseSettles(clientRealtime.connection.whenState('closed'), function () { clientRealtime.connection.once('connected', function () { //Should automatically reattach - clientChannel.presence.enter('second', function (err) { + whenPromiseSettles(clientChannel.presence.enter('second'), function (err) { cb(err, clientRealtime); }); }); @@ -1128,7 +1132,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var clientChannel = clientRealtime.channels.get(channelName); clientRealtime.connection.on('connected', function () { clientRealtime.close(); - clientChannel.presence.enterClient('clientId', function (err) { + whenPromiseSettles(clientChannel.presence.enterClient('clientId'), function (err) { try { expect(err.code).to.equal(80017, 'presence enter failed with correct code'); expect(err.statusCode).to.equal(400, 'presence enter failed with correct statusCode'); @@ -1166,17 +1170,17 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async originalSendPresence.apply(channel, arguments); }; - presence.enter(null, function (err) { + whenPromiseSettles(presence.enter(null), function (err) { if (err) { closeAndFinish(done, client, err); return; } - presence.update(null, function (err) { + whenPromiseSettles(presence.update(null), function (err) { if (err) { closeAndFinish(done, client, err); return; } - presence.leave(null, function (err) { + whenPromiseSettles(presence.leave(null), function (err) { if (err) { closeAndFinish(done, client, err); return; @@ -1187,39 +1191,6 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }); - /* - * Check that old deprecated on/off methods still work - */ - it('presenceOn', function (done) { - var channelName = 'enterOn'; - var testData = 'some data'; - var eventListener = function (channel, callback) { - var presenceHandler = function () { - callback(); - }; - channel.presence.on(presenceHandler); - }; - var enterOn = function (cb) { - var clientRealtime = helper.AblyRealtime({ clientId: testClientId, tokenDetails: authToken }); - clientRealtime.connection.on('connected', function () { - /* get channel, attach, and enter */ - var clientChannel = clientRealtime.channels.get(channelName); - clientChannel.attach(function (err) { - if (err) { - cb(err, clientRealtime); - return; - } - clientChannel.presence.enter(testData, function (err) { - cb(err, clientRealtime); - }); - }); - }); - monitorConnection(done, clientRealtime); - }; - - runTestWithEventListener(done, channelName, eventListener, enterOn); - }); - /* * Check that encodable presence messages are encoded correctly */ @@ -1260,7 +1231,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }; var channel = realtime.channels.get( - 'presence-' + (realtime.options.useBinaryProtocol ? 'bin' : 'json') + '-encoding' + 'presence-' + (realtime.options.useBinaryProtocol ? 'bin' : 'json') + '-encoding', ); channel.presence.enter(data); }); @@ -1278,7 +1249,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], function (err) { closeAndFinish(done, [realtimeBin, realtimeJson], err); - } + }, ); }); @@ -1290,7 +1261,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var channelName = 'enter_inherited_clientid'; var authCallback = function (tokenParams, callback) { - rest.auth.requestToken({ clientId: testClientId }, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken({ clientId: testClientId }), function (err, tokenDetails) { if (err) { done(err); return; @@ -1309,7 +1280,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async cb(err); return; } - channel.presence.enter('test data', function (err) { + whenPromiseSettles(channel.presence.enter('test data'), function (err) { cb(err, realtime); }); }); @@ -1328,7 +1299,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var channelName = 'enter_before_know_clientid'; var enterInheritedClientId = function (cb) { - rest.auth.requestToken({ clientId: testClientId }, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken({ clientId: testClientId }), function (err, tokenDetails) { if (err) { done(err); return; @@ -1341,7 +1312,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async closeAndFinish(done, realtime, err); return; } - channel.presence.enter('test data', function (err) { + whenPromiseSettles(channel.presence.enter('test data'), function (err) { try { expect(realtime.auth.clientId).to.equal(testClientId, 'clientId has been set by the time we entered'); } catch (err) { @@ -1381,7 +1352,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], function () { cb(); - } + }, ); } @@ -1389,20 +1360,20 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async async.parallel( [ function (enterCb) { - realtimeChannel.presence.enterClient('one', enterCb); + whenPromiseSettles(realtimeChannel.presence.enterClient('one'), enterCb); }, function (enterCb) { - realtimeChannel.presence.enterClient('two', enterCb); + whenPromiseSettles(realtimeChannel.presence.enterClient('two'), enterCb); }, ], - cb + cb, ); } function checkPresence(first, second, cb) { - observerChannel.presence.get(function (err, presenceMembers) { - var clientIds = utils - .arrMap(presenceMembers, function (msg) { + whenPromiseSettles(observerChannel.presence.get(), function (err, presenceMembers) { + var clientIds = presenceMembers + .map(function (msg) { return msg.clientId; }) .sort(); @@ -1422,13 +1393,13 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async async.parallel( [ function (innerCb) { - realtimeChannel.presence.leaveClient('two', innerCb); + whenPromiseSettles(realtimeChannel.presence.leaveClient('two'), innerCb); }, function (innerCb) { - realtimeChannel.presence.enterClient('three', innerCb); + whenPromiseSettles(realtimeChannel.presence.enterClient('three'), innerCb); }, ], - cb + cb, ); } @@ -1452,17 +1423,17 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async [ waitForBothConnect, function (cb) { - realtimeChannel.attach(cb); + whenPromiseSettles(realtimeChannel.attach(), cb); }, enterOneAndTwo, function (cb) { - observerChannel.attach(cb); + whenPromiseSettles(observerChannel.attach(), cb); }, function (cb) { checkPresence('one', 'two', cb); }, function (cb) { - observerChannel.detach(cb); + whenPromiseSettles(observerChannel.detach(), cb); }, swapTwoForThree, attachAndListen, @@ -1472,7 +1443,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], function (err) { closeAndFinish(done, [realtime, observer], err); - } + }, ); }); @@ -1495,7 +1466,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], function () { cb(); - } + }, ); } @@ -1503,13 +1474,13 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async [ waitForBothConnect, function (cb) { - entererChannel.presence.enter(cb); + whenPromiseSettles(entererChannel.presence.enter(), cb); }, function (cb) { - detacherChannel.attach(cb); + whenPromiseSettles(detacherChannel.attach(), cb); }, function (cb) { - detacherChannel.detach(cb); + whenPromiseSettles(detacherChannel.detach(), cb); }, function (cb) { try { @@ -1523,7 +1494,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], function (err) { closeAndFinish(done, [enterer, detacher], err); - } + }, ); }); @@ -1545,7 +1516,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (cb) { - channel.attach(cb); + whenPromiseSettles(channel.attach(), cb); }, function (cb) { if (!channel.presence.syncComplete) { @@ -1614,7 +1585,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (cb) { - channel.presence.get(function (err, results) { + whenPromiseSettles(channel.presence.get(), function (err, results) { if (err) { cb(err); return; @@ -1635,7 +1606,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], function (err) { closeAndFinish(done, realtime, err); - } + }, ); }); @@ -1653,7 +1624,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async /* Request a token without the capabilities to be in the presence set */ var tokenParams = { clientId: 'me', capability: {} }; tokenParams.capability[channelName] = ['publish', 'subscribe']; - rest.auth.requestToken(tokenParams, function (err, tokenDetails) { + whenPromiseSettles(rest.auth.requestToken(tokenParams), function (err, tokenDetails) { token = tokenDetails; cb(err); }); @@ -1666,7 +1637,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (cb) { - channel.attach(cb); + whenPromiseSettles(channel.attach(), cb); }, function (cb) { if (!channel.presence.syncComplete) { @@ -1676,7 +1647,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async } }, function (cb) { - channel.presence.get(function (err, members) { + whenPromiseSettles(channel.presence.get(), function (err, members) { try { expect(members.length).to.equal(0, 'Check no-one in presence set'); } catch (err) { @@ -1718,7 +1689,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (cb) { - channel.presence.get(function (err, members) { + whenPromiseSettles(channel.presence.get(), function (err, members) { try { expect(members.length).to.equal(0, 'Check no-one in presence set'); } catch (err) { @@ -1731,14 +1702,13 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], function (err) { closeAndFinish(done, realtime, err); - } + }, ); }); /* Enter ten clients while attaching, finish the attach, check they were all entered correctly */ it('multiple_pending', function (done) { - /* single transport to avoid upgrade stalling due to the stubbed attachImpl */ - var realtime = helper.AblyRealtime({ transports: [helper.bestTransport] }), + var realtime = helper.AblyRealtime(), channel = realtime.channels.get('multiple_pending'), originalAttachImpl = channel.attachImpl; @@ -1769,7 +1739,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (cb) { - channel.presence.get(function (err, results) { + whenPromiseSettles(channel.presence.get(), function (err, results) { try { expect(results.length).to.equal(10, 'Check all ten clients are there'); } catch (err) { @@ -1782,7 +1752,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], function (err) { closeAndFinish(done, realtime, err); - } + }, ); }); @@ -1803,26 +1773,26 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async async.series( [ function (cb) { - continuousRealtime.connection.whenState('connected', function () { + whenPromiseSettles(continuousRealtime.connection.whenState('connected'), function () { cb(); }); }, function (cb) { - continuousChannel.attach(cb); + whenPromiseSettles(continuousChannel.attach(), cb); }, function (cb) { - continuousChannel.presence.enter(cb); + whenPromiseSettles(continuousChannel.presence.enter(), cb); }, function (cb) { - realtime.connection.whenState('connected', function () { + whenPromiseSettles(realtime.connection.whenState('connected'), function () { cb(); }); }, function (cb) { - channel.attach(cb); + whenPromiseSettles(channel.attach(), cb); }, function (cb) { - channel.presence.get({ waitForSync: true }, function (err, members) { + whenPromiseSettles(channel.presence.get({ waitForSync: true }), function (err, members) { try { expect(members && members.length).to.equal(1, 'Check one member present'); } catch (err) { @@ -1834,19 +1804,28 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }, function (cb) { /* Inject an additional member locally */ - channel.onMessage({ - action: 14, - id: 'messageid:0', - connectionId: 'connid', - timestamp: utils.now(), - presence: [ - { - clientId: goneClientId, - action: 'enter', - }, - ], - }); - channel.presence.get(function (err, members) { + channel + .processMessage({ + action: 14, + id: 'messageid:0', + connectionId: 'connid', + timestamp: Date.now(), + presence: [ + { + clientId: goneClientId, + action: 'enter', + }, + ], + }) + .then(function () { + cb(null); + }) + .catch(function (err) { + cb(err); + }); + }, + function (cb) { + whenPromiseSettles(channel.presence.get(), function (err, members) { try { expect(members && members.length).to.equal(2, 'Check two members present'); } catch (err) { @@ -1871,12 +1850,12 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async channel.sync(); }, function (cb) { - channel.presence.get({ waitForSync: true }, function (err, members) { + whenPromiseSettles(channel.presence.get({ waitForSync: true }), function (err, members) { try { expect(members && members.length).to.equal(1, 'Check back to one member present'); expect(members && members[0] && members[0].clientId).to.equal( continuousClientId, - 'check cont still present' + 'check cont still present', ); } catch (err) { cb(err); @@ -1888,7 +1867,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], function (err) { closeAndFinish(done, [realtime, continuousRealtime], err); - } + }, ); }); @@ -1905,28 +1884,37 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async async.series( [ function (cb) { - realtime.connection.whenState('connected', function () { + whenPromiseSettles(realtime.connection.whenState('connected'), function () { cb(); }); }, function (cb) { - channel.attach(cb); + whenPromiseSettles(channel.attach(), cb); }, function (cb) { /* Inject a member locally */ - channel.onMessage({ - action: 14, - id: 'messageid:0', - connectionId: 'connid', - timestamp: utils.now(), - presence: [ - { - clientId: fakeClientId, - action: 'enter', - }, - ], - }); - channel.presence.get(function (err, members) { + channel + .processMessage({ + action: 14, + id: 'messageid:0', + connectionId: 'connid', + timestamp: Date.now(), + presence: [ + { + clientId: fakeClientId, + action: 'enter', + }, + ], + }) + .then(function () { + cb(); + }) + .catch(function () { + cb(err); + }); + }, + function (cb) { + whenPromiseSettles(channel.presence.get(), function (err, members) { try { expect(members && members.length).to.equal(1, 'Check one member present'); } catch (err) { @@ -1948,16 +1936,16 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async cb(); }); /* Inject an ATTACHED with RESUMED and HAS_PRESENCE both false */ - channel.onMessage( + channel.processMessage( createPM({ action: 11, channelSerial: channel.properties.attachSerial, flags: 0, - }) + }), ); }, function (cb) { - channel.presence.get(function (err, members) { + whenPromiseSettles(channel.presence.get(), function (err, members) { try { expect(members && members.length).to.equal(0, 'Check no members present'); } catch (err) { @@ -1970,7 +1958,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], function (err) { closeAndFinish(done, realtime, err); - } + }, ); }); @@ -1979,9 +1967,9 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async * and only members that changed between ATTACHED states should result in * presence events */ it('suspended_preserves_presence', function (done) { - var mainRealtime = helper.AblyRealtime({ clientId: 'main', logLevel: 4 }), - continuousRealtime = helper.AblyRealtime({ clientId: 'continuous', logLevel: 4 }), - leavesRealtime = helper.AblyRealtime({ clientId: 'leaves', logLevel: 4 }), + var mainRealtime = helper.AblyRealtime({ clientId: 'main' }), + continuousRealtime = helper.AblyRealtime({ clientId: 'continuous' }), + leavesRealtime = helper.AblyRealtime({ clientId: 'leaves' }), channelName = 'suspended_preserves_presence', mainChannel = mainRealtime.channels.get(channelName); @@ -1993,18 +1981,18 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async async.series( [ function (cb) { - rt.connection.whenState('connected', function () { + whenPromiseSettles(rt.connection.whenState('connected'), function () { cb(); }); }, function (cb) { - channel.attach(cb); + whenPromiseSettles(channel.attach(), cb); }, function (cb) { - channel.presence.enter(cb); + whenPromiseSettles(channel.presence.enter(), cb); }, ], - outerCb + outerCb, ); }; }; @@ -2030,7 +2018,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async async.parallel([waitFor('leaves'), enter(leavesRealtime)], cb); }, function (cb) { - mainChannel.presence.get(function (err, members) { + whenPromiseSettles(mainChannel.presence.get(), function (err, members) { try { expect(members.length).to.equal(3, 'Check all three expected members here'); } catch (err) { @@ -2044,7 +2032,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async helper.becomeSuspended(mainRealtime, cb); }, function (cb) { - mainChannel.presence.get(function (err) { + whenPromiseSettles(mainChannel.presence.get(), function (err) { /* Check RTP11d: get() returns an error by default */ try { expect(err, 'Check error returned by get() while suspended').to.be.ok; @@ -2057,7 +2045,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (cb) { - mainChannel.presence.get({ waitForSync: false }, function (err, members) { + whenPromiseSettles(mainChannel.presence.get({ waitForSync: false }), function (err, members) { /* Check RTP11d: get() works while suspended if waitForSync: false */ try { expect(!err, 'Check no error returned by get() while suspended if waitForSync: false').to.be.ok; @@ -2070,7 +2058,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }, function (cb) { - leavesRealtime.connection.whenState('closed', function () { + whenPromiseSettles(leavesRealtime.connection.whenState('closed'), function () { cb(); }); leavesRealtime.close(); @@ -2094,7 +2082,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async setTimeout(cb, 1000); }, function (cb) { - mainChannel.presence.get(function (err, members) { + whenPromiseSettles(mainChannel.presence.get(), function (err, members) { try { expect(members && members.length).to.equal(3, 'Check three expected members here'); } catch (err) { @@ -2107,7 +2095,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], function (err) { closeAndFinish(done, [mainRealtime, continuousRealtime, leavesRealtime], err); - } + }, ); }); @@ -2123,7 +2111,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async presence = channel.presence, numUpdates = 0; - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err) { closeAndFinish(done, client, err); } @@ -2134,7 +2122,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async async.timesSeries( 15, function (i, cb) { - presence.update(i.toString(), cb); + whenPromiseSettles(presence.update(i.toString()), cb); }, function (err) { if (err) { @@ -2151,174 +2139,9 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async } closeAndFinish(done, client); }, 1000); - } + }, ); }); }); - - if (typeof Promise !== 'undefined') { - describe('presence_promise', function () { - var options = { clientId: testClientId, promises: true }; - - it('enter_get', function (done) { - var client = helper.AblyRealtime(options); - var channel = client.channels.get('presence_promise_get'); - var testData = 'test_presence_data'; - - channel.presence - .subscribe(function () { - channel.presence - .get() - .then(function (members) { - expect(members.length).to.equal(1, 'Expect test client to be the only member present'); - expect(members[0].clientId).to.equal(testClientId, 'Expected test clientId to be correct'); - expect(members[0].data).to.equal(testData, 'Expected data to be correct'); - closeAndFinish(done, client); - }) - ['catch'](function (err) { - closeAndFinish(done, client, err); - }); - }) - .then(function () { - channel.presence.enter(testData)['catch'](function (err) { - closeAndFinish(done, client, err); - }); - }) - ['catch'](function (err) { - closeAndFinish(done, client, err); - }); - }); - - it('update', function (done) { - var client = helper.AblyRealtime(options); - var channel = client.channels.get('presence_promise_update'); - var testData1 = 'test_presence_data1'; - var testData2 = 'test_presence_data2'; - var idx = 0; - - channel.presence - .subscribe(function () { - if (idx === 0) { - idx++; - channel.presence - .get() - .then(function (members) { - expect(members.length).to.equal(1, 'Expect test client to be the only member present'); - expect(members[0].clientId).to.equal(testClientId, 'Expected test clientId to be correct'); - expect(members[0].data).to.equal(testData1, 'Expected data to be correct'); - channel.presence.update(testData2)['catch'](function (err) { - // If idx == 2 this means the update was succesful but the connection was closed before it was ACKed - if (idx !== 2) { - closeAndFinish(done, client, err); - } - }); - }) - ['catch'](function (err) { - closeAndFinish(done, client, err); - }); - } else { - idx++; - channel.presence - .get() - .then(function (members) { - expect(members.length).to.equal(1, 'Expect test client to be the only member present'); - expect(members[0].clientId).to.equal(testClientId, 'Expected test clientId to be correct'); - expect(members[0].data).to.equal(testData2, 'Expected data to be correct'); - closeAndFinish(done, client); - }) - ['catch'](function (err) { - closeAndFinish(done, client, err); - }); - } - }) - .then(function () { - channel.presence.enter(testData1)['catch'](function (err) { - closeAndFinish(done, client, err); - }); - }) - ['catch'](function (err) { - closeAndFinish(done, client, err); - }); - }); - - it('enterClient_leaveClient', function (done) { - var client = helper.AblyRealtime(options); - var channel = client.channels.get('presence_promise_leaveClient'); - var idx = 0; - - channel.presence - .subscribe(function () { - if (idx === 0) { - idx++; - channel.presence - .get() - .then(function (members) { - expect(members.length).to.equal(1, 'Expect test client to be the only member present'); - expect(members[0].clientId).to.equal(testClientId, 'Expected test clientId to be correct'); - channel.presence.leaveClient(testClientId)['catch'](function (err) { - // If idx == 2 this means the leave was succesful but the connection was closed before it was ACKed - if (idx !== 2) { - closeAndFinish(done, client, err); - } - }); - }) - ['catch'](function (err) { - closeAndFinish(done, client, err); - }); - } else { - idx++; - channel.presence - .get() - .then(function (members) { - expect(members.length).to.equal(0, 'Expect test client to no longer be present'); - closeAndFinish(done, client); - }) - ['catch'](function (err) { - closeAndFinish(done, client, err); - }); - } - }) - .then(function () { - channel.presence - .enterClient(testClientId) - .then(function () {}) - ['catch'](function (err) { - closeAndFinish(done, client, err); - }); - }) - ['catch'](function (err) { - closeAndFinish(done, client, err); - }); - }); - - it('history', function (done) { - var client = helper.AblyRealtime(options); - var channel = client.channels.get('presence_promise_history'); - channel.presence - .history() - .then(function (page) { - // Tests for promisified PaginatedResource exist elsewhere in the repo so are omitted here - closeAndFinish(done, client); - }) - ['catch'](function (err) { - closeAndFinish(done, client, err); - }); - }); - - it('subscribe', function (done) { - var client = helper.AblyRealtime(options); - var channel = client.channels.get('presence_promise_subscribe'); - channel.presence - .subscribe(function () {}) - .then(function () { - expect(channel.state).to.equal('attached'); - closeAndFinish(done, client); - }) - ['catch'](function (err) { - closeAndFinish(done, client, err); - }); - }); - }); - } }); }); diff --git a/test/realtime/reauth.test.js b/test/realtime/reauth.test.js index de5f1ab36d..65817dd8e1 100644 --- a/test/realtime/reauth.test.js +++ b/test/realtime/reauth.test.js @@ -6,6 +6,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { var rest; var mixin = helper.Utils.mixin; var displayError = helper.displayError; + var whenPromiseSettles = helper.whenPromiseSettles; describe('realtime/reauth', function () { this.timeout(60 * 1000); @@ -25,7 +26,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { function getToken(tokenParams) { return function (state, callback) { - rest.auth.requestToken(tokenParams, null, function (err, token) { + whenPromiseSettles(rest.auth.requestToken(tokenParams, null), function (err, token) { callback(err, mixin(state, { token: token })); }); }; @@ -72,7 +73,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { async.parallel( [ function (cb) { - state.realtime.auth.authorize(null, { token: state.token }, cb); + whenPromiseSettles(state.realtime.auth.authorize(null, { token: state.token }), cb); }, function (cb) { state.realtime.connection.on('update', function (stateChange) { @@ -82,7 +83,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { ], function (err) { callback(err, state); - } + }, ); }; } @@ -90,7 +91,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { function attach(channelName) { return function (state, callback) { var channel = state.realtime.channels.get(channelName); - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { callback(err, state); }); }; @@ -123,7 +124,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { 'waitChannelState: expected state not reached within 5s. Expected ' + expected + ', currently ' + - channel.state + channel.state, ); }, 5000); channel.once(function () { @@ -146,7 +147,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { function checkCantAttach(channelName) { return function (state, callback) { var channel = state.realtime.channels.get(channelName); - channel.attach(function (err) { + whenPromiseSettles(channel.attach(), function (err) { if (err && err.code === 40160) { callback(null, state); } else { @@ -159,7 +160,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { function checkCanPublish(channelName) { return function (state, callback) { var channel = state.realtime.channels.get(channelName); - channel.publish(null, null, function (err) { + whenPromiseSettles(channel.publish(null, null), function (err) { callback(err, state); }); }; @@ -168,7 +169,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { function checkCantPublish(channelName) { return function (state, callback) { var channel = state.realtime.channels.get(channelName); - channel.publish(null, null, function (err) { + whenPromiseSettles(channel.publish(null, null), function (err) { if (err && err.code === 40160) { callback(null, state); } else { diff --git a/test/realtime/resume.test.js b/test/realtime/resume.test.js index 31b1dab3c6..812db0f668 100644 --- a/test/realtime/resume.test.js +++ b/test/realtime/resume.test.js @@ -6,6 +6,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { var simulateDroppedConnection = helper.simulateDroppedConnection; var testOnAllTransports = helper.testOnAllTransports; var bestTransport = helper.bestTransport; + var whenPromiseSettles = helper.whenPromiseSettles; describe('realtime/resume', function () { this.timeout(120 * 1000); @@ -31,7 +32,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { receivingChannel.unsubscribe(event); callback(); }); - sendingChannel.publish(event, message, function (err) { + whenPromiseSettles(sendingChannel.publish(event, message), function (err) { if (err) callback(err); }); } @@ -51,7 +52,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { var rxCount = 0; function phase0(callback) { - rxChannel.attach(callback); + whenPromiseSettles(rxChannel.attach(), callback); } function phase1(callback) { @@ -137,15 +138,11 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { }); } - testOnAllTransports( - 'resume_inactive', - function (realtimeOpts) { - return function (done) { - resume_inactive(done, 'resume_inactive' + String(Math.random()), {}, realtimeOpts); - }; - }, - /* excludeUpgrade: */ true - ); + testOnAllTransports('resume_inactive', function (realtimeOpts) { + return function (done) { + resume_inactive(done, 'resume_inactive' + String(Math.random()), {}, realtimeOpts); + }; + }); /** * Simple resume case @@ -162,7 +159,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { var rxCount = 0; function phase0(callback) { - rxChannel.attach(callback); + whenPromiseSettles(rxChannel.attach(), callback); } function phase1(callback) { @@ -188,7 +185,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { var txCount = 0; function ph2TxOnce() { - txChannel.publish('sentWhileDisconnected', 'phase 2, message ' + txCount, function (err) { + whenPromiseSettles(txChannel.publish('sentWhileDisconnected', 'phase 2, message ' + txCount), function (err) { if (err) callback(err); }); if (++txCount == count) { @@ -258,15 +255,11 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { }); } - testOnAllTransports( - 'resume_active', - function (realtimeOpts) { - return function (done) { - resume_active(done, 'resume_active' + String(Math.random()), {}, realtimeOpts); - }; - }, - /* excludeUpgrade: */ true - ); + testOnAllTransports('resume_active', function (realtimeOpts) { + return function (done) { + resume_active(done, 'resume_active' + String(Math.random()), {}, realtimeOpts); + }; + }); /* RTN15c3 * Resume with loss of continuity @@ -291,7 +284,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { }, function (cb) { suspendedChannel.state = 'suspended'; - attachedChannel.attach(cb); + whenPromiseSettles(attachedChannel.attach(), cb); }, function (cb) { /* Sabotage the resume */ @@ -308,14 +301,14 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { try { expect(stateChange.reason && stateChange.reason.code).to.equal( 80018, - 'Unable to recover connection correctly set in the stateChange' + 'Unable to recover connection correctly set in the stateChange', ); expect(attachedChannel.state).to.equal('attaching', 'Attached channel went into attaching'); expect(suspendedChannel.state).to.equal('attaching', 'Suspended channel went into attaching'); expect(connection.connectionManager.msgSerial).to.equal(0, 'Check msgSerial is reset to 0'); expect( connection.connectionManager.connectionId !== 'ablyjs_tes', - 'Check connectionId is set by the new CONNECTED' + 'Check connectionId is set by the new CONNECTED', ).to.be.ok; } catch (err) { cb(err); @@ -327,11 +320,11 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { ], function (err) { closeAndFinish(done, realtime, err); - } + }, ); }; }, - true /* Use a fixed transport as attaches are resent when the transport changes */ + true /* Use a fixed transport as attaches are resent when the transport changes */, ); /* RTN15c5 @@ -353,7 +346,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { }); }, function (cb) { - realtime.auth.requestToken({ ttl: 1 }, null, function (err, token) { + whenPromiseSettles(realtime.auth.requestToken({ ttl: 1 }, null), function (err, token) { badtoken = token; cb(err); }); @@ -380,11 +373,11 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { ], function (err) { closeAndFinish(done, realtime, err); - } + }, ); }; }, - true + true, ); /* RTN15c4 @@ -432,11 +425,11 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { ], function (err) { closeAndFinish(done, realtime, err); - } + }, ); }; }, - true + true, ); /* RTL2f @@ -500,7 +493,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { ], function (err) { closeAndFinish(done, [realtime, realtimeTwo], err); - } + }, ); }); @@ -537,7 +530,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { ], function (err) { closeAndFinish(done, realtime, err); - } + }, ); }); @@ -546,13 +539,12 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { * connection was > connectionStateTtl ago */ it('no_resume_last_activity', function (done) { - /* Specify a best transport so that upgrade activity doesn't reset the last activity timer */ - var realtime = helper.AblyRealtime({ transports: [bestTransport] }), + var realtime = helper.AblyRealtime(), connection = realtime.connection, connectionManager = connection.connectionManager; connection.once('connected', function () { - connectionManager.lastActivity = helper.Utils.now() - 10000000; + connectionManager.lastActivity = Date.now() - 10000000; /* noop-out onProtocolMessage so that a DISCONNECTED message doesn't * reset the last activity timer */ connectionManager.activeProtocol.getTransport().onProtocolMessage = function () {}; @@ -584,7 +576,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { try { expect( JSON.stringify(testMessage) === JSON.stringify(message.data), - 'Check rewound message.data' + 'Check rewound message.data', ).to.be.ok; } catch (err) { closeAndFinish(done, [sender_realtime, receiver_realtime], err); @@ -607,7 +599,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { closeAndFinish( done, [sender_realtime, receiver_realtime, resumed_receiver_realtime], - new Error('Rewound message arrived on attach resume') + new Error('Rewound message arrived on attach resume'), ); }); @@ -634,7 +626,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { { transports: [helper.bestTransport], }, - true + true, ); const channelNames = Array(5) @@ -643,7 +635,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { const rxChannels = channelNames.map((name) => rxRealtime.channels.get(name)); function attachChannels(callback) { - async.each(rxChannels, (channel, cb) => channel.attach(cb), callback); + async.each(rxChannels, (channel, cb) => whenPromiseSettles(channel.attach(), cb), callback); } function publishSubscribeWhileConnectedOnce(callback) { @@ -654,7 +646,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { const rx = rxRealtime.channels.get(name); sendAndAwait(null, tx, rx, cb); }, - callback + callback, ); } @@ -664,7 +656,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { (_, cb) => { publishSubscribeWhileConnectedOnce(cb); }, - callback + callback, ); } @@ -673,9 +665,9 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { channelNames, (name, cb) => { const tx = txRest.channels.get(name); - tx.publish('sentWhileDisconnected', null, cb); + whenPromiseSettles(tx.publish('sentWhileDisconnected', null), cb); }, - callback + callback, ); } @@ -685,7 +677,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { (_, cb) => { publishSubscribeWhileDisconnectedOnce(cb); }, - callback + callback, ); } @@ -707,7 +699,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { } }); }, - callback + callback, ); } diff --git a/test/realtime/sync.test.js b/test/realtime/sync.test.js index 5469e2fe42..ac2d1496df 100644 --- a/test/realtime/sync.test.js +++ b/test/realtime/sync.test.js @@ -5,8 +5,9 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var displayError = helper.displayError; var utils = helper.Utils; var closeAndFinish = helper.closeAndFinish; - var createPM = Ably.Realtime.ProtocolMessage.fromDeserialized; + var createPM = Ably.protocolMessageFromDeserialized; var monitorConnection = helper.monitorConnection; + var whenPromiseSettles = helper.whenPromiseSettles; describe('realtime/sync', function () { this.timeout(60 * 1000); @@ -21,15 +22,15 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); function extractClientIds(presenceSet) { - return utils - .arrMap(presenceSet, function (presmsg) { + return presenceSet + .map(function (presmsg) { return presmsg.clientId; }) .sort(); } function extractMember(presenceSet, clientId) { - return helper.arrFind(presenceSet, function (member) { + return presenceSet.find(function (member) { return member.clientId === clientId; }); } @@ -42,123 +43,141 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async * sync in progress, then do one sync, then a second with a slightly * different presence set */ - it('sync_existing_set', function (done) { + it('sync_existing_set', async function () { var realtime = helper.AblyRealtime({ autoConnect: false }), channelName = 'syncexistingset', channel = realtime.channels.get(channelName); - channel.onMessage( + await channel.processMessage( createPM({ action: 11, channel: channelName, flags: 1, - }) + }), ); - async.series( - [ - function (cb) { - channel.onMessage({ - action: 16, - channel: channelName, - presence: [ - { - action: 'present', - clientId: 'one', - connectionId: 'one_connid', - id: 'one_connid:0:0', - timestamp: 1e12, - }, - { - action: 'present', - clientId: 'two', - connectionId: 'two_connid', - id: 'two_connid:0:0', - timestamp: 1e12, - }, - ], - }); - cb(); - }, - function (cb) { - channel.presence.get(function (err, results) { - try { - expect(results.length).to.equal(2, 'Check correct number of results'); - expect(channel.presence.syncComplete, 'Check in sync').to.be.ok; - expect(extractClientIds(results)).to.deep.equal(['one', 'two'], 'check correct members'); - } catch (err) { + await new Promise(function (resolve, reject) { + var done = function (err) { + err ? reject(err) : resolve(); + }; + + async.series( + [ + function (cb) { + channel + .processMessage({ + action: 16, + channel: channelName, + presence: [ + { + action: 'present', + clientId: 'one', + connectionId: 'one_connid', + id: 'one_connid:0:0', + timestamp: 1e12, + }, + { + action: 'present', + clientId: 'two', + connectionId: 'two_connid', + id: 'two_connid:0:0', + timestamp: 1e12, + }, + ], + }) + .then(function () { + cb(); + }) + .catch(function (err) { + cb(err); + }); + }, + function (cb) { + whenPromiseSettles(channel.presence.get(), function (err, results) { + try { + expect(results.length).to.equal(2, 'Check correct number of results'); + expect(channel.presence.syncComplete, 'Check in sync').to.be.ok; + expect(extractClientIds(results)).to.deep.equal(['one', 'two'], 'check correct members'); + } catch (err) { + cb(err); + return; + } cb(err); - return; - } - cb(err); - }); - }, - function (cb) { - /* Trigger another sync. Two has gone without so much as a `leave` message! */ - channel.onMessage({ - action: 16, - channel: channelName, - presence: [ - { - action: 'present', - clientId: 'one', - connectionId: 'one_connid', - id: 'one_connid:0:0', - timestamp: 1e12, - }, - { - action: 'present', - clientId: 'three', - connectionId: 'three_connid', - id: 'three_connid:0:0', - timestamp: 1e12, - }, - ], - }); - cb(); - }, - function (cb) { - channel.presence.get(function (err, results) { - try { - expect(results.length).to.equal(2, 'Check correct number of results'); - expect(channel.presence.syncComplete, 'Check in sync').to.be.ok; - expect(extractClientIds(results)).to.deep.equal( - ['one', 'three'], - 'check two has gone and three is there' - ); - } catch (err) { + }); + }, + function (cb) { + /* Trigger another sync. Two has gone without so much as a `leave` message! */ + channel + .processMessage({ + action: 16, + channel: channelName, + presence: [ + { + action: 'present', + clientId: 'one', + connectionId: 'one_connid', + id: 'one_connid:0:0', + timestamp: 1e12, + }, + { + action: 'present', + clientId: 'three', + connectionId: 'three_connid', + id: 'three_connid:0:0', + timestamp: 1e12, + }, + ], + }) + .then(function () { + cb(); + }) + .catch(function (err) { + cb(err); + }); + }, + function (cb) { + whenPromiseSettles(channel.presence.get(), function (err, results) { + try { + expect(results.length).to.equal(2, 'Check correct number of results'); + expect(channel.presence.syncComplete, 'Check in sync').to.be.ok; + expect(extractClientIds(results)).to.deep.equal( + ['one', 'three'], + 'check two has gone and three is there', + ); + } catch (err) { + cb(err); + return; + } cb(err); - return; - } - cb(err); - }); + }); + }, + ], + function (err) { + closeAndFinish(done, realtime, err); }, - ], - function (err) { - closeAndFinish(done, realtime, err); - } - ); + ); + }); }); /* * Sync with an existing presence set and a presence member added in the * middle of the sync should should discard the former, but not the latter * */ - it('sync_member_arrives_in_middle', function (done) { + it('sync_member_arrives_in_middle', async function () { var realtime = helper.AblyRealtime({ autoConnect: false }), channelName = 'sync_member_arrives_in_middle', channel = realtime.channels.get(channelName); - channel.onMessage( + await channel.processMessage( createPM({ action: 11, channel: channelName, flags: 1, - }) + }), ); /* First sync */ - channel.onMessage({ + await channel.processMessage({ action: 16, channel: channelName, presence: [ @@ -173,7 +192,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); /* A second sync, this time in multiple parts, with a presence message in the middle */ - channel.onMessage({ + await channel.processMessage({ action: 16, channel: channelName, channelSerial: 'serial:cursor', @@ -188,7 +207,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], }); - channel.onMessage({ + await channel.processMessage({ action: 14, channel: channelName, presence: [ @@ -202,7 +221,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], }); - channel.onMessage({ + await channel.processMessage({ action: 16, channel: channelName, channelSerial: 'serial:', @@ -217,40 +236,49 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], }); - channel.presence.get(function (err, results) { - if (err) { - closeAndFinish(done, realtime, err); - return; - } - try { - expect(results.length).to.equal(3, 'Check correct number of results'); - expect(channel.presence.syncComplete, 'Check in sync').to.be.ok; - expect(extractClientIds(results)).to.deep.equal(['four', 'three', 'two'], 'check expected presence members'); - } catch (err) { - closeAndFinish(done, realtime, err); - return; - } - closeAndFinish(done, realtime); + await new Promise(function (resolve, reject) { + var done = function (err) { + err ? reject(err) : resolve(); + }; + + whenPromiseSettles(channel.presence.get(), function (err, results) { + if (err) { + closeAndFinish(done, realtime, err); + return; + } + try { + expect(results.length).to.equal(3, 'Check correct number of results'); + expect(channel.presence.syncComplete, 'Check in sync').to.be.ok; + expect(extractClientIds(results)).to.deep.equal( + ['four', 'three', 'two'], + 'check expected presence members', + ); + } catch (err) { + closeAndFinish(done, realtime, err); + return; + } + closeAndFinish(done, realtime); + }); }); }); /* * Presence message that was in the sync arrives again as a normal message, after it's come in the sync */ - it('sync_member_arrives_normally_after_came_in_sync', function (done) { + it('sync_member_arrives_normally_after_came_in_sync', async function () { var realtime = helper.AblyRealtime({ autoConnect: false }), channelName = 'sync_member_arrives_normally_after_came_in_sync', channel = realtime.channels.get(channelName); - channel.onMessage( + await channel.processMessage( createPM({ action: 11, channel: channelName, flags: 1, - }) + }), ); - channel.onMessage({ + await channel.processMessage({ action: 16, channel: channelName, channelSerial: 'serial:cursor', @@ -265,7 +293,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], }); - channel.onMessage({ + await channel.processMessage({ action: 14, channel: channelName, presence: [ @@ -279,7 +307,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], }); - channel.onMessage({ + await channel.processMessage({ action: 16, channel: channelName, channelSerial: 'serial:', @@ -294,40 +322,46 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], }); - channel.presence.get(function (err, results) { - if (err) { - closeAndFinish(done, realtime, err); - return; - } - try { - expect(results.length).to.equal(2, 'Check correct number of results'); - expect(channel.presence.syncComplete, 'Check in sync').to.be.ok; - expect(extractClientIds(results)).to.deep.equal(['one', 'two'], 'check expected presence members'); - } catch (err) { - closeAndFinish(done, realtime, err); - return; - } - closeAndFinish(done, realtime); + await new Promise(function (resolve, reject) { + var done = function (err) { + err ? reject(err) : resolve(); + }; + + whenPromiseSettles(channel.presence.get(), function (err, results) { + if (err) { + closeAndFinish(done, realtime, err); + return; + } + try { + expect(results.length).to.equal(2, 'Check correct number of results'); + expect(channel.presence.syncComplete, 'Check in sync').to.be.ok; + expect(extractClientIds(results)).to.deep.equal(['one', 'two'], 'check expected presence members'); + } catch (err) { + closeAndFinish(done, realtime, err); + return; + } + closeAndFinish(done, realtime); + }); }); }); /* * Presence message that will be in the sync arrives as a normal message, before it comes in the sync */ - it('sync_member_arrives_normally_before_comes_in_sync', function (done) { + it('sync_member_arrives_normally_before_comes_in_sync', async function () { var realtime = helper.AblyRealtime({ autoConnect: false }), channelName = 'sync_member_arrives_normally_before_comes_in_sync', channel = realtime.channels.get(channelName); - channel.onMessage( + await channel.processMessage( createPM({ action: 11, channel: channelName, flags: 1, - }) + }), ); - channel.onMessage({ + await channel.processMessage({ action: 16, channel: channelName, channelSerial: 'serial:cursor', @@ -342,7 +376,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], }); - channel.onMessage({ + await channel.processMessage({ action: 14, channel: channelName, presence: [ @@ -356,7 +390,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], }); - channel.onMessage({ + await channel.processMessage({ action: 16, channel: channelName, channelSerial: 'serial:', @@ -371,20 +405,26 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], }); - channel.presence.get(function (err, results) { - if (err) { - closeAndFinish(done, realtime, err); - return; - } - try { - expect(results.length).to.equal(2, 'Check correct number of results'); - expect(channel.presence.syncComplete, 'Check in sync').to.be.ok; - expect(extractClientIds(results)).to.deep.equal(['one', 'two'], 'check expected presence members'); - } catch (err) { - closeAndFinish(done, realtime, err); - return; - } - closeAndFinish(done, realtime); + await new Promise(function (resolve, reject) { + var done = function (err) { + err ? reject(err) : resolve(); + }; + + whenPromiseSettles(channel.presence.get(), function (err, results) { + if (err) { + closeAndFinish(done, realtime, err); + return; + } + try { + expect(results.length).to.equal(2, 'Check correct number of results'); + expect(channel.presence.syncComplete, 'Check in sync').to.be.ok; + expect(extractClientIds(results)).to.deep.equal(['one', 'two'], 'check expected presence members'); + } catch (err) { + closeAndFinish(done, realtime, err); + return; + } + closeAndFinish(done, realtime); + }); }); }); @@ -392,20 +432,20 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async * Get several presence messages with various combinations of msgserial, * index, and synthesized leaves, check that the end result is correct */ - it('presence_ordering', function (done) { + it('presence_ordering', async function () { var realtime = helper.AblyRealtime({ autoConnect: false }), channelName = 'sync_ordering', channel = realtime.channels.get(channelName); - channel.onMessage( + await channel.processMessage( createPM({ action: 11, channel: channelName, - }) + }), ); /* One enters */ - channel.onMessage({ + await channel.processMessage({ action: 14, channel: channelName, id: 'one_connid:1', @@ -420,7 +460,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); /* An earlier leave from one (should be ignored) */ - channel.onMessage({ + await channel.processMessage({ action: 14, channel: channelName, connectionId: 'one_connid', @@ -435,7 +475,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); /* One adds some data in a newer msgSerial */ - channel.onMessage({ + await channel.processMessage({ action: 14, channel: channelName, connectionId: 'one_connid', @@ -451,7 +491,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); /* Two enters */ - channel.onMessage({ + await channel.processMessage({ action: 14, channel: channelName, connectionId: 'two_connid', @@ -466,7 +506,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); /* Two updates twice in the same message */ - channel.onMessage({ + await channel.processMessage({ action: 14, channel: channelName, connectionId: 'two_connid', @@ -487,7 +527,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); /* Three enters */ - channel.onMessage({ + await channel.processMessage({ action: 14, channel: channelName, connectionId: 'three_connid', @@ -503,7 +543,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async /* Synthesized leave for three (with earlier msgSerial, incompatible id, * and later timestamp) */ - channel.onMessage({ + await channel.processMessage({ action: 14, channel: channelName, connectionId: 'synthesized', @@ -518,110 +558,112 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ], }); - channel.presence.get(function (err, results) { - if (err) { - closeAndFinish(done, realtime, err); - return; - } - try { - expect(results.length).to.equal(2, 'Check correct number of results'); - expect(channel.presence.syncComplete, 'Check in sync').to.be.ok; - expect(extractClientIds(results)).to.deep.equal(['one', 'two'], 'check expected presence members'); - expect(extractMember(results, 'one').data).to.equal('onedata', 'check correct data on one'); - expect(extractMember(results, 'two').data).to.equal('twodata', 'check correct data on two'); - } catch (err) { - closeAndFinish(done, realtime, err); - return; - } - closeAndFinish(done, realtime); + await new Promise(function (resolve, reject) { + var done = function (err) { + err ? reject(err) : resolve(); + }; + whenPromiseSettles(channel.presence.get(), function (err, results) { + if (err) { + closeAndFinish(done, realtime, err); + return; + } + try { + expect(results.length).to.equal(2, 'Check correct number of results'); + expect(channel.presence.syncComplete, 'Check in sync').to.be.ok; + expect(extractClientIds(results)).to.deep.equal(['one', 'two'], 'check expected presence members'); + expect(extractMember(results, 'one').data).to.equal('onedata', 'check correct data on one'); + expect(extractMember(results, 'two').data).to.equal('twodata', 'check correct data on two'); + } catch (err) { + closeAndFinish(done, realtime, err); + return; + } + closeAndFinish(done, realtime); + }); }); }); - /* JSONP can't cope with entering 110 people in one go. */ - if (helper.bestTransport !== 'jsonp') { - /* - * Do a 110-member sync, so split into two sync messages. Inject a normal - * presence enter between the syncs. Check everything was entered correctly - */ - it('presence_sync_interruptus', function (done) { - var channelName = 'presence_sync_interruptus'; - var interrupterClientId = 'dark_horse'; - var enterer = helper.AblyRealtime(); - var syncer = helper.AblyRealtime(); - var entererChannel = enterer.channels.get(channelName); - var syncerChannel = syncer.channels.get(channelName); - - function waitForBothConnect(cb) { - async.parallel( - [ - function (connectCb) { - enterer.connection.on('connected', connectCb); - }, - function (connectCb) { - syncer.connection.on('connected', connectCb); - }, - ], - function () { - cb(); - } - ); - } - - async.series( + /* + * Do a 110-member sync, so split into two sync messages. Inject a normal + * presence enter between the syncs. Check everything was entered correctly + */ + it('presence_sync_interruptus', function (done) { + var channelName = 'presence_sync_interruptus'; + var interrupterClientId = 'dark_horse'; + var enterer = helper.AblyRealtime(); + var syncer = helper.AblyRealtime(); + var entererChannel = enterer.channels.get(channelName); + var syncerChannel = syncer.channels.get(channelName); + + function waitForBothConnect(cb) { + async.parallel( [ - waitForBothConnect, - function (cb) { - entererChannel.attach(cb); + function (connectCb) { + enterer.connection.on('connected', connectCb); }, - function (cb) { - async.times( - 110, - function (i, presCb) { - entererChannel.presence.enterClient(i.toString(), null, presCb); - }, - cb - ); - }, - function (cb) { - var originalOnMessage = syncerChannel.onMessage; - syncerChannel.onMessage = function (message) { - originalOnMessage.apply(this, arguments); - /* Inject an additional presence message after the first sync */ - if (message.action === 16) { - syncerChannel.onMessage = originalOnMessage; - syncerChannel.onMessage({ - action: 14, - id: 'messageid:0', - connectionId: 'connid', - timestamp: 2000000000000, - presence: [ - { - clientId: interrupterClientId, - action: 'enter', - }, - ], - }); - } - }; - syncerChannel.attach(cb); - }, - function (cb) { - syncerChannel.presence.get(function (err, presenceSet) { - try { - expect(presenceSet && presenceSet.length).to.equal(111, 'Check everyone’s in presence set'); - } catch (err) { - cb(err); - return; - } - cb(err); - }); + function (connectCb) { + syncer.connection.on('connected', connectCb); }, ], - function (err) { - closeAndFinish(done, [enterer, syncer], err); - } + function () { + cb(); + }, ); - }); - } + } + + async.series( + [ + waitForBothConnect, + function (cb) { + whenPromiseSettles(entererChannel.attach(), cb); + }, + function (cb) { + async.times( + 110, + function (i, presCb) { + whenPromiseSettles(entererChannel.presence.enterClient(i.toString(), null), presCb); + }, + cb, + ); + }, + function (cb) { + var originalProcessMessage = syncerChannel.processMessage; + syncerChannel.processMessage = async function (message) { + await originalProcessMessage.apply(this, arguments); + /* Inject an additional presence message after the first sync */ + if (message.action === 16) { + syncerChannel.processMessage = originalProcessMessage; + await syncerChannel.processMessage({ + action: 14, + id: 'messageid:0', + connectionId: 'connid', + timestamp: 2000000000000, + presence: [ + { + clientId: interrupterClientId, + action: 'enter', + }, + ], + }); + } + }; + whenPromiseSettles(syncerChannel.attach(), cb); + }, + function (cb) { + whenPromiseSettles(syncerChannel.presence.get(), function (err, presenceSet) { + try { + expect(presenceSet && presenceSet.length).to.equal(111, 'Check everyone’s in presence set'); + } catch (err) { + cb(err); + return; + } + cb(err); + }); + }, + ], + function (err) { + closeAndFinish(done, [enterer, syncer], err); + }, + ); + }); }); }); diff --git a/test/realtime/transports.test.js b/test/realtime/transports.test.js new file mode 100644 index 0000000000..155e2d563c --- /dev/null +++ b/test/realtime/transports.test.js @@ -0,0 +1,230 @@ +'use strict'; + +define(['shared_helper', 'async', 'chai', 'ably'], function (helper, async, chai, Ably) { + const expect = chai.expect; + const closeAndFinish = helper.closeAndFinish; + const monitorConnection = helper.monitorConnection; + const whenPromiseSettles = helper.whenPromiseSettles; + const Defaults = Ably.Rest.Platform.Defaults; + const originialWsCheckUrl = Defaults.wsConnectivityUrl; + const transportPreferenceName = 'ably-transport-preference'; + const localStorageSupported = globalThis.localStorage; + const urlScheme = 'https://'; + const echoServer = 'echo.ably.io'; + const failUrl = urlScheme + echoServer + '/respondwith?status=500'; + const availableTransports = helper.availableTransports; + const defaultTransports = new Ably.Realtime({ key: 'xxx:yyy', autoConnect: false }).connection.connectionManager + .transports; + const baseTransport = new Ably.Realtime({ key: 'xxx:yyy', autoConnect: false, transports: availableTransports }) + .connection.connectionManager.baseTransport; + const mixin = helper.Utils.mixin; + + function restoreWsConnectivityUrl() { + Defaults.wsConnectivityUrl = originialWsCheckUrl; + } + + const Config = Ably.Rest.Platform.Config; + const oldWs = Config.WebSocket; + + function restoreWebSocketConstructor() { + Config.WebSocket = oldWs; + } + + // drop in replacement for WebSocket which doesn't emit any events (same behaviour as when WebSockets upgrade headers are removed by a proxy) + class FakeWebSocket { + close() {} + } + + describe('realtime/transports', function () { + this.timeout(60 * 1000); + + before(function (done) { + helper.setupApp(function (err) { + if (err) { + done(err); + return; + } + done(); + }); + }); + + afterEach(restoreWsConnectivityUrl); + afterEach(restoreWebSocketConstructor); + + if (helper.availableTransports.length > 1) { + // ensure comet transport is used for nodejs tests + function options(opts) { + return mixin( + { + transports: helper.availableTransports, + }, + opts || {}, + ); + } + + it('websocket_is_default', function (done) { + const realtime = helper.AblyRealtime(options()); + + realtime.connection.on('connected', function () { + try { + expect(realtime.connection.connectionManager.activeProtocol.transport.shortName).to.equal('web_socket'); + } catch (err) { + closeAndFinish(done, realtime, err); + } + closeAndFinish(done, realtime); + }); + + monitorConnection(done, realtime); + }); + + it('no_ws_connectivity', function (done) { + Config.WebSocket = FakeWebSocket; + const realtime = helper.AblyRealtime(options({ webSocketSlowTimeout: 1000, webSocketConnectTimeout: 3000 })); + + realtime.connection.on('connected', function () { + try { + expect(realtime.connection.connectionManager.activeProtocol.transport.shortName).to.equal(baseTransport); + // check that transport preference is set + if (localStorageSupported) { + expect(window.localStorage.getItem(transportPreferenceName)).to.equal( + JSON.stringify({ value: baseTransport }), + ); + } + } catch (err) { + closeAndFinish(done, realtime, err); + } + closeAndFinish(done, realtime); + }); + + monitorConnection(done, realtime); + }); + + it('ws_primary_host_fails', function (done) { + const goodHost = helper.AblyRest().options.realtimeHost; + const realtime = helper.AblyRealtime( + options({ realtimeHost: helper.unroutableAddress, fallbackHosts: [goodHost] }), + ); + + realtime.connection.on('connected', function () { + expect(realtime.connection.connectionManager.activeProtocol.transport.shortName).to.equal('web_socket'); + closeAndFinish(done, realtime); + }); + + monitorConnection(done, realtime); + }); + + it('no_internet_connectivity', function (done) { + Config.WebSocket = FakeWebSocket; + const realtime = helper.AblyRealtime(options({ connectivityCheckUrl: failUrl, webSocketSlowTimeout: 1000 })); + + // expect client to transition to disconnected rather than attempting base transport (which would succeed in this instance) + realtime.connection.on('disconnected', function () { + closeAndFinish(done, realtime); + }); + }); + + it('no_websocket_or_base_transport', function (done) { + Config.WebSocket = FakeWebSocket; + const realtime = helper.AblyRealtime({ + transports: ['web_socket'], + realtimeRequestTimeout: 3000, + webSocketConnectTimeout: 3000, + }); + + realtime.connection.on('disconnected', function () { + closeAndFinish(done, realtime); + }); + }); + + if (localStorageSupported) { + it('base_transport_preference', function (done) { + window.localStorage.setItem(transportPreferenceName, JSON.stringify({ value: baseTransport })); + const realtime = helper.AblyRealtime(options()); + + // make ws connectivity check only resolve after connected with base transport. + // prevents a race condition where the wsConnectivity check succeeds before base transport is activated; + // in this case the base transport would be abandoned in favour of websocket + realtime.connection.connectionManager.checkWsConnectivity = function () { + return new Promise((resolve) => { + realtime.connection.once('connected', () => { + resolve(); + }); + }); + }; + + realtime.connection.on('connected', function () { + try { + expect(realtime.connection.connectionManager.activeProtocol.transport.shortName).to.equal(baseTransport); + } catch (err) { + closeAndFinish(done, realtime, err); + } + closeAndFinish(done, realtime); + }); + monitorConnection(done, realtime); + }); + + it('transport_preference_reset_while_connecting', function (done) { + window.localStorage.setItem(transportPreferenceName, JSON.stringify({ value: baseTransport })); + const realtime = helper.AblyRealtime(options()); + + // make ws connectivity check fast so that it succeeds while base transport is still connecting + realtime.connection.connectionManager.checkWsConnectivity = function () { + return new Promise((resolve) => { + setTimeout(() => resolve(), 1); + }); + }; + + realtime.connection.once('connected', function () { + try { + expect(realtime.connection.connectionManager.activeProtocol.transport.shortName).to.equal('web_socket'); + expect(realtime.connection.connectionManager.getTransportPreference()).to.equal('web_socket'); + } catch (err) { + closeAndFinish(done, realtime, err); + return; + } + closeAndFinish(done, realtime); + }); + monitorConnection(done, realtime); + }); + + it('transport_preference_reset_after_connected', function (done) { + window.localStorage.setItem(transportPreferenceName, JSON.stringify({ value: baseTransport })); + const realtime = helper.AblyRealtime(options()); + + // make ws connectivity check only resolve after connected with base transport + realtime.connection.connectionManager.checkWsConnectivity = function () { + return new Promise((resolve) => { + realtime.connection.once('connected', () => { + try { + expect(realtime.connection.connectionManager.activeProtocol.transport.shortName).to.equal( + baseTransport, + ); + resolve(); + } catch (err) { + closeAndFinish(done, realtime, err); + return; + } + }); + }); + }; + + realtime.connection.once('connected', function () { + // the checkWsConnectivity promise won't execute .then callbacks synchronously upon resolution + // so we need to wait one tick before the transport preference is unpersisted + setTimeout(() => { + try { + // ensure base transport preference is erased + expect(realtime.connection.connectionManager.getTransportPreference()).to.equal(null); + } catch (err) { + closeAndFinish(done, realtime, err); + return; + } + closeAndFinish(done, realtime); + }, 0); + }); + monitorConnection(done, realtime); + }); + } + } + }); +}); diff --git a/test/realtime/upgrade.test.js b/test/realtime/upgrade.test.js deleted file mode 100644 index 08c785e4b5..0000000000 --- a/test/realtime/upgrade.test.js +++ /dev/null @@ -1,711 +0,0 @@ -'use strict'; - -define(['shared_helper', 'async', 'chai', 'ably'], function (helper, async, chai, Ably) { - var expect = chai.expect; - var rest; - var publishIntervalHelper = function (currentMessageNum, channel, dataFn, onPublish) { - return function (currentMessageNum) { - channel.publish('event0', dataFn(), function () { - onPublish(); - }); - }; - }; - var publishAtIntervals = function (numMessages, channel, dataFn, onPublish) { - for (var i = numMessages; i > 0; i--) { - setTimeout(publishIntervalHelper(i, channel, dataFn, onPublish), 2 * i); - } - }; - var closeAndFinish = helper.closeAndFinish; - var monitorConnection = helper.monitorConnection; - var bestTransport = helper.bestTransport; - - if (bestTransport === 'web_socket') { - describe('realtime/upgrade', function () { - this.timeout(60 * 1000); - - before(function (done) { - helper.setupApp(function (err) { - if (err) { - done(err); - return; - } - rest = helper.AblyRest(); - done(); - }); - }); - - afterEach(helper.clearTransportPreference); - - /* - * Publish once with REST, before upgrade, verify message received - */ - it('publishpreupgrade', function (done) { - var transportOpts = { useBinaryProtocol: true, transports: helper.availableTransports }; - try { - var realtime = helper.AblyRealtime(transportOpts); - /* connect and attach */ - realtime.connection.on('connected', function () { - //console.log('publishpreupgrade: connected'); - var testMsg = 'Hello world'; - var rtChannel = realtime.channels.get('publishpreupgrade'); - rtChannel.attach(function (err) { - if (err) { - closeAndFinish(done, realtime, err); - return; - } - - /* subscribe to event */ - rtChannel.subscribe('event0', function (msg) { - try { - expect(msg.data).to.equal(testMsg, 'Unexpected msg text received'); - } catch (err) { - closeAndFinish(done, realtime, err); - return; - } - closeAndFinish(done, realtime); - }); - - /* publish event */ - var restChannel = rest.channels.get('publishpreupgrade'); - restChannel.publish('event0', testMsg, function (err) { - if (err) { - closeAndFinish(done, realtime, err); - } - }); - }); - }); - monitorConnection(done, realtime); - } catch (err) { - closeAndFinish(done, realtime, err); - } - }); - - /* - * Publish once with REST, after upgrade, verify message received on active transport - */ - it('publishpostupgrade0', function (done) { - var transportOpts = { useBinaryProtocol: true, transports: helper.availableTransports }; - try { - var realtime = helper.AblyRealtime(transportOpts); - - /* subscribe to event */ - var rtChannel = realtime.channels.get('publishpostupgrade0'); - rtChannel.subscribe('event0', function (msg) { - try { - expect(msg.data).to.equal(testMsg, 'Unexpected msg text received'); - } catch (err) { - closeAndFinish(done, realtime, err); - return; - } - var closeFn = function () { - closeAndFinish(done, realtime); - }; - if (isBrowser) setTimeout(closeFn, 0); - else process.nextTick(closeFn); - }); - - /* publish event */ - var testMsg = 'Hello world'; - var restChannel = rest.channels.get('publishpostupgrade0'); - var connectionManager = realtime.connection.connectionManager; - connectionManager.on('transport.active', function (transport) { - //console.log('publishpostupgrade0: transport active: transport = ' + transport); - if (transport.toString().match(/wss?\:/)) { - if (rtChannel.state == 'attached') { - //console.log('*** publishpostupgrade0: publishing (channel attached on transport active) ...'); - restChannel.publish('event0', testMsg, function (err) { - //console.log('publishpostupgrade0: publish returned err = ' + displayError(err)); - if (err) { - closeAndFinish(done, realtime, err); - } - }); - } else { - rtChannel.on('attached', function () { - //console.log('*** publishpostupgrade0: publishing (channel attached after wait) ...'); - restChannel.publish('event0', testMsg, function (err) { - //console.log('publishpostupgrade0: publish returned err = ' + displayError(err)); - if (err) { - closeAndFinish(done, realtime, err); - } - }); - }); - } - } - }); - monitorConnection(done, realtime); - } catch (err) { - closeAndFinish(done, realtime, err); - } - }); - - /* - * Publish once with REST, after upgrade, verify message not received on inactive transport - */ - it('publishpostupgrade1', function (done) { - var transportOpts = { useBinaryProtocol: true, transports: helper.availableTransports }; - try { - var realtime = helper.AblyRealtime(transportOpts); - - /* subscribe to event */ - var rtChannel = realtime.channels.get('publishpostupgrade1'); - rtChannel.subscribe('event0', function (msg) { - try { - expect(msg.data).to.equal(testMsg, 'Unexpected msg text received'); - } catch (err) { - closeAndFinish(done, realtime, err); - return; - } - var closeFn = function () { - closeAndFinish(done, realtime); - }; - if (isBrowser) setTimeout(closeFn, 0); - else process.nextTick(closeFn); - }); - - /* publish event */ - var testMsg = 'Hello world'; - var restChannel = rest.channels.get('publishpostupgrade1'); - var connectionManager = realtime.connection.connectionManager; - connectionManager.on('transport.active', function (transport) { - if (helper.isComet(transport)) { - /* override the processing of incoming messages on this channel - * so we can see if a message arrived. - * NOTE: this relies on knowledge of the internal implementation - * of the transport */ - - var originalOnProtocolMessage = transport.onProtocolMessage; - transport.onProtocolMessage = function (message) { - if (message.messages) { - closeAndFinish(done, realtime, new Error('Message received on comet transport')); - return; - } - originalOnProtocolMessage.apply(this, arguments); - }; - } - }); - connectionManager.on('transport.active', function (transport) { - if (helper.isWebsocket(transport)) { - if (rtChannel.state == 'attached') { - //console.log('*** publishing (channel attached on transport active) ...'); - restChannel.publish('event0', testMsg); - } else { - rtChannel.on('attached', function () { - //console.log('*** publishing (channel attached after wait) ...'); - restChannel.publish('event0', testMsg); - }); - } - } - }); - - monitorConnection(done, realtime); - } catch (err) { - closeAndFinish(done, realtime, err); - } - }); - - /** - * Publish and subscribe, text protocol - */ - it('upgradepublish0', function (done) { - var count = 10; - var cbCount = 10; - var checkFinish = function () { - if (count <= 0 && cbCount <= 0) { - closeAndFinish(done, realtime); - } - }; - var onPublish = function () { - --cbCount; - checkFinish(); - }; - var transportOpts = { useBinaryProtocol: false, transports: helper.availableTransports }; - var realtime = helper.AblyRealtime(transportOpts); - var channel = realtime.channels.get('upgradepublish0'); - /* subscribe to event */ - channel.subscribe( - 'event0', - function () { - --count; - checkFinish(); - }, - function () { - var dataFn = function () { - return 'Hello world at: ' + new Date(); - }; - publishAtIntervals(count, channel, dataFn, onPublish); - } - ); - }); - - /** - * Publish and subscribe, binary protocol - */ - it('upgradepublish1', function (done) { - var count = 10; - var cbCount = 10; - var checkFinish = function () { - if (count <= 0 && cbCount <= 0) { - closeAndFinish(done, realtime); - } - }; - var onPublish = function () { - --cbCount; - checkFinish(); - }; - var transportOpts = { useBinaryProtocol: true, transports: helper.availableTransports }; - var realtime = helper.AblyRealtime(transportOpts); - var channel = realtime.channels.get('upgradepublish1'); - /* subscribe to event */ - channel.subscribe( - 'event0', - function () { - --count; - checkFinish(); - }, - function () { - var dataFn = function () { - return 'Hello world at: ' + new Date(); - }; - publishAtIntervals(count, channel, dataFn, onPublish); - } - ); - }); - - /* - * Base upgrade case - */ - it('upgradebase0', function (done) { - var transportOpts = { useBinaryProtocol: true, transports: helper.availableTransports }; - var cometDeactivated = false; - try { - var realtime = helper.AblyRealtime(transportOpts); - /* check that we see the transport we're interested in get activated, - * and that we see the comet transport deactivated */ - var failTimer = setTimeout(function () { - closeAndFinish(done, realtime, new Error('upgrade heartbeat failed (timer expired)')); - }, 120000); - - var connectionManager = realtime.connection.connectionManager; - connectionManager.once('transport.inactive', function (transport) { - if (transport.toString().indexOf('/comet/') > -1) cometDeactivated = true; - }); - connectionManager.on('transport.active', function (transport) { - if (transport.toString().match(/wss?\:/)) { - clearTimeout(failTimer); - var closeFn = function () { - try { - expect(cometDeactivated).to.be.ok; - } catch (err) { - closeAndFinish(done, realtime, err); - return; - } - closeAndFinish(done, realtime); - }; - if (isBrowser) { - setTimeout(closeFn, 0); - } else { - process.nextTick(closeFn); - } - } - }); - monitorConnection(done, realtime); - } catch (err) { - closeAndFinish(done, realtime, err); - } - }); - - /* - * Check active heartbeat, text protocol - */ - it('upgradeheartbeat0', function (done) { - var transportOpts = { useBinaryProtocol: false, transports: helper.availableTransports }; - try { - var realtime = helper.AblyRealtime(transportOpts); - - /* when we see the transport we're interested in get activated, - * listen for the heartbeat event */ - var failTimer; - var connectionManager = realtime.connection.connectionManager; - connectionManager.on('transport.active', function (transport) { - if (transport.toString().match(/wss?\:/)) - transport.on('heartbeat', function () { - transport.off('heartbeat'); - clearTimeout(failTimer); - closeAndFinish(done, realtime); - }); - transport.ping(); - }); - - realtime.connection.on('connected', function () { - failTimer = setTimeout(function () { - closeAndFinish(done, realtime, new Error('upgrade heartbeat failed (timer expired)')); - }, 120000); - }); - monitorConnection(done, realtime); - } catch (err) { - closeAndFinish(done, realtime, err); - } - }); - - /* - * Check active heartbeat, binary protocol - */ - it('upgradeheartbeat1', function (done) { - var transportOpts = { useBinaryProtocol: true, transports: helper.availableTransports }; - try { - var realtime = helper.AblyRealtime(transportOpts); - - /* when we see the transport we're interested in get activated, - * listen for the heartbeat event */ - var failTimer; - var connectionManager = realtime.connection.connectionManager; - connectionManager.on('transport.active', function (transport) { - if (transport.toString().match(/wss?\:/)) - transport.on('heartbeat', function () { - transport.off('heartbeat'); - clearTimeout(failTimer); - closeAndFinish(done, realtime); - }); - transport.ping(); - }); - - realtime.connection.on('connected', function () { - failTimer = setTimeout(function () { - closeAndFinish(done, realtime, new Error('upgrade heartbeat failed (timer expired)')); - }, 120000); - }); - monitorConnection(done, realtime); - } catch (err) { - closeAndFinish(done, realtime, err); - } - }); - - /* - * Check heartbeat does not fire on inactive transport, text protocol - */ - it('upgradeheartbeat2', function (done) { - var transportOpts = { useBinaryProtocol: false, transports: helper.availableTransports }; - try { - var realtime = helper.AblyRealtime(transportOpts); - - /* when we see the transport we're interested in get activated, - * listen for the heartbeat event */ - var failTimer, cometTransport, wsTransport; - var connectionManager = realtime.connection.connectionManager; - connectionManager.on('transport.active', function (transport) { - var transportDescription = transport.toString(); - //console.log('active transport: ' + transportDescription); - if (transportDescription.indexOf('/comet/') > -1) { - cometTransport = transport; - cometTransport.on('heartbeat', function () { - closeAndFinish(done, realtime, new Error('verify heartbeat does not fire on inactive transport')); - }); - } - if (transportDescription.match(/wss?\:/)) { - wsTransport = transport; - wsTransport.on('heartbeat', function () { - clearTimeout(failTimer); - /* wait a couple of seconds to give it time - * in case it might still fire */ - setTimeout(function () { - closeAndFinish(done, realtime); - }, 2000); - }); - wsTransport.ping(); - } - }); - - realtime.connection.on('connected', function () { - failTimer = setTimeout(function () { - closeAndFinish(done, realtime, new Error('upgrade heartbeat failed (timer expired)')); - }, 120000); - }); - monitorConnection(done, realtime); - } catch (err) { - closeAndFinish(done, realtime, err); - } - }); - - /* - * Check heartbeat does not fire on inactive transport, binary protocol - */ - it('upgradeheartbeat3', function (done) { - var transportOpts = { useBinaryProtocol: true, transports: helper.availableTransports }; - try { - var realtime = helper.AblyRealtime(transportOpts); - - /* when we see the transport we're interested in get activated, - * listen for the heartbeat event */ - var failTimer, cometTransport, wsTransport; - var connectionManager = realtime.connection.connectionManager; - connectionManager.on('transport.active', function (transport) { - var transportDescription = transport.toString(); - //console.log('active transport: ' + transportDescription); - if (transportDescription.indexOf('/comet/') > -1) { - cometTransport = transport; - cometTransport.on('heartbeat', function () { - closeAndFinish(done, realtime, new Error('verify heartbeat does not fire on inactive transport')); - }); - } - if (transportDescription.match(/wss?\:/)) { - wsTransport = transport; - wsTransport.on('heartbeat', function () { - clearTimeout(failTimer); - /* wait a couple of seconds to give it time - * in case it might still fire */ - setTimeout(function () { - closeAndFinish(done, realtime); - }, 2000); - }); - wsTransport.ping(); - } - }); - - realtime.connection.on('connected', function () { - failTimer = setTimeout(function () { - closeAndFinish(done, realtime, new Error('upgrade heartbeat failed (timer expired)')); - }, 120000); - }); - monitorConnection(done, realtime); - } catch (err) { - closeAndFinish(done, realtime, err); - } - }); - - it('unrecoverableUpgrade', function (done) { - var realtime, - fakeConnectionKey = '_____!ablyjs_test_fake-key____', - fakeConnectionId = 'ablyjs_tes'; - - try { - /* on base transport active */ - realtime = helper.AblyRealtime({ transports: helper.availableTransports }); - realtime.connection.connectionManager.once('transport.active', function (transport) { - expect( - transport.toString().indexOf('/comet/') > -1, - 'assert first transport to become active is a comet transport' - ).to.be.ok; - try { - expect(realtime.connection.errorReason).to.equal(null, 'Check connection.errorReason is initially null'); - /* sabotage the upgrade */ - realtime.connection.connectionManager.connectionKey = fakeConnectionKey; - realtime.connection.connectionManager.connectionId = fakeConnectionId; - } catch (err) { - closeAndFinish(done, realtiem, err); - return; - } - - /* on upgrade failure */ - realtime.connection.once('update', function (stateChange) { - try { - expect(stateChange.reason.code).to.equal(80018, 'Check correct (unrecoverable connection) error'); - expect(stateChange.current).to.equal('connected', 'Check current is connected'); - expect(realtime.connection.errorReason.code).to.equal( - 80018, - 'Check error set in connection.errorReason' - ); - expect(realtime.connection.state).to.equal('connected', 'Check still connected'); - } catch (err) { - closeAndFinish(done, realtime, err); - return; - } - - /* Check events not still paused */ - var channel = realtime.channels.get('unrecoverableUpgrade'); - channel.attach(function (err) { - if (err) { - closeAndFinish(done, realtime, err); - return; - } - channel.subscribe(function (msg) { - closeAndFinish(done, realtime); - }); - channel.publish('msg', null); - }); - }); - }); - } catch (err) { - closeAndFinish(done, realtime, err); - } - }); - - /* - * Check that a message that fails to publish on a comet transport can be - * seamlessly transferred to the websocket transport and published there - */ - it('message_timeout_stalling_upgrade', function (done) { - var realtime = helper.AblyRealtime({ transports: helper.availableTransports, httpRequestTimeout: 3000 }), - channel = realtime.channels.get('timeout1'), - connectionManager = realtime.connection.connectionManager; - - realtime.connection.once('connected', function () { - /* Sabotage comet sending */ - var transport = connectionManager.activeProtocol.getTransport(); - try { - expect(helper.isComet(transport), 'Check active transport is still comet').to.be.ok; - } catch (err) { - closeAndFinish(done, realtime, err); - return; - } - transport.sendUri = helper.unroutableAddress; - - async.parallel( - [ - function (cb) { - channel.subscribe('event', function () { - cb(); - }); - }, - function (cb) { - channel.publish('event', null, function (err) { - try { - expect(!err, 'Successfully published message').to.be.ok; - } catch (err) { - cb(err); - return; - } - cb(); - }); - }, - ], - function (err) { - closeAndFinish(done, realtime, err); - } - ); - }); - }); - - /* - * Check that after a successful upgrade, the transport pref is persisted, - * and subsequent connections do not upgrade - */ - it('persist_transport_prefs', function (done) { - var realtime = helper.AblyRealtime({ transports: helper.availableTransports }), - connection = realtime.connection, - connectionManager = connection.connectionManager; - - async.series( - [ - function (cb) { - connectionManager.once('transport.active', function (transport) { - try { - expect(helper.isComet(transport), 'Check first transport to become active is comet').to.be.ok; - } catch (err) { - cb(err); - return; - } - cb(); - }); - }, - function (cb) { - connectionManager.once('transport.active', function (transport) { - try { - expect(helper.isWebsocket(transport), 'Check second transport to become active is ws').to.be.ok; - } catch (err) { - cb(err); - return; - } - cb(); - }); - }, - function (cb) { - connection.once('closed', function () { - cb(); - }); - Ably.Realtime.Platform.Config.nextTick(function () { - connection.close(); - }); - }, - function (cb) { - connectionManager.once('transport.active', function (transport) { - try { - expect( - helper.isWebsocket(transport), - 'Check first transport to become active the second time round is websocket' - ).to.be.ok; - } catch (err) { - cb(err); - return; - } - cb(); - }); - connection.connect(); - }, - ], - function (err) { - closeAndFinish(done, realtime, err); - } - ); - }); - - /* - * Check that upgrades succeed even if the original transport dies before the sync - */ - it('upgrade_original_transport_dies', function (done) { - var realtime = helper.AblyRealtime({ transports: helper.availableTransports }), - connection = realtime.connection, - connectionManager = connection.connectionManager; - - async.series( - [ - function (cb) { - connectionManager.once('transport.active', function (transport) { - try { - expect(helper.isComet(transport), 'Check first transport to become active is comet').to.be.ok; - } catch (err) { - cb(err); - return; - } - cb(); - }); - }, - function (cb) { - connectionManager.on('transport.pending', function (transport) { - if (!helper.isWebsocket(transport)) return; // in browser, might be xhr_streaming - connectionManager.off('transport.pending'); - /* Abort comet transport nonfatally */ - var baseTransport = connectionManager.activeProtocol.getTransport(); - try { - expect(helper.isComet(baseTransport), 'Check original transport is still comet').to.be.ok; - } catch (err) { - cb(err); - return; - } - /* Check that if we do get a statechange, it's to connecting, not disconnected. */ - var stateChangeListener = function (stateChange) { - try { - expect(stateChange.current).to.equal( - 'connecting', - 'check that deactivateTransport only drops us to connecting as another transport is ready for activation' - ); - } catch (err) { - cb(err); - } - }; - connection.once(stateChangeListener); - connectionManager.once('connectiondetails', function () { - connection.off(stateChangeListener); - /* Check the upgrade completed */ - var newActiveTransport = connectionManager.activeProtocol.getTransport(); - try { - expect(transport).to.equal(newActiveTransport, 'Check the upgrade transport is now active'); - } catch (err) { - cb(err); - return; - } - cb(); - }); - transport.once('connected', function () { - baseTransport.disconnect({ code: 50000, statusCode: 500, message: 'a non-fatal transport error' }); - }); - }); - }, - ], - function (err) { - closeAndFinish(done, realtime, err); - } - ); - }); - }); - } -}); diff --git a/test/rest/api.test.js b/test/rest/api.test.js index dd3610c982..5bd351b4b0 100644 --- a/test/rest/api.test.js +++ b/test/rest/api.test.js @@ -6,9 +6,12 @@ define(['ably', 'chai'], function (Ably, chai) { describe('rest/api', function () { it('Client constructors', function () { expect(typeof Ably.Rest).to.equal('function'); - expect(typeof Ably.Rest.Promise).to.equal('function'); - expect(typeof Ably.Rest.Callbacks).to.equal('function'); - expect(Ably.Rest.Callbacks).to.equal(Ably.Rest); + }); + + it('constructor without any arguments', function () { + expect(() => new Ably.Rest()).to.throw( + 'must be initialized with either a client options object, an Ably API key, or an Ably Token', + ); }); it('Crypto', function () { diff --git a/test/rest/auth.test.js b/test/rest/auth.test.js index 87273b55bc..e7aa752b53 100644 --- a/test/rest/auth.test.js +++ b/test/rest/auth.test.js @@ -10,437 +10,227 @@ define(['chai', 'shared_helper', 'async', 'globals'], function (chai, helper, as describe('rest/auth', function () { this.timeout(60 * 1000); - var getServerTime = function (callback) { - rest.time(function (err, time) { - if (err) { - callback(err); - } - callback(null, time); - }); - }; - before(function (done) { helper.setupApp(function () { rest = helper.AblyRest({ queryTime: true }); - getServerTime(function (err, time) { - if (err) { + rest + .time() + .then(function (time) { + currentTime = time; + expect(true, 'Obtained time').to.be.ok; + done(); + }) + .catch(function (err) { done(err); - return; - } - currentTime = time; - expect(true, 'Obtained time').to.be.ok; - done(); - }); + }); }); }); - it('Base token generation case', function (done) { - rest.auth.requestToken(function (err, tokenDetails) { - if (err) { - done(err); - return; - } - try { - expect(tokenDetails.token, 'Verify token value').to.be.ok; - expect(tokenDetails.issued && tokenDetails.issued >= currentTime, 'Verify token issued').to.be.ok; - expect(tokenDetails.expires && tokenDetails.expires > tokenDetails.issued, 'Verify token expires').to.be.ok; - expect(tokenDetails.expires).to.equal(60 * 60 * 1000 + tokenDetails.issued, 'Verify default expiry period'); - expect(JSON.parse(tokenDetails.capability)).to.deep.equal({ '*': ['*'] }, 'Verify token capability'); - done(); - } catch (err) { - done(err); - } - }); + it('Base token generation case', async function () { + var tokenDetails = await rest.auth.requestToken(); + expect(tokenDetails.token, 'Verify token value').to.be.ok; + expect(tokenDetails.issued && tokenDetails.issued >= currentTime, 'Verify token issued').to.be.ok; + expect(tokenDetails.expires && tokenDetails.expires > tokenDetails.issued, 'Verify token expires').to.be.ok; + expect(tokenDetails.expires).to.equal(60 * 60 * 1000 + tokenDetails.issued, 'Verify default expiry period'); + expect(JSON.parse(tokenDetails.capability)).to.deep.equal({ '*': ['*'] }, 'Verify token capability'); }); - it('Base token generation with options', function (done) { - rest.auth.requestToken(null, function (err, tokenDetails) { - if (err) { - done(err); - return; - } - try { - expect(tokenDetails.token, 'Verify token value').to.be.ok; - expect(tokenDetails.issued && tokenDetails.issued >= currentTime, 'Verify token issued').to.be.ok; - expect(tokenDetails.expires && tokenDetails.expires > tokenDetails.issued, 'Verify token expires').to.be.ok; - expect(JSON.parse(tokenDetails.capability)).to.deep.equal({ '*': ['*'] }, 'Verify token capability'); - done(); - } catch (err) { - done(err); - } - }); + it('Base token generation with options', async function () { + var tokenDetails = await rest.auth.requestToken(null); + expect(tokenDetails.token, 'Verify token value').to.be.ok; + expect(tokenDetails.issued && tokenDetails.issued >= currentTime, 'Verify token issued').to.be.ok; + expect(tokenDetails.expires && tokenDetails.expires > tokenDetails.issued, 'Verify token expires').to.be.ok; + expect(JSON.parse(tokenDetails.capability)).to.deep.equal({ '*': ['*'] }, 'Verify token capability'); }); - it('Generate token and init library with it', function (done) { - rest.auth.requestToken(function (err, tokenDetails) { - if (err) { - done(err); - return; - } - try { - expect(tokenDetails.token, 'Verify token value').to.be.ok; - helper.AblyRest({ token: tokenDetails.token }); - done(); - } catch (err) { - done(err); - } - }); + it('Generate token and init library with it', async function () { + var tokenDetails = await rest.auth.requestToken(); + expect(tokenDetails.token, 'Verify token value').to.be.ok; + helper.AblyRest({ token: tokenDetails.token }); }); - it('Token generation with explicit timestamp', function (done) { - getServerTime(function (err, serverTime) { - if (err) { - done(err); - return; - } - - rest.auth.requestToken({ timestamp: serverTime }, function (err, tokenDetails) { - if (err) { - done(err); - return; - } - try { - expect(tokenDetails.token).to.be.ok; - expect(tokenDetails.issued && tokenDetails.issued >= currentTime, 'Verify token issued').to.be.ok; - expect(tokenDetails.expires && tokenDetails.expires > tokenDetails.issued, 'Verify token expires').to.be.ok; - expect(JSON.parse(tokenDetails.capability)).to.deep.equal({ '*': ['*'] }, 'Verify token capability'); - done(); - } catch (err) { - done(err); - } - }); - }); + it('Token generation with explicit timestamp', async function () { + var serverTime = await rest.time(); + var tokenDetails = await rest.auth.requestToken({ timestamp: serverTime }); + expect(tokenDetails.token).to.be.ok; + expect(tokenDetails.issued && tokenDetails.issued >= currentTime, 'Verify token issued').to.be.ok; + expect(tokenDetails.expires && tokenDetails.expires > tokenDetails.issued, 'Verify token expires').to.be.ok; + expect(JSON.parse(tokenDetails.capability)).to.deep.equal({ '*': ['*'] }, 'Verify token capability'); }); - it('Token generation with invalid timestamp', function (done) { - var badTime = utils.now() - 30 * 60 * 1000; - rest.auth.requestToken({ timestamp: badTime }, function (err, tokenDetails) { - if (err) { - try { - expect(err.statusCode).to.equal(401, 'Verify token request rejected with bad timestamp'); - done(); - } catch (err) { - done(err); - } - return; - } - done(new Error('Invalid timestamp, expected rejection')); - }); + it('Token generation with invalid timestamp', async function () { + var badTime = Date.now() - 30 * 60 * 1000; + try { + var tokenDetails = await rest.auth.requestToken({ timestamp: badTime }); + } catch (err) { + expect(err.statusCode).to.equal(401, 'Verify token request rejected with bad timestamp'); + return; + } + throw new Error('Invalid timestamp, expected rejection'); }); - it('Token generation with system timestamp', function (done) { - rest.auth.requestToken(function (err, tokenDetails) { - if (err) { - done(err); - return; - } - try { - expect(tokenDetails.token, 'Verify token value').to.be.ok; - expect(tokenDetails.issued && tokenDetails.issued >= currentTime, 'Verify token issued').to.be.ok; - expect(tokenDetails.expires && tokenDetails.expires > tokenDetails.issued, 'Verify token expires').to.be.ok; - expect(JSON.parse(tokenDetails.capability)).to.deep.equal({ '*': ['*'] }, 'Verify token capability'); - done(); - } catch (err) { - done(err); - } - }); + it('Token generation with system timestamp', async function () { + var tokenDetails = await rest.auth.requestToken(); + expect(tokenDetails.token, 'Verify token value').to.be.ok; + expect(tokenDetails.issued && tokenDetails.issued >= currentTime, 'Verify token issued').to.be.ok; + expect(tokenDetails.expires && tokenDetails.expires > tokenDetails.issued, 'Verify token expires').to.be.ok; + expect(JSON.parse(tokenDetails.capability)).to.deep.equal({ '*': ['*'] }, 'Verify token capability'); }); - it('Token generation with duplicate nonce', function (done) { - getServerTime(function (err, serverTime) { - if (err) { - done(err); - return; - } - rest.auth.requestToken({ timestamp: serverTime, nonce: '1234567890123456' }, function (err, tokenDetails) { - if (err) { - done(err); - return; - } - rest.auth.requestToken({ timestamp: serverTime, nonce: '1234567890123456' }, function (err, tokenDetails) { - if (err) { - try { - expect(err.statusCode).to.equal(401, 'Verify request rejected with duplicated nonce'); - done(); - } catch (err) { - done(err); - } - return; - } - done(new Error('Invalid nonce, expected rejection')); - }); - }); - }); + it('Token generation with duplicate nonce', async function () { + var serverTime = await rest.time(); + await rest.auth.requestToken({ timestamp: serverTime, nonce: '1234567890123456' }); + try { + await rest.auth.requestToken({ timestamp: serverTime, nonce: '1234567890123456' }); + } catch (err) { + expect(err.statusCode).to.equal(401, 'Verify request rejected with duplicated nonce'); + return; + } + throw new Error('Invalid nonce, expected rejection'); }); - it('Token generation with clientId', function (done) { + it('Token generation with clientId', async function () { var testClientId = 'test client id'; - rest.auth.requestToken({ clientId: testClientId }, function (err, tokenDetails) { - if (err) { - done(err); - return; - } - try { - expect(tokenDetails.token, 'Verify token value').to.be.ok; - expect(tokenDetails.issued && tokenDetails.issued >= currentTime, 'Verify token issued').to.be.ok; - expect(tokenDetails.expires && tokenDetails.expires > tokenDetails.issued, 'Verify token expires').to.be.ok; - expect(tokenDetails.clientId).to.equal(testClientId, 'Verify client id'); - expect(JSON.parse(tokenDetails.capability)).to.deep.equal({ '*': ['*'] }, 'Verify token capability'); - done(); - } catch (err) { - done(err); - } - }); + var tokenDetails = await rest.auth.requestToken({ clientId: testClientId }); + expect(tokenDetails.token, 'Verify token value').to.be.ok; + expect(tokenDetails.issued && tokenDetails.issued >= currentTime, 'Verify token issued').to.be.ok; + expect(tokenDetails.expires && tokenDetails.expires > tokenDetails.issued, 'Verify token expires').to.be.ok; + expect(tokenDetails.clientId).to.equal(testClientId, 'Verify client id'); + expect(JSON.parse(tokenDetails.capability)).to.deep.equal({ '*': ['*'] }, 'Verify token capability'); }); - it('Token generation with empty string clientId should error', function (done) { - rest.auth.requestToken({ clientId: '' }, function (err, tokenDetails) { - if (err) { - expect(err.code).to.equal(40012); - done(); - return; - } - done(new Error('Expected token generation to error with empty string clientId')); - }); + it('Token generation with empty string clientId should error', async function () { + try { + var tokenDetails = await rest.auth.requestToken({ clientId: '' }); + } catch (err) { + expect(err.code).to.equal(40012); + return; + } + throw new Error('Expected token generation to error with empty string clientId'); }); - it('Token generation with capability that subsets key capability', function (done) { + it('Token generation with capability that subsets key capability', async function () { var testCapability = { onlythischannel: ['subscribe'] }; - rest.auth.requestToken({ capability: testCapability }, function (err, tokenDetails) { - if (err) { - done(err); - return; - } - try { - expect(tokenDetails.token, 'Verify token value').to.be.ok; - expect(tokenDetails.issued && tokenDetails.issued >= currentTime, 'Verify token issued').to.be.ok; - expect(tokenDetails.expires && tokenDetails.expires > tokenDetails.issued, 'Verify token expires').to.be.ok; - expect(JSON.parse(tokenDetails.capability)).to.deep.equal(testCapability, 'Verify token capability'); - done(); - } catch (err) { - done(err); - } - }); + var tokenDetails = await rest.auth.requestToken({ capability: testCapability }); + expect(tokenDetails.token, 'Verify token value').to.be.ok; + expect(tokenDetails.issued && tokenDetails.issued >= currentTime, 'Verify token issued').to.be.ok; + expect(tokenDetails.expires && tokenDetails.expires > tokenDetails.issued, 'Verify token expires').to.be.ok; + expect(JSON.parse(tokenDetails.capability)).to.deep.equal(testCapability, 'Verify token capability'); }); - it('Token generation with specified key', function (done) { + it('Token generation with specified key', async function () { var testKeyOpts = { key: helper.getTestApp().keys[1].keyStr }; var testCapability = JSON.parse(helper.getTestApp().keys[1].capability); - rest.auth.requestToken(null, testKeyOpts, function (err, tokenDetails) { - if (err) { - done(err); - return; - } - try { - expect(tokenDetails.token, 'Verify token value').to.be.ok; - expect(tokenDetails.issued && tokenDetails.issued >= currentTime, 'Verify token issued').to.be.ok; - expect(tokenDetails.expires && tokenDetails.expires > tokenDetails.issued, 'Verify token expires').to.be.ok; - expect(tokenDetails.keyName).to.equal(helper.getTestApp().keys[1].keyName, 'Verify token key'); - expect(JSON.parse(tokenDetails.capability)).to.deep.equal(testCapability, 'Verify token capability'); - done(); - } catch (err) { - done(err); - } - }); + var tokenDetails = await rest.auth.requestToken(null, testKeyOpts); + expect(tokenDetails.token, 'Verify token value').to.be.ok; + expect(tokenDetails.issued && tokenDetails.issued >= currentTime, 'Verify token issued').to.be.ok; + expect(tokenDetails.expires && tokenDetails.expires > tokenDetails.issued, 'Verify token expires').to.be.ok; + expect(tokenDetails.keyName).to.equal(helper.getTestApp().keys[1].keyName, 'Verify token key'); + expect(JSON.parse(tokenDetails.capability)).to.deep.equal(testCapability, 'Verify token capability'); }); - it('Token generation with explicit auth', function (done) { - rest.auth.getAuthHeaders(function (err, authHeaders) { - if (err) { - done(err); - return; - } - rest.auth.authOptions.requestHeaders = authHeaders; - rest.auth.requestToken(function (err, tokenDetails) { - delete rest.auth.authOptions.requestHeaders; - if (err) { - done(err); - return; - } - try { - expect(tokenDetails.token, 'Verify token value').to.be.ok; - expect(tokenDetails.issued && tokenDetails.issued >= currentTime, 'Verify token issued').to.be.ok; - expect(tokenDetails.expires && tokenDetails.expires > tokenDetails.issued, 'Verify token expires').to.be.ok; - expect(tokenDetails.keyName).to.equal(helper.getTestApp().keys[0].keyName, 'Verify token key'); - done(); - } catch (err) { - done(err); - } - }); - }); + it('Token generation with explicit auth', async function () { + const authHeaders = await rest.auth.getAuthHeaders(); + rest.auth.authOptions.requestHeaders = authHeaders; + var tokenDetails = await rest.auth.requestToken(); + delete rest.auth.authOptions.requestHeaders; + expect(tokenDetails.token, 'Verify token value').to.be.ok; + expect(tokenDetails.issued && tokenDetails.issued >= currentTime, 'Verify token issued').to.be.ok; + expect(tokenDetails.expires && tokenDetails.expires > tokenDetails.issued, 'Verify token expires').to.be.ok; + expect(tokenDetails.keyName).to.equal(helper.getTestApp().keys[0].keyName, 'Verify token key'); }); - it('Token generation with explicit auth, different key', function (done) { - rest.auth.getAuthHeaders(function (err, authHeaders) { - var testKeyOpts = { key: helper.getTestApp().keys[1].keyStr }; - var testCapability = JSON.parse(helper.getTestApp().keys[1].capability); - rest.auth.requestToken(null, testKeyOpts, function (err, tokenDetails) { - if (err) { - done(err); - return; - } - try { - expect(tokenDetails.token, 'Verify token value').to.be.ok; - expect(tokenDetails.issued && tokenDetails.issued >= currentTime, 'Verify token issued').to.be.ok; - expect(tokenDetails.expires && tokenDetails.expires > tokenDetails.issued, 'Verify token expires').to.be.ok; - expect(tokenDetails.keyName).to.equal(helper.getTestApp().keys[1].keyName, 'Verify token key'); - expect(JSON.parse(tokenDetails.capability)).to.deep.equal(testCapability, 'Verify token capability'); - done(); - } catch (e) { - done(e); - } - }); - }); + it('Token generation with explicit auth, different key', async function () { + const authHeaders = await rest.auth.getAuthHeaders(); + var testKeyOpts = { key: helper.getTestApp().keys[1].keyStr }; + var testCapability = JSON.parse(helper.getTestApp().keys[1].capability); + var tokenDetails = await rest.auth.requestToken(null, testKeyOpts); + expect(tokenDetails.token, 'Verify token value').to.be.ok; + expect(tokenDetails.issued && tokenDetails.issued >= currentTime, 'Verify token issued').to.be.ok; + expect(tokenDetails.expires && tokenDetails.expires > tokenDetails.issued, 'Verify token expires').to.be.ok; + expect(tokenDetails.keyName).to.equal(helper.getTestApp().keys[1].keyName, 'Verify token key'); + expect(JSON.parse(tokenDetails.capability)).to.deep.equal(testCapability, 'Verify token capability'); }); - it('Token generation with invalid mac', function (done) { - rest.auth.requestToken({ mac: '12345' }, function (err, tokenDetails) { - if (err) { - try { - expect(err.statusCode).to.equal(401, 'Verify request rejected with bad mac'); - done(); - } catch (e) { - done(e); - } - return; - } - done(new Error('Invalid mac, expected rejection')); - }); + it('Token generation with invalid mac', async function () { + try { + var tokenDetails = await rest.auth.requestToken({ mac: '12345' }); + } catch (err) { + expect(err.statusCode).to.equal(401, 'Verify request rejected with bad mac'); + return; + } + throw new Error('Invalid mac, expected rejection'); }); - it('Token generation with defaultTokenParams set and no tokenParams passed in', function (done) { + it('Token generation with defaultTokenParams set and no tokenParams passed in', async function () { var rest1 = helper.AblyRest({ defaultTokenParams: { ttl: 123, clientId: 'foo' } }); - rest1.auth.requestToken(function (err, tokenDetails) { - if (err) { - done(err); - return; - } - try { - expect(tokenDetails.token, 'Verify token value').to.be.ok; - expect(tokenDetails.clientId).to.equal('foo', 'Verify client id from defaultTokenParams used'); - expect(tokenDetails.expires - tokenDetails.issued).to.equal(123, 'Verify ttl from defaultTokenParams used'); - done(); - } catch (err) { - done(err); - } - }); + var tokenDetails = await rest1.auth.requestToken(); + expect(tokenDetails.token, 'Verify token value').to.be.ok; + expect(tokenDetails.clientId).to.equal('foo', 'Verify client id from defaultTokenParams used'); + expect(tokenDetails.expires - tokenDetails.issued).to.equal(123, 'Verify ttl from defaultTokenParams used'); }); - it('Token generation: if tokenParams passed in, defaultTokenParams should be ignored altogether, not merged', function (done) { + it('Token generation: if tokenParams passed in, defaultTokenParams should be ignored altogether, not merged', async function () { var rest1 = helper.AblyRest({ defaultTokenParams: { ttl: 123, clientId: 'foo' } }); - rest1.auth.requestToken({ clientId: 'bar' }, null, function (err, tokenDetails) { - if (err) { - done(err); - return; - } - try { - expect(tokenDetails.clientId).to.equal( - 'bar', - 'Verify clientId passed in is used, not the one from defaultTokenParams' - ); - expect(tokenDetails.expires - tokenDetails.issued).to.equal( - 60 * 60 * 1000, - 'Verify ttl from defaultTokenParams ignored completely, even though not overridden' - ); - done(); - } catch (err) { - done(err); - } - }); + var tokenDetails = await rest1.auth.requestToken({ clientId: 'bar' }, null); + expect(tokenDetails.clientId).to.equal( + 'bar', + 'Verify clientId passed in is used, not the one from defaultTokenParams', + ); + expect(tokenDetails.expires - tokenDetails.issued).to.equal( + 60 * 60 * 1000, + 'Verify ttl from defaultTokenParams ignored completely, even though not overridden', + ); }); /* * authorize with different args */ - it('Authorize with different args', function (done) { - async.parallel( - [ - function (cb) { - rest.auth.authorize(null, null, function (err, tokenDetails) { - expect(tokenDetails.token, 'Check token obtained').to.be.ok; - cb(err); - }); - }, - function (cb) { - rest.auth.authorize(null, function (err, tokenDetails) { - expect(tokenDetails.token, 'Check token obtained').to.be.ok; - cb(err); - }); - }, - function (cb) { - rest.auth.authorize(function (err, tokenDetails) { - expect(tokenDetails.token, 'Check token obtained').to.be.ok; - cb(err); - }); - }, - ], - function (err) { - if (err) { - done(err); - } - done(); - } - ); - }); + it('Authorize with different args', async function () { + var results = await Promise.all([ + rest.auth.authorize(), + rest.auth.authorize(null), + rest.auth.authorize(null, null), + ]); - it('Specify non-default ttl', function (done) { - rest.auth.requestToken({ ttl: 100 * 1000 }, function (err, tokenDetails) { - if (err) { - done(err); - return; - } - try { - expect(tokenDetails.expires).to.equal(100 * 1000 + tokenDetails.issued, 'Verify non-default expiry period'); - done(); - } catch (err) { - done(err); - } + results.forEach((tokenDetails) => { + expect(tokenDetails.token, 'Check token obtained').to.be.ok; }); }); - it('Should error with excessive ttl', function (done) { - rest.auth.requestToken({ ttl: 365 * 24 * 60 * 60 * 1000 }, function (err, tokenDetails) { - if (err) { - try { - expect(err.statusCode).to.equal(400, 'Verify request rejected with excessive expiry'); - done(); - } catch (err) { - done(err); - } - return; - } - done(new Error('Excessive expiry, expected rejection')); - }); + it('Specify non-default ttl', async function () { + var tokenDetails = await rest.auth.requestToken({ ttl: 100 * 1000 }); + expect(tokenDetails.expires).to.equal(100 * 1000 + tokenDetails.issued, 'Verify non-default expiry period'); }); - it('Should error with negative ttl', function (done) { - rest.auth.requestToken({ ttl: -1 }, function (err, tokenDetails) { - if (err) { - try { - expect(err.statusCode).to.equal(400, 'Verify request rejected with negative expiry'); - done(); - } catch (err) { - done(err); - } - return; - } - done(new Error('Negative expiry, expected rejection')); - }); + it('Should error with excessive ttl', async function () { + try { + var tokenDetails = await rest.auth.requestToken({ ttl: 365 * 24 * 60 * 60 * 1000 }); + } catch (err) { + expect(err.statusCode).to.equal(400, 'Verify request rejected with excessive expiry'); + return; + } + throw new Error('Excessive expiry, expected rejection'); }); - it('Should error with invalid ttl', function (done) { - rest.auth.requestToken({ ttl: 'notanumber' }, function (err, tokenDetails) { - if (err) { - try { - expect(err.statusCode).to.equal(400, 'Verify request rejected with invalid expiry'); - done(); - } catch (e) { - done(e); - } - return; - } - done(new Error('Invalid expiry, expected rejection')); - }); + it('Should error with negative ttl', async function () { + try { + var tokenDetails = await rest.auth.requestToken({ ttl: -1 }); + } catch (err) { + expect(err.statusCode).to.equal(400, 'Verify request rejected with negative expiry'); + return; + } + throw new Error('Negative expiry, expected rejection'); + }); + + it('Should error with invalid ttl', async function () { + try { + var tokenDetails = await rest.auth.requestToken({ ttl: 'notanumber' }); + } catch (err) { + expect(err.statusCode).to.equal(400, 'Verify request rejected with invalid expiry'); + return; + } + throw new Error('Invalid expiry, expected rejection'); }); /* @@ -448,91 +238,28 @@ define(['chai', 'shared_helper', 'async', 'globals'], function (chai, helper, as * and the token request includes all the fields it should include, but * doesn't include ttl or capability by default */ - it('createTokenRequest without authOptions', function (done) { - rest.auth.createTokenRequest(null, null, function (err, tokenRequest) { - if (err) { - done(err); - return; - } - try { - expect('mac' in tokenRequest, 'check tokenRequest contains a mac').to.be.ok; - expect('nonce' in tokenRequest, 'check tokenRequest contains a nonce').to.be.ok; - expect('timestamp' in tokenRequest, 'check tokenRequest contains a timestamp').to.be.ok; - expect(!('ttl' in tokenRequest), 'check tokenRequest does not contains a ttl by default').to.be.ok; - expect( - !('capability' in tokenRequest), - 'check tokenRequest does not contains capabilities by default' - ).to.be.ok; - expect(tokenRequest.keyName).to.equal(helper.getTestApp().keys[0].keyName); - done(); - } catch (err) { - done(err); - } - }); - }); - - it('createTokenRequest without authOptions, callback as 2nd param', function (done) { - rest.auth.createTokenRequest(null, function (err, tokenRequest) { - if (err) { - done(err); - return; - } - try { - expect(tokenRequest.keyName).to.equal(helper.getTestApp().keys[0].keyName); - done(); - } catch (err) { - done(err); - } - }); - }); - - it('createTokenRequest without authOptions or tokenParams, callback as 1st param', function (done) { - rest.auth.createTokenRequest(function (err, tokenRequest) { - if (err) { - done(err); - return; - } - try { - expect(tokenRequest.keyName).to.equal(helper.getTestApp().keys[0].keyName); - done(); - } catch (err) { - done(err); - } - }); + it('createTokenRequest without authOptions', async function () { + var tokenRequest = await rest.auth.createTokenRequest(null, null); + expect('mac' in tokenRequest, 'check tokenRequest contains a mac').to.be.ok; + expect('nonce' in tokenRequest, 'check tokenRequest contains a nonce').to.be.ok; + expect('timestamp' in tokenRequest, 'check tokenRequest contains a timestamp').to.be.ok; + expect(!('ttl' in tokenRequest), 'check tokenRequest does not contains a ttl by default').to.be.ok; + expect(!('capability' in tokenRequest), 'check tokenRequest does not contains capabilities by default').to.be.ok; + expect(tokenRequest.keyName).to.equal(helper.getTestApp().keys[0].keyName); }); - it('createTokenRequest uses the key it was initialized with if authOptions does not have a "key" key', function (done) { - rest.auth.createTokenRequest(function (err, tokenRequest) { - if (err) { - done(err); - return; - } - try { - expect(tokenRequest.keyName).to.equal(helper.getTestApp().keys[0].keyName); - done(); - } catch (err) { - done(err); - } - }); + it('createTokenRequest uses the key it was initialized with if authOptions does not have a "key" key', async function () { + var tokenRequest = await rest.auth.createTokenRequest(); + expect(tokenRequest.keyName).to.equal(helper.getTestApp().keys[0].keyName); }); - it('createTokenRequest should serialise capability object as JSON', function (done) { + it('createTokenRequest should serialise capability object as JSON', async function () { var capability = { '*': ['*'] }; - rest.auth.createTokenRequest({ capability: capability }, null, function (err, tokenRequest) { - if (err) { - done(err); - return; - } - try { - expect(JSON.parse(tokenRequest.capability)).to.deep.equal( - capability, - 'Verify createTokenRequest has JSON-stringified capability' - ); - done(); - } catch (err) { - done(err); - } - }); + var tokenRequest = await rest.auth.createTokenRequest({ capability: capability }, null); + expect(JSON.parse(tokenRequest.capability)).to.deep.equal( + capability, + 'Verify createTokenRequest has JSON-stringified capability', + ); }); /** @@ -541,27 +268,16 @@ define(['chai', 'shared_helper', 'async', 'globals'], function (chai, helper, as * @param {object} params The authParams to be tested */ function testJWTAuthParams(description, params) { - it(description, function (done) { + it(description, async function () { var currentKey = helper.getTestApp().keys[0]; var keys = { keyName: currentKey.keyName, keySecret: currentKey.keySecret }; var authParams = utils.mixin(keys, params); var authUrl = echoServer + '/createJWT' + utils.toQueryString(authParams); var restJWTRequester = helper.AblyRest({ authUrl: authUrl }); - restJWTRequester.auth.requestToken(function (err, tokenDetails) { - if (err) { - done(err); - return; - } - var restClient = helper.AblyRest({ token: tokenDetails.token }); - restClient.stats(function (err, stats) { - if (err) { - done(err); - return; - } - done(); - }); - }); + var tokenDetails = await restJWTRequester.auth.requestToken(); + var restClient = helper.AblyRest({ token: tokenDetails.token }); + await restClient.stats(); }); } @@ -580,147 +296,85 @@ define(['chai', 'shared_helper', 'async', 'globals'], function (chai, helper, as }); } - it('JWT request with invalid key', function (done) { + it('JWT request with invalid key', async function () { var keys = { keyName: 'invalid.invalid', keySecret: 'invalidinvalid' }; var authUrl = echoServer + '/createJWT' + utils.toQueryString(keys); var restJWTRequester = helper.AblyRest({ authUrl: authUrl }); - restJWTRequester.auth.requestToken(function (err, tokenDetails) { - if (err) { - done(err); - return; - } - var restClient = helper.AblyRest({ token: tokenDetails.token }); - restClient.stats(function (err, stats) { - try { - expect(err.code).to.equal(40400, 'Verify token is invalid because app id does not exist'); - expect(err.statusCode).to.equal(404, 'Verify token is invalid because app id does not exist'); - done(); - return; - } catch (err) { - done(err); - } - }); - }); + var tokenDetails = await restJWTRequester.auth.requestToken(); + var restClient = helper.AblyRest({ token: tokenDetails.token }); + try { + var stats = await restClient.stats(); + } catch (err) { + expect(err.code).to.equal(40400, 'Verify token is invalid because app id does not exist'); + expect(err.statusCode).to.equal(404, 'Verify token is invalid because app id does not exist'); + return; + } + throw new Error('Expected restClient.stats() to throw token error'); }); /* * RSA8g */ - it('Rest JWT with authCallback', function (done) { + it('Rest JWT with authCallback', async function () { var currentKey = helper.getTestApp().keys[0]; var keys = { keyName: currentKey.keyName, keySecret: currentKey.keySecret }; var authUrl = echoServer + '/createJWT' + utils.toQueryString(keys); var restJWTRequester = helper.AblyRest({ authUrl: authUrl }); var authCallback = function (tokenParams, callback) { - restJWTRequester.auth.requestToken(function (err, tokenDetails) { - if (err) { - done(err); - return; - } + restJWTRequester.auth.requestToken().then(function (tokenDetails) { callback(null, tokenDetails.token); }); }; var restClient = helper.AblyRest({ authCallback: authCallback }); - restClient.stats(function (err, stats) { - if (err) { - done(err); - return; - } - try { - expect(err).to.equal(null, 'Verify that the error is null'); - done(); - } catch (err) { - done(err); - } - }); + var stats = await restClient.stats(); }); /* * RSA8g */ - it('Rest JWT with authCallback and invalid keys', function (done) { + it('Rest JWT with authCallback and invalid keys', async function () { var keys = { keyName: 'invalid.invalid', keySecret: 'invalidinvalid' }; var authUrl = echoServer + '/createJWT' + utils.toQueryString(keys); var restJWTRequester = helper.AblyRest({ authUrl: authUrl }); var authCallback = function (tokenParams, callback) { - restJWTRequester.auth.requestToken(function (err, tokenDetails) { - if (err) { - done(err); - return; - } + restJWTRequester.auth.requestToken().then(function (tokenDetails) { callback(null, tokenDetails.token); }); }; var restClient = helper.AblyRest({ authCallback: authCallback }); - restClient.stats(function (err, stats) { - try { - expect(err.code).to.equal(40400, 'Verify code is 40400'); - expect(err.statusCode).to.equal(404, 'Verify token is invalid because app id does not exist'); - done(); - } catch (err) { - done(err); - } - }); + try { + await restClient.stats(); + } catch (err) { + expect(err.code).to.equal(40400, 'Verify code is 40400'); + expect(err.statusCode).to.equal(404, 'Verify token is invalid because app id does not exist'); + return; + } + throw new Error('Expected restClient.stats() to throw token error'); }); - it('authCallback is only invoked once on concurrent auth', function (done) { + it('authCallback is only invoked once on concurrent auth', async function () { var authCallbackInvocations = 0; function authCallback(tokenParams, callback) { authCallbackInvocations++; - rest.auth.createTokenRequest(tokenParams, callback); + rest.auth.createTokenRequest(tokenParams).then(function (tokenRequest) { + callback(null, tokenRequest); + }); } /* Example client-side using the token */ var restClient = helper.AblyRest({ authCallback: authCallback }); var channel = restClient.channels.get('auth_concurrent'); - async.parallel([channel.history.bind(channel), channel.history.bind(channel)], function (err) { - try { - expect(!err, err && helper.displayError(err)).to.be.ok; - expect(authCallbackInvocations).to.equal( - 1, - 'Check authCallback only invoked once -- was: ' + authCallbackInvocations - ); - done(); - } catch (err) { - done(err); - } - }); + await Promise.all([channel.history(), channel.history()]); + expect(authCallbackInvocations).to.equal( + 1, + 'Check authCallback only invoked once -- was: ' + authCallbackInvocations, + ); }); - - if (typeof Promise !== 'undefined') { - it('Promise based auth', function (done) { - var rest = helper.AblyRest({ promises: true }); - - var promise1 = rest.auth.requestToken(); - var promise2 = rest.auth.requestToken({ ttl: 200 }); - var promise3 = rest.auth.requestToken({ ttl: 200 }, { key: helper.getTestApp().keys[1].keyStr }); - var promise4 = rest.auth.createTokenRequest(); - var promise5 = rest.auth.createTokenRequest({ ttl: 200 }); - var promise6 = rest.auth.requestToken({ ttl: 200 }, { key: 'bad' })['catch'](function (err) { - expect(true, 'Token attempt with bad key was rejected').to.be.ok; - }); - - Promise.all([promise1, promise2, promise3, promise4, promise5, promise6]) - .then(function (results) { - try { - for (var i = 0; i < 5; i++) { - expect(results[i].token || results[i].nonce).to.be.ok; - } - done(); - } catch (err) { - done(err); - } - }) - ['catch'](function (err) { - done(err); - }); - }); - } }); }); diff --git a/test/rest/batch.test.js b/test/rest/batch.test.js index b4f747311f..e37f13b59d 100644 --- a/test/rest/batch.test.js +++ b/test/rest/batch.test.js @@ -1,8 +1,6 @@ 'use strict'; -// NOTE: All of the Promise-related tests in this file are intentionally a copy of the callback versions. This will allow us to simply remove the callback versions when merging this functionality into the integration/v2 branch (https://github.com/ably/ably-js/issues/1411). - -define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async, chai) { +define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { var expect = chai.expect; var closeAndFinish = helper.closeAndFinish; var randomString = helper.randomString; @@ -20,12 +18,12 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); describe('when invoked with an array of specs', function () { - it('performs a batch publish and returns an array of results', function (done) { + it('performs a batch publish and returns an array of results', async function () { const testApp = helper.getTestApp(); const rest = helper.AblyRest({ + promises: true, key: testApp.keys[2].keyStr /* we use this key so that some publishes fail due to capabilities */, }); - const verificationRest = helper.AblyRest(); const specs = [ { @@ -44,303 +42,98 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }, ]; - async.series( - [ - // First, we perform the batch publish request... - function (cb) { - rest.batchPublish(specs, function (err, batchResults) { - if (err) { - cb(err); - return; - } - - try { - expect(batchResults).to.have.lengthOf(specs.length); - - expect(batchResults[0].successCount).to.equal(1); - expect(batchResults[0].failureCount).to.equal(1); - - // Check the results of first BatchPublishSpec - - expect(batchResults[0].results).to.have.lengthOf(2); - - expect(batchResults[0].results[0].channel).to.equal('channel0'); - expect(batchResults[0].results[0].messageId).to.include(':0'); - expect('error' in batchResults[0].results[0]).to.be.false; - - expect(batchResults[0].results[1].channel).to.equal('channel3'); - expect('messageId' in batchResults[0].results[1]).to.be.false; - expect(batchResults[0].results[1].error.statusCode).to.equal(401); - - // Check the results of second BatchPublishSpec - - expect(batchResults[1].results).to.have.lengthOf(2); - - expect(batchResults[1].results[0].channel).to.equal('channel4'); - expect(batchResults[1].results[0].messageId).to.include(':0'); - expect('error' in batchResults[1].results[0]).to.be.false; - - expect(batchResults[1].results[1].channel).to.equal('channel5'); - expect('messageId' in batchResults[1].results[1]).to.be.false; - expect(batchResults[1].results[1].error.statusCode).to.equal(401); - } catch (err) { - cb(err); - return; - } - - cb(); - }); - }, - function (cb) { - // ...and now we use channel history to check that the expected messages have been published. - async.parallel( - [ - function (cb) { - const channel0 = verificationRest.channels.get('channel0'); - channel0.history({ limit: 2 }, function (err, result) { - if (err) { - cb(err); - return; - } - - const data = new Set([result.items[0].data, result.items[1].data]); - - try { - expect(data).to.deep.equal(new Set(['message1', 'message2'])); - } catch (err) { - cb(err); - return; - } - - cb(); - }); - }, - function (cb) { - const channel4 = verificationRest.channels.get('channel4'); - channel4.history({ limit: 2 }, function (err, result) { - if (err) { - cb(err); - return; - } - - const data = new Set([result.items[0].data, result.items[1].data]); - try { - expect(data).to.deep.equal(new Set(['message3', 'message4'])); - } catch (err) { - cb(err); - return; - } - - cb(); - }); - }, - ], - cb - ); - }, - ], - done - ); - }); - }); + // First, we perform the batch publish request... + const batchResults = await rest.batchPublish(specs); - describe('when invoked with a single spec', function () { - it('performs a batch publish and returns a single result', function (done) { - const testApp = helper.getTestApp(); - const rest = helper.AblyRest({ - key: testApp.keys[2].keyStr /* we use this key so that some publishes fail due to capabilities */, - }); - const verificationRest = helper.AblyRest(); + expect(batchResults).to.have.lengthOf(specs.length); - const spec = { - channels: [ - 'channel0' /* key allows publishing to this channel */, - 'channel3' /* key does not allow publishing to this channel */, - ], - messages: [{ data: 'message1' }, { data: 'message2' }], - }; + expect(batchResults[0].successCount).to.equal(1); + expect(batchResults[0].failureCount).to.equal(1); - async.series( - [ - // First, we perform the batch publish request... - function (cb) { - rest.batchPublish(spec, function (err, batchResult) { - if (err) { - cb(err); - return; - } - - try { - expect(batchResult.successCount).to.equal(1); - expect(batchResult.failureCount).to.equal(1); - - expect(batchResult.results).to.have.lengthOf(2); - - expect(batchResult.results[0].channel).to.equal('channel0'); - expect(batchResult.results[0].messageId).to.include(':0'); - expect('error' in batchResult.results[0]).to.be.false; - - expect(batchResult.results[1].channel).to.equal('channel3'); - expect('messageId' in batchResult.results[1]).to.be.false; - expect(batchResult.results[1].error.statusCode).to.equal(401); - } catch (err) { - cb(err); - return; - } - - cb(); - }); - }, - function (cb) { - // ...and now we use channel history to check that the expected messages have been published. - const channel0 = verificationRest.channels.get('channel0'); - channel0.history({ limit: 2 }, function (err, result) { - if (err) { - cb(err); - return; - } - - const data = new Set([result.items[0].data, result.items[1].data]); - try { - expect(data).to.deep.equal(new Set(['message1', 'message2'])); - } catch (err) { - cb(err); - return; - } - - cb(); - }); - }, - ], - done - ); - }); - }); + // Check the results of first BatchPublishSpec - if (typeof Promise !== 'undefined') { - describe('using promises', function () { - describe('when invoked with an array of specs', function () { - it('performs a batch publish and returns an array of results', async function () { - const testApp = helper.getTestApp(); - const rest = helper.AblyRest({ - promises: true, - key: testApp.keys[2].keyStr /* we use this key so that some publishes fail due to capabilities */, - }); + expect(batchResults[0].results).to.have.lengthOf(2); - const specs = [ - { - channels: [ - 'channel0' /* key allows publishing to this channel */, - 'channel3' /* key does not allow publishing to this channel */, - ], - messages: [{ data: 'message1' }, { data: 'message2' }], - }, - { - channels: [ - 'channel4' /* key allows publishing to this channel */, - 'channel5' /* key does not allow publishing to this channel */, - ], - messages: [{ data: 'message3' }, { data: 'message4' }], - }, - ]; + expect(batchResults[0].results[0].channel).to.equal('channel0'); + expect(batchResults[0].results[0].messageId).to.include(':0'); + expect('error' in batchResults[0].results[0]).to.be.false; - // First, we perform the batch publish request... - const batchResults = await rest.batchPublish(specs); + expect(batchResults[0].results[1].channel).to.equal('channel3'); + expect('messageId' in batchResults[0].results[1]).to.be.false; + expect(batchResults[0].results[1].error.statusCode).to.equal(401); - expect(batchResults).to.have.lengthOf(specs.length); + // Check the results of second BatchPublishSpec - expect(batchResults[0].successCount).to.equal(1); - expect(batchResults[0].failureCount).to.equal(1); + expect(batchResults[1].results).to.have.lengthOf(2); - // Check the results of first BatchPublishSpec + expect(batchResults[1].results[0].channel).to.equal('channel4'); + expect(batchResults[1].results[0].messageId).to.include(':0'); + expect('error' in batchResults[1].results[0]).to.be.false; - expect(batchResults[0].results).to.have.lengthOf(2); + expect(batchResults[1].results[1].channel).to.equal('channel5'); + expect('messageId' in batchResults[1].results[1]).to.be.false; + expect(batchResults[1].results[1].error.statusCode).to.equal(401); - expect(batchResults[0].results[0].channel).to.equal('channel0'); - expect(batchResults[0].results[0].messageId).to.include(':0'); - expect('error' in batchResults[0].results[0]).to.be.false; + // ...and now we use channel history to check that the expected messages have been published. + const verificationRest = helper.AblyRest({ promises: true }); - expect(batchResults[0].results[1].channel).to.equal('channel3'); - expect('messageId' in batchResults[0].results[1]).to.be.false; - expect(batchResults[0].results[1].error.statusCode).to.equal(401); + const channel0 = verificationRest.channels.get('channel0'); + const channel0HistoryPromise = channel0.history({ limit: 2 }); - // Check the results of second BatchPublishSpec + const channel4 = verificationRest.channels.get('channel4'); + const channel4HistoryPromise = channel4.history({ limit: 2 }); - expect(batchResults[1].results).to.have.lengthOf(2); + const [channel0History, channel4History] = await Promise.all([channel0HistoryPromise, channel4HistoryPromise]); - expect(batchResults[1].results[0].channel).to.equal('channel4'); - expect(batchResults[1].results[0].messageId).to.include(':0'); - expect('error' in batchResults[1].results[0]).to.be.false; + const channel0HistoryData = new Set([channel0History.items[0].data, channel0History.items[1].data]); + expect(channel0HistoryData).to.deep.equal(new Set(['message1', 'message2'])); - expect(batchResults[1].results[1].channel).to.equal('channel5'); - expect('messageId' in batchResults[1].results[1]).to.be.false; - expect(batchResults[1].results[1].error.statusCode).to.equal(401); + const channel4HistoryData = new Set([channel4History.items[0].data, channel4History.items[1].data]); + expect(channel4HistoryData).to.deep.equal(new Set(['message3', 'message4'])); + }); + }); - // ...and now we use channel history to check that the expected messages have been published. - const verificationRest = helper.AblyRest({ promises: true }); + describe('when invoked with a single spec', function () { + it('performs a batch publish and returns a single result', async function () { + const testApp = helper.getTestApp(); + const rest = helper.AblyRest({ + promises: true, + key: testApp.keys[2].keyStr /* we use this key so that some publishes fail due to capabilities */, + }); - const channel0 = verificationRest.channels.get('channel0'); - const channel0HistoryPromise = channel0.history({ limit: 2 }); + const spec = { + channels: [ + 'channel0' /* key allows publishing to this channel */, + 'channel3' /* key does not allow publishing to this channel */, + ], + messages: [{ data: 'message1' }, { data: 'message2' }], + }; - const channel4 = verificationRest.channels.get('channel4'); - const channel4HistoryPromise = channel4.history({ limit: 2 }); + // First, we perform the batch publish request... + const batchResult = await rest.batchPublish(spec); - const [channel0History, channel4History] = await Promise.all([ - channel0HistoryPromise, - channel4HistoryPromise, - ]); + expect(batchResult.successCount).to.equal(1); + expect(batchResult.failureCount).to.equal(1); - const channel0HistoryData = new Set([channel0History.items[0].data, channel0History.items[1].data]); - expect(channel0HistoryData).to.deep.equal(new Set(['message1', 'message2'])); + expect(batchResult.results).to.have.lengthOf(2); - const channel4HistoryData = new Set([channel4History.items[0].data, channel4History.items[1].data]); - expect(channel4HistoryData).to.deep.equal(new Set(['message3', 'message4'])); - }); - }); + expect(batchResult.results[0].channel).to.equal('channel0'); + expect(batchResult.results[0].messageId).to.include(':0'); + expect('error' in batchResult.results[0]).to.be.false; - describe('when invoked with a single spec', function () { - it('performs a batch publish and returns a single result', async function () { - const testApp = helper.getTestApp(); - const rest = helper.AblyRest({ - promises: true, - key: testApp.keys[2].keyStr /* we use this key so that some publishes fail due to capabilities */, - }); - - const spec = { - channels: [ - 'channel0' /* key allows publishing to this channel */, - 'channel3' /* key does not allow publishing to this channel */, - ], - messages: [{ data: 'message1' }, { data: 'message2' }], - }; - - // First, we perform the batch publish request... - const batchResult = await rest.batchPublish(spec); - - expect(batchResult.successCount).to.equal(1); - expect(batchResult.failureCount).to.equal(1); - - expect(batchResult.results).to.have.lengthOf(2); - - expect(batchResult.results[0].channel).to.equal('channel0'); - expect(batchResult.results[0].messageId).to.include(':0'); - expect('error' in batchResult.results[0]).to.be.false; - - expect(batchResult.results[1].channel).to.equal('channel3'); - expect('messageId' in batchResult.results[1]).to.be.false; - expect(batchResult.results[1].error.statusCode).to.equal(401); - - // ...and now we use channel history to check that the expected messages have been published. - const verificationRest = helper.AblyRest({ promises: true }); - const channel0 = verificationRest.channels.get('channel0'); - const channel0History = await channel0.history({ limit: 2 }); - - const channel0HistoryData = new Set([channel0History.items[0].data, channel0History.items[1].data]); - expect(channel0HistoryData).to.deep.equal(new Set(['message1', 'message2'])); - }); - }); + expect(batchResult.results[1].channel).to.equal('channel3'); + expect('messageId' in batchResult.results[1]).to.be.false; + expect(batchResult.results[1].error.statusCode).to.equal(401); + + // ...and now we use channel history to check that the expected messages have been published. + const verificationRest = helper.AblyRest({ promises: true }); + const channel0 = verificationRest.channels.get('channel0'); + const channel0History = await channel0.history({ limit: 2 }); + + const channel0HistoryData = new Set([channel0History.items[0].data, channel0History.items[1].data]); + expect(channel0HistoryData).to.deep.equal(new Set(['message1', 'message2'])); }); - } + }); }); describe('rest/batchPresence', function () { @@ -355,122 +148,53 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }); - it('performs a batch presence fetch and returns a result', function (done) { + it('performs a batch presence fetch and returns a result', async function () { const testApp = helper.getTestApp(); const rest = helper.AblyRest({ + promises: true, key: testApp.keys[2].keyStr /* we use this key so that some presence fetches fail due to capabilities */, }); - const presenceEnterRealtime = helper.AblyRealtime( - { - clientId: 'batchPresenceTest', - } /* note that the key used here has no capability limitations, so that we can use this instance to enter presence below */ - ); + const presenceEnterRealtime = helper.AblyRealtime({ + promises: true, + clientId: + 'batchPresenceTest' /* note that the key used here has no capability limitations, so that we can use this instance to enter presence below */, + }); const channelNames = [ 'channel0' /* key does not allow presence on this channel */, 'channel4' /* key allows presence on this channel */, ]; - async.series( - [ - // First, we enter presence on two channels... - function (cb) { - presenceEnterRealtime.channels.get('channel0').presence.enter(cb); - }, - function (cb) { - presenceEnterRealtime.channels.get('channel4').presence.enter(cb); - }, - // ...and now we perform the batch presence request. - function (cb) { - rest.batchPresence(channelNames, function (err, batchResult) { - if (err) { - cb(err); - } - - try { - expect(batchResult.successCount).to.equal(1); - expect(batchResult.failureCount).to.equal(1); - - // Check that the channel0 presence fetch request fails (due to key’s capabilities, as mentioned above) - - expect(batchResult.results[0].channel).to.equal('channel0'); - expect('presence' in batchResult.results[0]).to.be.false; - expect(batchResult.results[0].error.statusCode).to.equal(401); - - // Check that the channel4 presence fetch request reflects the presence enter performed above - - expect(batchResult.results[1].channel).to.equal('channel4'); - expect(batchResult.results[1].presence).to.have.lengthOf(1); - expect(batchResult.results[1].presence[0].clientId).to.equal('batchPresenceTest'); - expect('error' in batchResult.results[1]).to.be.false; - } catch (err) { - cb(err); - return; - } - - cb(); - }); - }, - function (cb) { - closeAndFinish(cb, presenceEnterRealtime); - }, - ], - done - ); - }); - - if (typeof Promise !== 'undefined') { - describe('using promises', function () { - it('performs a batch presence fetch and returns a result', async function () { - const testApp = helper.getTestApp(); - const rest = helper.AblyRest({ - promises: true, - key: testApp.keys[2].keyStr /* we use this key so that some presence fetches fail due to capabilities */, - }); - - const presenceEnterRealtime = helper.AblyRealtime({ - promises: true, - clientId: - 'batchPresenceTest' /* note that the key used here has no capability limitations, so that we can use this instance to enter presence below */, - }); - - const channelNames = [ - 'channel0' /* key does not allow presence on this channel */, - 'channel4' /* key allows presence on this channel */, - ]; + // First, we enter presence on two channels... + await presenceEnterRealtime.channels.get('channel0').presence.enter(); + await presenceEnterRealtime.channels.get('channel4').presence.enter(); - // First, we enter presence on two channels... - await presenceEnterRealtime.channels.get('channel0').presence.enter(); - await presenceEnterRealtime.channels.get('channel4').presence.enter(); + // ...and now we perform the batch presence request. + const batchResult = await rest.batchPresence(channelNames); - // ...and now we perform the batch presence request. - const batchResult = await rest.batchPresence(channelNames); + expect(batchResult.successCount).to.equal(1); + expect(batchResult.failureCount).to.equal(1); - expect(batchResult.successCount).to.equal(1); - expect(batchResult.failureCount).to.equal(1); + // Check that the channel0 presence fetch request fails (due to key’s capabilities, as mentioned above) - // Check that the channel0 presence fetch request fails (due to key’s capabilities, as mentioned above) + expect(batchResult.results[0].channel).to.equal('channel0'); + expect('presence' in batchResult.results[0]).to.be.false; + expect(batchResult.results[0].error.statusCode).to.equal(401); - expect(batchResult.results[0].channel).to.equal('channel0'); - expect('presence' in batchResult.results[0]).to.be.false; - expect(batchResult.results[0].error.statusCode).to.equal(401); + // Check that the channel4 presence fetch request reflects the presence enter performed above - // Check that the channel4 presence fetch request reflects the presence enter performed above + expect(batchResult.results[1].channel).to.equal('channel4'); + expect(batchResult.results[1].presence).to.have.lengthOf(1); + expect(batchResult.results[1].presence[0].clientId).to.equal('batchPresenceTest'); + expect('error' in batchResult.results[1]).to.be.false; - expect(batchResult.results[1].channel).to.equal('channel4'); - expect(batchResult.results[1].presence).to.have.lengthOf(1); - expect(batchResult.results[1].presence[0].clientId).to.equal('batchPresenceTest'); - expect('error' in batchResult.results[1]).to.be.false; - - await new Promise((resolve, reject) => { - closeAndFinish((err) => { - err ? reject(err) : resolve(); - }, presenceEnterRealtime); - }); - }); + await new Promise((resolve, reject) => { + closeAndFinish((err) => { + err ? reject(err) : resolve(); + }, presenceEnterRealtime); }); - } + }); }); describe('rest/revokeTokens', function () { @@ -485,260 +209,125 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }); - it('revokes tokens matching the given specifiers', function (done) { + it('revokes tokens matching the given specifiers', async function () { const testApp = helper.getTestApp(); const rest = helper.AblyRest({ + promises: true, key: testApp.keys[4].keyStr /* this key has revocableTokens enabled */, }); const clientId1 = `clientId1-${randomString()}`; const clientId2 = `clientId2-${randomString()}`; - let clientId1TokenDetails; - let clientId2TokenDetails; - - let clientId1Realtime; - let clientId2Realtime; - - // These (result, callback) pairings are a dance to simulate a Promise (specificially the fact that the order of the { resolve, then } operations doesn’t matter); see the promise-based version of this test - let clientId1RealtimeDisconnectedStateChange; - let onClientId1RealtimeDisconnected; - let clientId2RealtimeDisconnectedStateChange; - let onClientId2RealtimeDisconnected; - - async.series( - [ - function (cb) { - // First, we fetch tokens for a couple of different clientIds... - async.parallel( - [ - function (cb) { - rest.auth.requestToken({ clientId: clientId1 }, function (err, tokenDetails) { - if (err) { - cb(err); - return; - } - - clientId1TokenDetails = tokenDetails; - cb(); - }); - }, - function (cb) { - rest.auth.requestToken({ clientId: clientId2 }, function (err, tokenDetails) { - if (err) { - cb(err); - return; - } - - clientId2TokenDetails = tokenDetails; - cb(); - }); - }, - ], - cb - ); - }, - function (cb) { - // ...then, we set up Realtime instances that use these tokens and wait for them to become CONNECTED... - async.parallel( - [ - function (cb) { - clientId1Realtime = helper.AblyRealtime({ token: clientId1TokenDetails }); - clientId1Realtime.connection.once('connected', function () { - cb(); - }); - }, - function (cb) { - clientId2Realtime = helper.AblyRealtime({ token: clientId2TokenDetails }); - clientId2Realtime.connection.once('connected', function () { - cb(); - }); - }, - ], - cb - ); - }, - function (cb) { - // ...then, we set up listeners that will record the state change when these Realtime instances become DISCONNECTED (we need to set up these listeners here, before performing the revocation request, else we might miss the DISCONNECTED state changes that the token revocation provokes, and end up only seeing the subsequent RSA4a2-induced FAILED state change, which due to https://github.com/ably/ably-js/issues/1409 does not expose the 40141 "token revoked" error code)... - // - // Note: - // - // We use Realtime instances for verifying the side effects of a token revocation, as opposed to, say, trying to perform a REST request, because the nature of the Ably service is that token verification may take a small delay to become active, and so there's no guarantee that a REST request peformed immediately after a revocation request would fail. See discussion at https://ably-real-time.slack.com/archives/C030C5YLY/p1690322740850269?thread_ts=1690315022.372729&cid=C030C5YLY. - - clientId1Realtime.connection.once('disconnected', function (stateChange) { - clientId1RealtimeDisconnectedStateChange = stateChange; - if (onClientId1RealtimeDisconnected) { - onClientId1RealtimeDisconnected(); - } - }); - - clientId2Realtime.connection.once('disconnected', function (stateChange) { - clientId2RealtimeDisconnectedStateChange = stateChange; - if (onClientId2RealtimeDisconnected) { - onClientId2RealtimeDisconnected(); - } - }); - - cb(); - }, - function (cb) { - // ...then, we revoke all tokens for these clientIds... - - const specifiers = [ - { type: 'clientId', value: clientId1 }, - { type: 'clientId', value: clientId2 }, - { type: 'invalidType', value: 'abc' }, // we include an invalid specifier type to provoke a non-zero failureCount - ]; - - rest.auth.revokeTokens(specifiers, function (err, result) { - if (err) { - cb(err); - return; - } - - try { - // ...and check the response from the revocation request... - expect(result.successCount).to.equal(2); - expect(result.failureCount).to.equal(1); - expect(result.results).to.have.lengthOf(3); - - expect(result.results[0].target).to.equal(`clientId:${clientId1}`); - expect(typeof result.results[0].issuedBefore).to.equal('number'); - expect(typeof result.results[0].appliesAt).to.equal('number'); - expect('error' in result.results[0]).to.be.false; - - expect(result.results[1].target).to.equal(`clientId:${clientId2}`); - expect(typeof result.results[1].issuedBefore).to.equal('number'); - expect(typeof result.results[1].appliesAt).to.equal('number'); - expect('error' in result.results[1]).to.be.false; - - expect(result.results[2].target).to.equal('invalidType:abc'); - expect(result.results[2].error.statusCode).to.equal(400); - } catch (err) { - cb(err); - return; - } - - cb(); - }); - }, + // First, we fetch tokens for a couple of different clientIds... + const [clientId1TokenDetails, clientId2TokenDetails] = await Promise.all([ + rest.auth.requestToken({ clientId: clientId1 }), + rest.auth.requestToken({ clientId: clientId2 }), + ]); - // ...and then, we check that the Realtime instances transition to the DISCONNECTED state due to a "token revoked" (40141) error. - function (cb) { - async.parallel( - [ - function (cb) { - onClientId1RealtimeDisconnected = function () { - try { - expect(clientId1RealtimeDisconnectedStateChange.reason.code).to.equal(40141 /* token revoked */); - } catch (err) { - cb(err); - return; - } - cb(); - }; - if (clientId1RealtimeDisconnectedStateChange) { - onClientId1RealtimeDisconnected(); - } - }, - function (cb) { - onClientId2RealtimeDisconnected = function () { - try { - expect(clientId2RealtimeDisconnectedStateChange.reason.code).to.equal(40141 /* token revoked */); - } catch (err) { - cb(err); - return; - } - cb(); - }; - if (clientId2RealtimeDisconnectedStateChange) { - onClientId2RealtimeDisconnected(); - } - }, - ], - cb - ); - }, - function (cb) { - async.parallel( - [ - function (cb) { - closeAndFinish(cb, clientId1Realtime); - }, - function (cb) { - closeAndFinish(cb, clientId2Realtime); - }, - ], - cb - ); - }, - ], - done + // ...then, we set up Realtime instances that use these tokens and wait for them to become CONNECTED... + const clientId1Realtime = helper.AblyRealtime({ + promises: true, + token: clientId1TokenDetails, + }); + const clientId2Realtime = helper.AblyRealtime({ + promises: true, + token: clientId2TokenDetails, + }); + + await Promise.all([ + clientId1Realtime.connection.once('connected'), + clientId2Realtime.connection.once('connected'), + ]); + + // ...then, we set up listeners that will record the state change when these Realtime instances become DISCONNECTED (we need to set up these listeners here, before performing the revocation request, else we might miss the DISCONNECTED state changes that the token revocation provokes, and end up only seeing the subsequent RSA4a2-induced FAILED state change, which due to https://github.com/ably/ably-js/issues/1409 does not expose the 40141 "token revoked" error code)... + // + // Note: + // + // We use Realtime instances for verifying the side effects of a token revocation, as opposed to, say, trying to perform a REST request, because the nature of the Ably service is that token revocation may take a small delay to become active, and so there's no guarantee that a REST request peformed immediately after a revocation request would fail. See discussion at https://ably-real-time.slack.com/archives/C030C5YLY/p1690322740850269?thread_ts=1690315022.372729&cid=C030C5YLY. + const clientId1RealtimeDisconnectedStateChangePromise = clientId1Realtime.connection.once('disconnected'); + const clientId2RealtimeDisconnectedStateChangePromise = clientId2Realtime.connection.once('disconnected'); + + // ...then, we revoke all tokens for these clientIds... + + const specifiers = [ + { type: 'clientId', value: clientId1 }, + { type: 'clientId', value: clientId2 }, + { type: 'invalidType', value: 'abc' }, // we include an invalid specifier type to provoke a non-zero failureCount + ]; + + const result = await rest.auth.revokeTokens(specifiers); + + // ...and check the response from the revocation request... + expect(result.successCount).to.equal(2); + expect(result.failureCount).to.equal(1); + expect(result.results).to.have.lengthOf(3); + + expect(result.results[0].target).to.equal(`clientId:${clientId1}`); + expect(typeof result.results[0].issuedBefore).to.equal('number'); + expect(typeof result.results[0].appliesAt).to.equal('number'); + expect('error' in result.results[0]).to.be.false; + + expect(result.results[1].target).to.equal(`clientId:${clientId2}`); + expect(typeof result.results[1].issuedBefore).to.equal('number'); + expect(typeof result.results[1].appliesAt).to.equal('number'); + expect('error' in result.results[1]).to.be.false; + + expect(result.results[2].target).to.equal('invalidType:abc'); + expect(result.results[2].error.statusCode).to.equal(400); + + // ...and then, we check that the Realtime instances transition to the DISCONNECTED state due to a "token revoked" (40141) error. + const [clientId1RealtimeDisconnectedStateChange, clientId2RealtimeDisconnectedStateChange] = await Promise.all([ + clientId1RealtimeDisconnectedStateChangePromise, + clientId2RealtimeDisconnectedStateChangePromise, + ]); + + expect(clientId1RealtimeDisconnectedStateChange.reason.code).to.equal(40141 /* token revoked */); + expect(clientId2RealtimeDisconnectedStateChange.reason.code).to.equal(40141 /* token revoked */); + + await Promise.all( + [clientId1Realtime, clientId2Realtime].map((realtime) => { + new Promise((resolve, reject) => { + closeAndFinish((err) => { + err ? reject(err) : resolve(); + }, realtime); + }); + }), ); }); - it('accepts optional issuedBefore and allowReauthMargin parameters', function (done) { + it('accepts optional issuedBefore and allowReauthMargin parameters', async function () { const testApp = helper.getTestApp(); const rest = helper.AblyRest({ + promises: true, key: testApp.keys[4].keyStr /* this key has revocableTokens enabled */, }); const clientId = `clientId-${randomString()}`; - let serverTimeAtStartOfTest; - - async.series( - [ - function (cb) { - rest.time(function (err, time) { - if (err) { - cb(err); - return; - } - serverTimeAtStartOfTest = time; - cb(); - }); - }, - function (cb) { - const issuedBefore = serverTimeAtStartOfTest - 20 * 60 * 1000; // i.e. ~20 minutes ago (arbitrarily chosen) - - rest.auth.revokeTokens( - [{ type: 'clientId', value: clientId }], - { issuedBefore, allowReauthMargin: true }, - function (err, result) { - if (err) { - cb(err); - return; - } - - try { - expect(result.results[0].issuedBefore).to.equal(issuedBefore); - - // Verify the expected side effect of allowReauthMargin, which is to delay the revocation by 30 seconds - const serverTimeThirtySecondsAfterStartOfTest = serverTimeAtStartOfTest + 30 * 1000; - expect(result.results[0].appliesAt).to.be.greaterThan(serverTimeThirtySecondsAfterStartOfTest); - } catch (err) { - cb(err); - return; - } - - cb(); - } - ); - }, - ], - done - ); + const serverTimeAtStartOfTest = await rest.time(); + const issuedBefore = serverTimeAtStartOfTest - 20 * 60 * 1000; // i.e. ~20 minutes ago (arbitrarily chosen) + + const result = await rest.auth.revokeTokens([{ type: 'clientId', value: clientId }], { + issuedBefore, + allowReauthMargin: true, + }); + + expect(result.results[0].issuedBefore).to.equal(issuedBefore); + + // Verify the expected side effect of allowReauthMargin, which is to delay the revocation by 30 seconds + const serverTimeThirtySecondsAfterStartOfTest = serverTimeAtStartOfTest + 30 * 1000; + expect(result.results[0].appliesAt).to.be.greaterThan(serverTimeThirtySecondsAfterStartOfTest); }); - it('throws an error when using token auth', function () { + it('throws an error when using token auth', async function () { const rest = helper.AblyRest({ useTokenAuth: true, }); let verifiedError = false; try { - rest.auth.revokeTokens([{ type: 'clientId', value: 'clientId1' }], function () {}); + await rest.auth.revokeTokens([{ type: 'clientId', value: 'clientId1' }], function () {}); } catch (err) { expect(err.statusCode).to.equal(401); expect(err.code).to.equal(40162); @@ -747,121 +336,5 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async expect(verifiedError).to.be.true; }); - - if (typeof Promise !== 'undefined') { - describe('using promises', function () { - it('revokes tokens matching the given specifiers', async function () { - const testApp = helper.getTestApp(); - const rest = helper.AblyRest({ - promises: true, - key: testApp.keys[4].keyStr /* this key has revocableTokens enabled */, - }); - - const clientId1 = `clientId1-${randomString()}`; - const clientId2 = `clientId2-${randomString()}`; - - // First, we fetch tokens for a couple of different clientIds... - const [clientId1TokenDetails, clientId2TokenDetails] = await Promise.all([ - rest.auth.requestToken({ clientId: clientId1 }), - rest.auth.requestToken({ clientId: clientId2 }), - ]); - - // ...then, we set up Realtime instances that use these tokens and wait for them to become CONNECTED... - const clientId1Realtime = helper.AblyRealtime({ - promises: true, - token: clientId1TokenDetails, - }); - const clientId2Realtime = helper.AblyRealtime({ - promises: true, - token: clientId2TokenDetails, - }); - - await Promise.all([ - clientId1Realtime.connection.once('connected'), - clientId2Realtime.connection.once('connected'), - ]); - - // ...then, we set up listeners that will record the state change when these Realtime instances become DISCONNECTED (we need to set up these listeners here, before performing the revocation request, else we might miss the DISCONNECTED state changes that the token revocation provokes, and end up only seeing the subsequent RSA4a2-induced FAILED state change, which due to https://github.com/ably/ably-js/issues/1409 does not expose the 40141 "token revoked" error code)... - // - // Note: - // - // We use Realtime instances for verifying the side effects of a token revocation, as opposed to, say, trying to perform a REST request, because the nature of the Ably service is that token revocation may take a small delay to become active, and so there's no guarantee that a REST request peformed immediately after a revocation request would fail. See discussion at https://ably-real-time.slack.com/archives/C030C5YLY/p1690322740850269?thread_ts=1690315022.372729&cid=C030C5YLY. - const clientId1RealtimeDisconnectedStateChangePromise = clientId1Realtime.connection.once('disconnected'); - const clientId2RealtimeDisconnectedStateChangePromise = clientId2Realtime.connection.once('disconnected'); - - // ...then, we revoke all tokens for these clientIds... - - const specifiers = [ - { type: 'clientId', value: clientId1 }, - { type: 'clientId', value: clientId2 }, - { type: 'invalidType', value: 'abc' }, // we include an invalid specifier type to provoke a non-zero failureCount - ]; - - const result = await rest.auth.revokeTokens(specifiers); - - // ...and check the response from the revocation request... - expect(result.successCount).to.equal(2); - expect(result.failureCount).to.equal(1); - expect(result.results).to.have.lengthOf(3); - - expect(result.results[0].target).to.equal(`clientId:${clientId1}`); - expect(typeof result.results[0].issuedBefore).to.equal('number'); - expect(typeof result.results[0].appliesAt).to.equal('number'); - expect('error' in result.results[0]).to.be.false; - - expect(result.results[1].target).to.equal(`clientId:${clientId2}`); - expect(typeof result.results[1].issuedBefore).to.equal('number'); - expect(typeof result.results[1].appliesAt).to.equal('number'); - expect('error' in result.results[1]).to.be.false; - - expect(result.results[2].target).to.equal('invalidType:abc'); - expect(result.results[2].error.statusCode).to.equal(400); - - // ...and then, we check that the Realtime instances transition to the DISCONNECTED state due to a "token revoked" (40141) error. - const [clientId1RealtimeDisconnectedStateChange, clientId2RealtimeDisconnectedStateChange] = - await Promise.all([ - clientId1RealtimeDisconnectedStateChangePromise, - clientId2RealtimeDisconnectedStateChangePromise, - ]); - - expect(clientId1RealtimeDisconnectedStateChange.reason.code).to.equal(40141 /* token revoked */); - expect(clientId2RealtimeDisconnectedStateChange.reason.code).to.equal(40141 /* token revoked */); - - await Promise.all( - [clientId1Realtime, clientId2Realtime].map((realtime) => { - new Promise((resolve, reject) => { - closeAndFinish((err) => { - err ? reject(err) : resolve(); - }, realtime); - }); - }) - ); - }); - - it('accepts optional issuedBefore and allowReauthMargin parameters', async function () { - const testApp = helper.getTestApp(); - const rest = helper.AblyRest({ - promises: true, - key: testApp.keys[4].keyStr /* this key has revocableTokens enabled */, - }); - - const clientId = `clientId-${randomString()}`; - - const serverTimeAtStartOfTest = await rest.time(); - const issuedBefore = serverTimeAtStartOfTest - 20 * 60 * 1000; // i.e. ~20 minutes ago (arbitrarily chosen) - - const result = await rest.auth.revokeTokens([{ type: 'clientId', value: clientId }], { - issuedBefore, - allowReauthMargin: true, - }); - - expect(result.results[0].issuedBefore).to.equal(issuedBefore); - - // Verify the expected side effect of allowReauthMargin, which is to delay the revocation by 30 seconds - const serverTimeThirtySecondsAfterStartOfTest = serverTimeAtStartOfTest + 30 * 1000; - expect(result.results[0].appliesAt).to.be.greaterThan(serverTimeThirtySecondsAfterStartOfTest); - }); - }); - } }); }); diff --git a/test/rest/bufferutils.test.js b/test/rest/bufferutils.test.js index 86b56da784..58837fff88 100644 --- a/test/rest/bufferutils.test.js +++ b/test/rest/bufferutils.test.js @@ -6,9 +6,6 @@ define(['ably', 'chai'], function (Ably, chai) { var testString = 'test'; var testBase64 = 'dGVzdA=='; var testHex = '74657374'; - function isWordArray(ob) { - return ob !== null && ob !== undefined && ob.sigBytes !== undefined; - } describe('rest/bufferutils', function () { it('Basic encoding and decoding', function () { @@ -22,16 +19,16 @@ define(['ably', 'chai'], function (Ably, chai) { /* compare */ expect( - BufferUtils.bufferCompare(BufferUtils.utf8Encode(testString), BufferUtils.utf8Encode(testString)) - ).to.equal(0); + BufferUtils.areBuffersEqual(BufferUtils.utf8Encode(testString), BufferUtils.utf8Encode(testString)), + ).to.equal(true); expect( - BufferUtils.bufferCompare(BufferUtils.utf8Encode(testString), BufferUtils.utf8Encode('other')) - ).to.not.equal(0); + BufferUtils.areBuffersEqual(BufferUtils.utf8Encode(testString), BufferUtils.utf8Encode('other')), + ).to.not.equal(true); }); /* In node it's idiomatic for most methods dealing with binary data to * return Buffers. In the browser it's more idiomatic to return - * ArrayBuffers (or in browser too old to support ArrayBuffer, wordarrays). */ + * ArrayBuffers */ it('BufferUtils return correct types', function () { if (typeof Buffer !== 'undefined') { /* node */ @@ -40,23 +37,13 @@ define(['ably', 'chai'], function (Ably, chai) { expect(BufferUtils.base64Decode(testBase64).constructor).to.equal(Buffer); expect(BufferUtils.toBuffer(BufferUtils.utf8Encode(testString)).constructor).to.equal(Buffer); expect(BufferUtils.toArrayBuffer(BufferUtils.utf8Encode(testString)).constructor).to.equal(ArrayBuffer); - } else if (typeof ArrayBuffer !== 'undefined') { + } else { /* modern browsers */ - if (typeof TextDecoder !== 'undefined') { - expect(BufferUtils.utf8Encode(testString).constructor).to.equal(ArrayBuffer); - } else { - expect(isWordArray(BufferUtils.utf8Encode(testString))).to.be.ok; - } + expect(BufferUtils.utf8Encode(testString).constructor).to.equal(ArrayBuffer); expect(BufferUtils.hexDecode(testHex).constructor).to.equal(ArrayBuffer); expect(BufferUtils.base64Decode(testBase64).constructor).to.equal(ArrayBuffer); expect(BufferUtils.toBuffer(BufferUtils.utf8Encode(testString)).constructor).to.equal(Uint8Array); expect(BufferUtils.toArrayBuffer(BufferUtils.utf8Encode(testString)).constructor).to.equal(ArrayBuffer); - } else { - /* legacy browsers */ - expect(isWordArray(BufferUtils.utf8Encode(testString))).to.be.ok; - expect(isWordArray(BufferUtils.hexDecode(testHex))).to.be.ok; - expect(isWordArray(BufferUtils.base64Decode(testBase64))).to.be.ok; - expect(isWordArray(BufferUtils.toWordArray(BufferUtils.utf8Encode(testString)))).to.be.ok; } }); }); diff --git a/test/rest/capability.test.js b/test/rest/capability.test.js index 60e4172e73..6684cce8b4 100644 --- a/test/rest/capability.test.js +++ b/test/rest/capability.test.js @@ -19,266 +19,148 @@ define(['shared_helper', 'chai'], function (helper, chai) { this.timeout(60 * 1000); before(function (done) { - helper.setupApp(function (err) { - if (err) { - done(err); - return; - } - - rest = helper.AblyRest(); + helper.setupApp(function () { + rest = helper.AblyRest({ queryTime: true }); testApp = helper.getTestApp(); - rest.time(function (err, time) { - if (err) { + rest + .time() + .then(function (time) { + currentTime = time; + expect(true, 'Obtained time').to.be.ok; + done(); + }) + .catch(function (err) { done(err); - return; - } - currentTime = time; - done(); - }); + }); }); }); - it('Blanket intersection with specified key', function (done) { + it('Blanket intersection with specified key', async function () { var testKeyOpts = { key: testApp.keys[1].keyStr }; var testCapability = JSON.parse(testApp.keys[1].capability); - rest.auth.requestToken(null, testKeyOpts, function (err, tokenDetails) { - if (err) { - done(err); - return; - } - try { - expect(JSON.parse(tokenDetails.capability)).to.deep.equal(testCapability, 'Verify token capability'); - done(); - } catch (e) { - done(e); - } - }); + var tokenDetails = await rest.auth.requestToken(null, testKeyOpts); + expect(JSON.parse(tokenDetails.capability)).to.deep.equal(testCapability, 'Verify token capability'); }); - it('Equal intersection with specified key', function (done) { + it('Equal intersection with specified key', async function () { var testKeyOpts = { key: testApp.keys[1].keyStr }; var testCapability = JSON.parse(testApp.keys[1].capability); - rest.auth.requestToken({ capability: testCapability }, testKeyOpts, function (err, tokenDetails) { - if (err) { - done(err); - return; - } - try { - expect(JSON.parse(tokenDetails.capability)).to.deep.equal(testCapability, 'Verify token capability'); - done(); - } catch (err) { - done(err); - } - }); + var tokenDetails = await rest.auth.requestToken({ capability: testCapability }, testKeyOpts); + expect(JSON.parse(tokenDetails.capability)).to.deep.equal(testCapability, 'Verify token capability'); }); - it('Empty ops intersection', function (done) { + it('Empty ops intersection', async function () { var testKeyOpts = { key: testApp.keys[1].keyStr }; var testCapability = { 'canpublish:test': ['subscribe'] }; - rest.auth.requestToken({ capability: testCapability }, testKeyOpts, function (err, tokenDetails) { - if (err) { - try { - expect(err.statusCode).to.equal(401, 'Verify request rejected with insufficient capability'); - done(); - } catch (err) { - done(err); - } - return; - } - done(new Error('Invalid capability, expected rejection')); - }); + try { + var tokenDetails = await rest.auth.requestToken({ capability: testCapability }, testKeyOpts); + } catch (err) { + expect(err.statusCode).to.equal(401, 'Verify request rejected with insufficient capability'); + return; + } + expect.fail('Invalid capability, expected rejection'); }); - it('Empty paths intersection', function (done) { + it('Empty paths intersection', async function () { var testKeyOpts = { key: testApp.keys[2].keyStr }; var testCapability = { channelx: ['publish'] }; - rest.auth.requestToken({ capability: testCapability }, testKeyOpts, function (err, tokenDetails) { - if (err) { - try { - expect(err.statusCode).to.equal(401, 'Verify request rejected with insufficient capability'); - done(); - } catch (err) { - done(err); - } - return; - } - done('Invalid capability, expected rejection'); - }); + try { + var tokenDetails = await rest.auth.requestToken({ capability: testCapability }, testKeyOpts); + } catch (err) { + expect(err.statusCode).to.equal(401, 'Verify request rejected with insufficient capability'); + return; + } + expect.fail('Invalid capability, expected rejection'); }); - it('Ops intersection non-empty', function (done) { + it('Ops intersection non-empty', async function () { var testKeyOpts = { key: testApp.keys[2].keyStr }; var testCapability = { channel2: ['presence', 'subscribe'] }; var expectedIntersection = { channel2: ['subscribe'] }; - rest.auth.requestToken({ capability: testCapability }, testKeyOpts, function (err, tokenDetails) { - if (err) { - done(err); - return; - } - try { - expect(JSON.parse(tokenDetails.capability)).to.deep.equal(expectedIntersection, 'Verify token capability'); - done(); - } catch (err) { - done(err); - } - }); + var tokenDetails = await rest.auth.requestToken({ capability: testCapability }, testKeyOpts); + expect(JSON.parse(tokenDetails.capability)).to.deep.equal(expectedIntersection, 'Verify token capability'); }); - it('Paths intersection non-empty', function (done) { + it('Paths intersection non-empty', async function () { var testKeyOpts = { key: testApp.keys[2].keyStr }; var testCapability = { channel2: ['presence', 'subscribe'], channelx: ['presence', 'subscribe'], }; var expectedIntersection = { channel2: ['subscribe'] }; - rest.auth.requestToken({ capability: testCapability }, testKeyOpts, function (err, tokenDetails) { - if (err) { - done(err); - return; - } - try { - expect(JSON.parse(tokenDetails.capability)).to.deep.equal(expectedIntersection, 'Verify token capability'); - done(); - } catch (err) { - done(err); - } - }); + var tokenDetails = await rest.auth.requestToken({ capability: testCapability }, testKeyOpts); + expect(JSON.parse(tokenDetails.capability)).to.deep.equal(expectedIntersection, 'Verify token capability'); }); - it('Wildcard token with publish and subscribe key', function (done) { + it('Wildcard token with publish and subscribe key', async function () { var testKeyOpts = { key: testApp.keys[2].keyStr }; var testCapability = { channel2: ['*'] }; var expectedIntersection = { channel2: ['publish', 'subscribe'] }; - rest.auth.requestToken({ capability: testCapability }, testKeyOpts, function (err, tokenDetails) { - if (err) { - done(err); - return; - } - try { - expect(JSON.parse(tokenDetails.capability)).to.deep.equal(expectedIntersection, 'Verify token capability'); - done(); - } catch (err) { - done(err); - } - }); + var tokenDetails = await rest.auth.requestToken({ capability: testCapability }, testKeyOpts); + expect(JSON.parse(tokenDetails.capability)).to.deep.equal(expectedIntersection, 'Verify token capability'); }); - it('Publish and subscribe token with wildcard key', function (done) { + it('Publish and subscribe token with wildcard key', async function () { var testKeyOpts = { key: testApp.keys[2].keyStr }; var testCapability = { channel6: ['publish', 'subscribe'] }; var expectedIntersection = { channel6: ['publish', 'subscribe'] }; - rest.auth.requestToken({ capability: testCapability }, testKeyOpts, function (err, tokenDetails) { - if (err) { - done(err); - return; - } - try { - expect(JSON.parse(tokenDetails.capability)).to.deep.equal(expectedIntersection, 'Verify token capability'); - done(); - } catch (err) { - done(err); - } - }); + var tokenDetails = await rest.auth.requestToken({ capability: testCapability }, testKeyOpts); + expect(JSON.parse(tokenDetails.capability)).to.deep.equal(expectedIntersection, 'Verify token capability'); }); - it('Resources wildcard matching 1', function (done) { + it('Resources wildcard matching 1', async function () { var testKeyOpts = { key: testApp.keys[3].keyStr }; var testCapability = { cansubscribe: ['subscribe'] }; var expectedIntersection = testCapability; - rest.auth.requestToken({ capability: testCapability }, testKeyOpts, function (err, tokenDetails) { - if (err) { - done(err); - return; - } - try { - expect(JSON.parse(tokenDetails.capability)).to.deep.equal(expectedIntersection, 'Verify token capability'); - done(); - } catch (err) { - done(err); - } - }); + var tokenDetails = await rest.auth.requestToken({ capability: testCapability }, testKeyOpts); + expect(JSON.parse(tokenDetails.capability)).to.deep.equal(expectedIntersection, 'Verify token capability'); }); - it('Resources wildcard matching 2', function (done) { + it('Resources wildcard matching 2', async function () { var testKeyOpts = { key: testApp.keys[1].keyStr }; var testCapability = { 'canpublish:check': ['publish'] }; var expectedIntersection = testCapability; - rest.auth.requestToken({ capability: testCapability }, testKeyOpts, function (err, tokenDetails) { - if (err) { - done(err); - return; - } - try { - expect(JSON.parse(tokenDetails.capability)).to.deep.equal(expectedIntersection, 'Verify token capability'); - done(); - } catch (err) { - done(err); - } - }); + var tokenDetails = await rest.auth.requestToken({ capability: testCapability }, testKeyOpts); + expect(JSON.parse(tokenDetails.capability)).to.deep.equal(expectedIntersection, 'Verify token capability'); }); - it('Resources wildcard matching 3', function (done) { + it('Resources wildcard matching 3', async function () { var testKeyOpts = { key: testApp.keys[3].keyStr }; var testCapability = { 'cansubscribe:*': ['subscribe'] }; var expectedIntersection = testCapability; - rest.auth.requestToken({ capability: testCapability }, testKeyOpts, function (err, tokenDetails) { - if (err) { - done(err); - return; - } - try { - expect(JSON.parse(tokenDetails.capability)).to.deep.equal(expectedIntersection, 'Verify token capability'); - done(); - } catch (err) { - done(err); - } - }); + var tokenDetails = await rest.auth.requestToken({ capability: testCapability }, testKeyOpts); + expect(JSON.parse(tokenDetails.capability)).to.deep.equal(expectedIntersection, 'Verify token capability'); }); /* Invalid capabilities */ - it('Invalid capabilities 1', function (done) { - rest.auth.requestToken({ capability: invalid0 }, function (err, tokenDetails) { - if (err) { - try { - expect(err.statusCode).to.equal(400, 'Verify request rejected with bad capability'); - done(); - } catch (err) { - done(err); - } - return; - } - done(new Error('Invalid capability, expected rejection')); - }); + it('Invalid capabilities 1', async function () { + try { + var tokenDetails = await rest.auth.requestToken({ capability: invalid0 }); + } catch (err) { + expect(err.statusCode).to.equal(400, 'Verify request rejected with bad capability'); + return; + } + expect.fail('Invalid capability, expected rejection'); }); - it('Invalid capabilities 2', function (done) { - rest.auth.requestToken({ capability: invalid1 }, function (err, tokenDetails) { - if (err) { - try { - expect(err.statusCode).to.equal(400, 'Verify request rejected with bad capability'); - done(); - } catch (err) { - done(err); - } - return; - } - done(new Error('Invalid capability, expected rejection')); - }); + it('Invalid capabilities 2', async function () { + try { + var tokenDetails = await rest.auth.requestToken({ capability: invalid1 }); + } catch (err) { + expect(err.statusCode).to.equal(400, 'Verify request rejected with bad capability'); + return; + } + expect.fail('Invalid capability, expected rejection'); }); - it('Invalid capabilities 3', function (done) { - rest.auth.requestToken({ capability: invalid2 }, function (err, tokenDetails) { - if (err) { - try { - expect(err.statusCode).to.equal(400, 'Verify request rejected with bad capability'); - done(); - } catch (err) { - done(err); - } - return; - } - done(new Error('Invalid capability, expected rejection')); - }); + it('Invalid capabilities 3', async function () { + try { + var tokenDetails = await rest.auth.requestToken({ capability: invalid2 }); + } catch (err) { + expect(err.statusCode).to.equal(400, 'Verify request rejected with bad capability'); + return; + } + expect.fail('Invalid capability, expected rejection'); }); }); }); diff --git a/test/rest/defaults.test.js b/test/rest/defaults.test.js index 1abbcbba67..f45493d059 100644 --- a/test/rest/defaults.test.js +++ b/test/rest/defaults.test.js @@ -55,28 +55,7 @@ define(['ably', 'chai'], function (Ably, chai) { expect(Defaults.getHosts(normalisedOptions)[0]).to.deep.equal(normalisedOptions.restHost); expect(Defaults.getHost(normalisedOptions, 'sandbox-rest.ably.io', false)).to.deep.equal('sandbox-rest.ably.io'); expect(Defaults.getHost(normalisedOptions, 'sandbox-rest.ably.io', true)).to.deep.equal( - 'sandbox-realtime.ably.io' - ); - - expect(Defaults.getPort(normalisedOptions)).to.equal(443); - }); - - /* will emit a deprecation warning */ - it('Init with given environment and default fallbacks', function () { - var normalisedOptions = Defaults.normaliseOptions({ environment: 'sandbox', fallbackHostsUseDefault: true }); - - expect(normalisedOptions.restHost).to.equal('sandbox-rest.ably.io'); - expect(normalisedOptions.realtimeHost).to.equal('sandbox-realtime.ably.io'); - expect(normalisedOptions.port).to.equal(80); - expect(normalisedOptions.tlsPort).to.equal(443); - expect(normalisedOptions.fallbackHosts.sort()).to.deep.equal(Defaults.FALLBACK_HOSTS.sort()); - expect(normalisedOptions.tls).to.equal(true); - - expect(Defaults.getHosts(normalisedOptions).length).to.deep.equal(4); - expect(Defaults.getHosts(normalisedOptions)[0]).to.deep.equal(normalisedOptions.restHost); - expect(Defaults.getHost(normalisedOptions, 'sandbox-rest.ably.io', false)).to.deep.equal('sandbox-rest.ably.io'); - expect(Defaults.getHost(normalisedOptions, 'sandbox-rest.ably.io', true)).to.deep.equal( - 'sandbox-realtime.ably.io' + 'sandbox-realtime.ably.io', ); expect(Defaults.getPort(normalisedOptions)).to.equal(443); @@ -134,52 +113,6 @@ define(['ably', 'chai'], function (Ably, chai) { expect(Defaults.getPort(normalisedOptions)).to.equal(443); }); - /* init with given restHost and realtimeHost, using the default fallback hosts */ - it('Init with given restHost and realtimeHost, using the default fallback hosts', function () { - var normalisedOptions = Defaults.normaliseOptions({ - restHost: 'test.org', - realtimeHost: 'ws.test.org', - fallbackHostsUseDefault: true, - }); - - expect(normalisedOptions.restHost).to.equal('test.org'); - expect(normalisedOptions.realtimeHost).to.equal('ws.test.org'); - expect(normalisedOptions.fallbackHosts.sort()).to.deep.equal(Defaults.FALLBACK_HOSTS.sort()); - }); - - it('Throws an error when initiated with fallbackHosts and fallbackHostsUseDefault', function () { - expect(function () { - Defaults.normaliseOptions({ fallbackHosts: ['a.example.com', 'b.example.com'], fallbackHostsUseDefault: true }); - }, "Check fallbackHosts and fallbackHostsUseDefault can't both be set").to.throw(); - }); - - it('Throws an error with initiated with fallbackHostsUseDefault and port or tlsPort set', function () { - expect(function () { - Defaults.normaliseOptions({ fallbackHostsUseDefault: true, port: 8080 }); - }, "Check fallbackHostsUseDefault and port can't both be set").to.throw; - expect(function () { - Defaults.normaliseOptions({ fallbackHostsUseDefault: true, tlsPort: 8081 }); - }, "Check fallbackHostsUseDefault and tlsPort can't both be set").to.throw; - }); - - /* will emit a warning */ - it('Init with deprecated host and wsHost options', function () { - var normalisedOptions = Defaults.normaliseOptions({ host: 'test.org', wsHost: 'ws.test.org' }); - - expect(normalisedOptions.restHost).to.equal('test.org'); - expect(normalisedOptions.realtimeHost).to.equal('ws.test.org'); - expect(normalisedOptions.port).to.equal(80); - expect(normalisedOptions.tlsPort).to.equal(443); - expect(normalisedOptions.fallbackHosts).to.equal(undefined); - expect(normalisedOptions.tls).to.equal(true); - - expect(Defaults.getHosts(normalisedOptions)).to.deep.equal([normalisedOptions.restHost]); - expect(Defaults.getHost(normalisedOptions, 'test.org', false)).to.deep.equal('test.org'); - expect(Defaults.getHost(normalisedOptions, 'test.org', true)).to.deep.equal('ws.test.org'); - - expect(Defaults.getPort(normalisedOptions)).to.equal(443); - }); - it('Init with no endpoint-related options and given default environment', function () { Defaults.ENVIRONMENT = 'sandbox'; var normalisedOptions = Defaults.normaliseOptions({}); @@ -195,13 +128,35 @@ define(['ably', 'chai'], function (Ably, chai) { expect(Defaults.getHosts(normalisedOptions)[0]).to.deep.equal(normalisedOptions.restHost); expect(Defaults.getHost(normalisedOptions, 'sandbox-rest.ably.io', false)).to.deep.equal('sandbox-rest.ably.io'); expect(Defaults.getHost(normalisedOptions, 'sandbox-rest.ably.io', true)).to.deep.equal( - 'sandbox-realtime.ably.io' + 'sandbox-realtime.ably.io', ); expect(Defaults.getPort(normalisedOptions)).to.equal(443); Defaults.ENVIRONMENT = ''; }); + // TODO once https://github.com/ably/ably-js/issues/1424 is fixed, this should also test the case where the useBinaryProtocol option is not specified + describe('normaliseOptions with useBinaryProtocol == true', () => { + if (Ably.Realtime.Platform.Config.supportsBinary) { + describe('given MsgPack implementation', () => { + it('maintains useBinaryProtocol as true', () => { + const StubMsgPack = {}; + var normalisedOptions = Defaults.normaliseOptions({ useBinaryProtocol: true }, StubMsgPack); + + expect(normalisedOptions.useBinaryProtocol).to.be.true; + }); + }); + } + + describe('given no MsgPack implementation', () => { + it('changes useBinaryProtocol to false', () => { + var normalisedOptions = Defaults.normaliseOptions({ useBinaryProtocol: true }, null); + + expect(normalisedOptions.useBinaryProtocol).to.be.false; + }); + }); + }); + it('closeOnUnload', function () { var options; diff --git a/test/rest/fallbacks.test.js b/test/rest/fallbacks.test.js index 65e024e759..e0883def5f 100644 --- a/test/rest/fallbacks.test.js +++ b/test/rest/fallbacks.test.js @@ -2,7 +2,6 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { var expect = chai.expect; - var utils = helper.Utils; var goodHost; describe('rest/fallbacks', function () { @@ -20,69 +19,36 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { }); /* RSC15f */ - it('Store working fallback', function (done) { + it('Store working fallback', async function () { var rest = helper.AblyRest({ restHost: helper.unroutableHost, fallbackHosts: [goodHost], httpRequestTimeout: 3000, - logLevel: 4, }); var validUntil; - async.series( - [ - function (cb) { - rest.time(function (err, serverTime) { - if (err) { - return cb(err); - } - expect(serverTime, 'Check serverTime returned').to.be.ok; - var currentFallback = rest._currentFallback; - expect(currentFallback, 'Check current fallback stored').to.be.ok; - expect(currentFallback && currentFallback.host).to.equal(goodHost, 'Check good host set'); - validUntil = currentFallback.validUntil; - cb(); - }); - }, - /* now try again, check that this time it uses the remembered good endpoint straight away */ - function (cb) { - rest.time(function (err, serverTime) { - if (err) { - return cb(err); - } - expect(serverTime, 'Check serverTime returned').to.be.ok; - var currentFallback = rest._currentFallback; - expect(currentFallback.validUntil).to.equal( - validUntil, - 'Check validUntil is the same (implying currentFallback has not been re-set)' - ); - cb(); - }); - }, - /* set the validUntil to the past and check that the stored fallback is forgotten */ - function (cb) { - var now = utils.now(); - rest._currentFallback.validUntil = now - 1000; - rest.time(function (err, serverTime) { - if (err) { - return cb(err); - } - expect(serverTime, 'Check serverTime returned').to.be.ok; - var currentFallback = rest._currentFallback; - expect(currentFallback, 'Check current fallback re-stored').to.be.ok; - expect(currentFallback && currentFallback.host).to.equal(goodHost, 'Check good host set again'); - expect(currentFallback.validUntil > now, 'Check validUntil has been re-set').to.be.ok; - cb(); - }); - }, - ], - function (err) { - if (err) { - done(err); - return; - } - done(); - } + var serverTime = await rest.time(); + expect(serverTime, 'Check serverTime returned').to.be.ok; + var currentFallback = rest._currentFallback; + expect(currentFallback, 'Check current fallback stored').to.be.ok; + expect(currentFallback && currentFallback.host).to.equal(goodHost, 'Check good host set'); + validUntil = currentFallback.validUntil; + /* now try again, check that this time it uses the remembered good endpoint straight away */ + var serverTime = await rest.time(); + expect(serverTime, 'Check serverTime returned').to.be.ok; + var currentFallback = rest._currentFallback; + expect(currentFallback.validUntil).to.equal( + validUntil, + 'Check validUntil is the same (implying currentFallback has not been re-set)', ); + /* set the validUntil to the past and check that the stored fallback is forgotten */ + var now = Date.now(); + rest._currentFallback.validUntil = now - 1000; + var serverTime = await rest.time(); + expect(serverTime, 'Check serverTime returned').to.be.ok; + var currentFallback = rest._currentFallback; + expect(currentFallback, 'Check current fallback re-stored').to.be.ok; + expect(currentFallback && currentFallback.host).to.equal(goodHost, 'Check good host set again'); + expect(currentFallback.validUntil > now, 'Check validUntil has been re-set').to.be.ok; }); }); }); diff --git a/test/rest/history.test.js b/test/rest/history.test.js index 50f1059072..7ad092eff9 100644 --- a/test/rest/history.test.js +++ b/test/rest/history.test.js @@ -15,6 +15,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { { name: 'event5', data: { one: 1, two: 2, three: 3 } }, { name: 'event6', data: { foo: 'bar' } }, ]; + var reversedMessages = testMessages.map((_, i) => testMessages[testMessages.length - 1 - i]); describe('rest/history', function () { this.timeout(60 * 1000); @@ -26,478 +27,223 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { }); }); - restTestOnJsonMsgpack('history_simple', function (done, rest, channelName) { + restTestOnJsonMsgpack('history_simple', async function (rest, channelName) { var testchannel = rest.channels.get('persisted:' + channelName); /* first, send a number of events to this channel */ - - var publishTasks = utils.arrMap(testMessages, function (event) { - return function (publishCb) { - testchannel.publish(event.name, event.data, publishCb); - }; - }); - - publishTasks.push(function (waitCb) { - setTimeout(function () { - waitCb(null); - }, 1000); + await Promise.all([ + new Promise((resolve) => setTimeout(resolve, 1000)), + ...testMessages.map((event) => testchannel.publish(event.name, event.data)), + ]); + + /* so now the messages are there; try querying the timeline */ + var resultPage = await testchannel.history(); + /* verify all messages are received */ + var messages = resultPage.items; + expect(messages.length).to.equal(testMessages.length, 'Verify correct number of messages found'); + + /* verify message ids are unique */ + var ids = {}; + messages.forEach(function (msg) { + ids[msg.id] = msg; }); - try { - async.parallel(publishTasks, function (err) { - if (err) { - done(err); - return; - } - - /* so now the messages are there; try querying the timeline */ - testchannel.history(function (err, resultPage) { - if (err) { - done(err); - return; - } - /* verify all messages are received */ - var messages = resultPage.items; - expect(messages.length).to.equal(testMessages.length, 'Verify correct number of messages found'); - - /* verify message ids are unique */ - var ids = {}; - utils.arrForEach(messages, function (msg) { - ids[msg.id] = msg; - }); - expect(utils.keysArray(ids).length).to.equal( - testMessages.length, - 'Verify correct number of distinct message ids found' - ); - done(); - }); - }); - } catch (err) { - done(err); - } + expect(utils.keysArray(ids).length).to.equal( + testMessages.length, + 'Verify correct number of distinct message ids found', + ); }); - restTestOnJsonMsgpack('history_multiple', function (done, rest, channelName) { + restTestOnJsonMsgpack('history_multiple', async function (rest, channelName) { var testchannel = rest.channels.get('persisted:' + channelName); /* first, send a number of events to this channel */ - var publishTasks = [ - function (publishCb) { - testchannel.publish(testMessages, publishCb); - }, - ]; - - publishTasks.push(function (waitCb) { - setTimeout(function () { - waitCb(null); - }, 1000); + await Promise.all([new Promise((resolve) => setTimeout(resolve, 1000)), testchannel.publish(testMessages)]); + + /* so now the messages are there; try querying the timeline */ + var resultPage = await testchannel.history(); + /* verify all messages are received */ + var messages = resultPage.items; + expect(messages.length).to.equal(testMessages.length, 'Verify correct number of messages found'); + + /* verify message ids are unique */ + var ids = {}; + messages.forEach(function (msg) { + ids[msg.id] = msg; }); - try { - async.parallel(publishTasks, function (err) { - if (err) { - done(err); - return; - } - - /* so now the messages are there; try querying the timeline */ - testchannel.history(function (err, resultPage) { - if (err) { - done(err); - return; - } - /* verify all messages are received */ - var messages = resultPage.items; - expect(messages.length).to.equal(testMessages.length, 'Verify correct number of messages found'); - - /* verify message ids are unique */ - var ids = {}; - utils.arrForEach(messages, function (msg) { - ids[msg.id] = msg; - }); - expect(utils.keysArray(ids).length).to.equal( - testMessages.length, - 'Verify correct number of distinct message ids found' - ); - done(); - }); - }); - } catch (err) { - done(err); - } + expect(utils.keysArray(ids).length).to.equal( + testMessages.length, + 'Verify correct number of distinct message ids found', + ); }); - restTestOnJsonMsgpack('history_simple_paginated_b', function (done, rest, channelName) { + restTestOnJsonMsgpack('history_simple_paginated_b', async function (rest, channelName) { var testchannel = rest.channels.get('persisted:' + channelName); /* first, send a number of events to this channel */ - var publishTasks = utils.arrMap(testMessages, function (event) { - return function (publishCb) { - testchannel.publish(event.name, event.data, publishCb); - }; - }); + for (var message of testMessages) { + await testchannel.publish(message.name, message.data); + } - publishTasks.push(function (waitCb) { - setTimeout(function () { - waitCb(null); - }, 1000); - }); - try { - async.series(publishTasks, function (err) { - if (err) { - done(err); - return; - } - - /* so now the messages are there; try querying the timeline to get messages one at a time */ - var ids = {}, - totalMessagesExpected = testMessages.length, - nextPage = function (cb) { - testchannel.history({ limit: 1, direction: 'backwards' }, cb); - }; - - testMessages.reverse(); - async.mapSeries( - testMessages, - function (expectedMessage, cb) { - nextPage(function (err, resultPage) { - if (err) { - cb(err); - return; - } - /* verify expected number of messages in this page */ - expect(resultPage.items.length).to.equal(1, 'Verify a single message received'); - var resultMessage = resultPage.items[0]; - ids[resultMessage.id] = resultMessage; - - /* verify expected message */ - expect(expectedMessage.name).to.equal(resultMessage.name, 'Verify expected name value present'); - expect(expectedMessage.data).to.deep.equal(resultMessage.data, 'Verify expected data value present'); - - if (--totalMessagesExpected > 0) { - expect(resultPage.hasNext(), 'Verify next link is present').to.be.ok; - expect(!resultPage.isLast(), 'Verify not last page').to.be.ok; - nextPage = resultPage.next; - } - cb(); - }); - }, - function (err) { - if (err) { - done(err); - return; - } - /* verify message ids are unique */ - expect(utils.keysArray(ids).length).to.equal( - testMessages.length, - 'Verify correct number of distinct message ids found' - ); - done(); - } - ); - }); - } catch (err) { - done(err); + await new Promise((resolve) => setTimeout(resolve, 1000)); + + /* so now the messages are there; try querying the timeline to get messages one at a time */ + var ids = {}, + totalMessagesExpected = testMessages.length; + + var resultPage = await testchannel.history({ limit: 1, direction: 'backwards' }); + for (var expectedMessage of reversedMessages) { + /* verify expected number of messages in this page */ + expect(resultPage.items.length).to.equal(1, 'Verify a single message received'); + var resultMessage = resultPage.items[0]; + ids[resultMessage.id] = resultMessage; + + /* verify expected message */ + expect(expectedMessage.name).to.equal(resultMessage.name, 'Verify expected name value present'); + expect(expectedMessage.data).to.deep.equal(resultMessage.data, 'Verify expected data value present'); + + if (--totalMessagesExpected > 0) { + expect(resultPage.hasNext(), 'Verify next link is present').to.be.ok; + expect(!resultPage.isLast(), 'Verify not last page').to.be.ok; + resultPage = await resultPage.next(); + } } + /* verify message ids are unique */ + expect(utils.keysArray(ids).length).to.equal( + testMessages.length, + 'Verify correct number of distinct message ids found', + ); }); - it('history_simple_paginated_f', function (done) { + it('history_simple_paginated_f', async function () { var testchannel = rest.channels.get('persisted:history_simple_paginated_f'); /* first, send a number of events to this channel */ - var publishTasks = utils.arrMap(testMessages, function (event) { - return function (publishCb) { - testchannel.publish(event.name, event.data, publishCb); - }; - }); + for (var message of testMessages) { + await testchannel.publish(message.name, message.data); + } - publishTasks.push(function (waitCb) { - setTimeout(function () { - waitCb(null); - }, 1000); + await new Promise(function (resolve) { + setTimeout(resolve, 1000); }); - try { - async.series(publishTasks, function (err) { - if (err) { - done(err); - return; - } - - /* so now the messages are there; try querying the timeline to get messages one at a time */ - var ids = {}, - totalMessagesExpected = testMessages.length, - nextPage = function (cb) { - testchannel.history({ limit: 1, direction: 'forwards' }, cb); - }; - - async.mapSeries( - testMessages, - function (expectedMessage, cb) { - nextPage(function (err, resultPage) { - if (err) { - cb(err); - return; - } - /* verify expected number of messages in this page */ - expect(resultPage.items.length).to.equal(1, 'Verify a single message received'); - var resultMessage = resultPage.items[0]; - ids[resultMessage.id] = resultMessage; - - /* verify expected message */ - expect(expectedMessage.name).to.equal(resultMessage.name, 'Verify expected name value present'); - expect(expectedMessage.data).to.deep.equal(resultMessage.data, 'Verify expected data value present'); - - if (--totalMessagesExpected > 0) { - expect(resultPage.hasNext(), 'Verify next link is present').to.be.ok; - nextPage = resultPage.next; - } - cb(); - }); - }, - function (err) { - if (err) { - done(err); - return; - } - /* verify message ids are unique */ - expect(utils.keysArray(ids).length).to.equal( - testMessages.length, - 'Verify correct number of distinct message ids found' - ); - done(); - } - ); - }); - } catch (err) { - done(err); + + var ids = {}, + totalMessagesExpected = testMessages.length; + + var resultPage = await testchannel.history({ limit: 1, direction: 'forwards' }); + for (var expectedMessage of testMessages) { + /* verify expected number of messages in this page */ + expect(resultPage.items.length).to.equal(1, 'Verify a single message received'); + var resultMessage = resultPage.items[0]; + ids[resultMessage.id] = resultMessage; + + /* verify expected message */ + expect(expectedMessage.name).to.equal(resultMessage.name, 'Verify expected name value present'); + expect(expectedMessage.data).to.deep.equal(resultMessage.data, 'Verify expected data value present'); + + if (--totalMessagesExpected > 0) { + expect(resultPage.hasNext(), 'Verify next link is present').to.be.ok; + resultPage = await resultPage.next(); + } } + + /* verify message ids are unique */ + expect(utils.keysArray(ids).length).to.equal( + testMessages.length, + 'Verify correct number of distinct message ids found', + ); }); - it('history_multiple_paginated_b', function (done) { + it('history_multiple_paginated_b', async function () { var testchannel = rest.channels.get('persisted:history_multiple_paginated_b'); /* first, send a number of events to this channel */ - var publishTasks = [ - function (publishCb) { - testchannel.publish(testMessages, publishCb); - }, - ]; - - publishTasks.push(function (waitCb) { - setTimeout(function () { - waitCb(null); - }, 1000); + for (var message of testMessages) { + await testchannel.publish(message.name, message.data); + } + + await new Promise(function (resolve) { + setTimeout(resolve, 1000); }); - try { - async.series(publishTasks, function (err) { - if (err) { - done(err); - return; - } - - /* so now the messages are there; try querying the timeline to get messages one at a time */ - var ids = {}, - totalMessagesExpected = testMessages.length, - nextPage = function (cb) { - testchannel.history({ limit: 1, direction: 'backwards' }, cb); - }; - - testMessages.reverse(); - async.mapSeries( - testMessages, - function (expectedMessage, cb) { - nextPage(function (err, resultPage) { - if (err) { - cb(err); - return; - } - /* verify expected number of messages in this page */ - expect(resultPage.items.length).to.equal(1, 'Verify a single message received'); - var resultMessage = resultPage.items[0]; - ids[resultMessage.id] = resultMessage; - - /* verify expected message */ - expect(expectedMessage.name).to.equal(resultMessage.name, 'Verify expected name value present'); - expect(expectedMessage.data).to.deep.equal(resultMessage.data, 'Verify expected data value present'); - - if (--totalMessagesExpected > 0) { - expect(resultPage.hasNext(), 'Verify next link is present').to.be.ok; - nextPage = resultPage.next; - } - cb(); - }); - }, - function (err) { - if (err) { - done(err); - return; - } - /* verify message ids are unique */ - expect(utils.keysArray(ids).length).to.equal( - testMessages.length, - 'Verify correct number of distinct message ids found' - ); - done(); - } - ); - }); - } catch (err) { - done(err); + + /* so now the messages are there; try querying the timeline to get messages one at a time */ + var ids = {}, + totalMessagesExpected = testMessages.length; + + var resultPage = await testchannel.history({ limit: 1, direction: 'backwards' }); + for (var expectedMessage of reversedMessages) { + /* verify expected number of messages in this page */ + expect(resultPage.items.length).to.equal(1, 'Verify a single message received'); + var resultMessage = resultPage.items[0]; + ids[resultMessage.id] = resultMessage; + + /* verify expected message */ + expect(expectedMessage.name).to.equal(resultMessage.name, 'Verify expected name value present'); + expect(expectedMessage.data).to.deep.equal(resultMessage.data, 'Verify expected data value present'); + + if (--totalMessagesExpected > 0) { + expect(resultPage.hasNext(), 'Verify next link is present').to.be.ok; + resultPage = await resultPage.next(); + } } }); - it('history_multiple_paginated_f', function (done) { + it('history_multiple_paginated_f', async function () { var testchannel = rest.channels.get('persisted:history_multiple_paginated_f'); /* first, send a number of events to this channel */ - var publishTasks = [ - function (publishCb) { - testchannel.publish(testMessages, publishCb); - }, - ]; - - publishTasks.push(function (waitCb) { - setTimeout(function () { - waitCb(null); - }, 1000); + await testchannel.publish(testMessages); + + await new Promise(function (resolve) { + setTimeout(resolve, 1000); }); - try { - async.series(publishTasks, function (err) { - if (err) { - done(err); - return; - } - - /* so now the messages are there; try querying the timeline to get messages one at a time */ - var ids = {}, - totalMessagesExpected = testMessages.length, - nextPage = function (cb) { - testchannel.history({ limit: 1, direction: 'forwards' }, cb); - }; - - async.mapSeries( - testMessages, - function (expectedMessage, cb) { - nextPage(function (err, resultPage) { - if (err) { - cb(err); - return; - } - /* verify expected number of messages in this page */ - expect(resultPage.items.length).to.equal(1, 'Verify a single message received'); - var resultMessage = resultPage.items[0]; - ids[resultMessage.id] = resultMessage; - - /* verify expected message */ - expect(expectedMessage.name).to.equal(resultMessage.name, 'Verify expected name value present'); - expect(expectedMessage.data).to.deep.equal(resultMessage.data, 'Verify expected data value present'); - - if (--totalMessagesExpected > 0) { - expect(resultPage.hasNext(), 'Verify next link is present').to.be.ok; - nextPage = resultPage.next; - } - cb(); - }); - }, - function (err) { - if (err) { - done(err); - return; - } - /* verify message ids are unique */ - expect(utils.keysArray(ids).length).to.equal( - testMessages.length, - 'Verify correct number of distinct message ids found' - ); - done(); - } - ); - }); - } catch (err) { - done(err); + + /* so now the messages are there; try querying the timeline to get messages one at a time */ + var ids = {}, + totalMessagesExpected = testMessages.length; + + var resultPage = await testchannel.history({ limit: 1, direction: 'forwards' }); + for (var expectedMessage of testMessages) { + /* verify expected number of messages in this page */ + expect(resultPage.items.length).to.equal(1, 'Verify a single message received'); + var resultMessage = resultPage.items[0]; + ids[resultMessage.id] = resultMessage; + + /* verify expected message */ + expect(expectedMessage.name).to.equal(resultMessage.name, 'Verify expected name value present'); + expect(expectedMessage.data).to.deep.equal(resultMessage.data, 'Verify expected data value present'); + + if (--totalMessagesExpected > 0) { + expect(resultPage.hasNext(), 'Verify next link is present').to.be.ok; + var resultPage = await resultPage.next(); + } } + + /* verify message ids are unique */ + expect(utils.keysArray(ids).length).to.equal( + testMessages.length, + 'Verify correct number of distinct message ids found', + ); }); - restTestOnJsonMsgpack('history_encoding_errors', function (done, rest, channelName) { + restTestOnJsonMsgpack('history_encoding_errors', async function (rest, channelName) { var testchannel = rest.channels.get('persisted:' + channelName); var badMessage = { name: 'jsonUtf8string', encoding: 'json/utf-8', data: '{"foo":"bar"}' }; - try { - testchannel.publish(badMessage, function (err) { - if (err) { - done(err); - return; - } - setTimeout(function () { - testchannel.history(function (err, resultPage) { - if (err) { - done(err); - return; - } - /* verify all messages are received */ - var message = resultPage.items[0]; - expect(message.data).to.equal(badMessage.data, 'Verify data preserved'); - expect(message.encoding).to.equal(badMessage.encoding, 'Verify encoding preserved'); - done(); - }); - }, 1000); - }); - } catch (err) { - done(err); - } + testchannel.publish(badMessage); + await new Promise((resolve) => setTimeout(resolve, 1000)); + var resultPage = await testchannel.history(); + /* verify all messages are received */ + var message = resultPage.items[0]; + expect(message.data).to.equal(badMessage.data, 'Verify data preserved'); + expect(message.encoding).to.equal(badMessage.encoding, 'Verify encoding preserved'); }); - restTestOnJsonMsgpack('history_no_next_page', function (done, rest, channelName) { + restTestOnJsonMsgpack('history_no_next_page', async function (rest, channelName) { const channel = rest.channels.get(channelName); - channel.history(function (err, firstPage) { - if (err) { - done(err); - return; - } - firstPage.next(function (err, secondPage) { - if (err) { - done(err); - return; - } - - expect(secondPage).to.equal(null); - done(); - }); - }); - }); + const firstPage = await channel.history(); + const secondPage = await firstPage.next(); - if (typeof Promise !== 'undefined') { - it('historyPromise', function (done) { - var rest = helper.AblyRest({ promises: true }); - var testchannel = rest.channels.get('persisted:history_promise'); - - testchannel - .publish('one', null) - .then(function () { - return testchannel.publish('two', null); - }) - .then(function () { - return testchannel.history({ limit: 1, direction: 'forwards' }); - }) - .then(function (resultPage) { - expect(resultPage.items.length).to.equal(1); - expect(resultPage.items[0].name).to.equal('one'); - return resultPage.first(); - }) - .then(function (resultPage) { - expect(resultPage.items[0].name).to.equal('one'); - return resultPage.current(); - }) - .then(function (resultPage) { - expect(resultPage.items[0].name).to.equal('one'); - return resultPage.next(); - }) - .then(function (resultPage) { - expect(resultPage.items[0].name).to.equal('two'); - done(); - }) - ['catch'](function (err) { - done(err); - }); - }); - } + expect(secondPage).to.equal(null); + }); }); }); diff --git a/test/rest/http.test.js b/test/rest/http.test.js index b8cdf1914a..f8911dcc4e 100644 --- a/test/rest/http.test.js +++ b/test/rest/http.test.js @@ -21,68 +21,60 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { /** * RSC7a */ - it('Should send X-Ably-Version and Ably-Agent headers in get/post requests', function (done) { - //Intercept Http.do with test - function testRequestHandler(_, __, ___, headers) { - try { - expect('X-Ably-Version' in headers, 'Verify version header exists').to.be.ok; - expect('Ably-Agent' in headers, 'Verify agent header exists').to.be.ok; + it('Should send X-Ably-Version and Ably-Agent headers in get/post requests', async function () { + var originalDo = rest.http.do; + + // Intercept Http.do with test + async function testRequestHandler(method, path, headers, body, params) { + expect('X-Ably-Version' in headers, 'Verify version header exists').to.be.ok; + expect('Ably-Agent' in headers, 'Verify agent header exists').to.be.ok; - // This test should not directly validate version against Defaults.version, as - // ultimately the version header has been derived from that value. - expect(headers['X-Ably-Version']).to.equal('2', 'Verify current version number'); - expect(headers['Ably-Agent'].indexOf('ably-js/' + Defaults.version) > -1, 'Verify agent').to.be.ok; - expect(headers['Ably-Agent'].indexOf('custom-agent/0.1.2') > -1, 'Verify custom agent').to.be.ok; + // This test should not directly validate version against Defaults.version, as + // ultimately the version header has been derived from that value. + expect(headers['X-Ably-Version']).to.equal('3', 'Verify current version number'); + expect(headers['Ably-Agent'].indexOf('ably-js/' + Defaults.version) > -1, 'Verify agent').to.be.ok; + expect(headers['Ably-Agent'].indexOf('custom-agent/0.1.2') > -1, 'Verify custom agent').to.be.ok; - // We don't test on NativeScript so a check for that platform is excluded here - if (typeof document !== 'undefined') { - // browser - expect(headers['Ably-Agent'].indexOf('browser') > -1, 'Verify agent').to.be.ok; - } else if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') { - // reactnative - expect(headers['Ably-Agent'].indexOf('reactnative') > -1, 'Verify agent').to.be.ok; - } else { - // node - expect(headers['Ably-Agent'].indexOf('nodejs') > -1, 'Verify agent').to.be.ok; - } - } catch (err) { - done(err); + // We don't test on NativeScript so a check for that platform is excluded here + if (typeof document !== 'undefined') { + // browser + expect(headers['Ably-Agent'].indexOf('browser') > -1, 'Verify agent').to.be.ok; + } else if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') { + // reactnative + expect(headers['Ably-Agent'].indexOf('reactnative') > -1, 'Verify agent').to.be.ok; + } else { + // node + expect(headers['Ably-Agent'].indexOf('nodejs') > -1, 'Verify agent').to.be.ok; } + + return originalDo.call(rest.http, method, path, headers, body, params); } rest.http.do = testRequestHandler; // Call all methods that use rest http calls - rest.auth.requestToken(); - rest.time(); - rest.stats(); + await rest.auth.requestToken(); + await rest.time(); + await rest.stats(); var channel = rest.channels.get('http_test_channel'); - channel.publish('test', 'Testing http headers'); - channel.presence.get(); - - done(); + await channel.publish('test', 'Testing http headers'); + await channel.presence.get(); }); - it('Should handle no content responses', function (done) { + it('Should handle no content responses', async function () { //Intercept Http.do with test - function testRequestHandler(_, __, ___, ____, _____, ______, callback) { - callback(null, null, { 'X-Ably-Foo': 'headerValue' }, false, 204); + async function testRequestHandler() { + return { error: null, body: null, headers: { 'X-Ably-Foo': 'headerValue' }, unpacked: false, statusCode: 204 }; } rest.http.do = testRequestHandler; - rest.request('GET', '/foo', {}, null, {}, (error, response) => { - try { - expect(error).to.be.null; - expect(response.statusCode).to.equal(204); - expect(response.items).to.be.empty; - expect(response.headers).to.deep.equal({ 'X-Ably-Foo': 'headerValue' }); - done(); - } catch (err) { - done(err); - } - }); + const response = await rest.request('GET', '/foo', {}, null, {}); + + expect(response.statusCode).to.equal(204); + expect(response.items).to.be.empty; + expect(response.headers).to.deep.equal({ 'X-Ably-Foo': 'headerValue' }); }); }); }); diff --git a/test/rest/init.test.js b/test/rest/init.test.js index 7d83f277e0..2d4b316dde 100644 --- a/test/rest/init.test.js +++ b/test/rest/init.test.js @@ -23,27 +23,16 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { expect(rest.options.key).to.equal(keyStr); }); - it('Init with token string', function (done) { - try { - /* first generate a token ... */ - var rest = helper.AblyRest(); - var testKeyOpts = { key: helper.getTestApp().keys[1].keyStr }; + it('Init with token string', async function () { + /* first generate a token ... */ + var rest = helper.AblyRest(); + var testKeyOpts = { key: helper.getTestApp().keys[1].keyStr }; - rest.auth.requestToken(null, testKeyOpts, function (err, tokenDetails) { - if (err) { - done(err); - return; - } + var tokenDetails = await rest.auth.requestToken(null, testKeyOpts); + var tokenStr = tokenDetails.token, + rest = new helper.Ably.Rest(tokenStr); - var tokenStr = tokenDetails.token, - rest = new helper.Ably.Rest(tokenStr); - - expect(rest.options.token).to.equal(tokenStr); - done(); - }); - } catch (err) { - done(err); - } + expect(rest.options.token).to.equal(tokenStr); }); it('Init with tls: false', function () { @@ -73,26 +62,5 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { var rest = helper.AblyRest({ clientId: false }); }, 'Check can’t init library with a boolean clientId').to.throw; }); - - it('Init promises', function () { - var rest, - keyStr = helper.getTestApp().keys[0].keyStr; - - rest = new Ably.Rest(keyStr); - expect(!rest.options.promises, 'Check promises defaults to false').to.be.ok; - - rest = new Ably.Rest.Promise(keyStr); - expect(rest.options.promises, 'Check promises default to true with promise constructor').to.be.ok; - - if (!isBrowser && typeof require == 'function') { - var AblyPromises = require('../../promises'); - rest = new AblyPromises.Rest(keyStr); - expect(rest.options.promises, 'Check promises default to true with promise require target').to.be.ok; - - var AblyCallbacks = require('../../callbacks'); - rest = new AblyCallbacks.Rest(keyStr); - expect(!rest.options.promises, 'Check promises default to false with callback require target').to.be.ok; - } - }); }); }); diff --git a/test/rest/message.test.js b/test/rest/message.test.js index 125a91e981..40da61677e 100644 --- a/test/rest/message.test.js +++ b/test/rest/message.test.js @@ -18,205 +18,115 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async /* Authenticate with a clientId and ensure that the clientId is not sent in the Message and is implicitly added when published */ - it('Should implicitly send clientId when authenticated with clientId', function (done) { + it('Should implicitly send clientId when authenticated with clientId', async function () { var clientId = 'implicit_client_id_0', rest = helper.AblyRest({ clientId: clientId, useBinaryProtocol: false }), channel = rest.channels.get('rest_implicit_client_id_0'); var originalPublish = channel._publish; - channel._publish = function (requestBody) { + channel._publish = async function (requestBody) { var message = JSON.parse(requestBody)[0]; - try { - expect(message.name === 'event0', 'Outgoing message interecepted').to.be.ok; - expect(!message.clientId, 'client ID is not added by the client library as it is implicit').to.be.ok; - } catch (err) { - done(err); - } - originalPublish.apply(channel, arguments); + expect(message.name === 'event0', 'Outgoing message interecepted').to.be.ok; + expect(!message.clientId, 'client ID is not added by the client library as it is implicit').to.be.ok; + return originalPublish.apply(channel, arguments); }; - channel.publish('event0', null, function (err) { - if (err) { - done(err); - return; - } - - channel.history(function (err, page) { - if (err) { - done(err); - return; - } + await channel.publish('event0', null); + var page = await channel.history(); - var message = page.items[0]; - try { - expect(message.clientId == clientId, 'Client ID was added implicitly').to.be.ok; - done(); - } catch (err) { - done(err); - } - }); - }); + var message = page.items[0]; + expect(message.clientId == clientId, 'Client ID was added implicitly').to.be.ok; }); /* Authenticate with a clientId and explicitly provide the same clientId in the Message and ensure it is published */ - it('Should publish clientId when provided explicitly in message', function (done) { + it('Should publish clientId when provided explicitly in message', async function () { var clientId = 'explicit_client_id_0', rest = helper.AblyRest({ clientId: clientId, useBinaryProtocol: false }), channel = rest.channels.get('rest_explicit_client_id_0'); var originalPublish = channel._publish; - channel._publish = function (requestBody) { + channel._publish = async function (requestBody) { var message = JSON.parse(requestBody)[0]; - try { - expect(message.name === 'event0', 'Outgoing message interecepted').to.be.ok; - expect( - message.clientId == clientId, - 'client ID is added by the client library as it is explicit in the publish' - ).to.be.ok; - } catch (err) { - done(err); - } - originalPublish.apply(channel, arguments); + expect(message.name === 'event0', 'Outgoing message interecepted').to.be.ok; + expect( + message.clientId == clientId, + 'client ID is added by the client library as it is explicit in the publish', + ).to.be.ok; + return originalPublish.apply(channel, arguments); }; - channel.publish({ name: 'event0', clientId: clientId }, function (err) { - if (err) { - done(err); - } - - channel.history(function (err, page) { - if (err) { - done(err); - return; - } - - var message = page.items[0]; - try { - expect(message.clientId == clientId, 'Client ID was retained').to.be.ok; - done(); - } catch (err) { - done(err); - } - }); - }); + await channel.publish({ name: 'event0', clientId: clientId }); + var page = await channel.history(); + var message = page.items[0]; + expect(message.clientId == clientId, 'Client ID was retained').to.be.ok; }); /* Authenticate with a clientId and explicitly provide a different invalid clientId in the Message and expect it to not be published and be rejected */ - it('Should error when clientId sent in message is different than authenticated clientId', function (done) { + it('Should error when clientId sent in message is different than authenticated clientId', async function () { var clientId = 'explicit_client_id_0', invalidClientId = 'invalid'; - helper.AblyRest().auth.requestToken({ clientId: clientId }, function (err, token) { - try { - expect(token.clientId === clientId, 'client ID is present in the Token').to.be.ok; - } catch (err) { - done(err); - } + var token = await helper.AblyRest().auth.requestToken({ clientId: clientId }); + expect(token.clientId === clientId, 'client ID is present in the Token').to.be.ok; - // REST client uses a token string so is unaware of the clientId so cannot reject before communicating with Ably - var rest = helper.AblyRest({ token: token.token, useBinaryProtocol: false }), - channel = rest.channels.get('rest_explicit_client_id_1'); + // REST client uses a token string so is unaware of the clientId so cannot reject before communicating with Ably + var rest = helper.AblyRest({ token: token.token, useBinaryProtocol: false }), + channel = rest.channels.get('rest_explicit_client_id_1'); - var originalPublish = channel._publish; - channel._publish = function (requestBody) { - var message = JSON.parse(requestBody)[0]; - try { - expect(message.name === 'event0', 'Outgoing message interecepted').to.be.ok; - expect( - message.clientId == invalidClientId, - 'invalid client ID is added by the client library as it is explicit in the publish' - ).to.be.ok; - } catch (err) { - done(err); - } - originalPublish.apply(channel, arguments); - }; - - channel.publish({ name: 'event0', clientId: invalidClientId }, function (err) { - if (!err) { - done(new Error('Publish should have failed with invalid clientId')); - return; - } - - channel.history(function (err, page) { - if (err) { - done(err); - return; - } + var originalPublish = channel._publish; + channel._publish = async function (requestBody) { + var message = JSON.parse(requestBody)[0]; + expect(message.name === 'event0', 'Outgoing message interecepted').to.be.ok; + expect( + message.clientId == invalidClientId, + 'invalid client ID is added by the client library as it is explicit in the publish', + ).to.be.ok; + return originalPublish.apply(channel, arguments); + }; - try { - expect(page.items.length).to.equal(0, 'Message should not have been published'); - done(); - } catch (err) { - done(err); - } - }); - }); - }); + try { + await channel.publish({ name: 'event0', clientId: invalidClientId }); + } catch (err) { + var page = await channel.history(); + expect(page.items.length).to.equal(0, 'Message should not have been published'); + return; + } + expect.fail('Publish should have failed with invalid clientId'); }); /* TO3l8; CD2C; RSL1i */ - it('Should error when publishing message larger than maxMessageSize', function (done) { + it('Should error when publishing message larger than maxMessageSize', async function () { /* No connectionDetails mechanism for REST, so just pass the override into the constructor */ var realtime = helper.AblyRest({ maxMessageSize: 64 }), channel = realtime.channels.get('maxMessageSize'); - channel.publish('foo', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', function (err) { - try { - expect(err, 'Check publish refused').to.be.ok; - expect(err.code).to.equal(40009); - done(); - } catch (err) { - done(err); - } - }); + try { + await channel.publish('foo', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'); + } catch (err) { + expect(err, 'Check publish refused').to.be.ok; + expect(err.code).to.equal(40009); + return; + } + expect.fail('Expected channel.publish() to throw error'); }); /* Check ids are correctly sent */ - it('Should send correct IDs when idempotentRestPublishing set to false', function (done) { + it('Should send correct IDs when idempotentRestPublishing set to false', async function () { var rest = helper.AblyRest({ idempotentRestPublishing: false, useBinaryProtocol: false }), channel = rest.channels.get('idempotent_rest_publishing'), message = { name: 'test', id: 'idempotent-msg-id:0' }; - async.parallel( - [ - function (parCb) { - channel.publish(message, parCb); - }, - function (parCb) { - channel.publish(message, parCb); - }, - function (parCb) { - channel.publish(message, parCb); - }, - ], - function (err) { - if (err) { - done(err); - return; - } + await Promise.all([channel.publish(message), channel.publish(message), channel.publish(message)]); - channel.history(function (err, page) { - if (err) { - done(err); - return; - } - try { - expect(page.items.length).to.equal(1, 'Check only one message published'); - expect(page.items[0].id).to.equal(message.id, 'Check message id preserved in history'); - done(); - } catch (err) { - done(err); - } - }); - } - ); + var page = await channel.history(); + expect(page.items.length).to.equal(1, 'Check only one message published'); + expect(page.items[0].id).to.equal(message.id, 'Check message id preserved in history'); }); /* Check ids are added when automatic idempotent rest publishing option enabled */ - it('Should add IDs when automatic idempotent rest publishing option enabled', function (done) { + it('Should add IDs when automatic idempotent rest publishing option enabled', async function () { /* easiest way to get the host we're using for tests */ var dummyRest = helper.AblyRest(), host = dummyRest.options.restHost, @@ -231,118 +141,63 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async idOne, idTwo, originalPublish = channel._publish, - originalDoUri = Ably.Realtime.Platform.Http.doUri; - - channel._publish = function (requestBody) { - try { - var messageOne = JSON.parse(requestBody)[0]; - var messageTwo = JSON.parse(requestBody)[1]; - expect(messageOne.name).to.equal('one', 'Outgoing message 1 interecepted'); - expect(messageTwo.name).to.equal('two', 'Outgoing message 2 interecepted'); - idOne = messageOne.id; - idTwo = messageTwo.id; - expect(idOne, 'id set on message 1').to.be.ok; - expect(idTwo, 'id set on message 2').to.be.ok; - expect(idOne && idOne.split(':')[1]).to.equal('0', 'check zero-based index'); - expect(idTwo && idTwo.split(':')[1]).to.equal('1', 'check zero-based index'); - originalPublish.apply(channel, arguments); - } catch (err) { - done(err); - } + originalDoUri = Ably.Realtime._Http.doUri; + + channel._publish = async function (requestBody) { + var messageOne = JSON.parse(requestBody)[0]; + var messageTwo = JSON.parse(requestBody)[1]; + expect(messageOne.name).to.equal('one', 'Outgoing message 1 interecepted'); + expect(messageTwo.name).to.equal('two', 'Outgoing message 2 interecepted'); + idOne = messageOne.id; + idTwo = messageTwo.id; + expect(idOne, 'id set on message 1').to.be.ok; + expect(idTwo, 'id set on message 2').to.be.ok; + expect(idOne && idOne.split(':')[1]).to.equal('0', 'check zero-based index'); + expect(idTwo && idTwo.split(':')[1]).to.equal('1', 'check zero-based index'); + return originalPublish.apply(channel, arguments); }; - Ably.Rest.Platform.Http.doUri = function (method, rest, uri, headers, body, params, callback) { - originalDoUri(method, rest, uri, headers, body, params, function (err) { - if (err) { - done(err); - callback(err); - return; - } - /* Fake a publish error from realtime */ - callback({ message: 'moo', code: 50300, statusCode: 503 }); - }); - Ably.Rest.Platform.Http.doUri = originalDoUri; - }; - - channel.publish([{ name: 'one' }, { name: 'two' }], function (err) { - if (err) { - done(err); - return; + Ably.Rest._Http.doUri = async function (method, uri, headers, body, params) { + const resultPromise = originalDoUri(method, uri, headers, body, params); + Ably.Rest._Http.doUri = originalDoUri; + const result = await resultPromise; + if (result.error) { + return { error: result.error }; } + /* Fake a publish error from realtime */ + return { error: { message: 'moo', code: 50300, statusCode: 503 } }; + }; - channel.history({ direction: 'forwards' }, function (err, page) { - if (err) { - done(err); - return; - } - /* TODO uncomment when idempotent publishing works on sandbox - * until then, test with ABLY_ENV=idempotent-dev - test.equal(page.items.length, 2, 'Only one message (with two items) should have been published'); - */ - try { - expect(page.items[0].id).to.equal(idOne, 'Check message id 1 preserved in history'); - expect(page.items[1].id).to.equal(idTwo, 'Check message id 1 preserved in history'); - done(); - } catch (err) { - done(err); - } - }); - }); + await channel.publish([{ name: 'one' }, { name: 'two' }]); + var page = await channel.history({ direction: 'forwards' }); + /* TODO uncomment when idempotent publishing works on sandbox + * until then, test with ABLY_ENV=idempotent-dev + * test.equal(page.items.length, 2, 'Only one message (with two items) should have been published'); + */ + expect(page.items[0].id).to.equal(idOne, 'Check message id 1 preserved in history'); + expect(page.items[1].id).to.equal(idTwo, 'Check message id 1 preserved in history'); }); - if (typeof Promise !== undefined) { - it('Rest publish promise', function (done) { - var rest = helper.AblyRest({ promises: true }); - var channel = rest.channels.get('publishpromise'); - - channel - .publish('name', 'data') - .then(function () { - return channel.history(); - }) - .then(function (page) { - var message = page.items[0]; - try { - expect( - message.data == 'data', - 'Check publish and history promise methods both worked as expected' - ).to.be.ok; - done(); - } catch (err) { - done(err); - } - }) - ['catch'](function (err) { - done(err); - }); - }); - } - - it('Rest publish params', function (done) { + it('Rest publish params', async function () { var rest = helper.AblyRest(), channel = rest.channels.get('publish_params'); + var originalPublish = channel._publish; + /* Stub out _publish to check params */ - var i = 0; - channel._publish = function (requestBody, headers, params) { - try { - expect(params && params.testParam).to.equal('testParamValue'); - } catch (err) { - done(err); - } - if (++i === 8) { - done(); - } + channel._publish = async function (requestBody, headers, params) { + expect(params && params.testParam).to.equal('testParamValue'); + return originalPublish.apply(channel, arguments); }; - channel.publish('foo', 'bar', { testParam: 'testParamValue' }); - channel.publish('foo', { data: 'data' }, { testParam: 'testParamValue' }); - channel.publish('foo', { data: 'data' }, { testParam: 'testParamValue' }, noop); - channel.publish('foo', null, { testParam: 'testParamValue' }); - channel.publish(null, 'foo', { testParam: 'testParamValue' }); - channel.publish({ name: 'foo', data: 'bar' }, { testParam: 'testParamValue' }); - channel.publish([{ name: 'foo', data: 'bar' }], { testParam: 'testParamValue' }); - channel.publish([{ name: 'foo', data: 'bar' }], { testParam: 'testParamValue' }, noop); + await channel.publish('foo', 'bar', { testParam: 'testParamValue' }); + await channel.publish('foo', { data: 'data' }, { testParam: 'testParamValue' }); + await channel.publish('foo', { data: 'data' }, { testParam: 'testParamValue' }, noop); + await channel.publish('foo', null, { testParam: 'testParamValue' }); + await channel.publish(null, 'foo', { testParam: 'testParamValue' }); + await channel.publish({ name: 'foo', data: 'bar' }, { testParam: 'testParamValue' }); + await channel.publish([{ name: 'foo', data: 'bar' }], { testParam: 'testParamValue' }); + await channel.publish([{ name: 'foo', data: 'bar' }], { testParam: 'testParamValue' }, noop); }); }); }); diff --git a/test/rest/presence.test.js b/test/rest/presence.test.js index c7c6da664d..d0e6d98a74 100644 --- a/test/rest/presence.test.js +++ b/test/rest/presence.test.js @@ -6,7 +6,6 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var expect = chai.expect; var Crypto = Ably.Realtime.Platform.Crypto; var BufferUtils = Ably.Realtime.Platform.BufferUtils; - var arrFind = helper.arrFind; function cipherParamsFromConfig(cipherConfig) { var cipherParams = new Crypto.CipherParams(); @@ -21,6 +20,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async } describe('rest/presence', function () { + this.timeout(60 * 1000); + before(function (done) { helper.setupApp(function () { rest = helper.AblyRest(); @@ -30,43 +31,33 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); function presence_simple(operation) { - return function (done) { - try { - var cipherParams = cipherParamsFromConfig(cipherConfig); - var channel = rest.channels.get('persisted:presence_fixtures', { cipher: cipherParams }); - channel.presence[operation](function (err, resultPage) { - if (err) { - done(err); - return; - } - var presenceMessages = resultPage.items; - expect(presenceMessages.length).to.equal(6, 'Verify correct number of messages found'); - if (presenceMessages.length != 6) { - console.log('presenceMessages: ', JSON.stringify(presenceMessages)); - } - var encodedMessage = arrFind(presenceMessages, function (msg) { - return msg.clientId == 'client_encoded'; - }); - var decodedMessage = arrFind(presenceMessages, function (msg) { - return msg.clientId == 'client_decoded'; - }); - var boolMessage = arrFind(presenceMessages, function (msg) { - return msg.clientId == 'client_bool'; - }); - var intMessage = arrFind(presenceMessages, function (msg) { - return msg.clientId == 'client_int'; - }); - expect(encodedMessage.data).to.deep.equal(decodedMessage.data, 'Verify message decoding works correctly'); - expect(encodedMessage.encoding).to.equal(null, 'Decoding should remove encoding field'); - expect(decodedMessage.encoding).to.equal(null, 'Decoding should remove encoding field'); - expect(boolMessage.data).to.equal('true', 'should not attempt to parse string data when no encoding field'); - expect(intMessage.data).to.equal('24', 'should not attempt to parse string data when no encoding field'); - expect(boolMessage.action).to.equal(operation === 'get' ? 'present' : 'enter', 'appropriate action'); - done(); - }); - } catch (err) { - done(err); + return async function () { + var cipherParams = cipherParamsFromConfig(cipherConfig); + var channel = rest.channels.get('persisted:presence_fixtures', { cipher: cipherParams }); + var resultPage = await channel.presence[operation](); + var presenceMessages = resultPage.items; + expect(presenceMessages.length).to.equal(6, 'Verify correct number of messages found'); + if (presenceMessages.length != 6) { + console.log('presenceMessages: ', JSON.stringify(presenceMessages)); } + var encodedMessage = presenceMessages.find(function (msg) { + return msg.clientId == 'client_encoded'; + }); + var decodedMessage = presenceMessages.find(function (msg) { + return msg.clientId == 'client_decoded'; + }); + var boolMessage = presenceMessages.find(function (msg) { + return msg.clientId == 'client_bool'; + }); + var intMessage = presenceMessages.find(function (msg) { + return msg.clientId == 'client_int'; + }); + expect(encodedMessage.data).to.deep.equal(decodedMessage.data, 'Verify message decoding works correctly'); + expect(encodedMessage.encoding).to.equal(null, 'Decoding should remove encoding field'); + expect(decodedMessage.encoding).to.equal(null, 'Decoding should remove encoding field'); + expect(boolMessage.data).to.equal('true', 'should not attempt to parse string data when no encoding field'); + expect(intMessage.data).to.equal('24', 'should not attempt to parse string data when no encoding field'); + expect(boolMessage.action).to.equal(operation === 'get' ? 'present' : 'enter', 'appropriate action'); }; } @@ -75,91 +66,44 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async /* Ensure that calling JSON strinfigy on the Presence object converts the action string value back to a numeric value which the API requires */ - it('Presence message JSON serialisation', function (done) { + it('Presence message JSON serialisation', async function () { var channel = rest.channels.get('persisted:presence_fixtures'); - channel.presence.get(function (err, resultPage) { - if (err) { - done(err); - return; - } - var presenceMessages = resultPage.items; - var presenceBool = arrFind(presenceMessages, function (msg) { - return msg.clientId == 'client_bool'; - }); - try { - expect(JSON.parse(JSON.stringify(presenceBool)).action).to.equal(1); // present - presenceBool.action = 'leave'; - expect(JSON.parse(JSON.stringify(presenceBool)).action).to.equal(3); // leave - done(); - } catch (err) { - done(err); - } + var resultPage = await channel.presence.get(); + var presenceMessages = resultPage.items; + var presenceBool = presenceMessages.find(function (msg) { + return msg.clientId == 'client_bool'; }); + expect(JSON.parse(JSON.stringify(presenceBool)).action).to.equal(1); // present + presenceBool.action = 'leave'; + expect(JSON.parse(JSON.stringify(presenceBool)).action).to.equal(3); // leave }); - it('Presence get limits and filtering', function (done) { - try { - var channel = rest.channels.get('persisted:presence_fixtures'); + it('Presence get limits and filtering', async function () { + var channel = rest.channels.get('persisted:presence_fixtures'); - var tests = [ - // Result limit - function (cb) { - channel.presence.get({ limit: 3 }, function (err, resultPage) { - if (err) cb(err); - var presenceMessages = resultPage.items; - expect(presenceMessages.length).to.equal(3, 'Verify correct number of messages found'); - cb(); - }); - }, - // Filter by clientId - function (cb) { - channel.presence.get({ clientId: 'client_json' }, function (err, resultPage) { - if (err) cb(err); - var presenceMessages = resultPage.items; - expect(presenceMessages.length).to.equal(1, 'Verify correct number of messages found'); - cb(); - }); - }, - // Filter by connectionId - function (cb) { - channel.presence.get(function (err, resultPage) { - if (err) cb(err); - channel.presence.get({ connectionId: resultPage.items[0].connectionId }, function (err, resultPage) { - if (err) cb(err); - var presenceMessages = resultPage.items; - expect(presenceMessages.length).to.equal(6, 'Verify correct number of messages found'); - cb(); - }); - }); - }, - ]; + var tests = [ + // Result limit + async function () { + var resultPage = await channel.presence.get({ limit: 3 }); + var presenceMessages = resultPage.items; + expect(presenceMessages.length).to.equal(3, 'Verify correct number of messages found'); + }, + // Filter by clientId + async function (cb) { + var resultPage = await channel.presence.get({ clientId: 'client_json' }); + var presenceMessages = resultPage.items; + expect(presenceMessages.length).to.equal(1, 'Verify correct number of messages found'); + }, + // Filter by connectionId + async function () { + var resultPage = await channel.presence.get(); + var resultPage2 = await channel.presence.get({ connectionId: resultPage.items[0].connectionId }); + var presenceMessages = resultPage2.items; + expect(presenceMessages.length).to.equal(6, 'Verify correct number of messages found'); + }, + ]; - async.parallel(tests, function (err) { - if (err) { - done(err); - return; - } - done(); - }); - } catch (err) { - done(err); - } + await Promise.all(tests); }); - - if (typeof Promise !== 'undefined') { - it('presencePromise', function (done) { - var rest = helper.AblyRest({ promises: true }); - var channel = rest.channels.get('some_channel'); - - channel.presence - .get() - .then(function () { - done(); - }) - ['catch'](function (err) { - done(err); - }); - }); - } }); }); diff --git a/test/rest/push.test.js b/test/rest/push.test.js index 6b782e4dfe..461f6ea85f 100644 --- a/test/rest/push.test.js +++ b/test/rest/push.test.js @@ -39,7 +39,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async }); }); - it('Get subscriptions', function (done) { + it('Get subscriptions', async function () { var subscribes = []; var deletes = []; var subsByChannel = {}; @@ -52,57 +52,30 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async subsByChannel[sub.channel].push(sub); var rest = helper.AblyRest({ clientId: sub.clientId }); - subscribes.push(function (callback) { - rest.push.admin.channelSubscriptions.save(sub, callback); - }); - deletes.push(function (callback) { - rest.push.admin.channelSubscriptions.remove(sub, callback); - }); + subscribes.push(() => rest.push.admin.channelSubscriptions.save(sub)); + deletes.push(() => rest.push.admin.channelSubscriptions.remove(sub)); })(i); } var rest = helper.AblyRest(); - async.series( - [ - function (callback) { - async.parallel(subscribes, callback); - }, - function (callback) { - rest.push.admin.channelSubscriptions.list({ channel: 'pushenabled:foo1' }, callback); - }, - function (callback) { - rest.push.admin.channelSubscriptions.list({ channel: 'pushenabled:foo2' }, callback); - }, - function (callback) { - async.parallel(deletes, callback); - }, - ], - function (err, result) { - if (err) { - done(err); - return; - } - try { - testIncludesUnordered(untyped(result[1].items), untyped(subsByChannel['pushenabled:foo1'])); - testIncludesUnordered(untyped(result[2].items), untyped(subsByChannel['pushenabled:foo2'])); - done(); - } catch (err) { - done(err); - } - } - ); + await Promise.all(subscribes.map((sub) => sub())); + + var res1 = await rest.push.admin.channelSubscriptions.list({ channel: 'pushenabled:foo1' }); + var res2 = await rest.push.admin.channelSubscriptions.list({ channel: 'pushenabled:foo2' }); + + await Promise.all(deletes.map((del) => del())); + + testIncludesUnordered(untyped(res1.items), untyped(subsByChannel['pushenabled:foo1'])); + testIncludesUnordered(untyped(res2.items), untyped(subsByChannel['pushenabled:foo2'])); }); - it('Publish', function (done) { - var realtime = helper.AblyRealtime(); + it('Publish', async function () { + try { + var realtime = helper.AblyRealtime(); - var channel = realtime.channels.get('pushenabled:foo'); - channel.attach(function (err) { - if (err) { - closeAndFinish(done, realtime, err); - return; - } + var channel = realtime.channels.get('pushenabled:foo'); + await channel.attach(); var pushPayload = { notification: { title: 'Test message', body: 'Test message body' }, @@ -117,111 +90,38 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async ablyUrl: baseUri, }; - channel.subscribe('__ably_push__', function (msg) { - var receivedPushPayload = JSON.parse(msg.data); - try { + var prom = new Promise(function (resolve, reject) { + channel.subscribe('__ably_push__', function (msg) { expect(receivedPushPayload.data).to.deep.equal(pushPayload.data); expect(receivedPushPayload.notification.title).to.deep.equal(pushPayload.notification.title); expect(receivedPushPayload.notification.body).to.deep.equal(pushPayload.notification.body); - closeAndFinish(done, realtime); - } catch (err) { - closeAndFinish(done, realtime, err); - } + resolve(); + }); }); - realtime.push.admin.publish(pushRecipient, pushPayload, function (err) { - if (err) { - closeAndFinish(done, realtime, err); - } - }); - }); + await realtime.push.admin.publish(pushRecipient, pushPayload); + realtime.close(); + } catch (err) { + realtime.close(); + throw err; + } }); - if (typeof Promise !== 'undefined') { - it('Publish promise', function (done) { - var realtime = helper.AblyRealtime({ promises: true }); - var channelName = 'pushenabled:publish_promise'; - var channel = realtime.channels.get(channelName); - channel.attach(function (err) { - if (err) { - closeAndFinish(done, realtime, err); - return; - } - - var pushPayload = { - notification: { title: 'Test message', body: 'Test message body' }, - data: { foo: 'bar' }, - }; - - var baseUri = realtime.baseUri(Ably.Rest.Platform.Defaults.getHost(realtime.options)); - var pushRecipient = { - transportType: 'ablyChannel', - channel: 'pushenabled:foo', - ablyKey: realtime.options.key, - ablyUrl: baseUri, - }; - - channel.subscribe('__ably_push__', function (msg) { - var receivedPushPayload = JSON.parse(msg.data); - try { - expect(receivedPushPayload.data).to.deep.equal(pushPayload.data); - expect(receivedPushPayload.notification.title).to.deep.equal(pushPayload.notification.title); - expect(receivedPushPayload.notification.body).to.deep.equal(pushPayload.notification.body); - closeAndFinish(done, realtime, err); - } catch (err) { - done(err); - } - }); - - realtime.push.admin - .publish(pushRecipient, pushPayload) - .then(function () { - closeAndFinish(done, realtime); - }) - ['catch'](function (err) { - closeAndFinish(done, realtime, err); - }); - }); - }); - } - - it('deviceRegistrations save', function (done) { + it('deviceRegistrations save', async function () { var rest = helper.AblyRest(); - async.series( - [ - function (callback) { - rest.push.admin.deviceRegistrations.save(testDevice, callback); - }, - function (callback) { - rest.push.admin.deviceRegistrations.get(testDevice.id, callback); - }, - function (callback) { - rest.push.admin.deviceRegistrations.remove(testDevice.id, callback); - }, - ], - function (err, result) { - if (err) { - done(err); - return; - } - var saved = result[0]; - var got = result[1]; - try { - expect(got.push.state).to.equal('ACTIVE'); - delete got.metadata; // Ignore these properties for testing - delete got.push.state; - testIncludesUnordered(untyped(got), testDevice_withoutSecret); - testIncludesUnordered(untyped(saved), testDevice_withoutSecret); - done(); - } catch (err) { - done(err); - } - } - ); + var saved = await rest.push.admin.deviceRegistrations.save(testDevice); + var got = await rest.push.admin.deviceRegistrations.get(testDevice.id); + await rest.push.admin.deviceRegistrations.remove(testDevice.id); + + expect(got.push.state).to.equal('ACTIVE'); + delete got.metadata; // Ignore these properties for testing + delete got.push.state; + testIncludesUnordered(untyped(got), testDevice_withoutSecret); + testIncludesUnordered(untyped(saved), testDevice_withoutSecret); }); - it('deviceRegistrations get and list', function (done) { + it('deviceRegistrations get and list', async function () { var registrations = []; var deletes = []; var devices = []; @@ -263,184 +163,77 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async devices_withoutSecret.push(device_withoutSecret); var rest = helper.AblyRest({ clientId: device.clientId }); - registrations.push(function (callback) { - rest.push.admin.deviceRegistrations.save(device, callback); + registrations.push(function () { + return rest.push.admin.deviceRegistrations.save(device); }); - deletes.push(function (callback) { - rest.push.admin.deviceRegistrations.remove('device' + (i + 1), callback); + deletes.push(function () { + return rest.push.admin.deviceRegistrations.remove('device' + (i + 1)); }); })(i); } var rest = helper.AblyRest(); - async.series( - [ - function (callback) { - async.parallel(registrations, callback); - }, - function (callback) { - rest.push.admin.deviceRegistrations.list(null, callback); - }, - function (callback) { - rest.push.admin.deviceRegistrations.list({ clientId: 'testClient1' }, callback); - }, - function (callback) { - rest.push.admin.deviceRegistrations.list({ clientId: 'testClient2' }, callback); - }, - function (callback) { - rest.push.admin.deviceRegistrations.get(devices[0].id, callback); - }, - function (callback) { - async.parallel( - [ - function (callback) { - rest.push.admin.deviceRegistrations.removeWhere({ clientId: 'testClient1' }, callback); - }, - function (callback) { - rest.push.admin.deviceRegistrations.removeWhere({ clientId: 'testClient2' }, callback); - }, - ], - callback - ); - }, - function (callback) { - async.parallel(deletes, callback); - }, - ], - function (err, result) { - if (err) { - done(err); - return; - } - try { - expect(numberOfDevices).to.equal(result[0].length); - testIncludesUnordered(untyped(result[1].items), untyped(devices_withoutSecret)); - testIncludesUnordered(untyped(result[2].items), untyped(devicesByClientId['testClient1'])); - testIncludesUnordered(untyped(result[3].items), untyped(devicesByClientId['testClient2'])); - testIncludesUnordered(untyped(result[4]), untyped(devices[0])); - done(); - } catch (err) { - done(err); - } - } - ); + var res0 = await Promise.all(registrations.map((x) => x())); + var res1 = await rest.push.admin.deviceRegistrations.list(null); + var res2 = await rest.push.admin.deviceRegistrations.list({ clientId: 'testClient1' }); + var res3 = await rest.push.admin.deviceRegistrations.list({ clientId: 'testClient2' }); + var res4 = await rest.push.admin.deviceRegistrations.get(devices[0].id); + + await Promise.all([ + rest.push.admin.deviceRegistrations.removeWhere({ clientId: 'testClient1' }), + rest.push.admin.deviceRegistrations.removeWhere({ clientId: 'testClient2' }), + ]); + + await Promise.all(deletes.map((x) => x())); + + expect(numberOfDevices).to.equal(res0.length); + testIncludesUnordered(untyped(res1.items), untyped(devices_withoutSecret)); + testIncludesUnordered(untyped(res2.items), untyped(devicesByClientId['testClient1'])); + testIncludesUnordered(untyped(res3.items), untyped(devicesByClientId['testClient2'])); + testIncludesUnordered(untyped(res4), untyped(devices[0])); }); - it('deviceRegistrations remove removeWhere', function (done) { + it('deviceRegistrations remove removeWhere', async function () { var rest = helper.AblyRest(); - async.series( - [ - function (callback) { - rest.push.admin.deviceRegistrations.save(testDevice, callback); - }, - function (callback) { - rest.push.admin.deviceRegistrations.remove(testDevice.id, callback); - }, - function (callback) { - rest.push.admin.deviceRegistrations.get(testDevice.id, function (err, result) { - expect(err && err.statusCode).to.equal(404, 'Check device reg not found after removal'); - callback(null); - }); - }, - function (callback) { - rest.push.admin.deviceRegistrations.save(testDevice, callback); - }, - function (callback) { - rest.push.admin.deviceRegistrations.removeWhere({ deviceId: testDevice.id }, callback); - }, - function (callback) { - rest.push.admin.deviceRegistrations.get(testDevice.id, function (err, result) { - expect(err && err.statusCode).to.equal(404, 'Check device reg not found after removal'); - callback(null); - }); - }, - ], - function (err, result) { - if (err) { - done(err); - } - done(); - } - ); - }); + await rest.push.admin.deviceRegistrations.save(testDevice); + await rest.push.admin.deviceRegistrations.remove(testDevice.id); - if (typeof Promise !== undefined) { - it('deviceRegistrations promise', function (done) { - var rest = helper.AblyRest({ promises: true }); - - /* save */ - rest.push.admin.deviceRegistrations - .save(testDevice) - .then(function (saved) { - expect(saved.push.state).to.equal('ACTIVE'); - testIncludesUnordered(untyped(saved), testDevice_withoutSecret); - /* get */ - return rest.push.admin.deviceRegistrations.get(testDevice.id); - }) - .then(function (got) { - expect(got.push.state).to.equal('ACTIVE'); - delete got.metadata; // Ignore these properties for testing - delete got.push.state; - testIncludesUnordered(untyped(got), testDevice_withoutSecret); - /* list */ - return rest.push.admin.deviceRegistrations.list({ clientId: testDevice.clientId }); - }) - .then(function (result) { - expect(result.items.length).to.equal(1); - var got = result.items[0]; - expect(got.push.state).to.equal('ACTIVE'); - testIncludesUnordered(untyped(got), testDevice_withoutSecret); - /* remove */ - return rest.push.admin.deviceRegistrations.removeWhere({ deviceId: testDevice.id }); - }) - .then(function () { - done(); - }) - ['catch'](function (err) { - done(err); - }); - }); - } + try { + await rest.push.admin.deviceRegistrations.get(testDevice.id); + expect.fail('Expected push.admin.deviceRegistrations.get() to throw'); + } catch (err) { + expect(err.statusCode).to.equal(404, 'Check device reg not found after removal'); + } + + await rest.push.admin.deviceRegistrations.save(testDevice); + await rest.push.admin.deviceRegistrations.removeWhere({ deviceId: testDevice.id }); - it('channelSubscriptions save', function (done) { + try { + await rest.push.admin.deviceRegistrations.get(testDevice.id); + expect.fail('Expected push.admin.deviceRegistrations.get() to throw'); + } catch (err) { + expect(err.statusCode).to.equal(404, 'Check device reg not found after removal'); + } + }); + + it('channelSubscriptions save', async function () { var rest = helper.AblyRest({ clientId: 'testClient' }); var subscription = { clientId: 'testClient', channel: 'pushenabled:foo' }; - async.series( - [ - function (callback) { - rest.push.admin.channelSubscriptions.save(subscription, callback); - }, - function (callback) { - rest.push.admin.channelSubscriptions.list({ channel: 'pushenabled:foo' }, callback); - }, - function (callback) { - rest.push.admin.channelSubscriptions.remove(subscription, callback); - }, - ], - function (err, result) { - if (err) { - done(err); - return; - } - var saved = result[0]; - var sub = result[1].items[0]; - try { - expect(subscription.clientId).to.equal(saved.clientId); - expect(subscription.channel).to.equal(saved.channel); - expect(subscription.clientId).to.equal(sub.clientId); - expect(subscription.channel).to.equal(sub.channel); - done(); - } catch (err) { - done(err); - } - } - ); + var saved = await rest.push.admin.channelSubscriptions.save(subscription); + var result = await rest.push.admin.channelSubscriptions.list({ channel: 'pushenabled:foo' }); + var sub = result.items[0]; + await rest.push.admin.channelSubscriptions.remove(subscription); + + expect(subscription.clientId).to.equal(saved.clientId); + expect(subscription.channel).to.equal(saved.channel); + expect(subscription.clientId).to.equal(sub.clientId); + expect(subscription.channel).to.equal(sub.channel); }); - it('channelSubscriptions get', function (done) { + it('channelSubscriptions get', async function () { var subscribes = []; var deletes = []; var subsByChannel = {}; @@ -453,73 +246,37 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async subsByChannel[sub.channel].push(sub); var rest = helper.AblyRest(); - subscribes.push(function (callback) { - rest.push.admin.channelSubscriptions.save(sub, callback); + subscribes.push(function () { + return rest.push.admin.channelSubscriptions.save(sub); }); - deletes.push(function (callback) { - rest.push.admin.channelSubscriptions.remove({ clientId: 'testClient' + i }, callback); + deletes.push(function () { + return rest.push.admin.channelSubscriptions.remove({ clientId: 'testClient' + i }); }); })(i); } var rest = helper.AblyRest(); - async.series( - [ - function (callback) { - async.parallel(subscribes, callback); - }, - function (callback) { - rest.push.admin.channelSubscriptions.list({ channel: 'pushenabled:foo1' }, callback); - }, - function (callback) { - rest.push.admin.channelSubscriptions.list({ channel: 'pushenabled:foo2' }, callback); - }, - function (callback) { - async.parallel(deletes, callback); - }, - ], - function (err, result) { - if (err) { - done(err); - return; - } - try { - testIncludesUnordered(untyped(result[1].items), untyped(subsByChannel['pushenabled:foo1'])); - testIncludesUnordered(untyped(result[2].items), untyped(subsByChannel['pushenabled:foo2'])); - done(); - } catch (err) { - done(err); - } - } - ); + await Promise.all(subscribes.map((x) => x())); + + var res1 = await rest.push.admin.channelSubscriptions.list({ channel: 'pushenabled:foo1' }); + var res2 = await rest.push.admin.channelSubscriptions.list({ channel: 'pushenabled:foo2' }); + + await Promise.all(deletes.map((x) => x())); + + testIncludesUnordered(untyped(res1.items), untyped(subsByChannel['pushenabled:foo1'])); + testIncludesUnordered(untyped(res2.items), untyped(subsByChannel['pushenabled:foo2'])); }); - exports.push_channelSubscriptions_remove = function (test) { + it('push_channelSubscriptions_remove', async function () { var rest = helper.AblyRest({ clientId: 'testClient' }); var subscription = { clientId: 'testClient', channel: 'pushenabled:foo' }; - async.series( - [ - function (callback) { - rest.push.admin.channelSubscriptions.save(subscription, callback); - }, - function (callback) { - rest.push.admin.channelSubscriptions.remove(subscription, callback); - }, - ], - function (err, result) { - if (err) { - test.ok(false, err.message); - test.done(); - return; - } - test.done(); - } - ); - }; + await rest.push.admin.channelSubscriptions.save(subscription); + await rest.push.admin.channelSubscriptions.remove(subscription); + }); - it('channelSubscriptions listChannels', function (done) { + it('channelSubscriptions listChannels', async function () { var subscribes = []; var deletes = []; for (var i = 0; i < 5; i++) { @@ -527,74 +284,24 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var sub = { channel: 'pushenabled:listChannels' + ((i % 2) + 1), clientId: 'testClient' + ((i % 3) + 1) }; var rest = helper.AblyRest({ clientId: sub.clientId }); subscribes.push(function (callback) { - rest.push.admin.channelSubscriptions.save(sub, callback); + return rest.push.admin.channelSubscriptions.save(sub); }); - deletes.push(function (callback) { - rest.push.admin.channelSubscriptions.remove(sub, callback); + deletes.push(function () { + return rest.push.admin.channelSubscriptions.remove(sub); }); })(i); } var rest = helper.AblyRest(); - async.series( - [ - function (callback) { - async.parallel(subscribes, callback); - }, - function (callback) { - rest.push.admin.channelSubscriptions.listChannels(null, callback); - }, - function (callback) { - async.parallel(deletes, callback); - }, - ], - function (err, result) { - if (err) { - done(err); - return; - } - try { - testIncludesUnordered(['pushenabled:listChannels1', 'pushenabled:listChannels2'], result[1].items); - done(); - } catch (err) { - done(err); - } - } - ); - }); + await Promise.all(subscribes.map((x) => x())); - if (typeof Promise !== 'undefined') { - it('channelSubscriptions promise', function (done) { - var rest = helper.AblyRest({ promises: true }); - var channelId = 'pushenabled:channelsubscriptions_promise'; - var subscription = { clientId: 'testClient', channel: channelId }; - - rest.push.admin.channelSubscriptions - .save(subscription) - .then(function (saved) { - expect(subscription.clientId).to.equal(saved.clientId); - expect(subscription.channel).to.equal(saved.channel); - return rest.push.admin.channelSubscriptions.list({ channel: channelId }); - }) - .then(function (result) { - var sub = result.items[0]; - expect(subscription.clientId).to.equal(sub.clientId); - expect(subscription.channel).to.equal(sub.channel); - return rest.push.admin.channelSubscriptions.listChannels(null); - }) - .then(function (result) { - expect(Utils.arrIn(result.items, channelId)).to.be.ok; - return rest.push.admin.channelSubscriptions.remove(subscription); - }) - .then(function () { - done(); - }) - ['catch'](function (err) { - done(err); - }); - }); - } + var result = await rest.push.admin.channelSubscriptions.listChannels(null); + + await Promise.all(deletes.map((x) => x())); + + testIncludesUnordered(['pushenabled:listChannels1', 'pushenabled:listChannels2'], result.items); + }); function untyped(x) { return JSON.parse(JSON.stringify(x)); @@ -608,8 +315,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async * includesUnordered(x, y) -> string | true */ function includesUnordered(x, y) { - if (Utils.isArray(x)) { - if (!Utils.isArray(y)) { + if (Array.isArray(x)) { + if (!Array.isArray(y)) { return 'not both arrays'; } @@ -644,7 +351,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async return true; } else if (x instanceof Object) { - if (!(x instanceof Object) || Utils.isArray(y)) { + if (!(x instanceof Object) || Array.isArray(y)) { return 'not both objects'; } @@ -668,7 +375,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var eq = includesUnordered(x, y); expect(eq).to.equal( true, - JSON.stringify(x, null, 2) + ' includesUnordered ' + JSON.stringify(y, null, 2) + ' (' + eq + ')' + JSON.stringify(x, null, 2) + ' includesUnordered ' + JSON.stringify(y, null, 2) + ' (' + eq + ')', ); } }); diff --git a/test/rest/request.test.js b/test/rest/request.test.js index 573468e023..71e6cf445c 100644 --- a/test/rest/request.test.js +++ b/test/rest/request.test.js @@ -1,11 +1,12 @@ 'use strict'; -define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { +define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async, chai) { var rest; var expect = chai.expect; var utils = helper.Utils; var echoServerHost = 'echo.ably.io'; var restTestOnJsonMsgpack = helper.restTestOnJsonMsgpack; + var Defaults = Ably.Rest.Platform.Defaults; describe('rest/request', function () { this.timeout(60 * 1000); @@ -21,240 +22,157 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { }); }); - restTestOnJsonMsgpack('request_time', function (done, rest) { - rest.request('get', '/time', null, null, null, function (err, res) { + restTestOnJsonMsgpack('request_version', function (rest) { + const version = 150; // arbitrarily chosen + + async function testRequestHandler(_, __, headers) { try { - expect(!err, err && helper.displayError(err)).to.be.ok; - expect(res.statusCode).to.equal(200, 'Check statusCode'); - expect(res.success).to.equal(true, 'Check success'); - expect(utils.isArray(res.items), true, 'Check array returned').to.be.ok; - expect(res.items.length).to.equal(1, 'Check array was of length 1'); + expect('X-Ably-Version' in headers, 'Verify version header exists').to.be.ok; + expect(headers['X-Ably-Version']).to.equal(version.toString(), 'Verify version number sent in request'); done(); } catch (err) { done(err); } - }); + return new Promise(() => {}); + } + + rest.http.do = testRequestHandler; + + rest.request('get', '/time' /* arbitrarily chosen */, version, null, null, null); + }); + + restTestOnJsonMsgpack('request_time', async function (rest) { + const res = await rest.request('get', '/time', Defaults.protocolVersion, null, null, null); + expect(res.statusCode).to.equal(200, 'Check statusCode'); + expect(res.success).to.equal(true, 'Check success'); + expect(Array.isArray(res.items), true, 'Check array returned').to.be.ok; + expect(res.items.length).to.equal(1, 'Check array was of length 1'); }); - restTestOnJsonMsgpack('request_404', function (done, rest) { + restTestOnJsonMsgpack('request_404', async function (rest) { /* NB: can't just use /invalid or something as the CORS preflight will * fail. Need something superficially a valid path but where the actual * request fails */ - rest.request('get', '/keys/ablyjs.test/requestToken', null, null, null, function (err, res) { - try { - expect(err).to.equal( - null, - 'Check that we do not get an error response for a failure that returns an actual ably error code' - ); - expect(res.success).to.equal(false, 'Check res.success is false for a failure'); - expect(res.statusCode).to.equal(404, 'Check HPR.statusCode is 404'); - expect(res.errorCode).to.equal(40400, 'Check HPR.errorCode is 40400'); - expect(res.errorMessage, 'Check have an HPR.errorMessage').to.be.ok; - done(); - } catch (err) { - done(err); - } - }); + const res = await rest.request( + 'get', + '/keys/ablyjs.test/requestToken', + Defaults.protocolVersion, + null, + null, + null, + ); + expect(res.success).to.equal(false, 'Check res.success is false for a failure'); + expect(res.statusCode).to.equal(404, 'Check HPR.statusCode is 404'); + expect(res.errorCode).to.equal(40400, 'Check HPR.errorCode is 40400'); + expect(res.errorMessage, 'Check have an HPR.errorMessage').to.be.ok; }); /* With a network issue, should get an actual err, not an HttpPaginatedResponse with error members */ - it('request_network_error', function (done) { + it('request_network_error', async function () { rest = helper.AblyRest({ restHost: helper.unroutableAddress }); - rest.request('get', '/time', null, null, null, function (err, res) { - try { - expect(err, 'Check get an err').to.be.ok; - expect(!res, 'Check do not get a res').to.be.ok; - done(); - } catch (err) { - done(err); - } - }); + try { + var res = await rest.request('get', '/time', Defaults.protocolVersion, null, null, null); + } catch (err) { + expect(err, 'Check get an err').to.be.ok; + expect(!res, 'Check do not get a res').to.be.ok; + return; + } + expect.fail('Expected rest.request to throw'); }); /* Use the request feature to publish, then retrieve (one at a time), some messages */ - restTestOnJsonMsgpack('request_post_get_messages', function (done, rest, channelName) { + restTestOnJsonMsgpack('request_post_get_messages', async function (rest, channelName) { var channelPath = '/channels/' + channelName + '/messages', msgone = { name: 'faye', data: 'whittaker' }, msgtwo = { name: 'martin', data: 'reed' }; - async.waterfall( - [ - function (cb) { - rest.request('post', channelPath, null, msgone, null, function (err, res) { - try { - expect(!err, err && helper.displayError(err)).to.be.ok; - expect(res.statusCode).to.equal(201, 'Check statusCode is 201'); - expect(res.success).to.equal(true, 'Check post was a success'); - expect(res.items && res.items.length).to.equal(1, 'Check number of results is as expected'); - cb(); - } catch (err) { - cb(err); - } - }); - }, - function (cb) { - rest.request('post', channelPath, null, msgtwo, null, function (err, res) { - try { - expect(!err, err && helper.displayError(err)).to.be.ok; - expect(res.statusCode).to.equal(201, 'Check statusCode is 201'); - expect(res.items && res.items.length).to.equal(1, 'Check number of results is as expected'); - cb(); - } catch (err) { - cb(err); - } - }); - }, - function (cb) { - rest.request('get', channelPath, { limit: 1, direction: 'forwards' }, null, null, function (err, res) { - try { - expect(!err, err && helper.displayError(err)).to.be.ok; - expect(res.statusCode).to.equal(200, 'Check statusCode is 200'); - expect(res.items.length).to.equal(1, 'Check only one msg returned'); - expect(res.items[0].name).to.equal(msgone.name, 'Check name is as expected'); - expect(res.items[0].data).to.equal(msgone.data, 'Check data is as expected'); - expect(res.hasNext, 'Check hasNext is true').to.be.ok; - cb(null, res.next); - } catch (err) { - cb(err); - } - }); - }, - function (next, cb) { - next(function (err, res) { - try { - expect(!err, err && helper.displayError(err)).to.be.ok; - expect(res.statusCode).to.equal(200, 'Check statusCode is 200'); - expect(res.success).to.equal(true, 'Check success'); - expect(res.items.length).to.equal(1, 'Check only one msg returned'); - expect(res.items[0].name).to.equal(msgtwo.name, 'Check name is as expected'); - expect(res.items[0].data).to.equal(msgtwo.data, 'Check data is as expected'); - cb(); - } catch (err) { - cb(err); - } - }); - }, - function (cb) { - /* Finally check the messages the 'normal' way to make sure everything's as expected */ - rest.channels.get(channelName).history(function (err, res) { - try { - expect(!err, err && helper.displayError(err)).to.be.ok; - expect(res.items.length).to.equal(2, 'Check both msgs returned'); - expect(res.items[0].name).to.equal(msgtwo.name, 'Check name is as expected'); - expect(res.items[0].data).to.equal(msgtwo.data, 'Check data is as expected'); - cb(); - } catch (err) { - cb(err); - } - }); - }, - ], - function (err) { - if (err) { - done(err); - return; - } - done(); - } + var res = await rest.request('post', channelPath, Defaults.protocolVersion, null, msgone, null); + expect(res.statusCode).to.equal(201, 'Check statusCode is 201'); + expect(res.success).to.equal(true, 'Check post was a success'); + expect(res.items && res.items.length).to.equal(1, 'Check number of results is as expected'); + + res = await rest.request('post', channelPath, Defaults.protocolVersion, null, msgtwo, null); + expect(res.statusCode).to.equal(201, 'Check statusCode is 201'); + expect(res.items && res.items.length).to.equal(1, 'Check number of results is as expected'); + + res = await rest.request( + 'get', + channelPath, + Defaults.protocolVersion, + { limit: 1, direction: 'forwards' }, + null, + null, ); + expect(res.statusCode).to.equal(200, 'Check statusCode is 200'); + expect(res.items.length).to.equal(1, 'Check only one msg returned'); + expect(res.items[0].name).to.equal(msgone.name, 'Check name is as expected'); + expect(res.items[0].data).to.equal(msgone.data, 'Check data is as expected'); + expect(res.hasNext, 'Check hasNext is true').to.be.ok; + + res = await res.next(); + expect(res.statusCode).to.equal(200, 'Check statusCode is 200'); + expect(res.success).to.equal(true, 'Check success'); + expect(res.items.length).to.equal(1, 'Check only one msg returned'); + expect(res.items[0].name).to.equal(msgtwo.name, 'Check name is as expected'); + expect(res.items[0].data).to.equal(msgtwo.data, 'Check data is as expected'); + + /* Finally check the messages the 'normal' way to make sure everything's as expected */ + res = await rest.channels.get(channelName).history(); + expect(res.items.length).to.equal(2, 'Check both msgs returned'); + expect(res.items[0].name).to.equal(msgtwo.name, 'Check name is as expected'); + expect(res.items[0].data).to.equal(msgtwo.data, 'Check data is as expected'); }); - restTestOnJsonMsgpack('request_batch_api_success', function (done, rest, name) { + restTestOnJsonMsgpack('request_batch_api_success', async function (rest, name) { var body = { channels: [name + '1', name + '2'], messages: { data: 'foo' } }; - rest.request('POST', '/messages', {}, body, {}, function (err, res) { - try { - expect(err).to.equal(null, 'Check that we do not get an error response for a success'); - expect(res.success).to.equal(true, 'Check res.success is true for a success'); - expect(res.statusCode).to.equal(201, 'Check res.statusCode is 201 for a success'); - expect(res.errorCode).to.equal(null, 'Check res.errorCode is null for a success'); - expect(res.errorMessage).to.equal(null, 'Check res.errorMessage is null for a success'); - - expect( - !res.items[0].batchResponse, - 'Check no batchResponse, since items is now just a flat array of channel responses' - ).to.be.ok; - expect(res.items.length).to.equal(2, 'Verify batched response includes response for each channel'); - expect(!res.items[0].error, 'Verify channel1 response is not an error').to.be.ok; - expect(res.items[0].channel).to.equal(name + '1', 'Verify channel1 response includes correct channel'); - expect(!res.items[1].error, 'Verify channel2 response is not an error').to.be.ok; - expect(res.items[1].channel).to.equal(name + '2', 'Verify channel2 response includes correct channel'); - done(); - } catch (err) { - done(err); - } - }); + const res = await rest.request('POST', '/messages', 2, {}, body, {}); + expect(res.success).to.equal(true, 'Check res.success is true for a success'); + expect(res.statusCode).to.equal(201, 'Check res.statusCode is 201 for a success'); + expect(res.errorCode).to.equal(null, 'Check res.errorCode is null for a success'); + expect(res.errorMessage).to.equal(null, 'Check res.errorMessage is null for a success'); + + expect( + !res.items[0].batchResponse, + 'Check no batchResponse, since items is now just a flat array of channel responses', + ).to.be.ok; + expect(res.items.length).to.equal(2, 'Verify batched response includes response for each channel'); + expect(!res.items[0].error, 'Verify channel1 response is not an error').to.be.ok; + expect(res.items[0].channel).to.equal(name + '1', 'Verify channel1 response includes correct channel'); + expect(!res.items[1].error, 'Verify channel2 response is not an error').to.be.ok; + expect(res.items[1].channel).to.equal(name + '2', 'Verify channel2 response includes correct channel'); }); - restTestOnJsonMsgpack.skip('request_batch_api_partial_success', function (done, rest, name) { + restTestOnJsonMsgpack.skip('request_batch_api_partial_success', async function (rest, name) { var body = { channels: [name, '[invalid', ''], messages: { data: 'foo' } }; - rest.request('POST', '/messages', {}, body, {}, function (err, res) { - try { - expect(err).to.equal(null, 'Check that we do not get an error response for a partial success'); - expect(res.success).to.equal(false, 'Check res.success is false for a partial failure'); - expect(res.statusCode).to.equal(400, 'Check HPR.statusCode is 400 for a partial failure'); - expect(res.errorCode).to.equal(40020, 'Check HPR.errorCode is 40020 for a partial failure'); - expect(res.errorMessage, 'Check have an HPR.errorMessage').to.be.ok; - - var response = res.items[0]; - expect(response.error.code).to.equal(40020, 'Verify response has an errorCode'); - expect(response.batchResponse.length).to.equal( - 3, - 'Verify batched response includes response for each channel' - ); - expect(response.batchResponse[0].channel).to.equal(name, 'Verify channel1 response includes correct channel'); - expect(!response.batchResponse[0].error, 'Verify first channel response is not an error').to.be.ok; - expect(response.batchResponse[1].error.code).to.equal( - 40010, - 'Verify [invalid response includes an error with the right code' - ); - expect(response.batchResponse[2].error.code).to.equal( - 40010, - 'Verify empty channel response includes an error with the right code' - ); - done(); - } catch (err) { - done(err); - } - }); + var res = await rest.request('POST', '/messages', 2, {}, body, {}); + expect(res.success).to.equal(false, 'Check res.success is false for a partial failure'); + expect(res.statusCode).to.equal(400, 'Check HPR.statusCode is 400 for a partial failure'); + expect(res.errorCode).to.equal(40020, 'Check HPR.errorCode is 40020 for a partial failure'); + expect(res.errorMessage, 'Check have an HPR.errorMessage').to.be.ok; + + var response = res.items[0]; + expect(response.error.code).to.equal(40020, 'Verify response has an errorCode'); + expect(response.batchResponse.length).to.equal(3, 'Verify batched response includes response for each channel'); + expect(response.batchResponse[0].channel).to.equal(name, 'Verify channel1 response includes correct channel'); + expect(!response.batchResponse[0].error, 'Verify first channel response is not an error').to.be.ok; + expect(response.batchResponse[1].error.code).to.equal( + 40010, + 'Verify [invalid response includes an error with the right code', + ); + expect(response.batchResponse[2].error.code).to.equal( + 40010, + 'Verify empty channel response includes an error with the right code', + ); }); - utils.arrForEach(['put', 'patch', 'delete'], function (method) { - it('check' + method, function (done) { + ['put', 'patch', 'delete'].forEach(function (method) { + it('check' + method, async function () { var restEcho = helper.AblyRest({ useBinaryProtocol: false, restHost: echoServerHost, tls: true }); - restEcho.request(method, '/methods', {}, {}, {}, function (err, res) { - if (err) { - done(err); - } else { - try { - expect(res.items[0] && res.items[0].method).to.equal(method); - done(); - } catch (err) { - done(err); - } - } - }); + var res = await restEcho.request(method, '/methods', Defaults.protocolVersion, {}, {}, {}); + expect(res.items[0] && res.items[0].method).to.equal(method); }); }); - - if (typeof Promise !== 'undefined') { - it('request_promise', function (done) { - var client = helper.AblyRest({ promises: true }); - - client - .request('get', '/time', null, null, null) - .then(function (res) { - expect(res.statusCode).to.equal(200, 'Check statusCode'); - expect(res.success).to.equal(true, 'Check success'); - expect(utils.isArray(res.items), true, 'Check array returned').to.be.ok; - expect(res.items.length).to.equal(1, 'Check array was of length 1'); - done(); - }) - ['catch'](function (err) { - done(err); - }); - }); - } }); }); diff --git a/test/rest/stats.test.js b/test/rest/stats.test.js index 840acfe70f..171a69730a 100644 --- a/test/rest/stats.test.js +++ b/test/rest/stats.test.js @@ -75,545 +75,325 @@ define(['shared_helper', 'chai'], function (helper, chai) { }); }); + it('contains expected fields', async () => { + // To provoke a non-undefined `inProgress` in the response, we publish a message and fetch stats for the current hour. (I wasn’t able to provoke a non-undefined `inProgress` using stats API fixtures.) + const now = new Date(await rest.time()); + // If the hour is about to turn, wait for it to turn (with a 5-second extra wait to hopefully account for clock differences between Ably servers). + if (now.getUTCMinutes() === 59 && now.getUTCSeconds() > 45) { + await new Promise((resolve) => setTimeout(resolve, 1000 * (5 + (60 - now.getUTCSeconds())))); + } + await rest.channels.get('channel').publish('message', 'data'); + // ably.com documentation says "The most recent statistics are delayed by up to six seconds." + await new Promise((resolve) => setTimeout(resolve, 6000 + 4000 /* a bit of extra tolerance */)); + + const stats = (await rest.stats({ end: Date.now(), unit: 'hour' })).items[0]; + + expect(stats.entries).to.be.a('object'); + expect(stats.schema).to.be.a('string'); + expect(stats.appId).to.be.a('string'); + expect(stats.inProgress).to.be.a('string'); + expect(stats.unit).to.be.a('string'); + expect(stats.intervalId).to.be.a('string'); + }); + /** * Using an interval ID string format, check minute-level inbound and outbound stats match fixture data (forwards) * @spec : (RSC6b4) */ - it('appstats_minute0', function (done) { - rest.stats( - { - start: lastYear + '-02-03:15:03', - end: lastYear + '-02-03:15:05', - direction: 'forwards', - }, - function (err, page) { - if (err) { - done(err); - return; - } - try { - var stats = page.items; - expect(stats.length).to.equal(3, 'Verify 3 stat records found'); - - var totalInbound = 0, - totalOutbound = 0; - for (var i = 0; i < stats.length; i++) { - totalInbound += stats[i].inbound.all.messages.count; - totalOutbound += stats[i].outbound.all.messages.count; - } - - expect(totalInbound).to.equal(50 + 60 + 70, 'Verify all inbound messages found'); - expect(totalOutbound).to.equal(20 + 10 + 40, 'Verify all outbound messages found'); - done(); - } catch (err) { - done(err); - } - } - ); + it('appstats_minute0', async function () { + var page = await rest.stats({ + start: lastYear + '-02-03:15:03', + end: lastYear + '-02-03:15:05', + direction: 'forwards', + }); + var stats = page.items; + expect(stats.length).to.equal(3, 'Verify 3 stat records found'); + + var totalInbound = 0, + totalOutbound = 0; + for (var i = 0; i < stats.length; i++) { + totalInbound += stats[i].entries['messages.inbound.all.messages.count']; + totalOutbound += stats[i].entries['messages.outbound.all.messages.count']; + } + + expect(totalInbound).to.equal(50 + 60 + 70, 'Verify all inbound messages found'); + expect(totalOutbound).to.equal(20 + 10 + 40, 'Verify all outbound messages found'); }); /** * Using milliseconds since epoch, check minute-level inbound and outbound stats match fixture data (forwards) * @spec : (RSC6b4) */ - it('appstats_minute1', function (done) { - rest.stats( - { - start: firstIntervalEpoch, - end: secondIntervalEpoch, - direction: 'forwards', - }, - function (err, page) { - if (err) { - done(err); - return; - } - try { - var stats = page.items; - expect(stats.length).to.equal(3, 'Verify 3 stat records found'); - - var totalInbound = 0, - totalOutbound = 0; - for (var i = 0; i < stats.length; i++) { - totalInbound += stats[i].inbound.all.messages.count; - totalOutbound += stats[i].outbound.all.messages.count; - } - - expect(totalInbound).to.equal(50 + 60 + 70, 'Verify all inbound messages found'); - expect(totalOutbound).to.equal(20 + 10 + 40, 'Verify all outbound messages found'); - done(); - } catch (err) { - done(err); - } - } - ); + it('appstats_minute1', async function () { + var page = await rest.stats({ + start: firstIntervalEpoch, + end: secondIntervalEpoch, + direction: 'forwards', + }); + var stats = page.items; + expect(stats.length).to.equal(3, 'Verify 3 stat records found'); + + var totalInbound = 0, + totalOutbound = 0; + for (var i = 0; i < stats.length; i++) { + totalInbound += stats[i].entries['messages.inbound.all.messages.count']; + totalOutbound += stats[i].entries['messages.outbound.all.messages.count']; + } + + expect(totalInbound).to.equal(50 + 60 + 70, 'Verify all inbound messages found'); + expect(totalOutbound).to.equal(20 + 10 + 40, 'Verify all outbound messages found'); }); /** * Check hour-level inbound and outbound stats match fixture data (forwards) * @spec : (RSC6b4) */ - it('appstats_hour0', function (done) { - rest.stats( - { - start: lastYear + '-02-03:15', - end: lastYear + '-02-03:18', - direction: 'forwards', - by: 'hour', - }, - function (err, page) { - if (err) { - done(err); - return; - } - try { - var stats = page.items; - expect(stats.length).to.equal(1, 'Verify 1 stat record found'); - - var totalInbound = 0, - totalOutbound = 0; - for (var i = 0; i < stats.length; i++) { - totalInbound += stats[i].inbound.all.messages.count; - totalOutbound += stats[i].outbound.all.messages.count; - } - - expect(totalInbound).to.equal(50 + 60 + 70, 'Verify all inbound messages found'); - expect(totalOutbound).to.equal(20 + 10 + 40, 'Verify all outbound messages found'); - done(); - } catch (err) { - done(err); - } - } - ); + it('appstats_hour0', async function () { + var page = await rest.stats({ + start: lastYear + '-02-03:15', + end: lastYear + '-02-03:18', + direction: 'forwards', + by: 'hour', + }); + var stats = page.items; + expect(stats.length).to.equal(1, 'Verify 1 stat record found'); + + var totalInbound = 0, + totalOutbound = 0; + for (var i = 0; i < stats.length; i++) { + totalInbound += stats[i].entries['messages.inbound.all.messages.count']; + totalOutbound += stats[i].entries['messages.outbound.all.messages.count']; + } + + expect(totalInbound).to.equal(50 + 60 + 70, 'Verify all inbound messages found'); + expect(totalOutbound).to.equal(20 + 10 + 40, 'Verify all outbound messages found'); }); /** * Check day-level stats exist (forwards) * @spec : (RSC6b4) */ - it.skip('appstats_day0', function (done) { - rest.stats( - { - end: lastYear + '-02-03', - direction: 'forwards', - by: 'day', - }, - function (err, page) { - if (err) { - done(err); - return; - } - try { - var stats = page.items; - expect(stats.length == 1, 'Verify 1 stat records found').to.be.ok; - - var totalInbound = 0, - totalOutbound = 0; - for (var i = 0; i < stats.length; i++) { - totalInbound += stats[i].inbound.all.messages.count; - totalOutbound += stats[i].outbound.all.messages.count; - } - - expect(totalInbound).to.equal(50 + 60 + 70, 'Verify all inbound messages found'); - expect(totalOutbound).to.equal(20 + 10 + 40, 'Verify all outbound messages found'); - done(); - } catch (err) { - done(err); - } - } - ); + it.skip('appstats_day0', async function () { + var page = await rest.stats({ + end: lastYear + '-02-03', + direction: 'forwards', + by: 'day', + }); + var stats = page.items; + expect(stats.length == 1, 'Verify 1 stat records found').to.be.ok; + + var totalInbound = 0, + totalOutbound = 0; + for (var i = 0; i < stats.length; i++) { + totalInbound += stats[i].entries['messages.inbound.all.messages.count']; + totalOutbound += stats[i].entries['messages.outbound.all.messages.count']; + } + + expect(totalInbound).to.equal(50 + 60 + 70, 'Verify all inbound messages found'); + expect(totalOutbound).to.equal(20 + 10 + 40, 'Verify all outbound messages found'); }); /** * Check month-level stats exist (forwards) * @spec : (RSC6b4) */ - it.skip('appstats_month0', function (done) { - rest.stats( - { - end: lastYear + '-02', - direction: 'forwards', - by: 'month', - }, - function (err, page) { - if (err) { - done(err); - return; - } - try { - var stats = page.items; - expect(stats.length == 1, 'Verify 1 stat records found').to.be.ok; - - var totalInbound = 0, - totalOutbound = 0; - for (var i = 0; i < stats.length; i++) { - totalInbound += stats[i].inbound.all.messages.count; - totalOutbound += stats[i].outbound.all.messages.count; - } - - expect(totalInbound).to.equal(50 + 60 + 70, 'Verify all inbound messages found'); - expect(totalOutbound).to.equal(20 + 10 + 40, 'Verify all outbound messages found'); - done(); - } catch (err) { - done(err); - } - } - ); + it.skip('appstats_month0', async function () { + var page = await rest.stats({ + end: lastYear + '-02', + direction: 'forwards', + by: 'month', + }); + var stats = page.items; + expect(stats.length == 1, 'Verify 1 stat records found').to.be.ok; + + var totalInbound = 0, + totalOutbound = 0; + for (var i = 0; i < stats.length; i++) { + totalInbound += stats[i].entries['messages.inbound.all.messages.count']; + totalOutbound += stats[i].entries['messages.outbound.all.messages.count']; + } + + expect(totalInbound).to.equal(50 + 60 + 70, 'Verify all inbound messages found'); + expect(totalOutbound).to.equal(20 + 10 + 40, 'Verify all outbound messages found'); }); /** * Check limit query param (backwards) * @spec : (RSC6b3) */ - it('appstats_limit_backwards', function (done) { - rest.stats( - { - end: lastYear + '-02-03:15:04', - direction: 'backwards', - limit: 1, - }, - function (err, page) { - if (err) { - done(err); - return; - } - try { - var stats = page.items; - expect(stats.length == 1, 'Verify 1 stat records found').to.be.ok; - - var totalInbound = 0, - totalOutbound = 0; - for (var i = 0; i < stats.length; i++) { - totalInbound += stats[i].inbound.all.messages.count; - totalOutbound += stats[i].outbound.all.messages.count; - } - - expect(totalInbound).to.equal(60, 'Verify all inbound messages found'); - expect(totalOutbound).to.equal(10, 'Verify all outbound messages found'); - done(); - } catch (err) { - done(err); - } - } - ); + it('appstats_limit_backwards', async function () { + var page = await rest.stats({ + end: lastYear + '-02-03:15:04', + direction: 'backwards', + limit: 1, + }); + var stats = page.items; + expect(stats.length == 1, 'Verify 1 stat records found').to.be.ok; + + var totalInbound = 0, + totalOutbound = 0; + for (var i = 0; i < stats.length; i++) { + totalInbound += stats[i].entries['messages.inbound.all.messages.count']; + totalOutbound += stats[i].entries['messages.outbound.all.messages.count']; + } + + expect(totalInbound).to.equal(60, 'Verify all inbound messages found'); + expect(totalOutbound).to.equal(10, 'Verify all outbound messages found'); }); /** * Check limit query param (forwards) * @spec : (RSC6b3) */ - it('appstats_limit_forwards', function (done) { - rest.stats( - { - end: lastYear + '-02-03:15:04', - direction: 'forwards', - limit: 1, - }, - function (err, page) { - if (err) { - done(err); - return; - } - try { - var stats = page.items; - expect(stats.length == 1, 'Verify 1 stat records found').to.be.ok; - - var totalInbound = 0, - totalOutbound = 0; - for (var i = 0; i < stats.length; i++) { - totalInbound += stats[i].inbound.all.messages.count; - totalOutbound += stats[i].outbound.all.messages.count; - } - - expect(totalInbound).to.equal(50, 'Verify all inbound messages found'); - expect(totalOutbound).to.equal(20, 'Verify all outbound messages found'); - done(); - } catch (err) { - done(err); - } - } - ); + it('appstats_limit_forwards', async function () { + var page = await rest.stats({ + end: lastYear + '-02-03:15:04', + direction: 'forwards', + limit: 1, + }); + var stats = page.items; + expect(stats.length == 1, 'Verify 1 stat records found').to.be.ok; + + var totalInbound = 0, + totalOutbound = 0; + for (var i = 0; i < stats.length; i++) { + totalInbound += stats[i].entries['messages.inbound.all.messages.count']; + totalOutbound += stats[i].entries['messages.outbound.all.messages.count']; + } + + expect(totalInbound).to.equal(50, 'Verify all inbound messages found'); + expect(totalOutbound).to.equal(20, 'Verify all outbound messages found'); }); /** * Check query pagination (backwards) * @spec : (RSC6b2) */ - it('appstats_pagination_backwards', function (done) { - rest.stats( - { - end: lastYear + '-02-03:15:05', - direction: 'backwards', - limit: 1, - }, - function (err, page) { - if (err) { - done(err); - return; - } - - var stats = page.items; - expect(stats.length == 1, 'Verify exactly one stats record found').to.be.ok; - var totalData = 0; - for (var i = 0; i < stats.length; i++) totalData += stats[i].inbound.all.messages.data; - expect(totalData).to.equal(7000, 'Verify all published message data found'); - - /* get next page */ - expect(page.hasNext(), 'Verify next page rel link present').to.be.ok; - page.next(function (err, page) { - if (err) { - done(err); - return; - } - try { - var stats = page.items; - expect(stats.length == 1, 'Verify exactly one stats record found').to.be.ok; - var totalData = 0; - for (var i = 0; i < stats.length; i++) totalData += stats[i].inbound.all.messages.data; - expect(totalData).to.equal(6000, 'Verify all published message data found'); - - /* get next page */ - expect(page.hasNext(), 'Verify next page rel link present').to.be.ok; - } catch (err) { - done(err); - return; - } - page.next(function (err, page) { - if (err) { - done(err); - return; - } - try { - var stats = page.items; - expect(stats.length == 1, 'Verify exactly one stats record found').to.be.ok; - var totalData = 0; - for (var i = 0; i < stats.length; i++) totalData += stats[i].inbound.all.messages.data; - expect(totalData).to.equal(5000, 'Verify all published message data found'); - - /* verify no further pages */ - expect(page.isLast(), 'Verify last page').to.be.ok; - } catch (err) { - done(err); - return; - } - - page.first(function (err, page) { - if (err) { - done(err); - return; - } - try { - var totalData = 0; - var stats = page.items; - for (var i = 0; i < stats.length; i++) totalData += stats[i].inbound.all.messages.data; - expect(totalData).to.equal(7000, 'Verify all published message data found'); - - /* that's it */ - done(); - } catch (err) { - done(err); - return; - } - }); - }); - }); - } - ); + it('appstats_pagination_backwards', async function () { + var page = await rest.stats({ + end: lastYear + '-02-03:15:05', + direction: 'backwards', + limit: 1, + }); + var stats = page.items; + expect(stats.length == 1, 'Verify exactly one stats record found').to.be.ok; + var totalData = 0; + for (var i = 0; i < stats.length; i++) totalData += stats[i].entries['messages.inbound.all.messages.data']; + expect(totalData).to.equal(7000, 'Verify all published message data found'); + + /* get next page */ + expect(page.hasNext(), 'Verify next page rel link present').to.be.ok; + var page = await page.next(); + var stats = page.items; + expect(stats.length == 1, 'Verify exactly one stats record found').to.be.ok; + var totalData = 0; + for (var i = 0; i < stats.length; i++) totalData += stats[i].entries['messages.inbound.all.messages.data']; + expect(totalData).to.equal(6000, 'Verify all published message data found'); + + /* get next page */ + expect(page.hasNext(), 'Verify next page rel link present').to.be.ok; + var page = await page.next(); + var stats = page.items; + expect(stats.length == 1, 'Verify exactly one stats record found').to.be.ok; + var totalData = 0; + for (var i = 0; i < stats.length; i++) totalData += stats[i].entries['messages.inbound.all.messages.data']; + expect(totalData).to.equal(5000, 'Verify all published message data found'); + + /* verify no further pages */ + expect(page.isLast(), 'Verify last page').to.be.ok; + + var page = await page.first(); + var totalData = 0; + var stats = page.items; + for (var i = 0; i < stats.length; i++) totalData += stats[i].entries['messages.inbound.all.messages.data']; + expect(totalData).to.equal(7000, 'Verify all published message data found'); }); /** * Check query pagination (forwards) * @spec : (RSC6b2) */ - it('appstats_pagination_forwards', function (done) { - rest.stats( - { - end: lastYear + '-02-03:15:05', - direction: 'forwards', - limit: 1, - }, - function (err, page) { - if (err) { - done(err); - return; - } - - try { - var stats = page.items; - expect(stats.length == 1, 'Verify exactly one stats record found').to.be.ok; - var totalData = 0; - for (var i = 0; i < stats.length; i++) totalData += stats[i].inbound.all.messages.data; - expect(totalData).to.equal(5000, 'Verify all published message data found'); - - /* get next page */ - expect(page.hasNext(), 'Verify next page rel link present').to.be.ok; - } catch (err) { - done(err); - return; - } - page.next(function (err, page) { - if (err) { - done(err); - return; - } - try { - var stats = page.items; - expect(stats.length == 1, 'Verify exactly one stats record found').to.be.ok; - var totalData = 0; - for (var i = 0; i < stats.length; i++) totalData += stats[i].inbound.all.messages.data; - expect(totalData).to.equal(6000, 'Verify all published message data found'); - - /* get next page */ - expect(page.hasNext(), 'Verify next page rel link present').to.be.ok; - } catch (err) { - done(err); - return; - } - page.next(function (err, page) { - if (err) { - done(err); - return; - } - try { - var stats = page.items; - expect(stats.length == 1, 'Verify exactly one stats record found').to.be.ok; - var totalData = 0; - for (var i = 0; i < stats.length; i++) totalData += stats[i].inbound.all.messages.data; - expect(totalData).to.equal(7000, 'Verify all published message data found'); - - /* verify no further pages */ - expect(page.isLast(), 'Verify last page').to.be.ok; - } catch (err) { - done(err); - return; - } - - page.first(function (err, page) { - if (err) { - done(err); - return; - } - try { - var totalData = 0; - var stats = page.items; - for (var i = 0; i < stats.length; i++) totalData += stats[i].inbound.all.messages.data; - expect(totalData).to.equal(5000, 'Verify all published message data found'); - - /* that's it */ - done(); - } catch (err) { - done(err); - } - }); - }); - }); - } - ); + it('appstats_pagination_forwards', async function () { + var page = await rest.stats({ + end: lastYear + '-02-03:15:05', + direction: 'forwards', + limit: 1, + }); + var stats = page.items; + expect(stats.length == 1, 'Verify exactly one stats record found').to.be.ok; + var totalData = 0; + for (var i = 0; i < stats.length; i++) totalData += stats[i].entries['messages.inbound.all.messages.data']; + expect(totalData).to.equal(5000, 'Verify all published message data found'); + + /* get next page */ + expect(page.hasNext(), 'Verify next page rel link present').to.be.ok; + var page = await page.next(); + var stats = page.items; + expect(stats.length == 1, 'Verify exactly one stats record found').to.be.ok; + var totalData = 0; + for (var i = 0; i < stats.length; i++) totalData += stats[i].entries['messages.inbound.all.messages.data']; + expect(totalData).to.equal(6000, 'Verify all published message data found'); + + /* get next page */ + expect(page.hasNext(), 'Verify next page rel link present').to.be.ok; + var page = await page.next(); + var stats = page.items; + expect(stats.length == 1, 'Verify exactly one stats record found').to.be.ok; + var totalData = 0; + for (var i = 0; i < stats.length; i++) totalData += stats[i].entries['messages.inbound.all.messages.data']; + expect(totalData).to.equal(7000, 'Verify all published message data found'); + + /* verify no further pages */ + expect(page.isLast(), 'Verify last page').to.be.ok; + + var page = await page.first(); + var totalData = 0; + var stats = page.items; + for (var i = 0; i < stats.length; i++) totalData += stats[i].entries['messages.inbound.all.messages.data']; + expect(totalData).to.equal(5000, 'Verify all published message data found'); }); /** * Check query pagination omitted (defaults to backwards) * @spec : (RSC6b2) */ - it('appstats_pagination_omitted', function (done) { - rest.stats( - { - end: lastYear + '-02-03:15:05', - limit: 1, - }, - function (err, page) { - if (err) { - done(err); - return; - } - - try { - var stats = page.items; - expect(stats.length == 1, 'Verify exactly one stats record found').to.be.ok; - var totalData = 0; - for (var i = 0; i < stats.length; i++) totalData += stats[i].inbound.all.messages.data; - expect(totalData).to.equal(7000, 'Verify all published message data found'); - - /* get next page */ - expect(page.hasNext(), 'Verify next page rel link present').to.be.ok; - } catch (err) { - done(err); - return; - } - page.next(function (err, page) { - if (err) { - done(err); - return; - } - try { - var stats = page.items; - expect(stats.length == 1, 'Verify exactly one stats record found').to.be.ok; - var totalData = 0; - for (var i = 0; i < stats.length; i++) totalData += stats[i].inbound.all.messages.data; - expect(totalData).to.equal(6000, 'Verify all published message data found'); - - /* get next page */ - expect(page.hasNext(), 'Verify next page rel link present').to.be.ok; - } catch (err) { - done(err); - return; - } - page.next(function (err, page) { - if (err) { - done(err); - return; - } - try { - var stats = page.items; - expect(stats.length == 1, 'Verify exactly one stats record found').to.be.ok; - var totalData = 0; - for (var i = 0; i < stats.length; i++) totalData += stats[i].inbound.all.messages.data; - expect(totalData).to.equal(5000, 'Verify all published message data found'); - - /* verify no further pages */ - expect(page.isLast(), 'Verify last page').to.be.ok; - } catch (err) { - done(err); - return; - } - - page.first(function (err, page) { - if (err) { - done(err); - return; - } - try { - var totalData = 0; - var stats = page.items; - for (var i = 0; i < stats.length; i++) totalData += stats[i].inbound.all.messages.data; - expect(totalData).to.equal(7000, 'Verify all published message data found'); - - /* that's it */ - done(); - } catch (err) { - done(err); - } - }); - }); - }); - } - ); - }); - - if (typeof Promise !== 'undefined') { - it('stats_promise', function (done) { - var client = helper.AblyRest({ promises: true }); - - client - .stats() - .then(function () { - console.log('here'); - done(); - }) - ['catch'](function (err) { - done(err); - }); + it('appstats_pagination_omitted', async function () { + var page = await rest.stats({ + end: lastYear + '-02-03:15:05', + limit: 1, }); - } + var stats = page.items; + expect(stats.length == 1, 'Verify exactly one stats record found').to.be.ok; + var totalData = 0; + for (var i = 0; i < stats.length; i++) totalData += stats[i].entries['messages.inbound.all.messages.data']; + expect(totalData).to.equal(7000, 'Verify all published message data found'); + + /* get next page */ + expect(page.hasNext(), 'Verify next page rel link present').to.be.ok; + var page = await page.next(); + var stats = page.items; + expect(stats.length == 1, 'Verify exactly one stats record found').to.be.ok; + var totalData = 0; + for (var i = 0; i < stats.length; i++) totalData += stats[i].entries['messages.inbound.all.messages.data']; + expect(totalData).to.equal(6000, 'Verify all published message data found'); + + /* get next page */ + expect(page.hasNext(), 'Verify next page rel link present').to.be.ok; + var page = await page.next(); + var stats = page.items; + expect(stats.length == 1, 'Verify exactly one stats record found').to.be.ok; + var totalData = 0; + for (var i = 0; i < stats.length; i++) totalData += stats[i].entries['messages.inbound.all.messages.data']; + expect(totalData).to.equal(5000, 'Verify all published message data found'); + + /* verify no further pages */ + expect(page.isLast(), 'Verify last page').to.be.ok; + + var page = await page.first(); + var totalData = 0; + var stats = page.items; + for (var i = 0; i < stats.length; i++) totalData += stats[i].entries['messages.inbound.all.messages.data']; + expect(totalData).to.equal(7000, 'Verify all published message data found'); + }); }); }); diff --git a/test/rest/status.test.js b/test/rest/status.test.js index 55573b4594..55c33a8b59 100644 --- a/test/rest/status.test.js +++ b/test/rest/status.test.js @@ -21,52 +21,18 @@ define(['shared_helper', 'chai'], function (helper, chai) { }); }); - restTestOnJsonMsgpack('status0', function (done, rest) { + restTestOnJsonMsgpack('status0', async function (rest) { var channel = rest.channels.get('status0'); - channel.status(function (err, channelDetails) { - try { - expect(channelDetails.channelId).to.equal('status0'); - expect(channelDetails.status.isActive).to.be.a('boolean'); - var metrics = channelDetails.status.occupancy.metrics; - expect(metrics.connections).to.be.a('number'); - expect(metrics.presenceConnections).to.be.a('number'); - expect(metrics.presenceMembers).to.be.a('number'); - expect(metrics.presenceSubscribers).to.be.a('number'); - expect(metrics.publishers).to.be.a('number'); - expect(metrics.subscribers).to.be.a('number'); - done(); - } catch (err) { - done(err); - } - }); + var channelDetails = await channel.status(); + expect(channelDetails.channelId).to.equal('status0'); + expect(channelDetails.status.isActive).to.be.a('boolean'); + var metrics = channelDetails.status.occupancy.metrics; + expect(metrics.connections).to.be.a('number'); + expect(metrics.presenceConnections).to.be.a('number'); + expect(metrics.presenceMembers).to.be.a('number'); + expect(metrics.presenceSubscribers).to.be.a('number'); + expect(metrics.publishers).to.be.a('number'); + expect(metrics.subscribers).to.be.a('number'); }); - - if (typeof Promise !== 'undefined') { - it('statusPromise', function (done) { - var rest = helper.AblyRest({ promises: true }); - var channel = rest.channels.get('statusPromise'); - channel - .status() - .then(function (channelDetails) { - try { - expect(channelDetails.channelId).to.equal('statusPromise'); - expect(channelDetails.status.isActive).to.be.a('boolean'); - var metrics = channelDetails.status.occupancy.metrics; - expect(metrics.connections).to.be.a('number'); - expect(metrics.presenceConnections).to.be.a('number'); - expect(metrics.presenceMembers).to.be.a('number'); - expect(metrics.presenceSubscribers).to.be.a('number'); - expect(metrics.publishers).to.be.a('number'); - expect(metrics.subscribers).to.be.a('number'); - done(); - } catch (err) { - done(err); - } - }) - ['catch'](function (err) { - done(err); - }); - }); - } }); }); diff --git a/test/rest/time.test.js b/test/rest/time.test.js index 839584378e..f491e892c8 100644 --- a/test/rest/time.test.js +++ b/test/rest/time.test.js @@ -2,7 +2,6 @@ define(['shared_helper', 'chai'], function (helper, chai) { var rest; - var utils = helper.Utils; var expect = chai.expect; describe('rest/time', function () { @@ -17,37 +16,13 @@ define(['shared_helper', 'chai'], function (helper, chai) { }); }); - it('time0', function (done) { - rest.time(function (err, serverTime) { - if (err) { - done(err); - return; - } - try { - var localFiveMinutesAgo = utils.now() - 5 * 60 * 1000; - expect( - serverTime > localFiveMinutesAgo, - 'Verify returned time matches current local time with 5 minute leeway for badly synced local clocks' - ).to.be.ok; - done(); - } catch (err) { - done(err); - } - }); + it('time0', async function () { + var serverTime = await rest.time(); + var localFiveMinutesAgo = Date.now() - 5 * 60 * 1000; + expect( + serverTime > localFiveMinutesAgo, + 'Verify returned time matches current local time with 5 minute leeway for badly synced local clocks', + ).to.be.ok; }); - - if (typeof Promise !== 'undefined') { - it('timePromise', function (done) { - var rest = helper.AblyRest({ promises: true }); - rest - .time() - .then(function () { - done(); - }) - ['catch'](function (err) { - done(err); - }); - }); - } }); }); diff --git a/test/support/browser_file_list.js b/test/support/browser_file_list.js index 6175faaf54..4b2c4df0c0 100644 --- a/test/support/browser_file_list.js +++ b/test/support/browser_file_list.js @@ -1,21 +1,13 @@ window.__testFiles__ = { base: '../' }; window.__testFiles__.files = { - 'build/ably-commonjs.js': true, - 'build/ably-commonjs.noencryption.js': true, 'build/ably-nativescript.js': true, 'build/ably-node.js': true, 'build/ably-reactnative.js': true, - 'build/ably-webworker.min.js': true, 'build/ably.js': true, 'build/ably.min.js': true, - 'build/ably.noencryption.js': true, - 'build/ably.noencryption.min.js': true, 'browser/lib/util/base64.js': true, 'node_modules/async/lib/async.js': true, 'node_modules/@ably/vcdiff-decoder/dist/vcdiff-decoder.js': true, - 'node_modules/crypto-js/build/enc-base64.js': true, - 'node_modules/crypto-js/build/enc-utf8.js': true, - 'node_modules/crypto-js/build/core.js': true, 'test/common/globals/environment.js': true, 'test/common/globals/named_dependencies.js': true, 'test/common/modules/client_module.js': true, @@ -51,7 +43,7 @@ window.__testFiles__.files = { 'test/realtime/reauth.test.js': true, 'test/realtime/resume.test.js': true, 'test/realtime/sync.test.js': true, - 'test/realtime/upgrade.test.js': true, + 'test/realtime/transports.test.js': true, 'test/rest/auth.test.js': true, 'test/rest/bufferutils.test.js': true, 'test/rest/capability.test.js': true, diff --git a/test/support/browser_setup.js b/test/support/browser_setup.js index 6ab5e74ab7..2cab40ad30 100644 --- a/test/support/browser_setup.js +++ b/test/support/browser_setup.js @@ -1,6 +1,7 @@ 'use strict'; var allTestFiles = [], + TEST_HOOKS_REGEXP = /root_hooks\.js$/i, TEST_REGEXP = /\.test\.js$/i, // TEST_REGEXP = /simple\.test\.js$/i, TEAR_DOWN_REGEXP = /tear_down\.js$/i; @@ -17,6 +18,14 @@ var forEachKey = function (object, fn) { } }; +// Add the root mocha hooks +forEachKey(window.__testFiles__.files, function (file) { + if (TEST_HOOKS_REGEXP.test(file)) { + // Normalize paths to RequireJS module names. + allTestFiles.push(pathToModule(file)); + } +}); + // Match all test files forEachKey(window.__testFiles__.files, function (file) { if (TEST_REGEXP.test(file)) { @@ -55,9 +64,6 @@ require([(baseUrl + '/test/common/globals/named_dependencies.js').replace('//', ably: { exports: 'Ably', }, - 'ably.noencryption': { - exports: 'Ably', - }, 'browser-base64': { exports: 'Base64', }, @@ -69,7 +75,15 @@ require([(baseUrl + '/test/common/globals/named_dependencies.js').replace('//', // dynamically load all test files deps: allTestFiles, - // we have to kickoff mocha - callback: () => mocha.run(), + callback: () => { + // (For some reason things don’t work if you return a Promise from this callback, hence the nested async function) + (async () => { + // Let modular.test.js register its tests before we run the test suite + await registerAblyModularTests(); + + // we have to kickoff mocha + mocha.run(); + })(); + }, }); }); diff --git a/test/support/mocha_reporter.js b/test/support/mocha_reporter.js index 922d807276..feb56fc687 100644 --- a/test/support/mocha_reporter.js +++ b/test/support/mocha_reporter.js @@ -1,5 +1,5 @@ const Mocha = require('mocha'); -const MochaJUnitReporter = require('./mocha_junit_reporter/build/node'); +const MochaJUnitReporter = require('mocha-junit-reporter'); const path = require('path'); const jUnitDirectoryPath = require('./junit_directory_path'); diff --git a/test/support/playwrightSetup.js b/test/support/playwrightSetup.js index ae4f3408d7..99a0d68b7a 100644 --- a/test/support/playwrightSetup.js +++ b/test/support/playwrightSetup.js @@ -84,7 +84,7 @@ class CustomEventReporter extends Mocha.reporters.HTML { total: this.testResultStats.passes + this.testResultStats.failures, jUnitReport: this.jUnitReport, }, - }) + }), ); } } diff --git a/test/support/root_hooks.js b/test/support/root_hooks.js index 8c96f0d332..8b3d59e704 100644 --- a/test/support/root_hooks.js +++ b/test/support/root_hooks.js @@ -9,4 +9,8 @@ define(['shared_helper'], function (helper) { done(); }); }); + + afterEach(helper.closeActiveClients); + afterEach(helper.logTestResults); + beforeEach(helper.clearTransportPreference); }); diff --git a/test/support/runPlaywrightTests.js b/test/support/runPlaywrightTests.js index 624fff4d4c..5dd1ca2b51 100644 --- a/test/support/runPlaywrightTests.js +++ b/test/support/runPlaywrightTests.js @@ -1,30 +1,23 @@ const playwright = require('playwright'); const path = require('path'); -const serverProcess = require('child_process').fork(path.resolve(__dirname, '..', 'web_server'), { - env: { PLAYWRIGHT_TEST: 1 }, -}); +const MochaServer = require('../web_server'); const fs = require('fs'); const jUnitDirectoryPath = require('./junit_directory_path'); const port = process.env.PORT || 3000; const host = 'localhost'; - -const browserEnv = process.env.PLAYWRIGHT_BROWSER; - -if (!['chromium', 'firefox', 'webkit'].includes(browserEnv)) { - throw new Error( - `PLAYWRIGHT_BROWSER environment variable must be either 'chromium', 'webkit' or 'firefox' (currently ${browserEnv})` - ); -} +const playwrightBrowsers = ['chromium', 'firefox', 'webkit']; +const mochaServer = new MochaServer(/* playwrightTest: */ true); const runTests = async (browserType) => { + mochaServer.listen(); const browser = await browserType.launch(); const page = await browser.newPage(); await page.goto(`http://${host}:${port}`); console.log(`\nrunning tests in ${browserType.name()}`); - await new Promise((resolve) => { + await new Promise((resolve, reject) => { // Expose a function inside the playwright browser to log to the NodeJS process stdout page.exposeFunction('onTestLog', ({ detail }) => { console.log(detail); @@ -53,8 +46,7 @@ const runTests = async (browserType) => { browser.close(); resolve(); } else { - console.log(`${browserType.name()} tests failed, exiting with code 1`); - process.exit(1); + reject(new Error(`${browserType.name()} tests failed, exiting with code 1`)); } }); @@ -72,17 +64,32 @@ const runTests = async (browserType) => { }; (async () => { + let caughtError; + try { - if (browserEnv) { - // If the PLAYWRIGHT_BROWSER env var is set, only run tests in the specified browser... - await runTests(playwright[browserEnv]); - } else { - // ...otherwise run all the browsers - await runTests(playwright.chromium); - await runTests(playwright.webkit); - await runTests(playwright.firefox); + const browserEnv = process.env.PLAYWRIGHT_BROWSER; + + if (!playwrightBrowsers.includes(browserEnv)) { + throw new Error( + `PLAYWRIGHT_BROWSER environment variable must be one of: ${playwrightBrowsers.join( + ', ', + )}. Currently: ${browserEnv}`, + ); } - } finally { - serverProcess.kill(); + + await runTests(playwright[browserEnv]); + } catch (error) { + // save error for now, we must ensure we end mocha web server first. + // if we end current process too early, mocha web server will be left running, + // causing problems when launching tests the second time. + caughtError = error; + } + + mochaServer.close(); + + // now when mocha web server is terminated, if there was an error, we can log it and exit with a failure code + if (caughtError) { + console.log(caughtError.message); + process.exit(1); } })(); diff --git a/test/tasks/grunt-mocha.js b/test/tasks/grunt-mocha.js deleted file mode 100644 index b1aa8e8375..0000000000 --- a/test/tasks/grunt-mocha.js +++ /dev/null @@ -1,70 +0,0 @@ -'use strict'; - -var fs = require('fs'), - path = require('path'), - shell = require('shelljs'), - kexec = require('kexec'); - -module.exports = function (grunt) { - var file = grunt.option('file'), - debug = grunt.option('debug'), - inspector = grunt.option('inspector'), - fgrep = grunt.option('fgrep'), - helpers = ['test/support/modules_helper.js', 'test/support/test_helper.js', 'test/support/root_hooks.js']; - - function getRelativePath(files) { - return files.map(function (helperPath) { - var fullPath = path.join(path.dirname(fs.realpathSync(__filename)), '../..', helperPath); - return path.relative(process.cwd(), fullPath); - }); - } - - function resolveTests(testString) { - return testString.split(',').map(function (test) { - var fullPath = path.join(process.cwd(), test); - return path.relative(process.cwd(), fullPath); - }); - } - - grunt.registerTask('mocha:webserver', 'Run the Mocha web server', function () { - kexec('test/web_server'); - }); - - grunt.registerTask( - 'mocha', - 'Run the Mocha test suite.\nOptions:\n --file= e.g. --file=test/rest/auth.test.js\n --debug will debug using standard node debugger\n --inspector will start with node inspector', - function () { - var runTests = getRelativePath(helpers).concat(['test/realtime/*.test.js', 'test/rest/*.test.js']).join(' '); - grunt.log.writeln('Running Mocha test suite against ' + (file ? file : 'all tests')); - - if (file) { - runTests = getRelativePath(helpers).concat(resolveTests(file)).join(' '); - } - - if (fgrep) { - runTests += ' --fgrep ' + fgrep; - } - - runTests += ` --reporter ${path.join(__dirname, '..', 'support', 'mocha_reporter.js')}`; - - var done = this.async(), - nodeExecutable = 'node'; - - if (debug) { - nodeExecutable = 'node debug'; - } else if (inspector) { - nodeExecutable = 'node-debug'; - } - - shell.exec(nodeExecutable + ' node_modules/.bin/mocha ' + runTests, function (code) { - if (code !== 0) { - grunt.log.error('Mocha tests failed!'); - shell.exit(1); - } else { - grunt.log.ok('Mocha tests passed'); - } - done(); - }); - } - ); -}; diff --git a/test/web_server b/test/web_server deleted file mode 100755 index 1549b96692..0000000000 --- a/test/web_server +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env node - -/* - Runs a simple web server that runs the mocha.html tests - This is useful if you need to run the mocha tests visually - via a tunnel to your localhost server -*/ - -"use strict"; - -var express = require('express'), - cors = require('cors'); - -var server = express(); - -/* - * This environment varuable is used to let this script know that tests are - * running in a playwright context. When truthy, a different html document is - * returned on 'GET /' and console logging is skipped. - */ -const playwrightTest = !!process.env.PLAYWRIGHT_TEST; - -server.use(function(req, res, next) { - if (!playwrightTest) console.log('%s %s %s', req.method, req.url, req.path); - next(); -}); - -server.use(cors()); - -server.get('/', function(req, res) { - if (playwrightTest) { - res.redirect('/playwright.html'); - } else { - res.redirect('/mocha.html'); - } -}); - - -/** - * This is a rather hacky workaround since crypto-js base64 depends on node_modules/crypto-js/build/core but will request the file from the root level of the server. - * I'm leaving this here for now in lieu of a more elegant solution as the 'real' solution is to improve the way we use dependencies in these tests. - */ -server.get('/core.js', function (req, res) { - res.redirect('/node_modules/crypto-js/build/core.js'); -}); - -server.use('/node_modules', express.static(__dirname + '/../node_modules')); -server.use('/test', express.static(__dirname)); -server.use('/browser', express.static(__dirname + '/../src/web')); -server.use('/build', express.static(__dirname + '/../build')); -server.use(express.static(__dirname)); - -var port = process.env.PORT || 3000; -server.listen(port); - -console.log("Mocha test server listening on http://localhost:3000/"); diff --git a/test/web_server.js b/test/web_server.js new file mode 100755 index 0000000000..bc5c29bdb2 --- /dev/null +++ b/test/web_server.js @@ -0,0 +1,53 @@ +var express = require('express'), + cors = require('cors'); + +/** + * Runs a simple web server that runs the mocha.html tests + * This is useful if you need to run the mocha tests visually + * via a tunnel to your localhost server + * + * @param playwrightTest - used to let this script know that tests are + * running in a playwright context. When truthy, a different html document is + * returned on 'GET /' and console logging is skipped. + */ + +class MochaServer { + constructor(playwrightTest) { + this.playwrightTest = playwrightTest; + } + + async listen() { + const app = express(); + app.use((req, res, next) => { + if (!this.playwrightTest) console.log('%s %s %s', req.method, req.url, req.path); + next(); + }); + + app.use(cors()); + + app.get('/', (req, res) => { + if (this.playwrightTest) { + res.redirect('/playwright.html'); + } else { + res.redirect('/mocha.html'); + } + }); + + app.use('/node_modules', express.static(__dirname + '/../node_modules')); + app.use('/test', express.static(__dirname)); + app.use('/browser', express.static(__dirname + '/../src/web')); + app.use('/build', express.static(__dirname + '/../build')); + app.use(express.static(__dirname)); + + const port = process.env.PORT || 3000; + this.server = app.listen(port); + + console.log('Mocha test server listening on http://localhost:3000/'); + } + + close() { + this.server.close(); + } +} + +module.exports = MochaServer; diff --git a/tsconfig.json b/tsconfig.json index b3a547de9d..e1d81bdb0c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { - "target": "es5", + "target": "ES2017", "module": "commonjs", - "lib": ["ES5", "DOM", "webworker"], + "lib": ["ES2017", "DOM", "DOM.Iterable", "webworker"], "strict": true, "esModuleInterop": true, "skipLibCheck": true, @@ -12,6 +12,7 @@ "baseUrl": "./src" }, "exclude": [ - "src/platform/react-hooks" + "src/platform/react-hooks", + "test/package" ] } diff --git a/typedoc.json b/typedoc.json index 5a43b8897c..24faf3bad9 100644 --- a/typedoc.json +++ b/typedoc.json @@ -1,5 +1,8 @@ { "$schema": "https://typedoc.org/schema.json", + "entryPoints": ["ably.d.ts", "modular.d.ts"], + "out": "typedoc/generated", + "readme": "typedoc/landing-page.md", "treatWarningsAsErrors": true, "includeVersion": true, "validation": true, diff --git a/typedoc/landing-page.md b/typedoc/landing-page.md new file mode 100644 index 0000000000..bbc69d315f --- /dev/null +++ b/typedoc/landing-page.md @@ -0,0 +1,12 @@ +# Ably JavaScript Client Library SDK API Reference + +The JavaScript Client Library SDK supports a realtime and a REST interface. The JavaScript API references are generated from the [Ably JavaScript Client Library SDK source code](https://github.com/ably/ably-js/) using [TypeDoc](https://typedoc.org) and structured by classes. + +The realtime interface enables a client to maintain a persistent connection to Ably and publish, subscribe and be present on channels. The REST interface is stateless and typically implemented server-side. It is used to make requests such as retrieving statistics, token authentication and publishing to a channel. + +There are two variants of the Ably JavaScript Client Library SDK: + +- [Default variant](modules/ably.html): This variant of the SDK always creates a fully-featured Ably client. +- [Modular (tree-shakable) variant](modules/modular.html): Aimed at those who are concerned about their app’s bundle size, this allows you to create a client which has only the functionality that you choose. + +View the [Ably docs](https://ably.com/docs/) for conceptual information on using Ably, and for API references featuring all languages. The combined [API references](https://ably.com/docs/api/) are organized by features and split between the [realtime](https://ably.com/docs/api/realtime-sdk) and [REST](https://ably.com/docs/api/rest-sdk) interfaces. diff --git a/webpack.config.js b/webpack.config.js index 88ee200a8a..e1a056a131 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,11 +1,9 @@ /* eslint-disable @typescript-eslint/no-var-requires */ const path = require('path'); -const { BannerPlugin } = require('webpack'); +const { BannerPlugin, ProvidePlugin } = require('webpack'); const banner = require('./src/fragments/license'); -const CopyPlugin = require('copy-webpack-plugin'); // This is needed for baseUrl to resolve correctly from tsconfig const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); -const nodeExternals = require('webpack-node-externals'); const baseConfig = { mode: 'production', @@ -28,7 +26,7 @@ const baseConfig = { { test: /\.ts$/, loader: 'ts-loader' }, ], }, - target: 'web', + target: ['web', 'es2017'], externals: { request: false, ws: false, @@ -46,46 +44,6 @@ function platformPath(platform, ...dir) { return path.resolve(__dirname, 'src', 'platform', platform, ...dir); } -const nodeConfig = { - ...baseConfig, - entry: { - index: platformPath('nodejs'), - }, - output: { - ...baseConfig.output, - filename: 'ably-node.js', - }, - target: 'node', - externals: { - got: true, - ws: true, - }, - optimization: { - minimize: false, - }, -}; - -const browserConfig = { - ...baseConfig, - output: { - ...baseConfig.output, - filename: 'ably.js', - }, - entry: { - index: platformPath('web'), - }, - node: { - crypto: 'empty', - Buffer: false, - }, - externals: { - 'crypto-js': true, - }, - optimization: { - minimize: false, - }, -}; - const nativeScriptConfig = { ...baseConfig, output: { @@ -96,9 +54,11 @@ const nativeScriptConfig = { entry: { index: platformPath('nativescript'), }, - node: { - crypto: 'empty', - Buffer: false, + resolve: { + ...baseConfig.resolve, + fallback: { + crypto: false, + }, }, externals: { request: false, @@ -123,10 +83,9 @@ const reactNativeConfig = { resolve: { extensions: ['.js', '.ts'], plugins: [new TsconfigPathsPlugin()], - }, - node: { - crypto: 'empty', - Buffer: false, + fallback: { + crypto: false, + }, }, externals: { request: false, @@ -138,181 +97,47 @@ const reactNativeConfig = { }, }; -const browserMinConfig = { - ...browserConfig, - output: { - ...baseConfig.output, - filename: 'ably.min.js', - }, - optimization: { - minimize: true, - }, - performance: { - hints: 'warning', - }, - devtool: 'source-map', -}; - -const webworkerConfig = { - target: 'webworker', - ...browserConfig, - entry: { - index: platformPath('web', 'index-webworker.ts'), - }, - output: { - ...baseConfig.output, - filename: 'ably-webworker.min.js', - globalObject: 'this', - }, - optimization: { - minimize: true, - }, - performance: { - hints: 'warning', - }, - plugins: [ - new CopyPlugin({ - patterns: [ - { - from: path.resolve(__dirname, 'src', 'fragments', 'ably.d.ts'), - to: path.resolve(__dirname, 'build', 'ably-webworker.min.d.ts'), - }, - ], - }), - ], -}; - -const noEncryptionConfig = { - ...browserConfig, - entry: { - index: platformPath('web-noencryption'), - }, - output: { - ...baseConfig.output, - filename: 'ably.noencryption.js', - }, -}; - -const noEncryptionMinConfig = { - ...browserMinConfig, - entry: { - index: platformPath('web-noencryption'), - }, - output: { - ...baseConfig.output, - filename: 'ably.noencryption.min.js', - }, - devtool: 'source-map', -}; - -// We are using UMD in ably.js now so there is no need to build separately for CommonJS. These files are still being distributed to avoid breaking changes but should no longer be used. -const commonJsConfig = { - ...browserConfig, - output: { - ...baseConfig.output, - filename: 'ably-commonjs.js', - }, -}; - -const commonJsNoEncryptionConfig = { - ...noEncryptionConfig, - output: { - ...baseConfig.output, - filename: 'ably-commonjs.noencryption.js', - }, -}; - /** - * We create a bundle that exposes the mocha-junit-reporter package. We do this for the following reasons: - * - * - Browser: - * - * 1. This package is designed for Node only and hence requires polyfills of Node libraries (e.g. `stream`, `path`) — webpack takes care of this for us. - * 2. The package is not compatible with RequireJS and hence we don’t have any easy way to directly load it in our tests — the webpack bundle exposes it as a global named MochaJUnitReporter. - * - * - Node: The library uses optional chaining syntax, which is not supported by Node 12. + * We create a bundle that exposes the mocha-junit-reporter package to be able to use it in the browser. We need to do this for the following reasons: + * - This package is designed for Node only and hence requires polyfills of Node libraries (e.g. `stream`, `path`) — webpack takes care of this for us. + * - The package is not compatible with RequireJS and hence we don’t have any easy way to directly load it in our tests — the webpack bundle exposes it as a global named MochaJUnitReporter. */ -function createMochaJUnitReporterConfigs() { +function createMochaJUnitReporterConfig() { const dir = path.join(__dirname, 'test', 'support', 'mocha_junit_reporter'); - const baseConfig = { + return { mode: 'development', entry: path.join(dir, 'index.js'), - module: { - rules: [ - { - // The optional chaining syntax used by mocha-junit-reporter is not supported by: - // - // 1. webpack 4 (which we’re currently using) - // 2. Node 12 (see above) - // - // For these reasons, we transpile using Babel. - test: /\.js$/, - loader: 'babel-loader', - options: { - presets: [['@babel/preset-env']], - }, - }, - ], - }, externals: { mocha: 'mocha.Mocha', }, output: { path: path.join(dir, 'build'), - }, - }; - - const browserConfig = { - ...baseConfig, - externals: { - mocha: 'mocha.Mocha', - }, - output: { - ...baseConfig.output, filename: 'browser.js', library: 'MochaJUnitReporter', }, + plugins: [ + new ProvidePlugin({ + process: 'process/browser.js', + }), + ], resolve: { + fallback: { + // These are the modules suggested by webpack, post v5-upgrade, to replace v4’s built-in shims. + path: require.resolve('path-browserify'), + stream: require.resolve('stream-browserify'), + }, modules: [ - // Webpack doesn’t provide a useful shim for the `fs` module, so we provide our own. + // Webpack doesn’t suggest any useful shim for the `fs` module, so we provide our own. path.resolve(dir, 'shims'), 'node_modules', ], }, }; - - const nodeConfig = { - ...baseConfig, - target: 'node', - output: { - ...baseConfig.output, - filename: 'node.js', - libraryTarget: 'umd', - }, - // Don’t bundle any packages except mocha-junit-reporter. Using the - // webpack-node-externals library which I saw mentioned on - // https://v4.webpack.js.org/configuration/externals/#function; there may - // be a simpler way of doing this but seems OK. - externals: [nodeExternals({ allowlist: 'mocha-junit-reporter' })], - }; - - return { - mochaJUnitReporterBrowser: browserConfig, - mochaJUnitReporterNode: nodeConfig, - }; } module.exports = { - node: nodeConfig, - browser: browserConfig, - browserMin: browserMinConfig, - webworker: webworkerConfig, nativeScript: nativeScriptConfig, reactNative: reactNativeConfig, - noEncryption: noEncryptionConfig, - noEncryptionMin: noEncryptionMinConfig, - commonJs: commonJsConfig, - commonJsNoEncryption: commonJsNoEncryptionConfig, - ...createMochaJUnitReporterConfigs(), + mochaJUnitReporterBrowser: createMochaJUnitReporterConfig(), };