From 7916f5cebdee2f3080e1437b62ca0837181e75ed Mon Sep 17 00:00:00 2001
From: "8beeeaaat(sadao.komaki)" <8beeeaaat@gmail.com>
Date: Wed, 30 Nov 2022 03:44:33 +0900
Subject: [PATCH] for WebUSB
---
.eslintrc.js | 46 +
.github/FUNDING.yml | 12 -
.gitignore | 91 +-
.npmignore | 4 +
README.md | 79 +-
ant-plus.js | 33 -
example/.gitignore | 24 +
example/index.html | 13 +
example/public/vite.svg | 1 +
example/src/App.css | 27 +
example/src/App.tsx | 260 +
example/src/assets/react.svg | 1 +
example/src/index.css | 70 +
example/src/main.tsx | 11 +
example/src/vite-env.d.ts | 1 +
example/tsconfig.json | 21 +
example/tsconfig.node.json | 9 +
example/vite.config.ts | 7 +
package-lock.json | 7597 ++++++++++++++++-
package.json | 98 +-
sample/bicycle-power.js | 20 -
sample/cadence-sensor.js | 24 -
sample/fitness-equipment.js | 58 -
sample/multi-scan.js | 61 -
sample/muscle-oxygen.js | 25 -
sample/sample.js | 88 -
sample/two-sticks.js | 38 -
src/Constants.ts | 106 +
src/GarminStick2.ts | 7 +
src/GarminStick3.ts | 7 +
src/ICancellationToken.ts | 3 +
src/Messages.ts | 195 +
src/USBDriver.ts | 207 +
src/ant.ts | 916 +-
src/bicycle-power-sensors.ts | 164 -
src/cadence-sensors.ts | 171 -
src/environment-sensors.ts | 99 -
src/fitness-equipment-sensors.ts | 743 --
src/heart-rate-sensors.ts | 197 -
src/index.ts | 45 +
src/lib/EventEmitter.ts | 233 +
src/lib/UpdateState.ts | 145 +
src/muscle-oxygen-sensors.ts | 223 -
src/sensors/AntPlusBaseSensor.ts | 14 +
src/sensors/AntPlusScanner.ts | 77 +
src/sensors/AntPlusSensor.ts | 51 +
src/sensors/BaseSensor.ts | 277 +
src/sensors/BicyclePowerScanState.ts | 18 +
src/sensors/BicyclePowerScanner.ts | 36 +
src/sensors/BicyclePowerSensor.ts | 35 +
src/sensors/BicyclePowerSensorState.ts | 146 +
src/sensors/CadenceScanState.ts | 12 +
src/sensors/CadenceScanner.ts | 42 +
src/sensors/CadenceSensor.ts | 41 +
src/sensors/CadenceSensorState.ts | 136 +
src/sensors/EnvironmentScanState.ts | 32 +
src/sensors/EnvironmentScanner.ts | 56 +
src/sensors/EnvironmentSensor.ts | 55 +
src/sensors/EnvironmentSensorState.ts | 49 +
src/sensors/FitnessEquipmentScanState.ts | 12 +
src/sensors/FitnessEquipmentScanner.ts | 36 +
src/sensors/FitnessEquipmentSensor.ts | 327 +
src/sensors/FitnessEquipmentSensorState.ts | 809 ++
src/sensors/HeartRateScanState.ts | 12 +
src/sensors/HeartRateScanner.ts | 48 +
src/sensors/HeartRateSensor.ts | 41 +
src/sensors/HeartRateSensorState.ts | 173 +
src/sensors/MuscleOxygenScanState.ts | 12 +
src/sensors/MuscleOxygenScanner.ts | 36 +
src/sensors/MuscleOxygenSensor.ts | 76 +
src/sensors/MuscleOxygenSensorState.ts | 211 +
src/sensors/SpeedCadenceScanState.ts | 12 +
src/sensors/SpeedCadenceScanner.ts | 42 +
src/sensors/SpeedCadenceSensor.ts | 41 +
src/sensors/SpeedCadenceSensorState.ts | 117 +
src/sensors/SpeedScanState.ts | 12 +
src/sensors/SpeedScanner.ts | 42 +
src/sensors/SpeedSensor.ts | 41 +
src/sensors/SpeedSensorState.ts | 150 +
src/sensors/StrideSpeedDistanceScanState.ts | 12 +
src/sensors/StrideSpeedDistanceScanner.ts | 36 +
src/sensors/StrideSpeedDistanceSensor.ts | 35 +
src/sensors/StrideSpeedDistanceSensorState.ts | 71 +
src/speed-cadence-sensors.ts | 136 -
src/speed-sensors.ts | 177 -
src/stride-speed-distance-sensors.ts | 104 -
tsconfig.json | 22 +-
tslint.json | 121 -
typings.json | 7 -
89 files changed, 12246 insertions(+), 3982 deletions(-)
create mode 100644 .eslintrc.js
delete mode 100644 .github/FUNDING.yml
create mode 100644 .npmignore
delete mode 100644 ant-plus.js
create mode 100644 example/.gitignore
create mode 100644 example/index.html
create mode 100644 example/public/vite.svg
create mode 100644 example/src/App.css
create mode 100644 example/src/App.tsx
create mode 100644 example/src/assets/react.svg
create mode 100644 example/src/index.css
create mode 100644 example/src/main.tsx
create mode 100644 example/src/vite-env.d.ts
create mode 100644 example/tsconfig.json
create mode 100644 example/tsconfig.node.json
create mode 100644 example/vite.config.ts
delete mode 100644 sample/bicycle-power.js
delete mode 100644 sample/cadence-sensor.js
delete mode 100644 sample/fitness-equipment.js
delete mode 100644 sample/multi-scan.js
delete mode 100644 sample/muscle-oxygen.js
delete mode 100644 sample/sample.js
delete mode 100644 sample/two-sticks.js
create mode 100644 src/Constants.ts
create mode 100644 src/GarminStick2.ts
create mode 100644 src/GarminStick3.ts
create mode 100644 src/ICancellationToken.ts
create mode 100644 src/Messages.ts
create mode 100644 src/USBDriver.ts
delete mode 100644 src/bicycle-power-sensors.ts
delete mode 100644 src/cadence-sensors.ts
delete mode 100644 src/environment-sensors.ts
delete mode 100644 src/fitness-equipment-sensors.ts
delete mode 100644 src/heart-rate-sensors.ts
create mode 100644 src/index.ts
create mode 100644 src/lib/EventEmitter.ts
create mode 100644 src/lib/UpdateState.ts
delete mode 100644 src/muscle-oxygen-sensors.ts
create mode 100644 src/sensors/AntPlusBaseSensor.ts
create mode 100644 src/sensors/AntPlusScanner.ts
create mode 100644 src/sensors/AntPlusSensor.ts
create mode 100644 src/sensors/BaseSensor.ts
create mode 100644 src/sensors/BicyclePowerScanState.ts
create mode 100644 src/sensors/BicyclePowerScanner.ts
create mode 100644 src/sensors/BicyclePowerSensor.ts
create mode 100644 src/sensors/BicyclePowerSensorState.ts
create mode 100644 src/sensors/CadenceScanState.ts
create mode 100644 src/sensors/CadenceScanner.ts
create mode 100644 src/sensors/CadenceSensor.ts
create mode 100644 src/sensors/CadenceSensorState.ts
create mode 100644 src/sensors/EnvironmentScanState.ts
create mode 100644 src/sensors/EnvironmentScanner.ts
create mode 100644 src/sensors/EnvironmentSensor.ts
create mode 100644 src/sensors/EnvironmentSensorState.ts
create mode 100644 src/sensors/FitnessEquipmentScanState.ts
create mode 100644 src/sensors/FitnessEquipmentScanner.ts
create mode 100644 src/sensors/FitnessEquipmentSensor.ts
create mode 100644 src/sensors/FitnessEquipmentSensorState.ts
create mode 100644 src/sensors/HeartRateScanState.ts
create mode 100644 src/sensors/HeartRateScanner.ts
create mode 100644 src/sensors/HeartRateSensor.ts
create mode 100644 src/sensors/HeartRateSensorState.ts
create mode 100644 src/sensors/MuscleOxygenScanState.ts
create mode 100644 src/sensors/MuscleOxygenScanner.ts
create mode 100644 src/sensors/MuscleOxygenSensor.ts
create mode 100644 src/sensors/MuscleOxygenSensorState.ts
create mode 100644 src/sensors/SpeedCadenceScanState.ts
create mode 100644 src/sensors/SpeedCadenceScanner.ts
create mode 100644 src/sensors/SpeedCadenceSensor.ts
create mode 100644 src/sensors/SpeedCadenceSensorState.ts
create mode 100644 src/sensors/SpeedScanState.ts
create mode 100644 src/sensors/SpeedScanner.ts
create mode 100644 src/sensors/SpeedSensor.ts
create mode 100644 src/sensors/SpeedSensorState.ts
create mode 100644 src/sensors/StrideSpeedDistanceScanState.ts
create mode 100644 src/sensors/StrideSpeedDistanceScanner.ts
create mode 100644 src/sensors/StrideSpeedDistanceSensor.ts
create mode 100644 src/sensors/StrideSpeedDistanceSensorState.ts
delete mode 100644 src/speed-cadence-sensors.ts
delete mode 100644 src/speed-sensors.ts
delete mode 100644 src/stride-speed-distance-sensors.ts
delete mode 100644 tslint.json
delete mode 100644 typings.json
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 0000000..df9cf3b
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,46 @@
+module.exports = {
+ env: {
+ browser: true,
+ es2021: true,
+ 'w3c-web-usb': true,
+ },
+ extends: [
+ 'eslint:recommended',
+ 'plugin:@typescript-eslint/recommended',
+ 'plugin:@typescript-eslint/recommended-requiring-type-checking',
+ 'airbnb',
+ 'airbnb/hooks',
+ 'prettier',
+ ],
+ ignorePatterns: ['node_modules/', 'dist/', 'spec/', '*.js'],
+ parser: '@typescript-eslint/parser',
+ parserOptions: {
+ sourceType: 'module',
+ ecmaVersion: 2020,
+ ecmaFeatures: {
+ jsx: true,
+ },
+ project: './tsconfig.json',
+ },
+ plugins: ['@typescript-eslint'],
+ rules: {
+ '@typescript-eslint/no-floating-promises': ['error', { ignoreIIFE: true }],
+ 'import/extensions': [
+ 'error',
+ 'ignorePackages',
+ {
+ ts: 'never',
+ tsx: 'never',
+ },
+ ],
+ 'import/prefer-default-export': ['off'],
+ 'no-bitwise': ['off'],
+ },
+ settings: {
+ 'import/resolver': {
+ node: {
+ extensions: ['.js', '.jsx', '.ts', '.tsx'],
+ },
+ },
+ },
+};
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
deleted file mode 100644
index b5be231..0000000
--- a/.github/FUNDING.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-# These are supported funding model platforms
-
-github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
-patreon: # Replace with a single Patreon username
-open_collective: # Replace with a single Open Collective username
-ko_fi: # Replace with a single Ko-fi username
-tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
-community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
-liberapay: # Replace with a single Liberapay username
-issuehunt: # Replace with a single IssueHunt username
-otechie: # Replace with a single Otechie username
-custom: ['paypal.me/Loghorn'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/.gitignore b/.gitignore
index 7fb279f..6704566 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,37 +1,104 @@
# Logs
logs
*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
+*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
-# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
+# Bower dependency directory (https://bower.io/)
+bower_components
+
# node-waf configuration
.lock-wscript
-# Compiled binary addons (http://nodejs.org/api/addons.html)
+# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
-# Dependency directory
-# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
-node_modules
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# TypeScript v1 declaration files
+typings/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+.env.test
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+
+# Next.js build output
+.next
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Gatsby files
+.cache/
+# Comment in the public line in if your project uses Gatsby and *not* Next.js
+# https://nextjs.org/blog/next-9-1#public-directory-support
+# public
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless/
-# Compiled ts files
-build
+# FuseBox cache
+.fusebox/
-# Typescript typings
-typings
+# DynamoDB Local files
+.dynamodb/
-# VS Code settings
-.settings
-.vscode
\ No newline at end of file
+# TernJS port file
+.tern-port
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..8e546b4
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,4 @@
+/node_modules
+/src
+/spec
+tsconfig.json
diff --git a/README.md b/README.md
index a08e4c7..de4aded 100644
--- a/README.md
+++ b/README.md
@@ -1,88 +1,73 @@
-# ant-plus
+# web-ant-plus
-A node.js module for ANT+
+A package for ANT+ on Web browsers.
-## Prerequisites
+This repository was based on [ant-plus the original module for Node.js](https://github.com/Loghorn/ant-plus) by [@Loghorn](https://github.com/Loghorn).
-Libusb is included as a submodule. On Linux, you'll need libudev to build libusb. On Ubuntu/Debian: `sudo apt-get install build-essential libudev-dev`
+📝 This package uses the [WebUSB API](https://developer.mozilla.org/en-US/docs/Web/API/WebUSB_API). This API is [not available in some browsers](https://developer.mozilla.org/en-US/docs/Web/API/USB#browser_compatibility), so requires handling.
-### Windows
-
-Use [Zadig](http://sourceforge.net/projects/libwdi/files/zadig/) to install the WinUSB driver for your USB device. Otherwise you will get `LIBUSB_ERROR_NOT_SUPPORTED` when attempting to open devices.
-
-### macOS
-
-On macOS (tested on High Sierra and Mojave), installing `ant-plus` will also
-install the required `libusb`.
-
-Make sure that Garmin Express is not running,
-because it will attach to the ANT+ stick and prevent `ant-plus` from doing so.
-
-## Install
+## How to use
```sh
-npm install ant-plus
-```
-
-## usage
-
-```javascript
-var Ant = require('ant-plus');
+npm install web-ant-plus
```
#### Create USB stick
-```javascript
-var stick = new Ant.GarminStick3;
+```typescript
+import { GarminStick3 } from 'web-ant-plus';
+const stick = new GarminStick3();
```
#### Create sensors
-```javascript
-var sensor = new Ant.HeartRateSensor(stick);
+```typescript
+const hrSensor = new HeartRateSensor(stick);
```
#### Attach events
-```javascript
-sensor.on('hbData', function (data) {
- console.log(data.DeviceID, data.ComputedHeartRate);
+```typescript
+hrSensor.on('hbData', function (data: HeartRateSensorState) {
+ console.log(data.DeviceID, data.ComputedHeartRate);
});
stick.on('startup', function () {
- sensor.attach(0, 0);
+ hrSensor.attach(0, 0);
});
```
#### Open stick
-```javascript
+```typescript
if (!stick.open()) {
- console.log('Stick not found!');
+ console.log('Stick not found!');
}
```
### scanning
-```javascript
-sensor.on('hbData', function (data) {
- console.log(data.DeviceID, data.ComputedHeartRate);
+```typescript
+const hrScanner = new HeartRateScanner(stick);
+
+hrScanner.on('hbData', function (data: HeartRateSensorState) {
+ console.log(data.DeviceID, data.ComputedHeartRate);
});
stick.on('startup', function () {
- sensor.scan();
-);
+ hrScanner.scan();
+});
if (!stick.open()) {
- console.log('Stick not found!');
+ console.log('Stick not found!');
}
```
## Important notes
-* never attach a sensor before receiving the startup event
-* never attach a new sensor before receiving the attached or detached event of the previous sensor
-* never detach a sensor before receiving the attached or detached event of the previous sensor
+- never attach a sensor before receiving the startup event
+- never attach a new sensor before receiving the attached or detached event of the previous sensor
+- never detach a sensor before receiving the attached or detached event of the previous sensor
## Objects
@@ -133,7 +118,7 @@ Fired after the stick is correctly closed.
#### methods
-##### attach(channel, deviceId)
+##### attach(channel: number, deviceID: number)
Attaches the sensors, using the specified channel and deviceId (use 0 to connect to the first device found).
@@ -233,3 +218,9 @@ Fired when data is received.
The `state.EventCount` value can be used to tell when a new measurement has been made by the sensor -
it's value will have been incremented.
+
+```
+This software is subject to the ANT+ Shared Source License www.thisisant.com/swlicenses
+Copyright (c) Garmin Canada Inc. 2018
+All rights reserved.
+```
diff --git a/ant-plus.js b/ant-plus.js
deleted file mode 100644
index 3cb8da4..0000000
--- a/ant-plus.js
+++ /dev/null
@@ -1,33 +0,0 @@
-const Ant = require('./build/ant');
-const HRS = require('./build/heart-rate-sensors');
-const SSD = require('./build/stride-speed-distance-sensors');
-const SC = require('./build/speed-cadence-sensors');
-const S = require('./build/speed-sensors');
-const C = require('./build/cadence-sensors');
-const BP = require('./build/bicycle-power-sensors');
-const FE = require('./build/fitness-equipment-sensors');
-const MO = require('./build/muscle-oxygen-sensors');
-const E = require('./build/environment-sensors');
-
-module.exports = {
- GarminStick2: Ant.GarminStick2,
- GarminStick3: Ant.GarminStick3,
- HeartRateSensor: HRS.HeartRateSensor,
- HeartRateScanner: HRS.HeartRateScanner,
- StrideSpeedDistanceSensor: SSD.StrideSpeedDistanceSensor,
- StrideSpeedDistanceScanner: SSD.StrideSpeedDistanceScanner,
- SpeedCadenceSensor: SC.SpeedCadenceSensor,
- SpeedCadenceScanner: SC.SpeedCadenceScanner,
- SpeedSensor: S.SpeedSensor,
- SpeedScanner: S.SpeedScanner,
- CadenceSensor: C.CadenceSensor,
- CadenceScanner: C.CadenceScanner,
- BicyclePowerSensor: BP.BicyclePowerSensor,
- BicyclePowerScanner: BP.BicyclePowerScanner,
- FitnessEquipmentSensor: FE.FitnessEquipmentSensor,
- FitnessEquipmentScanner: FE.FitnessEquipmentScanner,
- MuscleOxygenSensor: MO.MuscleOxygenSensor,
- MuscleOxygenScanner: MO.MuscleOxygenScanner,
- EnvironmentSensor: E.EnvironmentSensor,
- EnvironmentScanner: E.EnvironmentScanner,
-};
diff --git a/example/.gitignore b/example/.gitignore
new file mode 100644
index 0000000..a547bf3
--- /dev/null
+++ b/example/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/example/index.html b/example/index.html
new file mode 100644
index 0000000..5d00616
--- /dev/null
+++ b/example/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ WebUSB ANT+®
+
+
+
+
+
+
diff --git a/example/public/vite.svg b/example/public/vite.svg
new file mode 100644
index 0000000..e7b8dfb
--- /dev/null
+++ b/example/public/vite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/example/src/App.css b/example/src/App.css
new file mode 100644
index 0000000..140a9ea
--- /dev/null
+++ b/example/src/App.css
@@ -0,0 +1,27 @@
+#root {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2rem;
+ text-align: center;
+}
+
+.logo {
+ height: 6em;
+ padding: 1.5em;
+ will-change: filter;
+ transition: cubic-bezier(0.075, 0.82, 0.165, 1) 1.5s;
+}
+.logo:hover {
+ filter: drop-shadow(0 0 2em #646cffaa);
+}
+.logo.react:hover {
+ filter: drop-shadow(0 0 2em #61dafbaa);
+}
+
+.card {
+ padding: 2em;
+}
+
+.read-the-docs {
+ color: #888;
+}
diff --git a/example/src/App.tsx b/example/src/App.tsx
new file mode 100644
index 0000000..a7aeff4
--- /dev/null
+++ b/example/src/App.tsx
@@ -0,0 +1,260 @@
+import { useEffect, useState } from 'react';
+import { Constants } from '../../src/Constants';
+import { GarminStick2 } from '../../src/GarminStick2';
+import { Messages } from '../../src/Messages';
+import { BicyclePowerSensor } from '../../src/sensors/BicyclePowerSensor';
+import { BicyclePowerSensorState } from '../../src/sensors/BicyclePowerSensorState';
+import { HeartRateSensor } from '../../src/sensors/HeartRateSensor';
+import { HeartRateSensorState } from '../../src/sensors/HeartRateSensorState';
+import { SpeedCadenceSensor } from '../../src/sensors/SpeedCadenceSensor';
+import { SpeedCadenceSensorState } from '../../src/sensors/SpeedCadenceSensorState';
+import './App.css';
+import reactLogo from './assets/react.svg';
+
+function App() {
+ const stick = new GarminStick2();
+ const hrSensor = new HeartRateSensor(stick);
+ const speedCadenceSensor = new SpeedCadenceSensor(stick);
+ speedCadenceSensor.setWheelCircumference(2.12);
+ const bicyclePowerSensor = new BicyclePowerSensor(stick);
+ stick.on('startup', async () => {
+ try {
+ console.log('Stick startup');
+ await hrSensor.attachSensor(0, 0);
+ await speedCadenceSensor.attachSensor(1, 0);
+ await bicyclePowerSensor.attachSensor(2, 0);
+ setConnected(true);
+ } catch (error) {
+ console.error(error);
+ }
+ });
+ const [connected, setConnected] = useState(stick.isScanning());
+ const [heartbeat, setHeartbeat] = useState(0);
+ const [hrState, setHRState] = useState>([]);
+ const [speedState, setSpeedState] = useState>(
+ []
+ );
+ const [powerState, setPowerState] = useState>(
+ []
+ );
+
+ const newestHRState = hrState.length
+ ? hrState[hrState.length - 1]
+ : undefined;
+ const newestSpeedState = speedState.length
+ ? speedState[speedState.length - 1]
+ : undefined;
+
+ const newestPowerState = powerState.length
+ ? powerState[powerState.length - 1]
+ : undefined;
+
+ const sumComputedHeartRate = hrState.reduce((sum, state) => {
+ if (state && state.ComputedHeartRate) {
+ return sum + state.ComputedHeartRate / 2;
+ }
+ return sum;
+ }, 0);
+
+ const sumCalculatedCadence = powerState.reduce((sum, state) => {
+ if (state && state.Cadence) {
+ return sum + state.Cadence / 3;
+ }
+ return sum;
+ }, 0);
+
+ useEffect(() => {
+ if (heartbeat > 0) {
+ return;
+ }
+ stick.write(Messages.requestMessage(0, Constants.MESSAGE_TX_SYNC));
+ setHeartbeat(0);
+ }, [heartbeat]);
+
+ const onHeartRateData = (state: HeartRateSensorState) => {
+ console.log(state);
+ setHRState((prev) => [...prev, state]);
+ setHeartbeat((prev) => prev + 1);
+ };
+
+ const onSpeedData = (state: SpeedCadenceSensorState) => {
+ console.log(state);
+ setSpeedState((prev) => [...prev, state]);
+ setHeartbeat((prev) => prev + 1);
+ };
+
+ const onBicyclePowerData = (state: BicyclePowerSensorState) => {
+ console.log(state);
+ setPowerState((prev) => [...prev, state]);
+ setHeartbeat((prev) => prev + 1);
+ };
+
+ function handleClickSearchDevice() {
+ console.log('searching...');
+ try {
+ (async () => {
+ hrSensor.on('hbData', onHeartRateData);
+ speedCadenceSensor.on('speedData', onSpeedData);
+ bicyclePowerSensor.on('powerData', onBicyclePowerData);
+ await stick.open();
+ })();
+ } catch (error) {
+ console.error(error);
+ }
+ }
+
+ function handleClickClose() {
+ console.log('closing...');
+ try {
+ (async () => {
+ const close = await stick.reset();
+ console.log('close', close);
+ })();
+ setConnected(false);
+ } catch (error) {
+ console.error(error);
+ }
+ }
+
+ function meterPerSecToKmPerHour(mps: number) {
+ return mps * 3.6;
+ }
+
+ return (
+
+
+
+ WebUSB ANT+
+
+ ®
+
+
+
+ {connected ? (
+ <>
+
+
+
+ -
+ Heart Rate
+
+ -
+ {newestHRState?.ComputedHeartRate}
+
+ bpm
+
+
+ -
+ CalculatedSpeed
+
+ -
+ {meterPerSecToKmPerHour(
+ newestSpeedState?.CalculatedSpeed || 0
+ ).toFixed(1)}
+
+ km/h
+
+
+ -
+ Cadence
+
+ -
+ {newestPowerState?.Cadence}
+
+ rpm
+
+
+ -
+ Power
+
+ -
+ {newestPowerState?.Power?.toFixed(1)}
+
+ w
+
+
+
+ >
+ ) : (
+
+ )}
+
+
+ );
+}
+
+export default App;
diff --git a/example/src/assets/react.svg b/example/src/assets/react.svg
new file mode 100644
index 0000000..6c87de9
--- /dev/null
+++ b/example/src/assets/react.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/example/src/index.css b/example/src/index.css
new file mode 100644
index 0000000..917888c
--- /dev/null
+++ b/example/src/index.css
@@ -0,0 +1,70 @@
+:root {
+ font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
+ font-size: 16px;
+ line-height: 24px;
+ font-weight: 400;
+
+ color-scheme: light dark;
+ color: rgba(255, 255, 255, 0.87);
+ background-color: #242424;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-text-size-adjust: 100%;
+}
+
+a {
+ font-weight: 500;
+ color: #646cff;
+ text-decoration: inherit;
+}
+a:hover {
+ color: #535bf2;
+}
+
+body {
+ margin: 0;
+ display: flex;
+ place-items: center;
+ min-width: 320px;
+ min-height: 100vh;
+}
+
+h1 {
+ font-size: 3.2em;
+ line-height: 1.1;
+}
+
+button {
+ border-radius: 8px;
+ border: 1px solid transparent;
+ padding: 0.6em 1.2em;
+ font-size: 1em;
+ font-weight: 500;
+ font-family: inherit;
+ background-color: #1a1a1a;
+ cursor: pointer;
+ transition: border-color 0.25s;
+}
+button:hover {
+ border-color: #646cff;
+}
+button:focus,
+button:focus-visible {
+ outline: 4px auto -webkit-focus-ring-color;
+}
+
+@media (prefers-color-scheme: light) {
+ :root {
+ color: #213547;
+ background-color: #ffffff;
+ }
+ a:hover {
+ color: #747bff;
+ }
+ button {
+ background-color: #f9f9f9;
+ }
+}
diff --git a/example/src/main.tsx b/example/src/main.tsx
new file mode 100644
index 0000000..ce0af67
--- /dev/null
+++ b/example/src/main.tsx
@@ -0,0 +1,11 @@
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import '../../src/Ant';
+import App from './App';
+import './index.css';
+
+ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
+
+
+
+);
diff --git a/example/src/vite-env.d.ts b/example/src/vite-env.d.ts
new file mode 100644
index 0000000..11f02fe
--- /dev/null
+++ b/example/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/example/tsconfig.json b/example/tsconfig.json
new file mode 100644
index 0000000..6bdbee6
--- /dev/null
+++ b/example/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "useDefineForClassFields": true,
+ "lib": ["DOM", "DOM.Iterable", "ESNext"],
+ "allowJs": false,
+ "skipLibCheck": true,
+ "esModuleInterop": false,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx"
+ },
+ "include": ["src", "../src/**/*.ts"],
+ "references": [{ "path": "./tsconfig.node.json" }]
+}
diff --git a/example/tsconfig.node.json b/example/tsconfig.node.json
new file mode 100644
index 0000000..9d31e2a
--- /dev/null
+++ b/example/tsconfig.node.json
@@ -0,0 +1,9 @@
+{
+ "compilerOptions": {
+ "composite": true,
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "allowSyntheticDefaultImports": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/example/vite.config.ts b/example/vite.config.ts
new file mode 100644
index 0000000..4e7004e
--- /dev/null
+++ b/example/vite.config.ts
@@ -0,0 +1,7 @@
+import react from '@vitejs/plugin-react';
+import { defineConfig } from 'vite';
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [react()],
+});
diff --git a/package-lock.json b/package-lock.json
index 6a8c543..a260733 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,470 +1,7131 @@
{
- "name": "ant-plus",
- "version": "0.1.25",
- "lockfileVersion": 1,
- "requires": true,
- "dependencies": {
- "@types/node": {
- "version": "6.14.2",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-6.14.2.tgz",
- "integrity": "sha512-JWB3xaVfsfnFY8Ofc9rTB/op0fqqTSqy4vBcVk1LuRJvta7KTX+D//fCkiTMeLGhdr2EbFZzQjC97gvmPilk9Q==",
- "dev": true
- },
- "@types/usb": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/@types/usb/-/usb-1.5.1.tgz",
- "integrity": "sha512-1qhcYMLJ0I2HcRG3G/nBcRZ0KrrTdGdUNcCkEVgcga4KMlDXWh6LZJjVA6MiWEDa+BOaQTEfGJfuNaQ71IQOpg==",
- "dev": true,
- "requires": {
- "@types/node": "*"
- }
- },
- "ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
- },
- "aproba": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
- "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
- },
- "are-we-there-yet": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
- "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
- "requires": {
- "delegates": "^1.0.0",
- "readable-stream": "^2.0.6"
- }
- },
- "bindings": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
- "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
- "requires": {
- "file-uri-to-path": "1.0.0"
- }
- },
- "bl": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/bl/-/bl-3.0.0.tgz",
- "integrity": "sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A==",
- "requires": {
- "readable-stream": "^3.0.1"
- },
- "dependencies": {
- "readable-stream": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz",
- "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==",
- "requires": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
- }
- }
- }
- },
- "chownr": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz",
- "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A=="
- },
- "code-point-at": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
- "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
- },
- "console-control-strings": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
- "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
- },
- "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="
- },
- "decompress-response": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
- "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=",
- "requires": {
- "mimic-response": "^1.0.0"
- }
- },
- "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=="
- },
- "delegates": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
- "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
- },
- "detect-libc": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
- "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
- },
- "end-of-stream": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
- "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
- "requires": {
- "once": "^1.4.0"
- }
- },
- "expand-template": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
- "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
- },
- "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=="
- },
- "fs-constants": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
- "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
- },
- "gauge": {
- "version": "2.7.4",
- "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
- "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
- "requires": {
- "aproba": "^1.0.3",
- "console-control-strings": "^1.0.0",
- "has-unicode": "^2.0.0",
- "object-assign": "^4.1.0",
- "signal-exit": "^3.0.0",
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1",
- "wide-align": "^1.1.0"
- }
- },
- "github-from-package": {
- "version": "0.0.0",
- "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
- "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4="
- },
- "has-unicode": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
- "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
- },
- "inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
- },
- "ini": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
- "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
- },
- "is-fullwidth-code-point": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
- "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
- "requires": {
- "number-is-nan": "^1.0.0"
- }
- },
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
- },
- "mimic-response": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
- "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
- },
- "minimist": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
- "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
- },
- "mkdirp": {
- "version": "0.5.1",
- "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
- "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
- "requires": {
- "minimist": "0.0.8"
- },
- "dependencies": {
- "minimist": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
- }
- }
- },
- "nan": {
- "version": "2.13.2",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz",
- "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw=="
- },
- "napi-build-utils": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.1.tgz",
- "integrity": "sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA=="
- },
- "node-abi": {
- "version": "2.11.0",
- "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.11.0.tgz",
- "integrity": "sha512-kuy/aEg75u40v378WRllQ4ZexaXJiCvB68D2scDXclp/I4cRq6togpbOoKhmN07tns9Zldu51NNERo0wehfX9g==",
- "requires": {
- "semver": "^5.4.1"
- }
- },
- "noop-logger": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
- "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI="
- },
- "npmlog": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
- "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
- "requires": {
- "are-we-there-yet": "~1.1.2",
- "console-control-strings": "~1.1.0",
- "gauge": "~2.7.3",
- "set-blocking": "~2.0.0"
- }
- },
- "number-is-nan": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
- "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
- },
- "object-assign": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
- },
- "once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
- "requires": {
- "wrappy": "1"
- }
- },
- "prebuild-install": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.1.tgz",
- "integrity": "sha512-lRLBU0JPXBbpC/ER9PtVYYk1y9Rme1WiMA3WKEQ4v78A5kTsqQtrEyYlbghvXCA6Uhr/769SkhibQznjDBRZpg==",
- "requires": {
- "detect-libc": "^1.0.3",
- "expand-template": "^2.0.3",
- "github-from-package": "0.0.0",
- "minimist": "^1.2.0",
- "mkdirp": "^0.5.1",
- "napi-build-utils": "^1.0.1",
- "node-abi": "^2.7.0",
- "noop-logger": "^0.1.1",
- "npmlog": "^4.0.1",
- "pump": "^3.0.0",
- "rc": "^1.2.7",
- "simple-get": "^3.0.3",
- "tar-fs": "^2.0.0",
- "tunnel-agent": "^0.6.0",
- "which-pm-runs": "^1.0.0"
- }
- },
- "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=="
- },
- "pump": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
- "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
- "requires": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.1"
- }
- },
- "rc": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
- "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
- "requires": {
- "deep-extend": "^0.6.0",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
- }
- },
- "readable-stream": {
- "version": "2.3.6",
- "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
- "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
- "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"
- }
- },
- "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=="
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
- },
- "set-blocking": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
- "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
- },
- "signal-exit": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
- "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
- },
- "simple-concat": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
- "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY="
- },
- "simple-get": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.0.3.tgz",
- "integrity": "sha512-Wvre/Jq5vgoz31Z9stYWPLn0PqRqmBDpFSdypAnHu5AvRVCYPRYGnvryNLiXu8GOBNDH82J2FRHUGMjjHUpXFw==",
- "requires": {
- "decompress-response": "^3.3.0",
- "once": "^1.3.1",
- "simple-concat": "^1.0.0"
- }
- },
- "string-width": {
- "version": "1.0.2",
- "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
- "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
- "requires": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- }
- },
- "string_decoder": {
- "version": "1.1.1",
- "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "requires": {
- "safe-buffer": "~5.1.0"
- }
- },
- "strip-ansi": {
- "version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
- "requires": {
- "ansi-regex": "^2.0.0"
- }
- },
- "strip-json-comments": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
- "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
- },
- "tar-fs": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.0.tgz",
- "integrity": "sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==",
- "requires": {
- "chownr": "^1.1.1",
- "mkdirp": "^0.5.1",
- "pump": "^3.0.0",
- "tar-stream": "^2.0.0"
- }
- },
- "tar-stream": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.0.tgz",
- "integrity": "sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw==",
- "requires": {
- "bl": "^3.0.0",
- "end-of-stream": "^1.4.1",
- "fs-constants": "^1.0.0",
- "inherits": "^2.0.3",
- "readable-stream": "^3.1.1"
- },
- "dependencies": {
- "readable-stream": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz",
- "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==",
- "requires": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
- }
- }
- }
- },
- "tunnel-agent": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
- "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
- "requires": {
- "safe-buffer": "^5.0.1"
- }
- },
- "typescript": {
- "version": "3.6.4",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz",
- "integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==",
- "dev": true
- },
- "usb": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/usb/-/usb-1.6.0.tgz",
- "integrity": "sha512-52DyWlCk9K+iw3LnvY95WXSnpHjxJoI++aGkV8HiMNPc4zmvDQlYvWAzrkbJ2JH3oUcx26XfU5sZcG4RAcVkMg==",
- "requires": {
- "bindings": "^1.4.0",
- "nan": "2.13.2",
- "prebuild-install": "^5.2.4"
- }
- },
- "util-deprecate": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
- },
- "which-pm-runs": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
- "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
- },
- "wide-align": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
- "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
- "requires": {
- "string-width": "^1.0.2 || 2"
- }
- },
- "wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
- }
- }
+ "name": "web-ant-plus",
+ "version": "1.0.1",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "web-ant-plus",
+ "version": "1.0.1",
+ "license": "MIT",
+ "workspaces": [
+ "example"
+ ],
+ "dependencies": {
+ "@js-temporal/polyfill": "^0.4.3",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
+ },
+ "devDependencies": {
+ "@types/react": "^18.0.24",
+ "@types/react-dom": "^18.0.8",
+ "@types/w3c-web-usb": "^1.0.6",
+ "@typescript-eslint/eslint-plugin": "^5.42.1",
+ "@vitejs/plugin-react": "^2.2.0",
+ "eslint": "^8.27.0",
+ "eslint-config-airbnb": "^19.0.4",
+ "eslint-config-prettier": "^8.5.0",
+ "eslint-plugin-import": "^2.26.0",
+ "prettier": "^2.7.1",
+ "typescript": "^4.6.4",
+ "vite": "^3.2.3"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
+ "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.1.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
+ "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/highlight": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz",
+ "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz",
+ "integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==",
+ "dev": true,
+ "dependencies": {
+ "@ampproject/remapping": "^2.1.0",
+ "@babel/code-frame": "^7.18.6",
+ "@babel/generator": "^7.20.5",
+ "@babel/helper-compilation-targets": "^7.20.0",
+ "@babel/helper-module-transforms": "^7.20.2",
+ "@babel/helpers": "^7.20.5",
+ "@babel/parser": "^7.20.5",
+ "@babel/template": "^7.18.10",
+ "@babel/traverse": "^7.20.5",
+ "@babel/types": "^7.20.5",
+ "convert-source-map": "^1.7.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.1",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz",
+ "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.20.5",
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "jsesc": "^2.5.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
+ "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+ "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/@babel/helper-annotate-as-pure": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz",
+ "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.20.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz",
+ "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.20.0",
+ "@babel/helper-validator-option": "^7.18.6",
+ "browserslist": "^4.21.3",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-environment-visitor": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
+ "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-function-name": {
+ "version": "7.19.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz",
+ "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.18.10",
+ "@babel/types": "^7.19.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-hoist-variables": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
+ "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
+ "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.20.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz",
+ "integrity": "sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-module-imports": "^7.18.6",
+ "@babel/helper-simple-access": "^7.20.2",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "@babel/helper-validator-identifier": "^7.19.1",
+ "@babel/template": "^7.18.10",
+ "@babel/traverse": "^7.20.1",
+ "@babel/types": "^7.20.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.20.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz",
+ "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-simple-access": {
+ "version": "7.20.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz",
+ "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.20.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-split-export-declaration": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
+ "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.19.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
+ "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.19.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
+ "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz",
+ "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.20.6",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz",
+ "integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.18.10",
+ "@babel/traverse": "^7.20.5",
+ "@babel/types": "^7.20.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
+ "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.18.6",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz",
+ "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==",
+ "dev": true,
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-jsx": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz",
+ "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx": {
+ "version": "7.19.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz",
+ "integrity": "sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.18.6",
+ "@babel/helper-module-imports": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.19.0",
+ "@babel/plugin-syntax-jsx": "^7.18.6",
+ "@babel/types": "^7.19.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-development": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz",
+ "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/plugin-transform-react-jsx": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.18.6.tgz",
+ "integrity": "sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.19.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz",
+ "integrity": "sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.19.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.20.6",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz",
+ "integrity": "sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "regenerator-runtime": "^0.13.11"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/runtime-corejs3": {
+ "version": "7.20.6",
+ "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.6.tgz",
+ "integrity": "sha512-tqeujPiuEfcH067mx+7otTQWROVMKHXEaOQcAeNV5dDdbPWvPcFA8/W9LXw2NfjNmOetqLl03dfnG2WALPlsRQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "core-js-pure": "^3.25.1",
+ "regenerator-runtime": "^0.13.11"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.18.10",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz",
+ "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/parser": "^7.18.10",
+ "@babel/types": "^7.18.10"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz",
+ "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/generator": "^7.20.5",
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-function-name": "^7.19.0",
+ "@babel/helper-hoist-variables": "^7.18.6",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "@babel/parser": "^7.20.5",
+ "@babel/types": "^7.20.5",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz",
+ "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.19.4",
+ "@babel/helper-validator-identifier": "^7.19.1",
+ "to-fast-properties": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.16.tgz",
+ "integrity": "sha512-nyB6CH++2mSgx3GbnrJsZSxzne5K0HMyNIWafDHqYy7IwxFc4fd/CgHVZXr8Eh+Q3KbIAcAe3vGyqIPhGblvMQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.16.tgz",
+ "integrity": "sha512-SDLfP1uoB0HZ14CdVYgagllgrG7Mdxhkt4jDJOKl/MldKrkQ6vDJMZKl2+5XsEY/Lzz37fjgLQoJBGuAw/x8kQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz",
+ "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.4.0",
+ "globals": "^13.15.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "13.18.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz",
+ "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.11.7",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz",
+ "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==",
+ "dev": true,
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^1.2.1",
+ "debug": "^4.1.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "1.2.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.1.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
+ "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/set-array": "^1.0.0",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "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.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
+ "dev": true
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.17",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
+ "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "3.1.0",
+ "@jridgewell/sourcemap-codec": "1.4.14"
+ }
+ },
+ "node_modules/@js-temporal/polyfill": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@js-temporal/polyfill/-/polyfill-0.4.3.tgz",
+ "integrity": "sha512-6Fmjo/HlkyVCmJzAPnvtEWlcbQUSRhi8qlN9EtJA/wP7FqXsevLLrlojR44kzNzrRkpf7eDJ+z7b4xQD/Ycypw==",
+ "dependencies": {
+ "jsbi": "^4.1.0",
+ "tslib": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "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,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "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": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.11",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
+ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
+ "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/node": {
+ "version": "18.11.10",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.10.tgz",
+ "integrity": "sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "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.0.25",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.25.tgz",
+ "integrity": "sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g==",
+ "dev": true,
+ "dependencies": {
+ "@types/prop-types": "*",
+ "@types/scheduler": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "18.0.9",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.9.tgz",
+ "integrity": "sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg==",
+ "dev": true,
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
+ "node_modules/@types/scheduler": {
+ "version": "0.16.2",
+ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
+ "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==",
+ "dev": true
+ },
+ "node_modules/@types/semver": {
+ "version": "7.3.13",
+ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
+ "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
+ "dev": true
+ },
+ "node_modules/@types/w3c-web-usb": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/w3c-web-usb/-/w3c-web-usb-1.0.6.tgz",
+ "integrity": "sha512-cSjhgrr8g4KbPnnijAr/KJDNKa/bBa+ixYkywFRvrhvi9n1WEl7yYbtRyzE6jqNQiSxxJxoAW3STaOQwJHndaw==",
+ "dev": true
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "5.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.0.tgz",
+ "integrity": "sha512-CXXHNlf0oL+Yg021cxgOdMHNTXD17rHkq7iW6RFHoybdFgQBjU3yIXhhcPpGwr1CjZlo6ET8C6tzX5juQoXeGA==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "5.45.0",
+ "@typescript-eslint/type-utils": "5.45.0",
+ "@typescript-eslint/utils": "5.45.0",
+ "debug": "^4.3.4",
+ "ignore": "^5.2.0",
+ "natural-compare-lite": "^1.4.0",
+ "regexpp": "^3.2.0",
+ "semver": "^7.3.7",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.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/@typescript-eslint/eslint-plugin/node_modules/semver": {
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+ "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "5.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.45.0.tgz",
+ "integrity": "sha512-brvs/WSM4fKUmF5Ot/gEve6qYiCMjm6w4HkHPfS6ZNmxTS0m0iNN4yOChImaCkqc1hRwFGqUyanMXuGal6oyyQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "5.45.0",
+ "@typescript-eslint/types": "5.45.0",
+ "@typescript-eslint/typescript-estree": "5.45.0",
+ "debug": "^4.3.4"
+ },
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "5.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.45.0.tgz",
+ "integrity": "sha512-noDMjr87Arp/PuVrtvN3dXiJstQR1+XlQ4R1EvzG+NMgXi8CuMCXpb8JqNtFHKceVSQ985BZhfRdowJzbv4yKw==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "5.45.0",
+ "@typescript-eslint/visitor-keys": "5.45.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "5.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.45.0.tgz",
+ "integrity": "sha512-DY7BXVFSIGRGFZ574hTEyLPRiQIvI/9oGcN8t1A7f6zIs6ftbrU0nhyV26ZW//6f85avkwrLag424n+fkuoJ1Q==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "5.45.0",
+ "@typescript-eslint/utils": "5.45.0",
+ "debug": "^4.3.4",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "5.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.45.0.tgz",
+ "integrity": "sha512-QQij+u/vgskA66azc9dCmx+rev79PzX8uDHpsqSjEFtfF2gBUTRCpvYMh2gw2ghkJabNkPlSUCimsyBEQZd1DA==",
+ "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.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.0.tgz",
+ "integrity": "sha512-maRhLGSzqUpFcZgXxg1qc/+H0bT36lHK4APhp0AEUVrpSwXiRAomm/JGjSG+kNUio5kAa3uekCYu/47cnGn5EQ==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "5.45.0",
+ "@typescript-eslint/visitor-keys": "5.45.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "semver": "^7.3.7",
+ "tsutils": "^3.21.0"
+ },
+ "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_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+ "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "5.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.45.0.tgz",
+ "integrity": "sha512-OUg2JvsVI1oIee/SwiejTot2OxwU8a7UfTFMOdlhD2y+Hl6memUSL4s98bpUTo8EpVEr0lmwlU7JSu/p2QpSvA==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.9",
+ "@types/semver": "^7.3.12",
+ "@typescript-eslint/scope-manager": "5.45.0",
+ "@typescript-eslint/types": "5.45.0",
+ "@typescript-eslint/typescript-estree": "5.45.0",
+ "eslint-scope": "^5.1.1",
+ "eslint-utils": "^3.0.0",
+ "semver": "^7.3.7"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/utils/node_modules/semver": {
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+ "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "5.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.0.tgz",
+ "integrity": "sha512-jc6Eccbn2RtQPr1s7th6jJWQHBHI6GBVQkCHoJFQ5UreaKm59Vxw+ynQUPPY2u2Amquc+7tmEoC2G52ApsGNNg==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "5.45.0",
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.2.0.tgz",
+ "integrity": "sha512-FFpefhvExd1toVRlokZgxgy2JtnBOdp4ZDsq7ldCWaqGSGn9UhWMAVm/1lxPL14JfNS5yGz+s9yFrQY6shoStA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.19.6",
+ "@babel/plugin-transform-react-jsx": "^7.19.0",
+ "@babel/plugin-transform-react-jsx-development": "^7.18.6",
+ "@babel/plugin-transform-react-jsx-self": "^7.18.6",
+ "@babel/plugin-transform-react-jsx-source": "^7.19.6",
+ "magic-string": "^0.26.7",
+ "react-refresh": "^0.14.0"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^3.0.0"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.8.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
+ "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "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,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "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"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "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==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "node_modules/aria-query": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz",
+ "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.10.2",
+ "@babel/runtime-corejs3": "^7.10.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "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==",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.4",
+ "es-shim-unscopables": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.4",
+ "es-shim-unscopables": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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==",
+ "dev": true,
+ "peer": 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"
+ }
+ },
+ "node_modules/ast-types-flow": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
+ "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/axe-core": {
+ "version": "4.5.2",
+ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.5.2.tgz",
+ "integrity": "sha512-u2MVsXfew5HBvjsczCv+xlwdNnB1oQR9HlAcsejZttNjKKSkeDNVwB1vMThIUIFI9GoT57Vtk8iQLwqOfAkboA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/axobject-query": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz",
+ "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==",
+ "dev": true,
+ "peer": true
+ },
+ "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/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/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/browserslist": {
+ "version": "4.21.4",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz",
+ "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ }
+ ],
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001400",
+ "electron-to-chromium": "^1.4.251",
+ "node-releases": "^2.0.6",
+ "update-browserslist-db": "^1.0.9"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "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==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001434",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz",
+ "integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ }
+ ]
+ },
+ "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/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/confusing-browser-globals": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz",
+ "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==",
+ "dev": true
+ },
+ "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/core-js-pure": {
+ "version": "3.26.1",
+ "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.1.tgz",
+ "integrity": "sha512-VVXcDpp/xJ21KdULRq/lXdLzQAtX7+37LzpyfFM973il0tWSsDEoyzG38G14AjTpK9VTfiNM9jnFauq/CpaWGQ==",
+ "dev": true,
+ "hasInstallScript": true,
+ "peer": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
+ "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": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz",
+ "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==",
+ "dev": true
+ },
+ "node_modules/damerau-levenshtein": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
+ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
+ "dev": true,
+ "peer": 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/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/define-properties": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
+ "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
+ "dev": true,
+ "dependencies": {
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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,
+ "dependencies": {
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.4.284",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz",
+ "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==",
+ "dev": true
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/es-abstract": {
+ "version": "1.20.4",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz",
+ "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "function.prototype.name": "^1.1.5",
+ "get-intrinsic": "^1.1.3",
+ "get-symbol-description": "^1.0.0",
+ "has": "^1.0.3",
+ "has-property-descriptors": "^1.0.0",
+ "has-symbols": "^1.0.3",
+ "internal-slot": "^1.0.3",
+ "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-weakref": "^1.0.2",
+ "object-inspect": "^1.12.2",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.4",
+ "regexp.prototype.flags": "^1.4.3",
+ "safe-regex-test": "^1.0.0",
+ "string.prototype.trimend": "^1.0.5",
+ "string.prototype.trimstart": "^1.0.5",
+ "unbox-primitive": "^1.0.2"
+ },
+ "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==",
+ "dev": true,
+ "dependencies": {
+ "has": "^1.0.3"
+ }
+ },
+ "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": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.16.tgz",
+ "integrity": "sha512-o6iS9zxdHrrojjlj6pNGC2NAg86ECZqIETswTM5KmJitq+R1YmahhWtMumeQp9lHqJaROGnsBi2RLawGnfo5ZQ==",
+ "dev": true,
+ "hasInstallScript": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/android-arm": "0.15.16",
+ "@esbuild/linux-loong64": "0.15.16",
+ "esbuild-android-64": "0.15.16",
+ "esbuild-android-arm64": "0.15.16",
+ "esbuild-darwin-64": "0.15.16",
+ "esbuild-darwin-arm64": "0.15.16",
+ "esbuild-freebsd-64": "0.15.16",
+ "esbuild-freebsd-arm64": "0.15.16",
+ "esbuild-linux-32": "0.15.16",
+ "esbuild-linux-64": "0.15.16",
+ "esbuild-linux-arm": "0.15.16",
+ "esbuild-linux-arm64": "0.15.16",
+ "esbuild-linux-mips64le": "0.15.16",
+ "esbuild-linux-ppc64le": "0.15.16",
+ "esbuild-linux-riscv64": "0.15.16",
+ "esbuild-linux-s390x": "0.15.16",
+ "esbuild-netbsd-64": "0.15.16",
+ "esbuild-openbsd-64": "0.15.16",
+ "esbuild-sunos-64": "0.15.16",
+ "esbuild-windows-32": "0.15.16",
+ "esbuild-windows-64": "0.15.16",
+ "esbuild-windows-arm64": "0.15.16"
+ }
+ },
+ "node_modules/esbuild-android-64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.16.tgz",
+ "integrity": "sha512-Vwkv/sT0zMSgPSVO3Jlt1pUbnZuOgtOQJkJkyyJFAlLe7BiT8e9ESzo0zQSx4c3wW4T6kGChmKDPMbWTgtliQA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-android-arm64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.16.tgz",
+ "integrity": "sha512-lqfKuofMExL5niNV3gnhMUYacSXfsvzTa/58sDlBET/hCOG99Zmeh+lz6kvdgvGOsImeo6J9SW21rFCogNPLxg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-darwin-64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.16.tgz",
+ "integrity": "sha512-wo2VWk/n/9V2TmqUZ/KpzRjCEcr00n7yahEdmtzlrfQ3lfMCf3Wa+0sqHAbjk3C6CKkR3WKK/whkMq5Gj4Da9g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-darwin-arm64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.16.tgz",
+ "integrity": "sha512-fMXaUr5ou0M4WnewBKsspMtX++C1yIa3nJ5R2LSbLCfJT3uFdcRoU/NZjoM4kOMKyOD9Sa/2vlgN8G07K3SJnw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-freebsd-64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.16.tgz",
+ "integrity": "sha512-UzIc0xlRx5x9kRuMr+E3+hlSOxa/aRqfuMfiYBXu2jJ8Mzej4lGL7+o6F5hzhLqWfWm1GWHNakIdlqg1ayaTNQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-freebsd-arm64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.16.tgz",
+ "integrity": "sha512-8xyiYuGc0DLZphFQIiYaLHlfoP+hAN9RHbE+Ibh8EUcDNHAqbQgUrQg7pE7Bo00rXmQ5Ap6KFgcR0b4ALZls1g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-32": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.16.tgz",
+ "integrity": "sha512-iGijUTV+0kIMyUVoynK0v+32Oi8yyp0xwMzX69GX+5+AniNy/C/AL1MjFTsozRp/3xQPl7jVux/PLe2ds10/2w==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.16.tgz",
+ "integrity": "sha512-tuSOjXdLw7VzaUj89fIdAaQT7zFGbKBcz4YxbWrOiXkwscYgE7HtTxUavreBbnRkGxKwr9iT/gmeJWNm4djy/g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-arm": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.16.tgz",
+ "integrity": "sha512-XKcrxCEXDTOuoRj5l12tJnkvuxXBMKwEC5j0JISw3ziLf0j4zIwXbKbTmUrKFWbo6ZgvNpa7Y5dnbsjVvH39bQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-arm64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.16.tgz",
+ "integrity": "sha512-mPYksnfHnemNrvjrDhZyixL/AfbJN0Xn9S34ZOHYdh6/jJcNd8iTsv3JwJoEvTJqjMggjMhGUPJAdjnFBHoH8A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-mips64le": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.16.tgz",
+ "integrity": "sha512-kSJO2PXaxfm0pWY39+YX+QtpFqyyrcp0ZeI8QPTrcFVQoWEPiPVtOfTZeS3ZKedfH+Ga38c4DSzmKMQJocQv6A==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-ppc64le": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.16.tgz",
+ "integrity": "sha512-NimPikwkBY0yGABw6SlhKrtT35sU4O23xkhlrTT/O6lSxv3Pm5iSc6OYaqVAHWkLdVf31bF4UDVFO+D990WpAA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-riscv64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.16.tgz",
+ "integrity": "sha512-ty2YUHZlwFOwp7pR+J87M4CVrXJIf5ZZtU/umpxgVJBXvWjhziSLEQxvl30SYfUPq0nzeWKBGw5i/DieiHeKfw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-s390x": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.16.tgz",
+ "integrity": "sha512-VkZaGssvPDQtx4fvVdZ9czezmyWyzpQhEbSNsHZZN0BHvxRLOYAQ7sjay8nMQwYswP6O2KlZluRMNPYefFRs+w==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-netbsd-64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.16.tgz",
+ "integrity": "sha512-ElQ9rhdY51et6MJTWrCPbqOd/YuPowD7Cxx3ee8wlmXQQVW7UvQI6nSprJ9uVFQISqSF5e5EWpwWqXZsECLvXg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-openbsd-64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.16.tgz",
+ "integrity": "sha512-KgxMHyxMCT+NdLQE1zVJEsLSt2QQBAvJfmUGDmgEq8Fvjrf6vSKB00dVHUEDKcJwMID6CdgCpvYNt999tIYhqA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-sunos-64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.16.tgz",
+ "integrity": "sha512-exSAx8Phj7QylXHlMfIyEfNrmqnLxFqLxdQF6MBHPdHAjT7fsKaX6XIJn+aQEFiOcE4X8e7VvdMCJ+WDZxjSRQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-windows-32": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.16.tgz",
+ "integrity": "sha512-zQgWpY5pUCSTOwqKQ6/vOCJfRssTvxFuEkpB4f2VUGPBpdddZfdj8hbZuFRdZRPIVHvN7juGcpgCA/XCF37mAQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-windows-64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.16.tgz",
+ "integrity": "sha512-HjW1hHRLSncnM3MBCP7iquatHVJq9l0S2xxsHHj4yzf4nm9TU4Z7k4NkeMlD/dHQ4jPlQQhwcMvwbJiOefSuZw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-windows-arm64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.16.tgz",
+ "integrity": "sha512-oCcUKrJaMn04Vxy9Ekd8x23O8LoU01+4NOkQ2iBToKgnGj5eo1vU9i27NQZ9qC8NFZgnQQZg5oZWAejmbsppNA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "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/eslint": {
+ "version": "8.28.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.28.0.tgz",
+ "integrity": "sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==",
+ "dev": true,
+ "dependencies": {
+ "@eslint/eslintrc": "^1.3.3",
+ "@humanwhocodes/config-array": "^0.11.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "ajv": "^6.10.0",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.1.1",
+ "eslint-utils": "^3.0.0",
+ "eslint-visitor-keys": "^3.3.0",
+ "espree": "^9.4.0",
+ "esquery": "^1.4.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.15.0",
+ "grapheme-splitter": "^1.0.4",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-sdsl": "^4.1.4",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.1",
+ "regexpp": "^3.2.0",
+ "strip-ansi": "^6.0.1",
+ "strip-json-comments": "^3.1.0",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-config-airbnb": {
+ "version": "19.0.4",
+ "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz",
+ "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==",
+ "dev": true,
+ "dependencies": {
+ "eslint-config-airbnb-base": "^15.0.0",
+ "object.assign": "^4.1.2",
+ "object.entries": "^1.1.5"
+ },
+ "engines": {
+ "node": "^10.12.0 || ^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^7.32.0 || ^8.2.0",
+ "eslint-plugin-import": "^2.25.3",
+ "eslint-plugin-jsx-a11y": "^6.5.1",
+ "eslint-plugin-react": "^7.28.0",
+ "eslint-plugin-react-hooks": "^4.3.0"
+ }
+ },
+ "node_modules/eslint-config-airbnb-base": {
+ "version": "15.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz",
+ "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==",
+ "dev": true,
+ "dependencies": {
+ "confusing-browser-globals": "^1.0.10",
+ "object.assign": "^4.1.2",
+ "object.entries": "^1.1.5",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^7.32.0 || ^8.2.0",
+ "eslint-plugin-import": "^2.25.2"
+ }
+ },
+ "node_modules/eslint-config-prettier": {
+ "version": "8.5.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz",
+ "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==",
+ "dev": true,
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint-import-resolver-node": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz",
+ "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^3.2.7",
+ "resolve": "^1.20.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/eslint-module-utils": {
+ "version": "2.7.4",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz",
+ "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^3.2.7"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
+ }
+ },
+ "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": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import": {
+ "version": "2.26.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz",
+ "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==",
+ "dev": true,
+ "dependencies": {
+ "array-includes": "^3.1.4",
+ "array.prototype.flat": "^1.2.5",
+ "debug": "^2.6.9",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.6",
+ "eslint-module-utils": "^2.7.3",
+ "has": "^1.0.3",
+ "is-core-module": "^2.8.1",
+ "is-glob": "^4.0.3",
+ "minimatch": "^3.1.2",
+ "object.values": "^1.1.5",
+ "resolve": "^1.22.0",
+ "tsconfig-paths": "^3.14.1"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8"
+ }
+ },
+ "node_modules/eslint-plugin-import/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/eslint-plugin-import/node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "node_modules/eslint-plugin-jsx-a11y": {
+ "version": "6.6.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz",
+ "integrity": "sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.18.9",
+ "aria-query": "^4.2.2",
+ "array-includes": "^3.1.5",
+ "ast-types-flow": "^0.0.7",
+ "axe-core": "^4.4.3",
+ "axobject-query": "^2.2.0",
+ "damerau-levenshtein": "^1.0.8",
+ "emoji-regex": "^9.2.2",
+ "has": "^1.0.3",
+ "jsx-ast-utils": "^3.3.2",
+ "language-tags": "^1.0.5",
+ "minimatch": "^3.1.2",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
+ }
+ },
+ "node_modules/eslint-plugin-react": {
+ "version": "7.31.11",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.11.tgz",
+ "integrity": "sha512-TTvq5JsT5v56wPa9OYHzsrOlHzKZKjV+aLgS+55NJP/cuzdiQPC7PfYoUjMoxlffKtvijpk7vA/jmuqRb9nohw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "array-includes": "^3.1.6",
+ "array.prototype.flatmap": "^1.3.1",
+ "array.prototype.tosorted": "^1.1.1",
+ "doctrine": "^2.1.0",
+ "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.3",
+ "semver": "^6.3.0",
+ "string.prototype.matchall": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
+ }
+ },
+ "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,
+ "peer": true,
+ "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==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "is-core-module": "^2.9.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/eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/eslint-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,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "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==",
+ "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_modules/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "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==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/eslint/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/eslint/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/eslint/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/eslint/node_modules/eslint-scope": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
+ "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/globals": {
+ "version": "13.18.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz",
+ "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/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/eslint/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/espree": {
+ "version": "9.4.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz",
+ "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.8.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
+ "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "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": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "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/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "node_modules/fast-glob": {
+ "version": "3.2.12",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
+ "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
+ "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": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/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": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "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/fastq": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
+ "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
+ "dev": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "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": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "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/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": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+ "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.1.0",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
+ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
+ "dev": true
+ },
+ "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.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.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.5",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
+ "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.19.0",
+ "functions-have-names": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "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.1.3",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
+ "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "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/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": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/grapheme-splitter": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
+ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+ "dev": true
+ },
+ "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"
+ }
+ },
+ "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,
+ "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.0",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
+ "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.1.1"
+ },
+ "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/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": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz",
+ "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "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": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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.8.19"
+ }
+ },
+ "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": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "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/internal-slot": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz",
+ "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.1.0",
+ "has": "^1.0.3",
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "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": {
+ "has-bigints": "^1.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
+ "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
+ "dev": true,
+ "dependencies": {
+ "has": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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/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,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-regex": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+ "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+ "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_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,
+ "dependencies": {
+ "call-bind": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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,
+ "dependencies": {
+ "has-symbols": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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,
+ "dependencies": {
+ "call-bind": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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/js-sdsl": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz",
+ "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==",
+ "dev": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/js-sdsl"
+ }
+ },
+ "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=="
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsbi": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-4.3.0.tgz",
+ "integrity": "sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g=="
+ },
+ "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/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.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
+ "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
+ "dev": true,
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsx-ast-utils": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz",
+ "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "array-includes": "^3.1.5",
+ "object.assign": "^4.1.3"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/language-subtag-registry": {
+ "version": "0.3.22",
+ "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz",
+ "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/language-tags": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.6.tgz",
+ "integrity": "sha512-HNkaCgM8wZgE/BZACeotAAgpL9FUjEnhgF0FVQMIgH//zqTPreLYMb3rWYkYAqPoF75Jwuycp1da7uz66cfFQg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "language-subtag-registry": "^0.3.20"
+ }
+ },
+ "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": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "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"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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/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==",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "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/magic-string": {
+ "version": "0.26.7",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz",
+ "integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==",
+ "dev": true,
+ "dependencies": {
+ "sourcemap-codec": "^1.4.8"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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/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": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "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": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
+ "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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.4",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
+ "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
+ "dev": true,
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "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/node-releases": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz",
+ "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==",
+ "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,
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
+ "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.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.entries": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz",
+ "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.fromentries": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz",
+ "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==",
+ "dev": true,
+ "peer": 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/object.hasown": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz",
+ "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz",
+ "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==",
+ "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/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "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/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,
+ "dependencies": {
+ "yocto-queue": "^0.1.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/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/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": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "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-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/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/postcss": {
+ "version": "8.4.19",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz",
+ "integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.4",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "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.8.0",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.0.tgz",
+ "integrity": "sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==",
+ "dev": true,
+ "bin": {
+ "prettier": "bin-prettier.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "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,
+ "peer": true,
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.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/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/react": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
+ "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
+ "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==",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.0"
+ },
+ "peerDependencies": {
+ "react": "^18.2.0"
+ }
+ },
+ "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,
+ "peer": true
+ },
+ "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/regenerator-runtime": {
+ "version": "0.13.11",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+ "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
+ "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "functions-have-names": "^1.2.2"
+ },
+ "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/resolve": {
+ "version": "1.22.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+ "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
+ "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"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "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": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "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/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/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/scheduler": {
+ "version": "0.23.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
+ "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.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/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/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/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/sourcemap-codec": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
+ "dev": true
+ },
+ "node_modules/string.prototype.matchall": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz",
+ "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.4",
+ "get-intrinsic": "^1.1.3",
+ "has-symbols": "^1.0.3",
+ "internal-slot": "^1.0.3",
+ "regexp.prototype.flags": "^1.4.3",
+ "side-channel": "^1.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-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-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/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/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-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": "3.14.1",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
+ "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.1",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "node_modules/tsconfig-paths/node_modules/json5": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "dev": true,
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
+ "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
+ },
+ "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/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-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,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "4.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz",
+ "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "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/update-browserslist-db": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
+ "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ }
+ ],
+ "dependencies": {
+ "escalade": "^3.1.1",
+ "picocolors": "^1.0.0"
+ },
+ "bin": {
+ "browserslist-lint": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "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": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-3.2.4.tgz",
+ "integrity": "sha512-Z2X6SRAffOUYTa+sLy3NQ7nlHFU100xwanq1WDwqaiFiCe+25zdxP1TfCS5ojPV2oDDcXudHIoPnI1Z/66B7Yw==",
+ "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/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/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/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "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/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,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ },
+ "dependencies": {
+ "@ampproject/remapping": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
+ "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/gen-mapping": "^0.1.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ },
+ "@babel/code-frame": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
+ "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.18.6"
+ }
+ },
+ "@babel/compat-data": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz",
+ "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==",
+ "dev": true
+ },
+ "@babel/core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz",
+ "integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==",
+ "dev": true,
+ "requires": {
+ "@ampproject/remapping": "^2.1.0",
+ "@babel/code-frame": "^7.18.6",
+ "@babel/generator": "^7.20.5",
+ "@babel/helper-compilation-targets": "^7.20.0",
+ "@babel/helper-module-transforms": "^7.20.2",
+ "@babel/helpers": "^7.20.5",
+ "@babel/parser": "^7.20.5",
+ "@babel/template": "^7.18.10",
+ "@babel/traverse": "^7.20.5",
+ "@babel/types": "^7.20.5",
+ "convert-source-map": "^1.7.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.1",
+ "semver": "^6.3.0"
+ }
+ },
+ "@babel/generator": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz",
+ "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.20.5",
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "jsesc": "^2.5.1"
+ },
+ "dependencies": {
+ "@jridgewell/gen-mapping": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
+ "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ }
+ }
+ },
+ "@babel/helper-annotate-as-pure": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz",
+ "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.18.6"
+ }
+ },
+ "@babel/helper-compilation-targets": {
+ "version": "7.20.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz",
+ "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.20.0",
+ "@babel/helper-validator-option": "^7.18.6",
+ "browserslist": "^4.21.3",
+ "semver": "^6.3.0"
+ }
+ },
+ "@babel/helper-environment-visitor": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
+ "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
+ "dev": true
+ },
+ "@babel/helper-function-name": {
+ "version": "7.19.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz",
+ "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.18.10",
+ "@babel/types": "^7.19.0"
+ }
+ },
+ "@babel/helper-hoist-variables": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
+ "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.18.6"
+ }
+ },
+ "@babel/helper-module-imports": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
+ "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.18.6"
+ }
+ },
+ "@babel/helper-module-transforms": {
+ "version": "7.20.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz",
+ "integrity": "sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-module-imports": "^7.18.6",
+ "@babel/helper-simple-access": "^7.20.2",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "@babel/helper-validator-identifier": "^7.19.1",
+ "@babel/template": "^7.18.10",
+ "@babel/traverse": "^7.20.1",
+ "@babel/types": "^7.20.2"
+ }
+ },
+ "@babel/helper-plugin-utils": {
+ "version": "7.20.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz",
+ "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==",
+ "dev": true
+ },
+ "@babel/helper-simple-access": {
+ "version": "7.20.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz",
+ "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.20.2"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
+ "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.18.6"
+ }
+ },
+ "@babel/helper-string-parser": {
+ "version": "7.19.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
+ "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
+ "dev": true
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.19.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
+ "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
+ "dev": true
+ },
+ "@babel/helper-validator-option": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz",
+ "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==",
+ "dev": true
+ },
+ "@babel/helpers": {
+ "version": "7.20.6",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz",
+ "integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.18.10",
+ "@babel/traverse": "^7.20.5",
+ "@babel/types": "^7.20.5"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
+ "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.18.6",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz",
+ "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==",
+ "dev": true
+ },
+ "@babel/plugin-syntax-jsx": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz",
+ "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ }
+ },
+ "@babel/plugin-transform-react-jsx": {
+ "version": "7.19.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz",
+ "integrity": "sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.18.6",
+ "@babel/helper-module-imports": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.19.0",
+ "@babel/plugin-syntax-jsx": "^7.18.6",
+ "@babel/types": "^7.19.0"
+ }
+ },
+ "@babel/plugin-transform-react-jsx-development": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz",
+ "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==",
+ "dev": true,
+ "requires": {
+ "@babel/plugin-transform-react-jsx": "^7.18.6"
+ }
+ },
+ "@babel/plugin-transform-react-jsx-self": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.18.6.tgz",
+ "integrity": "sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.18.6"
+ }
+ },
+ "@babel/plugin-transform-react-jsx-source": {
+ "version": "7.19.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz",
+ "integrity": "sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.19.0"
+ }
+ },
+ "@babel/runtime": {
+ "version": "7.20.6",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz",
+ "integrity": "sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "regenerator-runtime": "^0.13.11"
+ }
+ },
+ "@babel/runtime-corejs3": {
+ "version": "7.20.6",
+ "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.6.tgz",
+ "integrity": "sha512-tqeujPiuEfcH067mx+7otTQWROVMKHXEaOQcAeNV5dDdbPWvPcFA8/W9LXw2NfjNmOetqLl03dfnG2WALPlsRQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "core-js-pure": "^3.25.1",
+ "regenerator-runtime": "^0.13.11"
+ }
+ },
+ "@babel/template": {
+ "version": "7.18.10",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz",
+ "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/parser": "^7.18.10",
+ "@babel/types": "^7.18.10"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz",
+ "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/generator": "^7.20.5",
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-function-name": "^7.19.0",
+ "@babel/helper-hoist-variables": "^7.18.6",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "@babel/parser": "^7.20.5",
+ "@babel/types": "^7.20.5",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ }
+ },
+ "@babel/types": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz",
+ "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-string-parser": "^7.19.4",
+ "@babel/helper-validator-identifier": "^7.19.1",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "@esbuild/android-arm": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.16.tgz",
+ "integrity": "sha512-nyB6CH++2mSgx3GbnrJsZSxzne5K0HMyNIWafDHqYy7IwxFc4fd/CgHVZXr8Eh+Q3KbIAcAe3vGyqIPhGblvMQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-loong64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.16.tgz",
+ "integrity": "sha512-SDLfP1uoB0HZ14CdVYgagllgrG7Mdxhkt4jDJOKl/MldKrkQ6vDJMZKl2+5XsEY/Lzz37fjgLQoJBGuAw/x8kQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@eslint/eslintrc": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz",
+ "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.4.0",
+ "globals": "^13.15.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "dependencies": {
+ "globals": {
+ "version": "13.18.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz",
+ "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.20.2"
+ }
+ }
+ }
+ },
+ "@humanwhocodes/config-array": {
+ "version": "0.11.7",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz",
+ "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==",
+ "dev": true,
+ "requires": {
+ "@humanwhocodes/object-schema": "^1.2.1",
+ "debug": "^4.1.1",
+ "minimatch": "^3.0.5"
+ }
+ },
+ "@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true
+ },
+ "@humanwhocodes/object-schema": {
+ "version": "1.2.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.1.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
+ "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/set-array": "^1.0.0",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
+ "@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "dev": true
+ },
+ "@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
+ },
+ "@jridgewell/sourcemap-codec": {
+ "version": "1.4.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
+ "dev": true
+ },
+ "@jridgewell/trace-mapping": {
+ "version": "0.3.17",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
+ "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/resolve-uri": "3.1.0",
+ "@jridgewell/sourcemap-codec": "1.4.14"
+ }
+ },
+ "@js-temporal/polyfill": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@js-temporal/polyfill/-/polyfill-0.4.3.tgz",
+ "integrity": "sha512-6Fmjo/HlkyVCmJzAPnvtEWlcbQUSRhi8qlN9EtJA/wP7FqXsevLLrlojR44kzNzrRkpf7eDJ+z7b4xQD/Ycypw==",
+ "requires": {
+ "jsbi": "^4.1.0",
+ "tslib": "^2.3.1"
+ }
+ },
+ "@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,
+ "requires": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ }
+ },
+ "@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
+ },
+ "@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,
+ "requires": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ }
+ },
+ "@types/json-schema": {
+ "version": "7.0.11",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
+ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
+ "dev": true
+ },
+ "@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "18.11.10",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.10.tgz",
+ "integrity": "sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@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
+ },
+ "@types/react": {
+ "version": "18.0.25",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.25.tgz",
+ "integrity": "sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g==",
+ "dev": true,
+ "requires": {
+ "@types/prop-types": "*",
+ "@types/scheduler": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "@types/react-dom": {
+ "version": "18.0.9",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.9.tgz",
+ "integrity": "sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg==",
+ "dev": true,
+ "requires": {
+ "@types/react": "*"
+ }
+ },
+ "@types/scheduler": {
+ "version": "0.16.2",
+ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
+ "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==",
+ "dev": true
+ },
+ "@types/semver": {
+ "version": "7.3.13",
+ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
+ "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
+ "dev": true
+ },
+ "@types/w3c-web-usb": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/w3c-web-usb/-/w3c-web-usb-1.0.6.tgz",
+ "integrity": "sha512-cSjhgrr8g4KbPnnijAr/KJDNKa/bBa+ixYkywFRvrhvi9n1WEl7yYbtRyzE6jqNQiSxxJxoAW3STaOQwJHndaw==",
+ "dev": true
+ },
+ "@typescript-eslint/eslint-plugin": {
+ "version": "5.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.0.tgz",
+ "integrity": "sha512-CXXHNlf0oL+Yg021cxgOdMHNTXD17rHkq7iW6RFHoybdFgQBjU3yIXhhcPpGwr1CjZlo6ET8C6tzX5juQoXeGA==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/scope-manager": "5.45.0",
+ "@typescript-eslint/type-utils": "5.45.0",
+ "@typescript-eslint/utils": "5.45.0",
+ "debug": "^4.3.4",
+ "ignore": "^5.2.0",
+ "natural-compare-lite": "^1.4.0",
+ "regexpp": "^3.2.0",
+ "semver": "^7.3.7",
+ "tsutils": "^3.21.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+ "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ }
+ }
+ },
+ "@typescript-eslint/parser": {
+ "version": "5.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.45.0.tgz",
+ "integrity": "sha512-brvs/WSM4fKUmF5Ot/gEve6qYiCMjm6w4HkHPfS6ZNmxTS0m0iNN4yOChImaCkqc1hRwFGqUyanMXuGal6oyyQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@typescript-eslint/scope-manager": "5.45.0",
+ "@typescript-eslint/types": "5.45.0",
+ "@typescript-eslint/typescript-estree": "5.45.0",
+ "debug": "^4.3.4"
+ }
+ },
+ "@typescript-eslint/scope-manager": {
+ "version": "5.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.45.0.tgz",
+ "integrity": "sha512-noDMjr87Arp/PuVrtvN3dXiJstQR1+XlQ4R1EvzG+NMgXi8CuMCXpb8JqNtFHKceVSQ985BZhfRdowJzbv4yKw==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "5.45.0",
+ "@typescript-eslint/visitor-keys": "5.45.0"
+ }
+ },
+ "@typescript-eslint/type-utils": {
+ "version": "5.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.45.0.tgz",
+ "integrity": "sha512-DY7BXVFSIGRGFZ574hTEyLPRiQIvI/9oGcN8t1A7f6zIs6ftbrU0nhyV26ZW//6f85avkwrLag424n+fkuoJ1Q==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/typescript-estree": "5.45.0",
+ "@typescript-eslint/utils": "5.45.0",
+ "debug": "^4.3.4",
+ "tsutils": "^3.21.0"
+ }
+ },
+ "@typescript-eslint/types": {
+ "version": "5.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.45.0.tgz",
+ "integrity": "sha512-QQij+u/vgskA66azc9dCmx+rev79PzX8uDHpsqSjEFtfF2gBUTRCpvYMh2gw2ghkJabNkPlSUCimsyBEQZd1DA==",
+ "dev": true
+ },
+ "@typescript-eslint/typescript-estree": {
+ "version": "5.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.0.tgz",
+ "integrity": "sha512-maRhLGSzqUpFcZgXxg1qc/+H0bT36lHK4APhp0AEUVrpSwXiRAomm/JGjSG+kNUio5kAa3uekCYu/47cnGn5EQ==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "5.45.0",
+ "@typescript-eslint/visitor-keys": "5.45.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "semver": "^7.3.7",
+ "tsutils": "^3.21.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+ "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ }
+ }
+ },
+ "@typescript-eslint/utils": {
+ "version": "5.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.45.0.tgz",
+ "integrity": "sha512-OUg2JvsVI1oIee/SwiejTot2OxwU8a7UfTFMOdlhD2y+Hl6memUSL4s98bpUTo8EpVEr0lmwlU7JSu/p2QpSvA==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.9",
+ "@types/semver": "^7.3.12",
+ "@typescript-eslint/scope-manager": "5.45.0",
+ "@typescript-eslint/types": "5.45.0",
+ "@typescript-eslint/typescript-estree": "5.45.0",
+ "eslint-scope": "^5.1.1",
+ "eslint-utils": "^3.0.0",
+ "semver": "^7.3.7"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+ "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ }
+ }
+ },
+ "@typescript-eslint/visitor-keys": {
+ "version": "5.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.0.tgz",
+ "integrity": "sha512-jc6Eccbn2RtQPr1s7th6jJWQHBHI6GBVQkCHoJFQ5UreaKm59Vxw+ynQUPPY2u2Amquc+7tmEoC2G52ApsGNNg==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "5.45.0",
+ "eslint-visitor-keys": "^3.3.0"
+ }
+ },
+ "@vitejs/plugin-react": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.2.0.tgz",
+ "integrity": "sha512-FFpefhvExd1toVRlokZgxgy2JtnBOdp4ZDsq7ldCWaqGSGn9UhWMAVm/1lxPL14JfNS5yGz+s9yFrQY6shoStA==",
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.19.6",
+ "@babel/plugin-transform-react-jsx": "^7.19.0",
+ "@babel/plugin-transform-react-jsx-development": "^7.18.6",
+ "@babel/plugin-transform-react-jsx-self": "^7.18.6",
+ "@babel/plugin-transform-react-jsx-source": "^7.19.6",
+ "magic-string": "^0.26.7",
+ "react-refresh": "^0.14.0"
+ }
+ },
+ "acorn": {
+ "version": "8.8.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
+ "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==",
+ "dev": true
+ },
+ "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,
+ "requires": {}
+ },
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "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
+ },
+ "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"
+ }
+ },
+ "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
+ },
+ "aria-query": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz",
+ "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@babel/runtime": "^7.10.2",
+ "@babel/runtime-corejs3": "^7.10.2"
+ }
+ },
+ "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==",
+ "dev": true,
+ "requires": {
+ "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"
+ }
+ },
+ "array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true
+ },
+ "array.prototype.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==",
+ "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.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,
+ "peer": 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,
+ "peer": 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"
+ }
+ },
+ "ast-types-flow": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
+ "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==",
+ "dev": true,
+ "peer": true
+ },
+ "axe-core": {
+ "version": "4.5.2",
+ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.5.2.tgz",
+ "integrity": "sha512-u2MVsXfew5HBvjsczCv+xlwdNnB1oQR9HlAcsejZttNjKKSkeDNVwB1vMThIUIFI9GoT57Vtk8iQLwqOfAkboA==",
+ "dev": true,
+ "peer": true
+ },
+ "axobject-query": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz",
+ "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==",
+ "dev": true,
+ "peer": true
+ },
+ "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
+ },
+ "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,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "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"
+ }
+ },
+ "browserslist": {
+ "version": "4.21.4",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz",
+ "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==",
+ "dev": true,
+ "requires": {
+ "caniuse-lite": "^1.0.30001400",
+ "electron-to-chromium": "^1.4.251",
+ "node-releases": "^2.0.6",
+ "update-browserslist-db": "^1.0.9"
+ }
+ },
+ "call-bind": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+ "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.2"
+ }
+ },
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true
+ },
+ "caniuse-lite": {
+ "version": "1.0.30001434",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz",
+ "integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==",
+ "dev": true
+ },
+ "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
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "confusing-browser-globals": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz",
+ "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==",
+ "dev": true
+ },
+ "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
+ },
+ "core-js-pure": {
+ "version": "3.26.1",
+ "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.1.tgz",
+ "integrity": "sha512-VVXcDpp/xJ21KdULRq/lXdLzQAtX7+37LzpyfFM973il0tWSsDEoyzG38G14AjTpK9VTfiNM9jnFauq/CpaWGQ==",
+ "dev": true,
+ "peer": true
+ },
+ "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,
+ "requires": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ }
+ },
+ "csstype": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz",
+ "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==",
+ "dev": true
+ },
+ "damerau-levenshtein": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
+ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
+ "dev": true,
+ "peer": true
+ },
+ "debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "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
+ },
+ "define-properties": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
+ "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
+ "dev": true,
+ "requires": {
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ }
+ },
+ "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,
+ "requires": {
+ "path-type": "^4.0.0"
+ }
+ },
+ "doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "electron-to-chromium": {
+ "version": "1.4.284",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz",
+ "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true,
+ "peer": true
+ },
+ "es-abstract": {
+ "version": "1.20.4",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz",
+ "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "function.prototype.name": "^1.1.5",
+ "get-intrinsic": "^1.1.3",
+ "get-symbol-description": "^1.0.0",
+ "has": "^1.0.3",
+ "has-property-descriptors": "^1.0.0",
+ "has-symbols": "^1.0.3",
+ "internal-slot": "^1.0.3",
+ "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-weakref": "^1.0.2",
+ "object-inspect": "^1.12.2",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.4",
+ "regexp.prototype.flags": "^1.4.3",
+ "safe-regex-test": "^1.0.0",
+ "string.prototype.trimend": "^1.0.5",
+ "string.prototype.trimstart": "^1.0.5",
+ "unbox-primitive": "^1.0.2"
+ }
+ },
+ "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==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "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,
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "esbuild": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.16.tgz",
+ "integrity": "sha512-o6iS9zxdHrrojjlj6pNGC2NAg86ECZqIETswTM5KmJitq+R1YmahhWtMumeQp9lHqJaROGnsBi2RLawGnfo5ZQ==",
+ "dev": true,
+ "requires": {
+ "@esbuild/android-arm": "0.15.16",
+ "@esbuild/linux-loong64": "0.15.16",
+ "esbuild-android-64": "0.15.16",
+ "esbuild-android-arm64": "0.15.16",
+ "esbuild-darwin-64": "0.15.16",
+ "esbuild-darwin-arm64": "0.15.16",
+ "esbuild-freebsd-64": "0.15.16",
+ "esbuild-freebsd-arm64": "0.15.16",
+ "esbuild-linux-32": "0.15.16",
+ "esbuild-linux-64": "0.15.16",
+ "esbuild-linux-arm": "0.15.16",
+ "esbuild-linux-arm64": "0.15.16",
+ "esbuild-linux-mips64le": "0.15.16",
+ "esbuild-linux-ppc64le": "0.15.16",
+ "esbuild-linux-riscv64": "0.15.16",
+ "esbuild-linux-s390x": "0.15.16",
+ "esbuild-netbsd-64": "0.15.16",
+ "esbuild-openbsd-64": "0.15.16",
+ "esbuild-sunos-64": "0.15.16",
+ "esbuild-windows-32": "0.15.16",
+ "esbuild-windows-64": "0.15.16",
+ "esbuild-windows-arm64": "0.15.16"
+ }
+ },
+ "esbuild-android-64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.16.tgz",
+ "integrity": "sha512-Vwkv/sT0zMSgPSVO3Jlt1pUbnZuOgtOQJkJkyyJFAlLe7BiT8e9ESzo0zQSx4c3wW4T6kGChmKDPMbWTgtliQA==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-android-arm64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.16.tgz",
+ "integrity": "sha512-lqfKuofMExL5niNV3gnhMUYacSXfsvzTa/58sDlBET/hCOG99Zmeh+lz6kvdgvGOsImeo6J9SW21rFCogNPLxg==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-darwin-64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.16.tgz",
+ "integrity": "sha512-wo2VWk/n/9V2TmqUZ/KpzRjCEcr00n7yahEdmtzlrfQ3lfMCf3Wa+0sqHAbjk3C6CKkR3WKK/whkMq5Gj4Da9g==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-darwin-arm64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.16.tgz",
+ "integrity": "sha512-fMXaUr5ou0M4WnewBKsspMtX++C1yIa3nJ5R2LSbLCfJT3uFdcRoU/NZjoM4kOMKyOD9Sa/2vlgN8G07K3SJnw==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-freebsd-64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.16.tgz",
+ "integrity": "sha512-UzIc0xlRx5x9kRuMr+E3+hlSOxa/aRqfuMfiYBXu2jJ8Mzej4lGL7+o6F5hzhLqWfWm1GWHNakIdlqg1ayaTNQ==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-freebsd-arm64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.16.tgz",
+ "integrity": "sha512-8xyiYuGc0DLZphFQIiYaLHlfoP+hAN9RHbE+Ibh8EUcDNHAqbQgUrQg7pE7Bo00rXmQ5Ap6KFgcR0b4ALZls1g==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-32": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.16.tgz",
+ "integrity": "sha512-iGijUTV+0kIMyUVoynK0v+32Oi8yyp0xwMzX69GX+5+AniNy/C/AL1MjFTsozRp/3xQPl7jVux/PLe2ds10/2w==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.16.tgz",
+ "integrity": "sha512-tuSOjXdLw7VzaUj89fIdAaQT7zFGbKBcz4YxbWrOiXkwscYgE7HtTxUavreBbnRkGxKwr9iT/gmeJWNm4djy/g==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-arm": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.16.tgz",
+ "integrity": "sha512-XKcrxCEXDTOuoRj5l12tJnkvuxXBMKwEC5j0JISw3ziLf0j4zIwXbKbTmUrKFWbo6ZgvNpa7Y5dnbsjVvH39bQ==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-arm64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.16.tgz",
+ "integrity": "sha512-mPYksnfHnemNrvjrDhZyixL/AfbJN0Xn9S34ZOHYdh6/jJcNd8iTsv3JwJoEvTJqjMggjMhGUPJAdjnFBHoH8A==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-mips64le": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.16.tgz",
+ "integrity": "sha512-kSJO2PXaxfm0pWY39+YX+QtpFqyyrcp0ZeI8QPTrcFVQoWEPiPVtOfTZeS3ZKedfH+Ga38c4DSzmKMQJocQv6A==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-ppc64le": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.16.tgz",
+ "integrity": "sha512-NimPikwkBY0yGABw6SlhKrtT35sU4O23xkhlrTT/O6lSxv3Pm5iSc6OYaqVAHWkLdVf31bF4UDVFO+D990WpAA==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-riscv64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.16.tgz",
+ "integrity": "sha512-ty2YUHZlwFOwp7pR+J87M4CVrXJIf5ZZtU/umpxgVJBXvWjhziSLEQxvl30SYfUPq0nzeWKBGw5i/DieiHeKfw==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-s390x": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.16.tgz",
+ "integrity": "sha512-VkZaGssvPDQtx4fvVdZ9czezmyWyzpQhEbSNsHZZN0BHvxRLOYAQ7sjay8nMQwYswP6O2KlZluRMNPYefFRs+w==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-netbsd-64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.16.tgz",
+ "integrity": "sha512-ElQ9rhdY51et6MJTWrCPbqOd/YuPowD7Cxx3ee8wlmXQQVW7UvQI6nSprJ9uVFQISqSF5e5EWpwWqXZsECLvXg==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-openbsd-64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.16.tgz",
+ "integrity": "sha512-KgxMHyxMCT+NdLQE1zVJEsLSt2QQBAvJfmUGDmgEq8Fvjrf6vSKB00dVHUEDKcJwMID6CdgCpvYNt999tIYhqA==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-sunos-64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.16.tgz",
+ "integrity": "sha512-exSAx8Phj7QylXHlMfIyEfNrmqnLxFqLxdQF6MBHPdHAjT7fsKaX6XIJn+aQEFiOcE4X8e7VvdMCJ+WDZxjSRQ==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-windows-32": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.16.tgz",
+ "integrity": "sha512-zQgWpY5pUCSTOwqKQ6/vOCJfRssTvxFuEkpB4f2VUGPBpdddZfdj8hbZuFRdZRPIVHvN7juGcpgCA/XCF37mAQ==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-windows-64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.16.tgz",
+ "integrity": "sha512-HjW1hHRLSncnM3MBCP7iquatHVJq9l0S2xxsHHj4yzf4nm9TU4Z7k4NkeMlD/dHQ4jPlQQhwcMvwbJiOefSuZw==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-windows-arm64": {
+ "version": "0.15.16",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.16.tgz",
+ "integrity": "sha512-oCcUKrJaMn04Vxy9Ekd8x23O8LoU01+4NOkQ2iBToKgnGj5eo1vU9i27NQZ9qC8NFZgnQQZg5oZWAejmbsppNA==",
+ "dev": true,
+ "optional": true
+ },
+ "escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "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
+ },
+ "eslint": {
+ "version": "8.28.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.28.0.tgz",
+ "integrity": "sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==",
+ "dev": true,
+ "requires": {
+ "@eslint/eslintrc": "^1.3.3",
+ "@humanwhocodes/config-array": "^0.11.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "ajv": "^6.10.0",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.1.1",
+ "eslint-utils": "^3.0.0",
+ "eslint-visitor-keys": "^3.3.0",
+ "espree": "^9.4.0",
+ "esquery": "^1.4.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.15.0",
+ "grapheme-splitter": "^1.0.4",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-sdsl": "^4.1.4",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.1",
+ "regexpp": "^3.2.0",
+ "strip-ansi": "^6.0.1",
+ "strip-json-comments": "^3.1.0",
+ "text-table": "^0.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"
+ }
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "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
+ },
+ "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-scope": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
+ "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ }
+ },
+ "globals": {
+ "version": "13.18.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz",
+ "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.20.2"
+ }
+ },
+ "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"
+ }
+ }
+ }
+ },
+ "eslint-config-airbnb": {
+ "version": "19.0.4",
+ "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz",
+ "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==",
+ "dev": true,
+ "requires": {
+ "eslint-config-airbnb-base": "^15.0.0",
+ "object.assign": "^4.1.2",
+ "object.entries": "^1.1.5"
+ }
+ },
+ "eslint-config-airbnb-base": {
+ "version": "15.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz",
+ "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==",
+ "dev": true,
+ "requires": {
+ "confusing-browser-globals": "^1.0.10",
+ "object.assign": "^4.1.2",
+ "object.entries": "^1.1.5",
+ "semver": "^6.3.0"
+ }
+ },
+ "eslint-config-prettier": {
+ "version": "8.5.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz",
+ "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==",
+ "dev": true,
+ "requires": {}
+ },
+ "eslint-import-resolver-node": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz",
+ "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==",
+ "dev": true,
+ "requires": {
+ "debug": "^3.2.7",
+ "resolve": "^1.20.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ }
+ }
+ },
+ "eslint-module-utils": {
+ "version": "2.7.4",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz",
+ "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==",
+ "dev": true,
+ "requires": {
+ "debug": "^3.2.7"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ }
+ }
+ },
+ "eslint-plugin-import": {
+ "version": "2.26.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz",
+ "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==",
+ "dev": true,
+ "requires": {
+ "array-includes": "^3.1.4",
+ "array.prototype.flat": "^1.2.5",
+ "debug": "^2.6.9",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.6",
+ "eslint-module-utils": "^2.7.3",
+ "has": "^1.0.3",
+ "is-core-module": "^2.8.1",
+ "is-glob": "^4.0.3",
+ "minimatch": "^3.1.2",
+ "object.values": "^1.1.5",
+ "resolve": "^1.22.0",
+ "tsconfig-paths": "^3.14.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"
+ }
+ },
+ "doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-plugin-jsx-a11y": {
+ "version": "6.6.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz",
+ "integrity": "sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@babel/runtime": "^7.18.9",
+ "aria-query": "^4.2.2",
+ "array-includes": "^3.1.5",
+ "ast-types-flow": "^0.0.7",
+ "axe-core": "^4.4.3",
+ "axobject-query": "^2.2.0",
+ "damerau-levenshtein": "^1.0.8",
+ "emoji-regex": "^9.2.2",
+ "has": "^1.0.3",
+ "jsx-ast-utils": "^3.3.2",
+ "language-tags": "^1.0.5",
+ "minimatch": "^3.1.2",
+ "semver": "^6.3.0"
+ }
+ },
+ "eslint-plugin-react": {
+ "version": "7.31.11",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.11.tgz",
+ "integrity": "sha512-TTvq5JsT5v56wPa9OYHzsrOlHzKZKjV+aLgS+55NJP/cuzdiQPC7PfYoUjMoxlffKtvijpk7vA/jmuqRb9nohw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "array-includes": "^3.1.6",
+ "array.prototype.flatmap": "^1.3.1",
+ "array.prototype.tosorted": "^1.1.1",
+ "doctrine": "^2.1.0",
+ "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.3",
+ "semver": "^6.3.0",
+ "string.prototype.matchall": "^4.0.8"
+ },
+ "dependencies": {
+ "doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "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==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "is-core-module": "^2.9.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ }
+ }
+ }
+ },
+ "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,
+ "peer": true,
+ "requires": {}
+ },
+ "eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ },
+ "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": {
+ "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"
+ },
+ "dependencies": {
+ "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
+ }
+ }
+ },
+ "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
+ },
+ "espree": {
+ "version": "9.4.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz",
+ "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==",
+ "dev": true,
+ "requires": {
+ "acorn": "^8.8.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.3.0"
+ }
+ },
+ "esquery": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
+ "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.1.0"
+ }
+ },
+ "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,
+ "requires": {
+ "estraverse": "^5.2.0"
+ }
+ },
+ "estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true
+ },
+ "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
+ },
+ "fast-glob": {
+ "version": "3.2.12",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
+ "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
+ "dev": true,
+ "requires": {
+ "@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"
+ },
+ "dependencies": {
+ "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,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ }
+ }
+ },
+ "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
+ },
+ "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
+ },
+ "fastq": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
+ "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
+ "dev": true,
+ "requires": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "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,
+ "requires": {
+ "flat-cache": "^3.0.4"
+ }
+ },
+ "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"
+ }
+ },
+ "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,
+ "requires": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "flat-cache": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+ "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+ "dev": true,
+ "requires": {
+ "flatted": "^3.1.0",
+ "rimraf": "^3.0.2"
+ }
+ },
+ "flatted": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
+ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
+ "dev": true
+ },
+ "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
+ },
+ "fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "optional": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "function.prototype.name": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
+ "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.19.0",
+ "functions-have-names": "^1.2.2"
+ }
+ },
+ "functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true
+ },
+ "gensync": {
+ "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
+ },
+ "get-intrinsic": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
+ "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.3"
+ }
+ },
+ "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==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.1.1"
+ }
+ },
+ "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"
+ }
+ },
+ "glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.3"
+ }
+ },
+ "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
+ },
+ "globby": {
+ "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.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ }
+ },
+ "grapheme-splitter": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
+ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+ "dev": true
+ },
+ "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-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
+ },
+ "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
+ },
+ "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==",
+ "dev": true,
+ "requires": {
+ "get-intrinsic": "^1.1.1"
+ }
+ },
+ "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
+ },
+ "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,
+ "requires": {
+ "has-symbols": "^1.0.2"
+ }
+ },
+ "ignore": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz",
+ "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==",
+ "dev": true
+ },
+ "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,
+ "requires": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true
+ },
+ "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,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "internal-slot": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz",
+ "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==",
+ "dev": true,
+ "requires": {
+ "get-intrinsic": "^1.1.0",
+ "has": "^1.0.3",
+ "side-channel": "^1.0.4"
+ }
+ },
+ "is-bigint": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
+ "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
+ "dev": true,
+ "requires": {
+ "has-bigints": "^1.0.1"
+ }
+ },
+ "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,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true
+ },
+ "is-core-module": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
+ "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "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,
+ "requires": {
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true
+ },
+ "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,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "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
+ },
+ "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
+ },
+ "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,
+ "requires": {
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true
+ },
+ "is-regex": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+ "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "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,
+ "requires": {
+ "call-bind": "^1.0.2"
+ }
+ },
+ "is-string": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
+ "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
+ "dev": true,
+ "requires": {
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "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,
+ "requires": {
+ "has-symbols": "^1.0.2"
+ }
+ },
+ "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,
+ "requires": {
+ "call-bind": "^1.0.2"
+ }
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "js-sdsl": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz",
+ "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==",
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ },
+ "js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "requires": {
+ "argparse": "^2.0.1"
+ }
+ },
+ "jsbi": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-4.3.0.tgz",
+ "integrity": "sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g=="
+ },
+ "jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true
+ },
+ "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
+ },
+ "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
+ },
+ "json5": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
+ "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
+ "dev": true
+ },
+ "jsx-ast-utils": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz",
+ "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "array-includes": "^3.1.5",
+ "object.assign": "^4.1.3"
+ }
+ },
+ "language-subtag-registry": {
+ "version": "0.3.22",
+ "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz",
+ "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==",
+ "dev": true,
+ "peer": true
+ },
+ "language-tags": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.6.tgz",
+ "integrity": "sha512-HNkaCgM8wZgE/BZACeotAAgpL9FUjEnhgF0FVQMIgH//zqTPreLYMb3rWYkYAqPoF75Jwuycp1da7uz66cfFQg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "language-subtag-registry": "^0.3.20"
+ }
+ },
+ "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,
+ "requires": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ }
+ },
+ "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,
+ "requires": {
+ "p-locate": "^5.0.0"
+ }
+ },
+ "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
+ },
+ "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==",
+ "requires": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ }
+ },
+ "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"
+ }
+ },
+ "magic-string": {
+ "version": "0.26.7",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz",
+ "integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==",
+ "dev": true,
+ "requires": {
+ "sourcemap-codec": "^1.4.8"
+ }
+ },
+ "merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "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"
+ }
+ },
+ "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"
+ }
+ },
+ "minimist": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
+ "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "nanoid": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
+ "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
+ "dev": true
+ },
+ "natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "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
+ },
+ "node-releases": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz",
+ "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==",
+ "dev": true
+ },
+ "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,
+ "peer": true
+ },
+ "object-inspect": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
+ "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
+ "dev": true
+ },
+ "object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true
+ },
+ "object.assign": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
+ "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "has-symbols": "^1.0.3",
+ "object-keys": "^1.1.1"
+ }
+ },
+ "object.entries": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz",
+ "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.4"
+ }
+ },
+ "object.fromentries": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz",
+ "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.4"
+ }
+ },
+ "object.hasown": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz",
+ "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.4"
+ }
+ },
+ "object.values": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz",
+ "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.4"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "optionator": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+ "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+ "dev": true,
+ "requires": {
+ "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"
+ }
+ },
+ "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,
+ "requires": {
+ "yocto-queue": "^0.1.0"
+ }
+ },
+ "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,
+ "requires": {
+ "p-limit": "^3.0.2"
+ }
+ },
+ "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,
+ "requires": {
+ "callsites": "^3.0.0"
+ }
+ },
+ "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
+ },
+ "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
+ },
+ "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
+ },
+ "path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "path-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
+ },
+ "picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+ "dev": true
+ },
+ "picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true
+ },
+ "postcss": {
+ "version": "8.4.19",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz",
+ "integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==",
+ "dev": true,
+ "requires": {
+ "nanoid": "^3.3.4",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ }
+ },
+ "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
+ },
+ "prettier": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.0.tgz",
+ "integrity": "sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==",
+ "dev": true
+ },
+ "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,
+ "peer": true,
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.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==",
+ "dev": true
+ },
+ "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
+ },
+ "react": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
+ "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
+ "requires": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "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==",
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.0"
+ }
+ },
+ "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,
+ "peer": true
+ },
+ "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
+ },
+ "regenerator-runtime": {
+ "version": "0.13.11",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+ "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
+ "dev": true,
+ "peer": true
+ },
+ "regexp.prototype.flags": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
+ "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "functions-have-names": "^1.2.2"
+ }
+ },
+ "regexpp": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
+ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.22.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+ "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
+ "dev": true,
+ "requires": {
+ "is-core-module": "^2.9.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ }
+ },
+ "resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true
+ },
+ "reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "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": {
+ "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"
+ }
+ },
+ "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,
+ "requires": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "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,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.1.3",
+ "is-regex": "^1.1.4"
+ }
+ },
+ "scheduler": {
+ "version": "0.23.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
+ "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
+ "requires": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ },
+ "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,
+ "requires": {
+ "shebang-regex": "^3.0.0"
+ }
+ },
+ "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
+ },
+ "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,
+ "requires": {
+ "call-bind": "^1.0.0",
+ "get-intrinsic": "^1.0.2",
+ "object-inspect": "^1.9.0"
+ }
+ },
+ "slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true
+ },
+ "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
+ },
+ "sourcemap-codec": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
+ "dev": true
+ },
+ "string.prototype.matchall": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz",
+ "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.4",
+ "get-intrinsic": "^1.1.3",
+ "has-symbols": "^1.0.3",
+ "internal-slot": "^1.0.3",
+ "regexp.prototype.flags": "^1.4.3",
+ "side-channel": "^1.0.4"
+ }
+ },
+ "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,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.4"
+ }
+ },
+ "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,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.4"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.1"
+ }
+ },
+ "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
+ },
+ "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
+ },
+ "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"
+ }
+ },
+ "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
+ },
+ "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
+ },
+ "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-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": "3.14.1",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
+ "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==",
+ "dev": true,
+ "requires": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.1",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ },
+ "dependencies": {
+ "json5": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ }
+ }
+ },
+ "tslib": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
+ "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
+ },
+ "tsutils": {
+ "version": "3.21.0",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
+ "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.8.1"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
+ }
+ },
+ "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,
+ "requires": {
+ "prelude-ls": "^1.2.1"
+ }
+ },
+ "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
+ },
+ "typescript": {
+ "version": "4.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz",
+ "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==",
+ "dev": true
+ },
+ "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,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.0.3",
+ "which-boxed-primitive": "^1.0.2"
+ }
+ },
+ "update-browserslist-db": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
+ "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
+ "dev": true,
+ "requires": {
+ "escalade": "^3.1.1",
+ "picocolors": "^1.0.0"
+ }
+ },
+ "uri-js": {
+ "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"
+ }
+ },
+ "vite": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-3.2.4.tgz",
+ "integrity": "sha512-Z2X6SRAffOUYTa+sLy3NQ7nlHFU100xwanq1WDwqaiFiCe+25zdxP1TfCS5ojPV2oDDcXudHIoPnI1Z/66B7Yw==",
+ "dev": true,
+ "requires": {
+ "esbuild": "^0.15.9",
+ "fsevents": "~2.3.2",
+ "postcss": "^8.4.18",
+ "resolve": "^1.22.1",
+ "rollup": "^2.79.1"
+ }
+ },
+ "which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "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": {
+ "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"
+ }
+ },
+ "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
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "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 4f481ec..b73d41f 100644
--- a/package.json
+++ b/package.json
@@ -1,44 +1,58 @@
{
- "name": "ant-plus",
- "version": "0.1.25",
- "description": "A node module for ANT+",
- "main": "ant-plus.js",
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1",
- "prepare": "tsc"
- },
- "keywords": [
- "ANT",
- "ANT+"
- ],
- "author": {
- "name": "Alessandro Vergani",
- "email": "alessandro.vergani@gmail.com"
- },
- "license": "MIT",
- "engines": {
- "node": ">=6.0.0"
- },
- "dependencies": {
- "usb": "^1.6.0"
- },
- "devDependencies": {
- "typescript": "^3.6.4",
- "@types/node": "^6.0.0",
- "@types/usb": "^1.5.1"
- },
- "files": [
- "ant-plus.js",
- "src/",
- "build/"
- ],
- "directories": {
- "example": "./sample",
- "lib": "./src"
- },
- "repository": {
- "type": "git",
- "url": "https://github.com/loghorn/ant-plus.git"
- },
- "bugs": "https://github.com/Loghorn/ant-plus/issues"
+ "name": "web-ant-plus",
+ "version": "1.0.1",
+ "description": "A package for ANT+ on Web browsers.",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "directories": {
+ "lib": "src",
+ "example": "example"
+ },
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "build": "rimraf ./dist && tsc",
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "dev": "vite ./example",
+ "preview": "vite preview ./example"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/8beeeaaat/web-ant-plus.git"
+ },
+ "keywords": [
+ "ANT+",
+ "ant-plus",
+ "ant plus",
+ "Garmin"
+ ],
+ "author": "8beeeaaat",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/8beeeaaat/web-ant-plus/issues"
+ },
+ "homepage": "https://github.com/8beeeaaat/web-ant-plus#readme",
+ "workspaces": [
+ "example"
+ ],
+ "devDependencies": {
+ "@types/react": "^18.0.24",
+ "@types/react-dom": "^18.0.8",
+ "@types/w3c-web-usb": "^1.0.6",
+ "@typescript-eslint/eslint-plugin": "^5.42.1",
+ "@vitejs/plugin-react": "^2.2.0",
+ "eslint": "^8.27.0",
+ "eslint-config-airbnb": "^19.0.4",
+ "eslint-config-prettier": "^8.5.0",
+ "eslint-plugin-import": "^2.26.0",
+ "prettier": "^2.7.1",
+ "typescript": "^4.6.4",
+ "vite": "^3.2.3"
+ },
+ "dependencies": {
+ "@js-temporal/polyfill": "^0.4.3",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
+ }
}
diff --git a/sample/bicycle-power.js b/sample/bicycle-power.js
deleted file mode 100644
index c4a2003..0000000
--- a/sample/bicycle-power.js
+++ /dev/null
@@ -1,20 +0,0 @@
-'use strict';
-
-let Ant = require('../ant-plus');
-let stick = new Ant.GarminStick2();
-let bicyclePowerSensor = new Ant.BicyclePowerSensor(stick);
-
-
-bicyclePowerSensor.on('powerData', data => {
- console.log(`id: ${data.DeviceID}, cadence: ${data.Cadence}, power: ${data.Power}`);
-});
-
-
-stick.on('startup', function () {
- console.log('startup');
- bicyclePowerSensor.attach(0, 0);
-});
-
-if (!stick.open()) {
- console.log('Stick not found!');
-}
diff --git a/sample/cadence-sensor.js b/sample/cadence-sensor.js
deleted file mode 100644
index 53cd021..0000000
--- a/sample/cadence-sensor.js
+++ /dev/null
@@ -1,24 +0,0 @@
-'use strict';
-
-const Ant = require('../ant-plus');
-const stick = new Ant.GarminStick2();
-const speedCadenceSensor = new Ant.SpeedCadenceSensor(stick);
-speedCadenceSensor.setWheelCircumference(2.120); //Wheel circumference in meters
-
-speedCadenceSensor.on('speedData', data => {
- console.log(`speed: ${data.CalculatedSpeed}`);
-});
-
-speedCadenceSensor.on('cadenceData', data => {
- console.log(`cadence: ${data.CalculatedCadence}`);
-});
-
-
-stick.on('startup', function () {
- console.log('startup');
- speedCadenceSensor.attach(0, 0);
-});
-
-if (!stick.open()) {
- console.log('Stick not found!');
-}
diff --git a/sample/fitness-equipment.js b/sample/fitness-equipment.js
deleted file mode 100644
index ea06f87..0000000
--- a/sample/fitness-equipment.js
+++ /dev/null
@@ -1,58 +0,0 @@
-'use strict';
-
-const Ant = require('../ant-plus');
-const stick = new Ant.GarminStick2();
-
-const fitnessEquipmentSensor = new Ant.FitnessEquipmentSensor(stick);
-
-fitnessEquipmentSensor.on('fitnessData', data => {
- console.log(`id: ${data.DeviceID}`);
- console.dir(data);
-});
-
-fitnessEquipmentSensor.on('attached', function() {
- console.log('sensor attached');
-
- fitnessEquipmentSensor.setUserConfiguration(82.3, 12.7, 70.5, function() {
- console.log('set User Weight = 82.3kg, Bike Weight = 12.7kg, Wheel = 70.5cm');
- });
- fitnessEquipmentSensor.setBasicResistance(20.5, function() {
- console.log('set resistance to 20.5%');
- });
- fitnessEquipmentSensor.setTrackResistance(1.1, function() {
- console.log('set slope to 1.1%');
- });
- fitnessEquipmentSensor.setWindResistance(0.51, function() {
- console.log('set wind resistance coeff 0.51 kg/m');
- simulateTraining(30, 5, Date.now() + 1 * 60 * 1000);
- });
-});
-
-function simulateTraining(targetPower, increment, simulationEnd) {
- if (Date.now() > simulationEnd) {
- console.log('Simulation ended');
- stick.close();
- return;
- }
-
- const tp = targetPower;
- fitnessEquipmentSensor.setTargetPower(tp, function() {
- console.log('set target power to ' + tp + 'W');
- });
-
- if (targetPower >= 200 || targetPower <= 20) {
- increment = -1 * increment;
- }
- targetPower += increment;
-
- setTimeout(simulateTraining, 5000, targetPower, increment, simulationEnd);
-}
-
-stick.on('startup', function() {
- console.log('startup');
- fitnessEquipmentSensor.attach(0, 0);
-});
-
-if (!stick.open()) {
- console.log('Stick not found!');
-}
diff --git a/sample/multi-scan.js b/sample/multi-scan.js
deleted file mode 100644
index 60f6c0b..0000000
--- a/sample/multi-scan.js
+++ /dev/null
@@ -1,61 +0,0 @@
-'use strict';
-
-const Ant = require('../ant-plus');
-const stick = new Ant.GarminStick2();
-
-const hrScanner = new Ant.HeartRateScanner(stick);
-hrScanner.on('hbData', data => {
- console.log(`id: ${data.DeviceID}`);
- console.dir(data);
-});
-hrScanner.on('attached', () => {
- speedCadenceScanner.scan();
- speedScanner.scan();
- cadenceScanner.scan();
- fitnessEquipmentScanner.scan();
- environmentScanner.scan();
-});
-
-const environmentScanner = new Ant.EnvironmentScanner(stick);
-environmentScanner.on('envData', data => {
- console.log(`id: ${data.DeviceID}`);
- console.dir(data);
-});
-
-const fitnessEquipmentScanner = new Ant.FitnessEquipmentScanner(stick);
-fitnessEquipmentScanner.on('fitnessData', data => {
- console.log(`id: ${data.DeviceID}`);
- console.dir(data);
-});
-
-const speedCadenceScanner = new Ant.SpeedCadenceScanner(stick);
-speedCadenceScanner.on('speedData', data => {
- console.log(`id: ${data.DeviceID}`);
- console.dir(data);
-});
-speedCadenceScanner.on('cadenceData', data => {
- console.log(`id: ${data.DeviceID}`);
- console.dir(data);
-});
-
-const speedScanner = new Ant.SpeedScanner(stick);
-speedScanner.on('speedData', data => {
- console.log(`id: ${data.DeviceID}`);
- console.dir(data);
-});
-
-const cadenceScanner = new Ant.CadenceScanner(stick);
-cadenceScanner.on('cadenceData', data => {
- console.log(`id: ${data.DeviceID}`);
- console.dir(data);
-});
-
-
-stick.on('startup', function() {
- console.log('startup');
- hrScanner.scan();
-});
-
-if (!stick.open()) {
- console.log('Stick not found!');
-}
diff --git a/sample/muscle-oxygen.js b/sample/muscle-oxygen.js
deleted file mode 100644
index 79c6597..0000000
--- a/sample/muscle-oxygen.js
+++ /dev/null
@@ -1,25 +0,0 @@
-'use strict';
-
-let Ant = require('../ant-plus');
-let stick = new Ant.GarminStick2();
-let muscleOxygenSensor = new Ant.MuscleOxygenSensor(stick);
-
-
-muscleOxygenSensor.on('oxygenData', data => {
- console.log(`id: ${data.DeviceID}`);
- console.dir(data);
-
- if (data.UTCTimeRequired) {
- muscleOxygenSensor.setUTCTime(function() { console.log('Set UTC time') });
- }
-});
-
-
-stick.on('startup', function () {
- console.log('startup');
- muscleOxygenSensor.attach(0, 0);
-});
-
-if (!stick.open()) {
- console.log('Stick not found!');
-}
diff --git a/sample/sample.js b/sample/sample.js
deleted file mode 100644
index 59d5e48..0000000
--- a/sample/sample.js
+++ /dev/null
@@ -1,88 +0,0 @@
-var Ant = require('../ant-plus');
-
-function openStick(stick, stickid) {
- var sensor1 = new Ant.HeartRateSensor(stick);
-
- var dev_id = 0;
-
- sensor1.on('hbdata', function(data) {
- console.log(stickid, 'sensor 1: ', data.DeviceID, data.ComputedHeartRate, data);
- if (data.DeviceID !== 0 && dev_id === 0) {
- dev_id = data.DeviceID;
- console.log(stickid, 'detaching...');
- sensor1.detach();
- sensor1.once('detached', function() {
- sensor1.attach(0, dev_id);
- });
- }
- });
-
- sensor1.on('attached', function() { console.log(stickid, 'sensor1 attached'); });
- sensor1.on('detached', function() { console.log(stickid, 'sensor1 detached'); });
-
- var sensor2 = new Ant.StrideSpeedDistanceSensor(stick);
-
- sensor2.on('ssddata', function(data) {
- console.log(stickid, 'sensor 2: ', data.DeviceID, data);
- });
-
- sensor2.on('attached', function() { console.log(stickid, 'sensor2 attached'); });
- sensor2.on('detached', function() { console.log(stickid, 'sensor2 detached'); });
-
- var scanner = new Ant.HeartRateScanner(stick);
-
- scanner.on('hbdata', function(data) {
- console.log(stickid, 'scanner: ', data.DeviceID, data.ComputedHeartRate, data.Rssi, data);
- });
-
- scanner.on('attached', function() { console.log(stickid, 'scanner attached'); });
- scanner.on('detached', function() { console.log(stickid, 'scanner detached'); });
-
- stick.on('startup', function() {
- console.log(stickid, 'startup');
-
- console.log(stickid, 'Max channels:', stick.maxChannels);
-
- sensor1.attach(0, 0);
-
- setTimeout(function(data) {
- sensor2.attach(1, 0);
- }, 2000);
-
- setTimeout(function() {
- sensor1.once('detached', function() { sensor2.detach(); });
- sensor2.once('detached', function() {
- scanner.scan();
- });
- sensor1.detach();
- }, 5000);
- });
-
- stick.on('shutdown', function() { console.log(stickid, 'shutdown'); });
-
- function tryOpen(stick) {
- let token = stick.openAsync((err) => {
- token = null;
- if (err) {
- console.error(stickid, err);
- } else {
- console.log(stickid, 'Stick found');
- setTimeout(function() { stick.close(); }, 10000);
- }
- });
-
- setTimeout(function() { token && token.cancel(); }, 60000);
-
- return token;
- }
-
- tryOpen(stick);
-}
-
-openStick(new Ant.GarminStick2(), 1);
-openStick(new Ant.GarminStick2(), 2);
-openStick(new Ant.GarminStick3(), 3);
-
-openStick(new Ant.GarminStick2(), 4);
-openStick(new Ant.GarminStick2(), 5);
-openStick(new Ant.GarminStick3(), 6);
diff --git a/sample/two-sticks.js b/sample/two-sticks.js
deleted file mode 100644
index 8796a5c..0000000
--- a/sample/two-sticks.js
+++ /dev/null
@@ -1,38 +0,0 @@
-'use strict';
-
-const Ant = require('../ant-plus');
-const stick = new Ant.GarminStick2();
-
-const fitnessEquipmentSensor = new Ant.FitnessEquipmentSensor(stick);
-
-fitnessEquipmentSensor.on('fitnessData', data => {
- console.log(`id: ${data.DeviceID}`);
- console.dir(data);
-});
-
-stick.on('startup', function() {
- console.log('startup');
- fitnessEquipmentSensor.attach(0, 0);
-});
-
-if (!stick.open()) {
- console.log('Stick not found!');
-}
-
-const stick2 = new Ant.GarminStick2();
-
-const hrSensor = new Ant.HeartRateSensor(stick2);
-
-hrSensor.on('hbData', data => {
- console.log(`id: ${data.DeviceID}`);
- console.dir(data);
-});
-
-stick2.on('startup', function() {
- console.log('startup2');
- hrSensor.attach(0, 0);
-});
-
-if (!stick2.open()) {
- console.log('Stick 2 not found!');
-}
diff --git a/src/Constants.ts b/src/Constants.ts
new file mode 100644
index 0000000..fff0379
--- /dev/null
+++ b/src/Constants.ts
@@ -0,0 +1,106 @@
+export enum Constants {
+ MESSAGE_RF = 0x01,
+
+ MESSAGE_TX_SYNC = 0xa4,
+ DEFAULT_NETWORK_NUMBER = 0x00,
+
+ // Configuration messages
+ MESSAGE_CHANNEL_UNASSIGN = 0x41,
+ MESSAGE_CHANNEL_ASSIGN = 0x42,
+ MESSAGE_CHANNEL_ID = 0x51,
+ MESSAGE_CHANNEL_PERIOD = 0x43,
+ MESSAGE_CHANNEL_SEARCH_TIMEOUT = 0x44,
+ MESSAGE_CHANNEL_FREQUENCY = 0x45,
+ MESSAGE_CHANNEL_TX_POWER = 0x60,
+ MESSAGE_NETWORK_KEY = 0x46,
+ MESSAGE_TX_POWER = 0x47,
+ MESSAGE_PROXIMITY_SEARCH = 0x71,
+ MESSAGE_ENABLE_RX_EXT = 0x66,
+ MESSAGE_LIB_CONFIG = 0x6e,
+ MESSAGE_CHANNEL_OPEN_RX_SCAN = 0x5b,
+
+ // Notification messages
+ MESSAGE_STARTUP = 0x6f,
+
+ // Control messages
+ MESSAGE_SYSTEM_RESET = 0x4a,
+ MESSAGE_CHANNEL_OPEN = 0x4b,
+ MESSAGE_CHANNEL_CLOSE = 0x4c,
+ MESSAGE_CHANNEL_REQUEST = 0x4d,
+
+ // Data messages
+ MESSAGE_CHANNEL_BROADCAST_DATA = 0x4e,
+ MESSAGE_CHANNEL_ACKNOWLEDGED_DATA = 0x4f,
+ MESSAGE_CHANNEL_BURST_DATA = 0x50,
+
+ // Channel event messages
+ MESSAGE_CHANNEL_EVENT = 0x40,
+
+ // Requested response messages
+ MESSAGE_CHANNEL_STATUS = 0x52,
+ // MESSAGE_CHANNEL_ID = 0x51,
+ MESSAGE_VERSION = 0x3e,
+ MESSAGE_CAPABILITIES = 0x54,
+ MESSAGE_SERIAL_NUMBER = 0x61,
+
+ // Message parameters
+ CHANNEL_TYPE_TWOWAY_RECEIVE = 0x00,
+ CHANNEL_TYPE_TWOWAY_TRANSMIT = 0x10,
+ CHANNEL_TYPE_SHARED_RECEIVE = 0x20,
+ CHANNEL_TYPE_SHARED_TRANSMIT = 0x30,
+ CHANNEL_TYPE_ONEWAY_RECEIVE = 0x40,
+ CHANNEL_TYPE_ONEWAY_TRANSMIT = 0x50,
+ RADIO_TX_POWER_MINUS20DB = 0x00,
+ RADIO_TX_POWER_MINUS10DB = 0x01,
+ RADIO_TX_POWER_0DB = 0x02,
+ RADIO_TX_POWER_PLUS4DB = 0x03,
+ RESPONSE_NO_ERROR = 0x00,
+ EVENT_RX_SEARCH_TIMEOUT = 0x01,
+ EVENT_RX_FAIL = 0x02,
+ EVENT_TX = 0x03,
+ EVENT_TRANSFER_RX_FAILED = 0x04,
+ EVENT_TRANSFER_TX_COMPLETED = 0x05,
+ EVENT_TRANSFER_TX_FAILED = 0x06,
+ EVENT_CHANNEL_CLOSED = 0x07,
+ EVENT_RX_FAIL_GO_TO_SEARCH = 0x08,
+ EVENT_CHANNEL_COLLISION = 0x09,
+ EVENT_TRANSFER_TX_START = 0x0a,
+ CHANNEL_IN_WRONG_STATE = 0x15,
+ CHANNEL_NOT_OPENED = 0x16,
+ CHANNEL_ID_NOT_SET = 0x18,
+ CLOSE_ALL_CHANNELS = 0x19,
+ TRANSFER_IN_PROGRESS = 0x1f,
+ TRANSFER_SEQUENCE_NUMBER_ERROR = 0x20,
+ TRANSFER_IN_ERROR = 0x21,
+ MESSAGE_SIZE_EXCEEDS_LIMIT = 0x27,
+ INVALID_MESSAGE = 0x28,
+ INVALID_NETWORK_NUMBER = 0x29,
+ INVALID_LIST_ID = 0x30,
+ INVALID_SCAN_TX_CHANNEL = 0x31,
+ INVALID_PARAMETER_PROVIDED = 0x33,
+ EVENT_QUEUE_OVERFLOW = 0x35,
+ USB_STRING_WRITE_FAIL = 0x70,
+ CHANNEL_STATE_UNASSIGNED = 0x00,
+ CHANNEL_STATE_ASSIGNED = 0x01,
+ CHANNEL_STATE_SEARCHING = 0x02,
+ CHANNEL_STATE_TRACKING = 0x03,
+ CAPABILITIES_NO_RECEIVE_CHANNELS = 0x01,
+ CAPABILITIES_NO_TRANSMIT_CHANNELS = 0x02,
+ CAPABILITIES_NO_RECEIVE_MESSAGES = 0x04,
+ CAPABILITIES_NO_TRANSMIT_MESSAGES = 0x08,
+ CAPABILITIES_NO_ACKNOWLEDGED_MESSAGES = 0x10,
+ CAPABILITIES_NO_BURST_MESSAGES = 0x20,
+ CAPABILITIES_NETWORK_ENABLED = 0x02,
+ CAPABILITIES_SERIAL_NUMBER_ENABLED = 0x08,
+ CAPABILITIES_PER_CHANNEL_TX_POWER_ENABLED = 0x10,
+ CAPABILITIES_LOW_PRIORITY_SEARCH_ENABLED = 0x20,
+ CAPABILITIES_SCRIPT_ENABLED = 0x40,
+ CAPABILITIES_SEARCH_LIST_ENABLED = 0x80,
+ CAPABILITIES_LED_ENABLED = 0x01,
+ CAPABILITIES_EXT_MESSAGE_ENABLED = 0x02,
+ CAPABILITIES_SCAN_MODE_ENABLED = 0x04,
+ CAPABILITIES_PROX_SEARCH_ENABLED = 0x10,
+ CAPABILITIES_EXT_ASSIGN_ENABLED = 0x20,
+ CAPABILITIES_FS_ANTFS_ENABLED = 0x40,
+ TIMEOUT_NEVER = 0xff,
+}
diff --git a/src/GarminStick2.ts b/src/GarminStick2.ts
new file mode 100644
index 0000000..efbbf5d
--- /dev/null
+++ b/src/GarminStick2.ts
@@ -0,0 +1,7 @@
+import { USBDriver } from './USBDriver';
+
+export class GarminStick2 extends USBDriver {
+ constructor() {
+ super(0x0fcf, 0x1008);
+ }
+}
diff --git a/src/GarminStick3.ts b/src/GarminStick3.ts
new file mode 100644
index 0000000..4b06a6e
--- /dev/null
+++ b/src/GarminStick3.ts
@@ -0,0 +1,7 @@
+import { USBDriver } from './USBDriver';
+
+export class GarminStick3 extends USBDriver {
+ constructor() {
+ super(0x0fcf, 0x1009);
+ }
+}
diff --git a/src/ICancellationToken.ts b/src/ICancellationToken.ts
new file mode 100644
index 0000000..6259308
--- /dev/null
+++ b/src/ICancellationToken.ts
@@ -0,0 +1,3 @@
+export interface ICancellationToken {
+ cancel(): void;
+}
diff --git a/src/Messages.ts b/src/Messages.ts
new file mode 100644
index 0000000..8df4ca4
--- /dev/null
+++ b/src/Messages.ts
@@ -0,0 +1,195 @@
+import { Constants } from './Constants';
+
+export class Messages {
+ static BUFFER_INDEX_MSG_LEN = 1;
+
+ static BUFFER_INDEX_MSG_TYPE = 2;
+
+ static BUFFER_INDEX_CHANNEL_NUM = 3;
+
+ static BUFFER_INDEX_MSG_DATA = 4;
+
+ static BUFFER_INDEX_EXT_MSG_BEGIN = 12;
+
+ static resetSystem(): DataView {
+ const payload: number[] = [];
+ payload.push(0x00);
+ return this.buildMessage(payload, Constants.MESSAGE_SYSTEM_RESET);
+ }
+
+ static requestMessage(channel: number, messageID: number): DataView {
+ let payload: number[] = [];
+ payload = payload.concat(this.intToLEHexArray(channel));
+ payload.push(messageID);
+ return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_REQUEST);
+ }
+
+ static setNetworkKey(): DataView {
+ const payload: number[] = [];
+ payload.push(Constants.DEFAULT_NETWORK_NUMBER);
+ payload.push(0xb9);
+ payload.push(0xa5);
+ payload.push(0x21);
+ payload.push(0xfb);
+ payload.push(0xbd);
+ payload.push(0x72);
+ payload.push(0xc3);
+ payload.push(0x45);
+ return this.buildMessage(payload, Constants.MESSAGE_NETWORK_KEY);
+ }
+
+ static assignChannel(channel: number, type = 'receive'): DataView {
+ let payload: number[] = [];
+ payload = payload.concat(this.intToLEHexArray(channel));
+ if (type === 'receive') {
+ payload.push(Constants.CHANNEL_TYPE_TWOWAY_RECEIVE);
+ } else if (type === 'receive_only') {
+ payload.push(Constants.CHANNEL_TYPE_ONEWAY_RECEIVE);
+ } else if (type === 'receive_shared') {
+ payload.push(Constants.CHANNEL_TYPE_SHARED_RECEIVE);
+ } else if (type === 'transmit') {
+ payload.push(Constants.CHANNEL_TYPE_TWOWAY_TRANSMIT);
+ } else if (type === 'transmit_only') {
+ payload.push(Constants.CHANNEL_TYPE_ONEWAY_TRANSMIT);
+ } else if (type === 'transmit_shared') {
+ payload.push(Constants.CHANNEL_TYPE_SHARED_TRANSMIT);
+ } else {
+ throw 'type not allowed';
+ }
+ payload.push(Constants.DEFAULT_NETWORK_NUMBER);
+ return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_ASSIGN);
+ }
+
+ static setDevice(
+ channel: number,
+ deviceID: number,
+ deviceType: number,
+ transmissionType: number
+ ): DataView {
+ let payload: number[] = [];
+ payload = payload.concat(this.intToLEHexArray(channel));
+ payload = payload.concat(this.intToLEHexArray(deviceID, 2));
+ payload = payload.concat(this.intToLEHexArray(deviceType));
+ payload = payload.concat(this.intToLEHexArray(transmissionType));
+ return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_ID);
+ }
+
+ static searchChannel(channel: number, timeout: number): DataView {
+ let payload: number[] = [];
+ payload = payload.concat(this.intToLEHexArray(channel));
+ payload = payload.concat(this.intToLEHexArray(timeout));
+ return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_SEARCH_TIMEOUT);
+ }
+
+ static setPeriod(channel: number, period: number): DataView {
+ let payload: number[] = [];
+ payload = payload.concat(this.intToLEHexArray(channel));
+ payload = payload.concat(this.intToLEHexArray(period));
+ return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_PERIOD);
+ }
+
+ static setFrequency(channel: number, frequency: number): DataView {
+ let payload: number[] = [];
+ payload = payload.concat(this.intToLEHexArray(channel));
+ payload = payload.concat(this.intToLEHexArray(frequency));
+ return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_FREQUENCY);
+ }
+
+ static setRxExt(): DataView {
+ let payload: number[] = [];
+ payload = payload.concat(this.intToLEHexArray(0));
+ payload = payload.concat(this.intToLEHexArray(1));
+ return this.buildMessage(payload, Constants.MESSAGE_ENABLE_RX_EXT);
+ }
+
+ static libConfig(channel: number, how: number): DataView {
+ let payload: number[] = [];
+ payload = payload.concat(this.intToLEHexArray(channel));
+ payload = payload.concat(this.intToLEHexArray(how));
+ return this.buildMessage(payload, Constants.MESSAGE_LIB_CONFIG);
+ }
+
+ static openRxScan(): DataView {
+ let payload: number[] = [];
+ payload = payload.concat(this.intToLEHexArray(0));
+ payload = payload.concat(this.intToLEHexArray(1));
+ return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_OPEN_RX_SCAN);
+ }
+
+ static openChannel(channel: number): DataView {
+ let payload: number[] = [];
+ payload = payload.concat(this.intToLEHexArray(channel));
+ return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_OPEN);
+ }
+
+ static closeChannel(channel: number): DataView {
+ let payload: number[] = [];
+ payload = payload.concat(this.intToLEHexArray(channel));
+ return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_CLOSE);
+ }
+
+ static unassignChannel(channel: number): DataView {
+ let payload: number[] = [];
+ payload = payload.concat(this.intToLEHexArray(channel));
+ return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_UNASSIGN);
+ }
+
+ static acknowledgedData(channel: number, payload: number[]): DataView {
+ payload = this.intToLEHexArray(channel).concat(payload);
+ return this.buildMessage(
+ payload,
+ Constants.MESSAGE_CHANNEL_ACKNOWLEDGED_DATA
+ );
+ }
+
+ static broadcastData(channel: number, payload: number[]): DataView {
+ payload = this.intToLEHexArray(channel).concat(payload);
+ return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_BROADCAST_DATA);
+ }
+
+ static buildMessage(payload: number[] = [], msgID = 0x00): DataView {
+ const m: number[] = [];
+ m.push(Constants.MESSAGE_TX_SYNC);
+ m.push(payload.length);
+ m.push(msgID);
+ payload.forEach((byte) => {
+ m.push(byte);
+ });
+ m.push(this.getChecksum(m));
+ return new DataView(new Uint8Array(m).buffer);
+ }
+
+ static intToLEHexArray(int: number, numBytes = 1): number[] {
+ numBytes = numBytes || 1;
+ const ret: number[] = [];
+ const hexStr = this.decimalToHex(int, numBytes * 2);
+ const b = new DataView(
+ new Uint8Array(
+ hexStr.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16)) || []
+ ).buffer
+ );
+ let i = b.byteLength - 1;
+ while (i >= 0) {
+ ret.push(b.getUint8(i));
+ i--;
+ }
+ return ret;
+ }
+
+ static decimalToHex(d: number, numDigits: number): string {
+ let hex = Number(d).toString(16);
+ numDigits = numDigits || 2;
+ while (hex.length < numDigits) {
+ hex = `0${hex}`;
+ }
+ return hex;
+ }
+
+ static getChecksum(message: any[]): number {
+ let checksum = 0;
+ message.forEach((byte) => {
+ checksum = (checksum ^ byte) % 0xff;
+ });
+ return checksum;
+ }
+}
diff --git a/src/USBDriver.ts b/src/USBDriver.ts
new file mode 100644
index 0000000..c440cc9
--- /dev/null
+++ b/src/USBDriver.ts
@@ -0,0 +1,207 @@
+import { Constants } from './Constants';
+import { EventEmitter } from './lib/EventEmitter';
+import { Messages } from './Messages';
+import { BaseSensor } from './sensors/BaseSensor';
+
+export class USBDriver extends EventEmitter {
+ private static deviceInUse: USBDevice[] = [];
+ private attachedSensors: BaseSensor[] = [];
+ private device: USBDevice | undefined;
+ private inEndpoint: USBEndpoint | undefined;
+ private interface: USBInterface | undefined;
+ private leftover: DataView | undefined;
+ private outEndpoint: USBEndpoint | undefined;
+ private usedChannels: number = 0;
+
+ maxChannels: number = 0;
+ canScan: boolean = false;
+
+ constructor(private vendorId: number, private productId: number) {
+ super();
+ this.setMaxListeners(50);
+ }
+
+ private async getDevice() {
+ const device = await navigator.usb.requestDevice({
+ filters: [{ vendorId: this.vendorId, productId: this.productId }],
+ });
+ return device;
+ }
+
+ public async is_present(): Promise {
+ const device = await this.getDevice();
+ return device !== undefined;
+ }
+
+ public async open(): Promise {
+ this.reset();
+ this.device = await this.getDevice();
+ try {
+ if (this.device === undefined) {
+ throw new Error('No device found');
+ }
+ await this.device.open();
+ if (this.device.configuration?.interfaces[0] === undefined) {
+ throw new Error('No interface found');
+ }
+ this.interface = this.device.configuration?.interfaces[0];
+ await this.device.claimInterface(this.interface.interfaceNumber);
+ } catch (err) {
+ console.error(err);
+ this.reset();
+ }
+ if (!this.device) {
+ return;
+ }
+ USBDriver.deviceInUse.push(this.device);
+
+ this.inEndpoint = this.interface?.alternate.endpoints.find(
+ (e) => e.direction === 'in'
+ );
+ this.outEndpoint = this.interface?.alternate.endpoints.find(
+ (e) => e.direction === 'out'
+ );
+
+ if (!this.inEndpoint || !this.outEndpoint) {
+ throw new Error('No endpoints found');
+ }
+
+ const readInEndPoint = async () => {
+ if (this.inEndpoint === undefined || this.device === undefined) {
+ return;
+ }
+
+ try {
+ const result = await this.device.transferIn(
+ this.inEndpoint.endpointNumber,
+ this.inEndpoint.packetSize
+ );
+ if (!result.data) {
+ return;
+ }
+ let data = result.data;
+ if (this.leftover) {
+ const tmp = new Uint8Array(
+ this.leftover.byteLength + data.byteLength
+ );
+ tmp.set(new Uint8Array(this.leftover.buffer), 0);
+ tmp.set(new Uint8Array(data.buffer), this.leftover.byteLength);
+ data = new DataView(tmp.buffer);
+ this.leftover = undefined;
+ }
+ if (data.getUint8(0) !== 0xa4) {
+ throw 'SYNC missing';
+ }
+ if (result.status === 'ok') {
+ const len = data.byteLength;
+ let beginBlock = 0;
+ while (beginBlock < len) {
+ if (beginBlock + 1 === len) {
+ this.leftover = new DataView(data.buffer.slice(beginBlock));
+ break;
+ }
+ const blockLen = data.getUint8(beginBlock + 1);
+ const endBlock = beginBlock + blockLen + 4;
+ if (endBlock > len) {
+ this.leftover = new DataView(data.buffer.slice(beginBlock));
+ break;
+ }
+ const readData = new DataView(
+ data.buffer.slice(beginBlock, endBlock)
+ );
+ this.read(readData);
+ beginBlock = endBlock;
+ }
+ }
+ readInEndPoint();
+ } catch (error) {
+ console.error(error);
+ }
+ };
+
+ readInEndPoint();
+ await this.reset();
+ return this.device;
+ }
+
+ public async reset() {
+ await this.detach_all();
+ this.maxChannels = 0;
+ this.usedChannels = 0;
+ await this.write(Messages.resetSystem());
+ }
+
+ public isScanning(): boolean {
+ return this.usedChannels === -1;
+ }
+
+ public attach(sensor: BaseSensor, forScan: boolean): boolean {
+ if (this.usedChannels < 0) {
+ return false;
+ }
+ if (forScan) {
+ if (this.usedChannels !== 0) {
+ return false;
+ }
+ this.usedChannels = -1;
+ } else {
+ if (this.maxChannels <= this.usedChannels) {
+ return false;
+ }
+ ++this.usedChannels;
+ }
+ this.attachedSensors.push(sensor);
+ return true;
+ }
+
+ public detach(sensor: BaseSensor): boolean {
+ const idx = this.attachedSensors.indexOf(sensor);
+ if (idx < 0) {
+ return false;
+ }
+ if (this.usedChannels < 0) {
+ this.usedChannels = 0;
+ } else {
+ --this.usedChannels;
+ }
+ this.attachedSensors.splice(idx, 1);
+ return true;
+ }
+
+ public detach_all(): Promise {
+ const copy = this.attachedSensors;
+ return Promise.all(copy.map((s) => s.detach()));
+ }
+
+ public async write(data: DataView) {
+ try {
+ if (this.outEndpoint === undefined) {
+ throw new Error('No out endpoint');
+ }
+
+ await this.device?.transferOut(this.outEndpoint?.endpointNumber, data);
+ } catch (error) {
+ console.error(error);
+ }
+ }
+
+ public async read(data: DataView) {
+ const messageID = data.getUint8(2);
+ if (messageID === Constants.MESSAGE_STARTUP) {
+ await this.write(
+ Messages.requestMessage(0, Constants.MESSAGE_CAPABILITIES)
+ );
+ } else if (messageID === Constants.MESSAGE_CAPABILITIES) {
+ this.maxChannels = data.getUint8(3);
+ this.canScan = (data.getUint8(7) & 0x06) === 0x06;
+ await this.write(Messages.setNetworkKey());
+ } else if (
+ messageID === Constants.MESSAGE_CHANNEL_EVENT &&
+ data.getUint8(4) === Constants.MESSAGE_NETWORK_KEY
+ ) {
+ this.emit('startup', data);
+ } else {
+ this.emit('read', data);
+ }
+ }
+}
diff --git a/src/ant.ts b/src/ant.ts
index a063222..703847b 100644
--- a/src/ant.ts
+++ b/src/ant.ts
@@ -1,911 +1,11 @@
-import events = require('events');
-
-import usb = require('usb');
-
-export enum Constants {
- MESSAGE_RF = 0x01,
-
- MESSAGE_TX_SYNC = 0xA4,
- DEFAULT_NETWORK_NUMBER = 0x00,
-
- // Configuration messages
- MESSAGE_CHANNEL_UNASSIGN = 0x41,
- MESSAGE_CHANNEL_ASSIGN = 0x42,
- MESSAGE_CHANNEL_ID = 0x51,
- MESSAGE_CHANNEL_PERIOD = 0x43,
- MESSAGE_CHANNEL_SEARCH_TIMEOUT = 0x44,
- MESSAGE_CHANNEL_FREQUENCY = 0x45,
- MESSAGE_CHANNEL_TX_POWER = 0x60,
- MESSAGE_NETWORK_KEY = 0x46,
- MESSAGE_TX_POWER = 0x47,
- MESSAGE_PROXIMITY_SEARCH = 0x71,
- MESSAGE_ENABLE_RX_EXT = 0x66,
- MESSAGE_LIB_CONFIG = 0x6E,
- MESSAGE_CHANNEL_OPEN_RX_SCAN = 0x5B,
-
- // Notification messages
- MESSAGE_STARTUP = 0x6F,
-
- // Control messages
- MESSAGE_SYSTEM_RESET = 0x4A,
- MESSAGE_CHANNEL_OPEN = 0x4B,
- MESSAGE_CHANNEL_CLOSE = 0x4C,
- MESSAGE_CHANNEL_REQUEST = 0x4D,
-
- // Data messages
- MESSAGE_CHANNEL_BROADCAST_DATA = 0x4E,
- MESSAGE_CHANNEL_ACKNOWLEDGED_DATA = 0x4F,
- MESSAGE_CHANNEL_BURST_DATA = 0x50,
-
- // Channel event messages
- MESSAGE_CHANNEL_EVENT = 0x40,
-
- // Requested response messages
- MESSAGE_CHANNEL_STATUS = 0x52,
- //MESSAGE_CHANNEL_ID = 0x51,
- MESSAGE_VERSION = 0x3E,
- MESSAGE_CAPABILITIES = 0x54,
- MESSAGE_SERIAL_NUMBER = 0x61,
-
- // Message parameters
- CHANNEL_TYPE_TWOWAY_RECEIVE = 0x00,
- CHANNEL_TYPE_TWOWAY_TRANSMIT = 0x10,
- CHANNEL_TYPE_SHARED_RECEIVE = 0x20,
- CHANNEL_TYPE_SHARED_TRANSMIT = 0x30,
- CHANNEL_TYPE_ONEWAY_RECEIVE = 0x40,
- CHANNEL_TYPE_ONEWAY_TRANSMIT = 0x50,
- RADIO_TX_POWER_MINUS20DB = 0x00,
- RADIO_TX_POWER_MINUS10DB = 0x01,
- RADIO_TX_POWER_0DB = 0x02,
- RADIO_TX_POWER_PLUS4DB = 0x03,
- RESPONSE_NO_ERROR = 0x00,
- EVENT_RX_SEARCH_TIMEOUT = 0x01,
- EVENT_RX_FAIL = 0x02,
- EVENT_TX = 0x03,
- EVENT_TRANSFER_RX_FAILED = 0x04,
- EVENT_TRANSFER_TX_COMPLETED = 0x05,
- EVENT_TRANSFER_TX_FAILED = 0x06,
- EVENT_CHANNEL_CLOSED = 0x07,
- EVENT_RX_FAIL_GO_TO_SEARCH = 0x08,
- EVENT_CHANNEL_COLLISION = 0x09,
- EVENT_TRANSFER_TX_START = 0x0A,
- CHANNEL_IN_WRONG_STATE = 0x15,
- CHANNEL_NOT_OPENED = 0x16,
- CHANNEL_ID_NOT_SET = 0x18,
- CLOSE_ALL_CHANNELS = 0x19,
- TRANSFER_IN_PROGRESS = 0x1F,
- TRANSFER_SEQUENCE_NUMBER_ERROR = 0x20,
- TRANSFER_IN_ERROR = 0x21,
- MESSAGE_SIZE_EXCEEDS_LIMIT = 0x27,
- INVALID_MESSAGE = 0x28,
- INVALID_NETWORK_NUMBER = 0x29,
- INVALID_LIST_ID = 0x30,
- INVALID_SCAN_TX_CHANNEL = 0x31,
- INVALID_PARAMETER_PROVIDED = 0x33,
- EVENT_QUEUE_OVERFLOW = 0x35,
- USB_STRING_WRITE_FAIL = 0x70,
- CHANNEL_STATE_UNASSIGNED = 0x00,
- CHANNEL_STATE_ASSIGNED = 0x01,
- CHANNEL_STATE_SEARCHING = 0x02,
- CHANNEL_STATE_TRACKING = 0x03,
- CAPABILITIES_NO_RECEIVE_CHANNELS = 0x01,
- CAPABILITIES_NO_TRANSMIT_CHANNELS = 0x02,
- CAPABILITIES_NO_RECEIVE_MESSAGES = 0x04,
- CAPABILITIES_NO_TRANSMIT_MESSAGES = 0x08,
- CAPABILITIES_NO_ACKNOWLEDGED_MESSAGES = 0x10,
- CAPABILITIES_NO_BURST_MESSAGES = 0x20,
- CAPABILITIES_NETWORK_ENABLED = 0x02,
- CAPABILITIES_SERIAL_NUMBER_ENABLED = 0x08,
- CAPABILITIES_PER_CHANNEL_TX_POWER_ENABLED = 0x10,
- CAPABILITIES_LOW_PRIORITY_SEARCH_ENABLED = 0x20,
- CAPABILITIES_SCRIPT_ENABLED = 0x40,
- CAPABILITIES_SEARCH_LIST_ENABLED = 0x80,
- CAPABILITIES_LED_ENABLED = 0x01,
- CAPABILITIES_EXT_MESSAGE_ENABLED = 0x02,
- CAPABILITIES_SCAN_MODE_ENABLED = 0x04,
- CAPABILITIES_PROX_SEARCH_ENABLED = 0x10,
- CAPABILITIES_EXT_ASSIGN_ENABLED = 0x20,
- CAPABILITIES_FS_ANTFS_ENABLED = 0x40,
- TIMEOUT_NEVER = 0xFF,
-}
-
-export class Messages {
- static BUFFER_INDEX_MSG_LEN: number = 1;
- static BUFFER_INDEX_MSG_TYPE: number = 2;
- static BUFFER_INDEX_CHANNEL_NUM: number = 3;
- static BUFFER_INDEX_MSG_DATA: number = 4;
- static BUFFER_INDEX_EXT_MSG_BEGIN: number = 12;
-
- static resetSystem(): Buffer {
- const payload: number[] = [];
- payload.push(0x00);
- return this.buildMessage(payload, Constants.MESSAGE_SYSTEM_RESET);
- }
-
- static requestMessage(channel: number, messageID: number): Buffer {
- let payload: number[] = [];
- payload = payload.concat(this.intToLEHexArray(channel));
- payload.push(messageID);
- return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_REQUEST);
- }
-
- static setNetworkKey(): Buffer {
- const payload: number[] = [];
- payload.push(Constants.DEFAULT_NETWORK_NUMBER);
- payload.push(0xB9);
- payload.push(0xA5);
- payload.push(0x21);
- payload.push(0xFB);
- payload.push(0xBD);
- payload.push(0x72);
- payload.push(0xC3);
- payload.push(0x45);
- return this.buildMessage(payload, Constants.MESSAGE_NETWORK_KEY);
- }
-
- static assignChannel(channel: number, type = 'receive'): Buffer {
- let payload: number[] = [];
- payload = payload.concat(this.intToLEHexArray(channel));
- if (type === 'receive') {
- payload.push(Constants.CHANNEL_TYPE_TWOWAY_RECEIVE);
- } else if (type === 'receive_only') {
- payload.push(Constants.CHANNEL_TYPE_ONEWAY_RECEIVE);
- } else if (type === 'receive_shared') {
- payload.push(Constants.CHANNEL_TYPE_SHARED_RECEIVE);
- } else if (type === 'transmit') {
- payload.push(Constants.CHANNEL_TYPE_TWOWAY_TRANSMIT);
- } else if (type === 'transmit_only') {
- payload.push(Constants.CHANNEL_TYPE_ONEWAY_TRANSMIT);
- } else if (type === 'transmit_shared') {
- payload.push(Constants.CHANNEL_TYPE_SHARED_TRANSMIT);
- } else {
- throw 'type not allowed';
- }
- payload.push(Constants.DEFAULT_NETWORK_NUMBER);
- return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_ASSIGN);
- }
-
- static setDevice(channel: number, deviceID: number, deviceType: number, transmissionType: number): Buffer {
- let payload: number[] = [];
- payload = payload.concat(this.intToLEHexArray(channel));
- payload = payload.concat(this.intToLEHexArray(deviceID, 2));
- payload = payload.concat(this.intToLEHexArray(deviceType));
- payload = payload.concat(this.intToLEHexArray(transmissionType));
- return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_ID);
- }
-
- static searchChannel(channel: number, timeout: number): Buffer {
- let payload: number[] = [];
- payload = payload.concat(this.intToLEHexArray(channel));
- payload = payload.concat(this.intToLEHexArray(timeout));
- return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_SEARCH_TIMEOUT);
- }
-
- static setPeriod(channel: number, period: number): Buffer {
- let payload: number[] = [];
- payload = payload.concat(this.intToLEHexArray(channel));
- payload = payload.concat(this.intToLEHexArray(period));
- return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_PERIOD);
- }
-
- static setFrequency(channel: number, frequency: number): Buffer {
- let payload: number[] = [];
- payload = payload.concat(this.intToLEHexArray(channel));
- payload = payload.concat(this.intToLEHexArray(frequency));
- return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_FREQUENCY);
- }
-
- static setRxExt(): Buffer {
- let payload: number[] = [];
- payload = payload.concat(this.intToLEHexArray(0));
- payload = payload.concat(this.intToLEHexArray(1));
- return this.buildMessage(payload, Constants.MESSAGE_ENABLE_RX_EXT);
- }
-
- static libConfig(channel: number, how: number): Buffer {
- let payload: number[] = [];
- payload = payload.concat(this.intToLEHexArray(channel));
- payload = payload.concat(this.intToLEHexArray(how));
- return this.buildMessage(payload, Constants.MESSAGE_LIB_CONFIG);
- }
-
- static openRxScan(): Buffer {
- let payload: number[] = [];
- payload = payload.concat(this.intToLEHexArray(0));
- payload = payload.concat(this.intToLEHexArray(1));
- return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_OPEN_RX_SCAN);
- }
-
- static openChannel(channel: number): Buffer {
- let payload: number[] = [];
- payload = payload.concat(this.intToLEHexArray(channel));
- return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_OPEN);
- }
-
- static closeChannel(channel: number): Buffer {
- let payload: number[] = [];
- payload = payload.concat(this.intToLEHexArray(channel));
- return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_CLOSE);
- }
-
- static unassignChannel(channel: number): Buffer {
- let payload: number[] = [];
- payload = payload.concat(this.intToLEHexArray(channel));
- return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_UNASSIGN);
- }
-
- static acknowledgedData(channel: number, payload: number[]): Buffer {
- payload = this.intToLEHexArray(channel).concat(payload);
- return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_ACKNOWLEDGED_DATA);
- }
-
- static broadcastData(channel: number, payload: number[]): Buffer {
- payload = this.intToLEHexArray(channel).concat(payload);
- return this.buildMessage(payload, Constants.MESSAGE_CHANNEL_BROADCAST_DATA);
- }
-
- static buildMessage(payload: number[] = [], msgID = 0x00): Buffer {
- const m: number[] = [];
- m.push(Constants.MESSAGE_TX_SYNC);
- m.push(payload.length);
- m.push(msgID);
- payload.forEach((byte) => {
- m.push(byte);
- });
- m.push(this.getChecksum(m));
- return Buffer.from(m);
- }
-
- static intToLEHexArray(int: number, numBytes = 1): number[] {
- numBytes = numBytes || 1;
- const a: number[] = [];
- const b = Buffer.from(this.decimalToHex(int, numBytes * 2), 'hex');
- let i = b.length - 1;
- while (i >= 0) {
- a.push(b[i]);
- i--;
- }
- return a;
- }
-
- static decimalToHex(d: number, numDigits: number): string {
- let hex = Number(d).toString(16);
- numDigits = numDigits || 2;
- while (hex.length < numDigits) {
- hex = '0' + hex;
- }
- // console.log(hex);
- return hex;
- }
-
- static getChecksum(message: any[]): number {
- let checksum = 0;
- message.forEach((byte) => {
- checksum = (checksum ^ byte) % 0xFF;
- });
- return checksum;
- }
-}
-
-export interface ICancellationToken {
- cancel(): void;
-}
-
-class CancellationTokenListener {
- _completed = false;
- constructor(private fn: (d: any) => void, private cb: (err: Error) => void) { }
- cancel() {
- if (!this._completed) {
- this._completed = true;
- // @ts-ignore
- usb.removeListener('attach', this.fn);
- this.cb(new Error('Canceled'));
- }
- }
-}
-
-export class USBDriver extends events.EventEmitter {
- private static deviceInUse: usb.Device[] = [];
- private device: usb.Device;
- private iface: usb.Interface;
- private detachedKernelDriver = false;
- private inEp: usb.InEndpoint & events.EventEmitter;
- private outEp: usb.OutEndpoint & events.EventEmitter;
- private leftover: Buffer;
- private usedChannels: number = 0;
- private attachedSensors: BaseSensor[] = [];
-
- maxChannels: number = 0;
- canScan: boolean = false;
-
- constructor(private idVendor: number, private idProduct: number, dbgLevel = 0) {
- super();
- this.setMaxListeners(50);
- usb.setDebugLevel(dbgLevel);
- }
-
- private getDevices() {
- const allDevices = usb.getDeviceList();
- return allDevices
- .filter((d) => d.deviceDescriptor.idVendor === this.idVendor && d.deviceDescriptor.idProduct === this.idProduct)
- .filter(d => USBDriver.deviceInUse.indexOf(d) === -1);
- }
-
- public is_present(): boolean {
- return this.getDevices().length > 0;
- }
-
- public open(): boolean {
- const devices = this.getDevices();
- while (devices.length) {
- try {
- this.device = devices.shift();
- this.device.open();
- this.iface = this.device.interfaces[0];
- try {
- if (this.iface.isKernelDriverActive()) {
- this.detachedKernelDriver = true;
- this.iface.detachKernelDriver();
- }
- } catch {
- // Ignore kernel driver errors;
- }
- this.iface.claim();
- break;
- } catch {
- // Ignore the error and try with the next device, if present
- this.device.close();
- this.device = undefined;
- this.iface = undefined;
- }
- }
- if (!this.device) {
- return false;
- }
- USBDriver.deviceInUse.push(this.device);
-
- this.inEp = this.iface.endpoints[0] as usb.InEndpoint;
-
- this.inEp.on('data', (data: Buffer) => {
- if (!data.length) {
- return;
- }
-
- if (this.leftover) {
- data = Buffer.concat([this.leftover, data]);
- this.leftover = undefined;
- }
-
- if (data.readUInt8(0) !== 0xA4) {
- throw 'SYNC missing';
- }
-
- const len = data.length;
- let beginBlock = 0;
- while (beginBlock < len) {
- if (beginBlock + 1 === len) {
- this.leftover = data.slice(beginBlock);
- break;
- }
- const blockLen = data.readUInt8(beginBlock + 1);
- const endBlock = beginBlock + blockLen + 4;
- if (endBlock > len) {
- this.leftover = data.slice(beginBlock);
- break;
- }
- const readData = data.slice(beginBlock, endBlock);
- this.read(readData);
- beginBlock = endBlock;
- }
- });
-
- this.inEp.on('error', (err: any) => {
- //console.log('ERROR RECV: ', err);
- });
-
- this.inEp.on('end', () => {
- //console.log('STOP RECV');
- });
-
- this.inEp.startPoll();
-
- this.outEp = this.iface.endpoints[1] as usb.OutEndpoint;
-
- this.reset();
-
- return true;
- }
-
- public openAsync(cb: (err: Error) => void): ICancellationToken {
- let ct: CancellationTokenListener;
- const doOpen = () => {
- try {
- const result = this.open();
- if (result) {
- ct._completed = true;
- try {
- cb(undefined);
- } catch {
- // ignore errors
- }
- } else {
- return false;
- }
- } catch (err) {
- cb(err);
- }
- return true;
- };
- const fn = (d) => {
- if (!d || (d.deviceDescriptor.idVendor === this.idVendor && d.deviceDescriptor.idProduct === this.idProduct)) {
- if (doOpen()) {
- // @ts-ignore
- usb.removeListener('attach', fn);
- }
- }
- };
- usb.on('attach', fn);
- if (this.is_present()) {
- // @ts-ignore
- setImmediate(() => usb.emit('attach', this.device));
- }
- return ct = new CancellationTokenListener(fn, cb);
- }
-
- public close() {
- this.detach_all();
- this.inEp.stopPoll(() => {
- // @ts-ignore
- this.iface.release(true, () => {
- if (this.detachedKernelDriver) {
- this.detachedKernelDriver = false;
- try {
- this.iface.attachKernelDriver();
- } catch {
- // Ignore kernel driver errors;
- }
- }
- this.iface = undefined;
- this.device.reset(() => {
- this.device.close();
- this.emit('shutdown');
- const devIdx = USBDriver.deviceInUse.indexOf(this.device);
- if (devIdx >= 0) {
- USBDriver.deviceInUse.splice(devIdx, 1);
- }
- // @ts-ignore
- if (usb.listenerCount('attach')) {
- // @ts-ignore
- usb.emit('attach', this.device);
- }
- this.device = undefined;
- });
- });
- });
- }
-
- public reset() {
- this.detach_all();
- this.maxChannels = 0;
- this.usedChannels = 0;
- this.write(Messages.resetSystem());
- }
-
- public isScanning(): boolean {
- return this.usedChannels === -1;
- }
-
- public attach(sensor: BaseSensor, forScan: boolean): boolean {
- if (this.usedChannels < 0) {
- return false;
- }
- if (forScan) {
- if (this.usedChannels !== 0) {
- return false;
- }
- this.usedChannels = -1;
- } else {
- if (this.maxChannels <= this.usedChannels) {
- return false;
- }
- ++this.usedChannels;
- }
- this.attachedSensors.push(sensor);
- return true;
- }
-
- public detach(sensor: BaseSensor): boolean {
- const idx = this.attachedSensors.indexOf(sensor);
- if (idx < 0) {
- return false;
- }
- if (this.usedChannels < 0) {
- this.usedChannels = 0;
- } else {
- --this.usedChannels;
- }
- this.attachedSensors.splice(idx, 1);
- return true;
- }
-
- public detach_all() {
- const copy = this.attachedSensors;
- copy.forEach((sensor: BaseSensor) => sensor.detach());
- }
-
- public write(data: Buffer) {
- //console.log('DATA SEND: ', data);
- this.outEp.transfer(data, (error) => {
- if (error) {
- //console.log('ERROR SEND: ', error);
- }
- });
- }
-
- public read(data: Buffer) {
- //console.log('DATA RECV: ', data);
- const messageID = data.readUInt8(2);
- if (messageID === Constants.MESSAGE_STARTUP) {
- this.write(Messages.requestMessage(0, Constants.MESSAGE_CAPABILITIES));
- } else if (messageID === Constants.MESSAGE_CAPABILITIES) {
- this.maxChannels = data.readUInt8(3);
- this.canScan = (data.readUInt8(7) & 0x06) === 0x06;
- this.write(Messages.setNetworkKey());
- } else if (messageID === Constants.MESSAGE_CHANNEL_EVENT && data.readUInt8(4) === Constants.MESSAGE_NETWORK_KEY) {
- this.emit('startup', data);
- } else {
- this.emit('read', data);
- }
- }
-}
-
-export class GarminStick2 extends USBDriver {
- constructor(dbgLevel = 0) {
- super(0x0fcf, 0x1008, dbgLevel);
- }
-}
-
-export class GarminStick3 extends USBDriver {
- constructor(dbgLevel = 0) {
- super(0x0fcf, 0x1009, dbgLevel);
- }
-}
-
export type SendCallback = (result: boolean) => void;
-
-export abstract class BaseSensor extends events.EventEmitter {
- channel: number;
- deviceID: number;
- transmissionType: number;
-
- private msgQueue: { msg: Buffer, cbk?: SendCallback }[] = [];
-
- protected decodeDataCbk: (data: Buffer) => void;
- protected statusCbk: (status: { msg: number, code: number }) => boolean;
-
- protected abstract updateState(deviceId: number, data: Buffer): void;
-
- constructor(private stick: USBDriver) {
- super();
- stick.on('read', this.handleEventMessages.bind(this));
- }
-
- protected scan(type: string, frequency: number) {
- if (this.channel !== undefined) {
- throw 'already attached';
- }
-
- if (!this.stick.canScan) {
- throw 'stick cannot scan';
- }
-
- const channel = 0;
-
- const onStatus = (status) => {
- switch (status.msg) {
- case Constants.MESSAGE_RF:
- switch (status.code) {
- case Constants.EVENT_CHANNEL_CLOSED:
- case Constants.EVENT_RX_FAIL_GO_TO_SEARCH:
- this.write(Messages.unassignChannel(channel));
- return true;
- case Constants.EVENT_TRANSFER_TX_COMPLETED:
- case Constants.EVENT_TRANSFER_TX_FAILED:
- case Constants.EVENT_RX_FAIL:
- case Constants.INVALID_SCAN_TX_CHANNEL:
- const mc = this.msgQueue.shift();
- if (mc && mc.cbk) {
- mc.cbk(status.code === Constants.EVENT_TRANSFER_TX_COMPLETED);
- }
- if (this.msgQueue.length) {
- this.write(this.msgQueue[0].msg);
- }
- return true;
- default:
- break;
- }
- break;
- case Constants.MESSAGE_CHANNEL_ASSIGN:
- this.write(Messages.setDevice(channel, 0, 0, 0));
- return true;
- case Constants.MESSAGE_CHANNEL_ID:
- this.write(Messages.setFrequency(channel, frequency));
- return true;
- case Constants.MESSAGE_CHANNEL_FREQUENCY:
- this.write(Messages.setRxExt());
- return true;
- case Constants.MESSAGE_ENABLE_RX_EXT:
- this.write(Messages.libConfig(channel, 0xE0));
- return true;
- case Constants.MESSAGE_LIB_CONFIG:
- this.write(Messages.openRxScan());
- return true;
- case Constants.MESSAGE_CHANNEL_OPEN_RX_SCAN:
- process.nextTick(() => this.emit('attached'));
- return true;
- case Constants.MESSAGE_CHANNEL_CLOSE:
- return true;
- case Constants.MESSAGE_CHANNEL_UNASSIGN:
- this.statusCbk = undefined;
- this.channel = undefined;
- process.nextTick(() => this.emit('detached'));
- return true;
- case Constants.MESSAGE_CHANNEL_ACKNOWLEDGED_DATA:
- return (status.code === Constants.TRANSFER_IN_PROGRESS);
- default:
- break;
- }
- return false;
- };
-
- if (this.stick.isScanning()) {
- this.channel = channel;
- this.deviceID = 0;
- this.transmissionType = 0;
-
- this.statusCbk = onStatus;
-
- process.nextTick(() => this.emit('attached'));
- } else if (this.stick.attach(this, true)) {
- this.channel = channel;
- this.deviceID = 0;
- this.transmissionType = 0;
-
- this.statusCbk = onStatus;
-
- this.write(Messages.assignChannel(channel, type));
- } else {
- throw 'cannot attach';
- }
- }
-
- protected attach(channel: number, type: string, deviceID: number, deviceType: number, transmissionType: number,
- timeout: number, period: number, frequency: number) {
- if (this.channel !== undefined) {
- throw 'already attached';
- }
- if (!this.stick.attach(this, false)) {
- throw 'cannot attach';
- }
- this.channel = channel;
- this.deviceID = deviceID;
- this.transmissionType = transmissionType;
-
- const onStatus = (status) => {
- switch (status.msg) {
- case Constants.MESSAGE_RF:
- switch (status.code) {
- case Constants.EVENT_CHANNEL_CLOSED:
- case Constants.EVENT_RX_FAIL_GO_TO_SEARCH:
- this.write(Messages.unassignChannel(channel));
- return true;
- case Constants.EVENT_TRANSFER_TX_COMPLETED:
- case Constants.EVENT_TRANSFER_TX_FAILED:
- case Constants.EVENT_RX_FAIL:
- case Constants.INVALID_SCAN_TX_CHANNEL:
- const mc = this.msgQueue.shift();
- if (mc && mc.cbk) {
- mc.cbk(status.code === Constants.EVENT_TRANSFER_TX_COMPLETED);
- }
- if (this.msgQueue.length) {
- this.write(this.msgQueue[0].msg);
- }
- return true;
- default:
- break;
- }
- break;
- case Constants.MESSAGE_CHANNEL_ASSIGN:
- this.write(Messages.setDevice(channel, deviceID, deviceType, transmissionType));
- return true;
- case Constants.MESSAGE_CHANNEL_ID:
- this.write(Messages.searchChannel(channel, timeout));
- return true;
- case Constants.MESSAGE_CHANNEL_SEARCH_TIMEOUT:
- this.write(Messages.setFrequency(channel, frequency));
- return true;
- case Constants.MESSAGE_CHANNEL_FREQUENCY:
- this.write(Messages.setPeriod(channel, period));
- return true;
- case Constants.MESSAGE_CHANNEL_PERIOD:
- this.write(Messages.libConfig(channel, 0xE0));
- return true;
- case Constants.MESSAGE_LIB_CONFIG:
- this.write(Messages.openChannel(channel));
- return true;
- case Constants.MESSAGE_CHANNEL_OPEN:
- process.nextTick(() => this.emit('attached'));
- return true;
- case Constants.MESSAGE_CHANNEL_CLOSE:
- return true;
- case Constants.MESSAGE_CHANNEL_UNASSIGN:
- this.statusCbk = undefined;
- this.channel = undefined;
- process.nextTick(() => this.emit('detached'));
- return true;
- case Constants.MESSAGE_CHANNEL_ACKNOWLEDGED_DATA:
- return (status.code === Constants.TRANSFER_IN_PROGRESS);
- default:
- break;
- }
- return false;
- };
-
- this.statusCbk = onStatus;
-
- this.write(Messages.assignChannel(channel, type));
- }
-
- public detach() {
- if (this.channel === undefined) {
- return;
- }
- this.write(Messages.closeChannel(this.channel));
- if (!this.stick.detach(this)) {
- throw 'error detaching';
- }
- }
-
- protected write(data: Buffer) {
- this.stick.write(data);
- }
-
- private handleEventMessages(data: Buffer) {
- const messageID = data.readUInt8(Messages.BUFFER_INDEX_MSG_TYPE);
- const channel = data.readUInt8(Messages.BUFFER_INDEX_CHANNEL_NUM);
-
- if (channel === this.channel) {
- if (messageID === Constants.MESSAGE_CHANNEL_EVENT) {
- const status = {
- msg: data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA),
- code: data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1),
- };
-
- const handled = this.statusCbk && this.statusCbk(status);
- if (!handled) {
- console.log('Unhandled event: ' + data.toString('hex'));
- this.emit('eventData', {
- message: data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA),
- code: data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1),
- });
- }
- } else if (this.decodeDataCbk) {
- this.decodeDataCbk(data);
- }
- }
- }
-
- protected send(data: Buffer, cbk?: SendCallback) {
- this.msgQueue.push({ msg: data, cbk });
- if (this.msgQueue.length === 1) {
- this.write(data);
- }
- }
-}
-
-export abstract class AntPlusBaseSensor extends BaseSensor {
-
- protected scan(type: string) {
- return super.scan(type, 57);
- }
-
- protected attach(channel: number, type: string, deviceID: number, deviceType: number, transmissionType: number,
- timeout: number, period: number) {
- return super.attach(channel, type, deviceID, deviceType, transmissionType, timeout, period, 57);
- }
-}
-
-export abstract class AntPlusSensor extends AntPlusBaseSensor {
-
- constructor(stick) {
- super(stick);
- this.decodeDataCbk = this.decodeData.bind(this);
- }
-
- protected scan() {
- throw 'scanning unsupported';
- }
-
- protected attach(channel: number, type: string, deviceID: number, deviceType: number, transmissionType: number,
- timeout: number, period: number) {
- return super.attach(channel, type, deviceID, deviceType, transmissionType, timeout, period);
- }
-
- private decodeData(data: Buffer) {
- switch (data.readUInt8(Messages.BUFFER_INDEX_MSG_TYPE)) {
- case Constants.MESSAGE_CHANNEL_BROADCAST_DATA:
- case Constants.MESSAGE_CHANNEL_ACKNOWLEDGED_DATA:
- case Constants.MESSAGE_CHANNEL_BURST_DATA:
- if (this.deviceID === 0) {
- this.write(Messages.requestMessage(this.channel, Constants.MESSAGE_CHANNEL_ID));
- }
- this.updateState(this.deviceID, data);
- break;
- case Constants.MESSAGE_CHANNEL_ID:
- this.deviceID = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA);
- this.transmissionType = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- break;
- default:
- break;
- }
- }
+export enum PageState {
+ INIT_PAGE,
+ STD_PAGE,
+ EXT_PAGE,
}
-export abstract class AntPlusScanner extends AntPlusBaseSensor {
-
- protected abstract deviceType(): number;
- protected abstract createStateIfNew(deviceId: number): void;
- protected abstract updateRssiAndThreshold(deviceId: number, rssi: number, threshold: number): void;
-
- constructor(stick) {
- super(stick);
- this.decodeDataCbk = this.decodeData.bind(this);
- }
-
- public scan() {
- return super.scan('receive');
- }
-
- protected attach() {
- throw 'attach unsupported';
- }
-
- protected send() {
- throw 'send unsupported';
- }
-
- private decodeData(data: Buffer) {
- if (data.length <= (Messages.BUFFER_INDEX_EXT_MSG_BEGIN + 3) || !(data.readUInt8(Messages.BUFFER_INDEX_EXT_MSG_BEGIN) & 0x80)) {
- console.log('wrong message format', data.toString('hex'));
- return;
- }
-
- const deviceId = data.readUInt16LE(Messages.BUFFER_INDEX_EXT_MSG_BEGIN + 1);
- const deviceType = data.readUInt8(Messages.BUFFER_INDEX_EXT_MSG_BEGIN + 3);
-
- if (deviceType !== this.deviceType()) {
- return;
- }
-
- this.createStateIfNew(deviceId);
-
- if (data.readUInt8(Messages.BUFFER_INDEX_EXT_MSG_BEGIN) & 0x40) {
- if (data.readUInt8(Messages.BUFFER_INDEX_EXT_MSG_BEGIN + 5) === 0x20) {
- this.updateRssiAndThreshold(
- deviceId,
- data.readInt8(Messages.BUFFER_INDEX_EXT_MSG_BEGIN + 6),
- data.readInt8(Messages.BUFFER_INDEX_EXT_MSG_BEGIN + 7));
- }
- }
-
- switch (data.readUInt8(Messages.BUFFER_INDEX_MSG_TYPE)) {
- case Constants.MESSAGE_CHANNEL_BROADCAST_DATA:
- case Constants.MESSAGE_CHANNEL_ACKNOWLEDGED_DATA:
- case Constants.MESSAGE_CHANNEL_BURST_DATA:
- this.updateState(deviceId, data);
- break;
- default:
- break;
- }
- }
-}
+export type Page = {
+ oldPage: number;
+ pageState: PageState; // sets the state of the receiver - INIT, STD_PAGE, EXT_PAGE
+};
diff --git a/src/bicycle-power-sensors.ts b/src/bicycle-power-sensors.ts
deleted file mode 100644
index c029829..0000000
--- a/src/bicycle-power-sensors.ts
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
-* ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#521_tab
-* Spec sheet: https://www.thisisant.com/resources/bicycle-power/
-*/
-
-import { AntPlusSensor, AntPlusScanner, Messages } from './ant';
-
-class BicyclePowerSensorState {
- constructor(deviceID: number) {
- this.DeviceID = deviceID;
- }
-
- DeviceID: number;
- PedalPower?: number;
- RightPedalPower?: number;
- LeftPedalPower?: number;
- Cadence?: number;
- AccumulatedPower?: number;
- Power?: number;
- offset: number = 0;
- EventCount?: number;
- TimeStamp?: number;
- Slope?: number;
- TorqueTicksStamp?: number;
- CalculatedCadence?: number;
- CalculatedTorque?: number;
- CalculatedPower?: number;
-}
-
-class BicyclePowerScanState extends BicyclePowerSensorState {
- Rssi: number;
- Threshold: number;
-}
-
-export class BicyclePowerSensor extends AntPlusSensor {
- static deviceType = 0x0B;
-
- public attach(channel, deviceID): void {
- super.attach(channel, 'receive', deviceID, BicyclePowerSensor.deviceType, 0, 255, 8182);
- this.state = new BicyclePowerSensorState(deviceID);
- }
-
- private state: BicyclePowerSensorState;
-
- protected updateState(deviceId, data) {
- this.state.DeviceID = deviceId;
- updateState(this, this.state, data);
- }
-}
-
-export class BicyclePowerScanner extends AntPlusScanner {
- protected deviceType() {
- return BicyclePowerSensor.deviceType;
- }
-
- private states: { [id: number]: BicyclePowerScanState } = {};
-
- protected createStateIfNew(deviceId) {
- if (!this.states[deviceId]) {
- this.states[deviceId] = new BicyclePowerScanState(deviceId);
- }
- }
-
- protected updateRssiAndThreshold(deviceId, rssi, threshold) {
- this.states[deviceId].Rssi = rssi;
- this.states[deviceId].Threshold = threshold;
- }
-
- protected updateState(deviceId, data) {
- updateState(this, this.states[deviceId], data);
- }
-}
-
-function updateState(
- sensor: BicyclePowerSensor | BicyclePowerScanner,
- state: BicyclePowerSensorState | BicyclePowerScanState,
- data: Buffer) {
-
- const page = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA);
- switch (page) {
- case 0x01: {
- const calID = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1);
- if (calID === 0x10) {
- const calParam = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2);
- if (calParam === 0x01) {
- state.offset = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 6);
- }
- }
- break;
- }
- case 0x10: {
- const pedalPower = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2);
- if (pedalPower !== 0xFF) {
- if (pedalPower & 0x80) {
- state.PedalPower = pedalPower & 0x7F;
- state.RightPedalPower = state.PedalPower;
- state.LeftPedalPower = 100 - state.RightPedalPower;
- } else {
- state.PedalPower = pedalPower & 0x7F;
- state.RightPedalPower = undefined;
- state.LeftPedalPower = undefined;
- }
- } else {
- state.PedalPower = undefined;
- state.RightPedalPower = undefined;
- state.LeftPedalPower = undefined;
- }
- const cadence = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- if (cadence !== 0xFF) {
- state.Cadence = cadence;
- } else {
- state.Cadence = undefined;
- }
- state.AccumulatedPower = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 4);
- state.Power = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 6);
- break;
- }
- case 0x20: {
- const oldEventCount = state.EventCount;
- const oldTimeStamp = state.TimeStamp;
- const oldTorqueTicksStamp = state.TorqueTicksStamp;
-
- let eventCount = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1);
- const slope = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 3);
- let timeStamp = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 5);
- let torqueTicksStamp = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 7);
-
- if (timeStamp !== oldTimeStamp && eventCount !== oldEventCount) {
- state.EventCount = eventCount;
- if (oldEventCount > eventCount) { //Hit rollover value
- eventCount += 255;
- }
-
- state.TimeStamp = timeStamp;
- if (oldTimeStamp > timeStamp) { //Hit rollover value
- timeStamp += 65400;
- }
-
- state.Slope = slope;
- state.TorqueTicksStamp = torqueTicksStamp;
- if (oldTorqueTicksStamp > torqueTicksStamp) { //Hit rollover value
- torqueTicksStamp += 65535;
- }
-
- const elapsedTime = (timeStamp - oldTimeStamp) * 0.0005;
- const torqueTicks = torqueTicksStamp - oldTorqueTicksStamp;
-
- const cadencePeriod = elapsedTime / (eventCount - oldEventCount); // s
- const cadence = Math.round(60 / cadencePeriod); // rpm
- state.CalculatedCadence = cadence;
-
- const torqueFrequency = (1 / (elapsedTime / torqueTicks)) - state.offset; // Hz
- const torque = torqueFrequency / (slope / 10); // Nm
- state.CalculatedTorque = torque;
-
- state.CalculatedPower = torque * cadence * Math.PI / 30; // Watts
- }
- break;
- }
- default:
- return;
- }
- sensor.emit('powerData', state);
-}
diff --git a/src/cadence-sensors.ts b/src/cadence-sensors.ts
deleted file mode 100644
index b23c636..0000000
--- a/src/cadence-sensors.ts
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#523_tab
- * Spec sheet: https://www.thisisant.com/resources/bicycle-speed-and-cadence/
- */
-
-import { AntPlusSensor, AntPlusScanner, Messages } from './ant';
-
-class CadenceSensorState {
- constructor(deviceID: number) {
- this.DeviceID = deviceID;
- }
-
- DeviceID: number;
- CadenceEventTime: number;
- CumulativeCadenceRevolutionCount: number;
- CalculatedCadence: number;
-
- OperatingTime?: number;
- ManId?: number;
- SerialNumber?: number;
- HwVersion?: number;
- SwVersion?: number;
- ModelNum?: number;
- BatteryVoltage?: number;
- BatteryStatus?: 'New' | 'Good' | 'Ok' | 'Low' | 'Critical' | 'Invalid';
- Motion?: boolean;
-}
-
-class CadenceScanState extends CadenceSensorState {
- Rssi: number;
- Threshold: number;
-}
-
-export class CadenceSensor extends AntPlusSensor {
- static deviceType = 0x7A;
-
- wheelCircumference: number = 2.199; // default 70cm wheel
-
- public setWheelCircumference(wheelCircumference: number) {
- this.wheelCircumference = wheelCircumference;
- }
-
- public attach(channel, deviceID): void {
- super.attach(channel, 'receive', deviceID, CadenceSensor.deviceType, 0, 255, 8086);
- this.state = new CadenceSensorState(deviceID);
- }
-
- private state: CadenceSensorState;
-
- protected updateState(deviceId, data) {
- this.state.DeviceID = deviceId;
- updateState(this, this.state, data);
- }
-}
-
-export class CadenceScanner extends AntPlusScanner {
- protected deviceType() {
- return CadenceSensor.deviceType;
- }
-
- wheelCircumference: number = 2.199; // default 70cm wheel
-
- public setWheelCircumference(wheelCircumference: number) {
- this.wheelCircumference = wheelCircumference;
- }
-
- private states: { [id: number]: CadenceScanState } = {};
-
- protected createStateIfNew(deviceId) {
- if (!this.states[deviceId]) {
- this.states[deviceId] = new CadenceScanState(deviceId);
- }
- }
-
- protected updateRssiAndThreshold(deviceId, rssi, threshold) {
- this.states[deviceId].Rssi = rssi;
- this.states[deviceId].Threshold = threshold;
- }
-
- protected updateState(deviceId, data) {
- updateState(this, this.states[deviceId], data);
- }
-}
-
-const TOGGLE_MASK = 0x80;
-
-function updateState(sensor: CadenceSensor | CadenceScanner, state: CadenceSensorState | CadenceScanState, data: Buffer) {
- const pageNum = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA);
- switch (pageNum & ~TOGGLE_MASK) { //check the new pages and remove the toggle bit
- case 1:
- //decode the cumulative operating time
- state.OperatingTime = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1);
- state.OperatingTime |= data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2) << 8;
- state.OperatingTime |= data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3) << 16;
- state.OperatingTime *= 2;
- break;
- case 2:
- //decode the Manufacturer ID
- state.ManId = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1);
- //decode the 4 byte serial number
- state.SerialNumber = state.DeviceID;
- state.SerialNumber |= data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 2) << 16;
- state.SerialNumber >>>= 0;
- break;
- case 3:
- //decode HW version, SW version, and model number
- state.HwVersion = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1);
- state.SwVersion = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2);
- state.ModelNum = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- break;
- case 4: {
- const batteryFrac = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2);
- const batteryStatus = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- state.BatteryVoltage = (batteryStatus & 0x0F) + (batteryFrac / 256);
- const batteryFlags = (batteryStatus & 0x70) >>> 4;
- switch (batteryFlags) {
- case 1:
- state.BatteryStatus = 'New';
- break;
- case 2:
- state.BatteryStatus = 'Good';
- break;
- case 3:
- state.BatteryStatus = 'Ok';
- break;
- case 4:
- state.BatteryStatus = 'Low';
- break;
- case 5:
- state.BatteryStatus = 'Critical';
- break;
- default:
- state.BatteryVoltage = undefined;
- state.BatteryStatus = 'Invalid';
- break;
- }
- break;
- }
- case 5:
- state.Motion = (data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1) & 0x01) === 0x01;
- break;
- default:
- break;
- }
-
- //get old state for calculating cumulative values
- const oldCadenceTime = state.CadenceEventTime;
- const oldCadenceCount = state.CumulativeCadenceRevolutionCount;
-
- let cadenceTime = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 4);
- let cadenceCount = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 6);
-
- if (cadenceTime !== oldCadenceTime) {
- state.CadenceEventTime = cadenceTime;
- state.CumulativeCadenceRevolutionCount = cadenceCount;
-
- if (oldCadenceTime > cadenceTime) { //Hit rollover value
- cadenceTime += (1024 * 64);
- }
-
- if (oldCadenceCount > cadenceCount) { //Hit rollover value
- cadenceCount += (1024 * 64);
- }
-
- const cadence = ((60 * (cadenceCount - oldCadenceCount) * 1024) / (cadenceTime - oldCadenceTime));
- if (!isNaN(cadence)) {
- state.CalculatedCadence = cadence;
- sensor.emit('cadenceData', state);
- }
- }
-}
diff --git a/src/environment-sensors.ts b/src/environment-sensors.ts
deleted file mode 100644
index cf874d9..0000000
--- a/src/environment-sensors.ts
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (c) 2019 Tom Cosgrove
- * Copyright (c) 2015 Alessandro Vergani
- *
- * This file is licensed under the MIT License (MIT):
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-/*
- * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#524_tab
- * Spec sheet: https://www.thisisant.com/resources/environment/
- */
-
-import { AntPlusSensor, AntPlusScanner, Messages } from './ant';
-
-class EnvironmentSensorState {
- constructor(deviceId: number) {
- this.DeviceID = deviceId;
- }
-
- DeviceID: number;
- EventCount: number;
- Temperature: number;
-}
-
-class EnvironmentScanState extends EnvironmentSensorState {
- Rssi: number;
- Threshold: number;
-}
-
-export class EnvironmentSensor extends AntPlusSensor {
- static deviceType = 25;
-
- public attach(channel, deviceID) {
- super.attach(channel, 'receive', deviceID, EnvironmentSensor.deviceType, 0, 255, 8192);
- this.state = new EnvironmentSensorState(deviceID);
- }
-
- private state: EnvironmentSensorState;
-
- protected updateState(deviceId, data) {
- this.state.DeviceID = deviceId;
- updateState(this, this.state, data);
- }
-}
-
-export class EnvironmentScanner extends AntPlusScanner {
- protected deviceType() {
- return EnvironmentSensor.deviceType;
- }
-
- private states: { [id: number]: EnvironmentScanState } = {};
-
- protected createStateIfNew(deviceId) {
- if (!this.states[deviceId]) {
- this.states[deviceId] = new EnvironmentScanState(deviceId);
- }
- }
-
- protected updateRssiAndThreshold(deviceId, rssi, threshold) {
- this.states[deviceId].Rssi = rssi;
- this.states[deviceId].Threshold = threshold;
- }
-
- protected updateState(deviceId, data) {
- updateState(this, this.states[deviceId], data);
- }
-}
-
-function updateState(
- sensor: EnvironmentSensor | EnvironmentScanner,
- state: EnvironmentSensorState | EnvironmentScanState,
- data: Buffer) {
-
- const page = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA);
- if (page === 1) {
- state.EventCount = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2);
- state.Temperature = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 6) / 100;
- }
- sensor.emit('envdata', state);
- sensor.emit('envData', state);
-}
diff --git a/src/fitness-equipment-sensors.ts b/src/fitness-equipment-sensors.ts
deleted file mode 100644
index d51b03e..0000000
--- a/src/fitness-equipment-sensors.ts
+++ /dev/null
@@ -1,743 +0,0 @@
-/*
- * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#521_tab
- * Spec sheet: https://www.thisisant.com/resources/bicycle-power/
- */
-
-import { Messages, SendCallback, AntPlusSensor, AntPlusScanner } from './ant';
-
-class FitnessEquipmentSensorState {
- constructor(deviceID: number) {
- this.DeviceID = deviceID;
- }
-
- _EventCount0x19?: number;
- _EventCount0x1A?: number;
-
- DeviceID: number;
- Temperature?: number;
- ZeroOffset?: number;
- SpinDownTime?: number;
-
- EquipmentType?: 'Treadmill' | 'Elliptical' | 'Reserved' | 'Rower' | 'Climber' | 'NordicSkier' | 'Trainer/StationaryBike' | 'General';
- ElapsedTime?: number;
- Distance?: number;
- RealSpeed?: number;
- VirtualSpeed?: number;
- HeartRate?: number;
- HeartRateSource?: 'HandContact' | 'EM' | 'ANT+';
- State?: 'OFF' | 'READY' | 'IN_USE' | 'FINISHED';
-
- CycleLength?: number;
- Incline?: number;
- Resistance?: number;
-
- METs?: number;
- CaloricBurnRate?: number;
- Calories?: number;
-
- AscendedDistance?: number;
- DescendedDistance?: number;
-
- Strides?: number;
- Strokes?: number;
-
- Cadence?: number;
- AccumulatedPower?: number;
- InstantaneousPower?: number;
- AveragePower?: number;
- TrainerStatus?: number;
- TargetStatus?: 'OnTarget' | 'LowSpeed' | 'HighSpeed';
-
- WheelTicks?: number;
- WheelPeriod?: number;
- Torque?: number;
-
- HwVersion?: number;
- ManId?: number;
- ModelNum?: number;
-
- SwVersion?: number;
- SerialNumber?: number;
-
- PairedDevices: any[] = [];
-}
-
-class FitnessEquipmentScanState extends FitnessEquipmentSensorState {
- Rssi: number;
- Threshold: number;
-}
-
-export class FitnessEquipmentSensor extends AntPlusSensor {
- static deviceType = 0x11;
-
- public attach(channel, deviceID): void {
- super.attach(channel, 'receive', deviceID, FitnessEquipmentSensor.deviceType, 0, 255, 8192);
- this.state = new FitnessEquipmentSensorState(deviceID);
- }
-
- private state: FitnessEquipmentSensorState;
-
- protected updateState(deviceId, data) {
- this.state.DeviceID = deviceId;
- updateState(this, this.state, data);
- }
-
- private _setUserConfiguration(userWeight?: number, bikeWeight?: number, wheelDiameter?: number, gearRatio?: number,
- cbk?: SendCallback) {
- const m = userWeight === undefined ? 0xFFFF : Math.max(0, Math.min(65534, Math.round(userWeight * 100)));
- const df = wheelDiameter === undefined ? 0xFF : Math.round(wheelDiameter * 10) % 10;
- const mb = bikeWeight === undefined ? 0xFFF : Math.max(0, Math.min(1000, Math.round(bikeWeight * 20)));
- const d = wheelDiameter === undefined ? 0xFF : Math.max(0, Math.min(254, Math.round(wheelDiameter)));
- const gr = gearRatio === undefined ? 0x00 : Math.max(1, Math.min(255, Math.round(gearRatio / .03)));
- const payload = [0x37, m & 0xFF, (m >> 8) & 0xFF, 0xFF, (df & 0xF) | ((mb & 0xF) << 4), (mb >> 4) & 0xF, d & 0xFF, gr & 0xFF];
- const msg = Messages.acknowledgedData(this.channel, payload);
- this.send(msg, cbk);
- }
-
- public setUserConfiguration(cbk: SendCallback);
- public setUserConfiguration(userWeight: number, cbk?: SendCallback);
- public setUserConfiguration(userWeight: number, bikeWeight: number, cbk?: SendCallback);
- public setUserConfiguration(userWeight: number, bikeWeight: number, wheelDiameter: number, cbk?: SendCallback);
- public setUserConfiguration(userWeight: number, bikeWeight: number, wheelDiameter: number, gearRatio: number, cbk?: SendCallback);
- public setUserConfiguration(userWeight?: number | SendCallback, bikeWeight?: number | SendCallback, wheelDiameter?: number | SendCallback,
- gearRatio?: number | SendCallback, cbk?: SendCallback) {
- if (typeof (userWeight) === 'function') {
- return this._setUserConfiguration(undefined, undefined, undefined, undefined, userWeight);
- } else if (typeof (bikeWeight) === 'function') {
- return this._setUserConfiguration(userWeight, undefined, undefined, undefined, bikeWeight);
- } else if (typeof (wheelDiameter) === 'function') {
- return this._setUserConfiguration(userWeight, bikeWeight, undefined, undefined, wheelDiameter);
- } else if (typeof (gearRatio) === 'function') {
- return this._setUserConfiguration(userWeight, bikeWeight, wheelDiameter, undefined, gearRatio);
- } else {
- return this._setUserConfiguration(userWeight, bikeWeight, wheelDiameter, gearRatio, cbk);
- }
- }
-
- public setBasicResistance(resistance: number, cbk?: SendCallback) {
- const res = Math.max(0, Math.min(200, Math.round(resistance * 2)));
- const payload = [0x30, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, res & 0xFF];
- const msg = Messages.acknowledgedData(this.channel, payload);
- this.send(msg, cbk);
- }
-
- public setTargetPower(power: number, cbk?: SendCallback) {
- const p = Math.max(0, Math.min(4000, Math.round(power * 4)));
- const payload = [0x31, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, p & 0xFF, (p >> 8) & 0xFF];
- const msg = Messages.acknowledgedData(this.channel, payload);
- this.send(msg, cbk);
- }
-
- private _setWindResistance(windCoeff?: number, windSpeed?: number, draftFactor?: number, cbk?: SendCallback) {
- const wc = windCoeff === undefined ? 0xFF : Math.max(0, Math.min(186, Math.round(windCoeff * 100)));
- const ws = windSpeed === undefined ? 0xFF : Math.max(0, Math.min(254, Math.round(windSpeed + 127)));
- const df = draftFactor === undefined ? 0xFF : Math.max(0, Math.min(100, Math.round(draftFactor * 100)));
- const payload = [0x32, 0xFF, 0xFF, 0xFF, 0xFF, wc & 0xFF, ws & 0xFF, df & 0xFF];
- const msg = Messages.acknowledgedData(this.channel, payload);
- this.send(msg, cbk);
- }
-
- public setWindResistance(cbk: SendCallback);
- public setWindResistance(windCoeff: number, cbk?: SendCallback);
- public setWindResistance(windCoeff: number, windSpeed: number, cbk?: SendCallback);
- public setWindResistance(windCoeff: number, windSpeed: number, draftFactor: number, cbk?: SendCallback);
- public setWindResistance(windCoeff?: number | SendCallback, windSpeed?: number | SendCallback, draftFactor?: number | SendCallback,
- cbk?: SendCallback) {
- if (typeof (windCoeff) === 'function') {
- return this._setWindResistance(undefined, undefined, undefined, windCoeff);
- } else if (typeof (windSpeed) === 'function') {
- return this._setWindResistance(windCoeff, undefined, undefined, windSpeed);
- } else if (typeof (draftFactor) === 'function') {
- return this._setWindResistance(windCoeff, windSpeed, undefined, draftFactor);
- } else {
- return this._setWindResistance(windCoeff, windSpeed, draftFactor, cbk);
- }
- }
-
- private _setTrackResistance(slope?: number, rollingResistanceCoeff?: number, cbk?: SendCallback) {
- const s = slope === undefined ? 0xFFFF : Math.max(0, Math.min(40000, Math.round((slope + 200) * 100)));
- const rr = rollingResistanceCoeff === undefined ? 0xFF : Math.max(0, Math.min(254, Math.round(rollingResistanceCoeff * 20000)));
- const payload = [0x33, 0xFF, 0xFF, 0xFF, 0xFF, s & 0xFF, (s >> 8) & 0xFF, rr & 0xFF];
- const msg = Messages.acknowledgedData(this.channel, payload);
- this.send(msg, cbk);
- }
-
- public setTrackResistance(cbk: SendCallback);
- public setTrackResistance(slope: number, cbk?: SendCallback);
- public setTrackResistance(slope: number, rollingResistanceCoeff: number, cbk?: SendCallback);
- public setTrackResistance(slope?: number | SendCallback, rollingResistanceCoeff?: number | SendCallback, cbk?: SendCallback) {
- if (typeof (slope) === 'function') {
- return this._setTrackResistance(undefined, undefined, slope);
- } else if (typeof (rollingResistanceCoeff) === 'function') {
- return this._setTrackResistance(slope, undefined, rollingResistanceCoeff);
- } else {
- return this._setTrackResistance(slope, rollingResistanceCoeff, cbk);
- }
- }
-}
-
-export class FitnessEquipmentScanner extends AntPlusScanner {
- protected deviceType() {
- return FitnessEquipmentSensor.deviceType;
- }
-
- private states: { [id: number]: FitnessEquipmentScanState } = {};
-
- protected createStateIfNew(deviceId) {
- if (!this.states[deviceId]) {
- this.states[deviceId] = new FitnessEquipmentScanState(deviceId);
- }
- }
-
- protected updateRssiAndThreshold(deviceId, rssi, threshold) {
- this.states[deviceId].Rssi = rssi;
- this.states[deviceId].Threshold = threshold;
- }
-
- protected updateState(deviceId, data) {
- updateState(this, this.states[deviceId], data);
- }
-}
-
-function resetState(state: FitnessEquipmentSensorState | FitnessEquipmentScanState) {
- delete state.ElapsedTime;
- delete state.Distance;
- delete state.RealSpeed;
- delete state.VirtualSpeed;
- delete state.HeartRate;
- delete state.HeartRateSource;
- delete state.CycleLength;
- delete state.Incline;
- delete state.Resistance;
- delete state.METs;
- delete state.CaloricBurnRate;
- delete state.Calories;
- delete state._EventCount0x19;
- delete state._EventCount0x1A;
- delete state.Cadence;
- delete state.AccumulatedPower;
- delete state.InstantaneousPower;
- delete state.AveragePower;
- delete state.TrainerStatus;
- delete state.TargetStatus;
- delete state.AscendedDistance;
- delete state.DescendedDistance;
- delete state.Strides;
- delete state.Strokes;
- delete state.WheelTicks;
- delete state.WheelPeriod;
- delete state.Torque;
-}
-
-function updateState(
- sensor: FitnessEquipmentSensor | FitnessEquipmentScanner,
- state: FitnessEquipmentSensorState | FitnessEquipmentScanState,
- data: Buffer) {
-
- const page = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA);
- switch (page) {
- case 0x01: {
- const temperature = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- if (temperature !== 0xFF) {
- state.Temperature = -25 + temperature * 0.5;
- }
- const calBF = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1);
- if (calBF & 0x40) {
- state.ZeroOffset = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 4);
- }
- if (calBF & 0x80) {
- state.SpinDownTime = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 6);
- }
- break;
- }
- case 0x10: {
- const equipmentTypeBF = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1);
- switch (equipmentTypeBF & 0x1F) {
- case 19: state.EquipmentType = 'Treadmill'; break;
- case 20: state.EquipmentType = 'Elliptical'; break;
- case 21: state.EquipmentType = 'Reserved'; break;
- case 22: state.EquipmentType = 'Rower'; break;
- case 23: state.EquipmentType = 'Climber'; break;
- case 24: state.EquipmentType = 'NordicSkier'; break;
- case 25: state.EquipmentType = 'Trainer/StationaryBike'; break;
- default: state.EquipmentType = 'General'; break;
- }
- let elapsedTime = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2);
- let distance = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- const speed = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 4);
- const heartRate = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 6);
- const capStateBF = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 7);
- if (heartRate !== 0xFF) {
- switch (capStateBF & 0x03) {
- case 3: {
- state.HeartRate = heartRate;
- state.HeartRateSource = 'HandContact';
- break;
- }
- case 2: {
- state.HeartRate = heartRate;
- state.HeartRateSource = 'EM';
- break;
- }
- case 1: {
- state.HeartRate = heartRate;
- state.HeartRateSource = 'ANT+';
- break;
- }
- default: {
- delete state.HeartRate;
- delete state.HeartRateSource;
- break;
- }
- }
- }
-
- elapsedTime /= 4;
- const oldElapsedTime = (state.ElapsedTime || 0) % 64;
- if (elapsedTime !== oldElapsedTime) {
- if (oldElapsedTime > elapsedTime) { //Hit rollover value
- elapsedTime += 64;
- }
- }
- state.ElapsedTime = (state.ElapsedTime || 0) + elapsedTime - oldElapsedTime;
-
- if (capStateBF & 0x04) {
- const oldDistance = (state.Distance || 0) % 256;
- if (distance !== oldDistance) {
- if (oldDistance > distance) { //Hit rollover value
- distance += 256;
- }
- }
- state.Distance = (state.Distance || 0) + distance - oldDistance;
- } else {
- delete state.Distance;
- }
- if (capStateBF & 0x08) {
- state.VirtualSpeed = speed / 1000;
- delete state.RealSpeed;
- } else {
- delete state.VirtualSpeed;
- state.RealSpeed = speed / 1000;
- }
- switch ((capStateBF & 0x70) >> 4) {
- case 1: state.State = 'OFF'; break;
- case 2: state.State = 'READY'; resetState(state); break;
- case 3: state.State = 'IN_USE'; break;
- case 4: state.State = 'FINISHED'; break;
- default: delete state.State; break;
- }
- if (capStateBF & 0x80) {
- // lap
- }
- break;
- }
- case 0x11: {
- const cycleLen = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- const incline = data.readInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 4);
- const resistance = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 6);
- const capStateBF = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 7);
- if (cycleLen !== 0xFF) {
- state.CycleLength = cycleLen / 100;
- }
- if (incline >= -10000 && incline <= 10000) {
- state.Incline = incline / 100;
- }
- if (resistance !== 0xFF) {
- state.Resistance = resistance;
- }
- switch ((capStateBF & 0x70) >> 4) {
- case 1: state.State = 'OFF'; break;
- case 2: state.State = 'READY'; resetState(state); break;
- case 3: state.State = 'IN_USE'; break;
- case 4: state.State = 'FINISHED'; break;
- default: delete state.State; break;
- }
- if (capStateBF & 0x80) {
- // lap
- }
- break;
- }
- case 0x12: {
- const mets = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 2);
- const caloricbr = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 4);
- const calories = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 6);
- const capStateBF = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 7);
- if (mets !== 0xFFFF) {
- state.METs = mets / 100;
- }
- if (caloricbr !== 0xFFFF) {
- state.CaloricBurnRate = caloricbr / 10;
- }
- if (capStateBF & 0x01) {
- state.Calories = calories;
- }
- switch ((capStateBF & 0x70) >> 4) {
- case 1: state.State = 'OFF'; break;
- case 2: state.State = 'READY'; resetState(state); break;
- case 3: state.State = 'IN_USE'; break;
- case 4: state.State = 'FINISHED'; break;
- default: delete state.State; break;
- }
- if (capStateBF & 0x80) {
- // lap
- }
- break;
- }
- case 0x13: {
- const cadence = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 4);
- let negDistance = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 5);
- let posDistance = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 6);
- const flagStateBF = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 7);
-
- if (cadence !== 0xFF) {
- state.Cadence = cadence;
- }
-
- if (flagStateBF & 0x02) {
- const oldNegDistance = (state.DescendedDistance || 0) % 256;
- if (negDistance !== oldNegDistance) {
- if (oldNegDistance > negDistance) {
- negDistance += 256;
- }
- }
- state.DescendedDistance = (state.DescendedDistance || 0) + negDistance - oldNegDistance;
- }
-
- if (flagStateBF & 0x01) {
- const oldPosDistance = (state.AscendedDistance || 0) % 256;
- if (posDistance !== oldPosDistance) {
- if (oldPosDistance > posDistance) {
- posDistance += 256;
- }
- }
- state.AscendedDistance = (state.AscendedDistance || 0) + posDistance - oldPosDistance;
- }
-
- switch ((flagStateBF & 0x70) >> 4) {
- case 1: state.State = 'OFF'; break;
- case 2: state.State = 'READY'; resetState(state); break;
- case 3: state.State = 'IN_USE'; break;
- case 4: state.State = 'FINISHED'; break;
- default: delete state.State; break;
- }
- if (flagStateBF & 0x80) {
- // lap
- }
-
- break;
- }
- case 0x14: {
- let posDistance = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2);
- let strides = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- const cadence = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 4);
- const power = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 5);
- const flagStateBF = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 7);
-
- if (cadence !== 0xFF) {
- state.Cadence = cadence;
- }
-
- if (power !== 0xFFFF) {
- state.InstantaneousPower = power;
- }
-
- if (flagStateBF & 0x02) {
- const oldPosDistance = (state.AscendedDistance || 0) % 256;
- if (posDistance !== oldPosDistance) {
- if (oldPosDistance > posDistance) {
- posDistance += 256;
- }
- }
- state.AscendedDistance = (state.AscendedDistance || 0) + posDistance - oldPosDistance;
- }
-
- if (flagStateBF & 0x01) {
- const oldStrides = (state.Strides || 0) % 256;
- if (strides !== oldStrides) {
- if (oldStrides > strides) {
- strides += 256;
- }
- }
- state.Strides = (state.Strides || 0) + strides - oldStrides;
- }
-
- switch ((flagStateBF & 0x70) >> 4) {
- case 1: state.State = 'OFF'; break;
- case 2: state.State = 'READY'; resetState(state); break;
- case 3: state.State = 'IN_USE'; break;
- case 4: state.State = 'FINISHED'; break;
- default: delete state.State; break;
- }
- if (flagStateBF & 0x80) {
- // lap
- }
-
- break;
- }
- case 0x16: {
- let strokes = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- const cadence = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 4);
- const power = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 5);
- const flagStateBF = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 7);
-
- if (cadence !== 0xFF) {
- state.Cadence = cadence;
- }
-
- if (power !== 0xFFFF) {
- state.InstantaneousPower = power;
- }
-
- if (flagStateBF & 0x01) {
- const oldStrokes = (state.Strokes || 0) % 256;
- if (strokes !== oldStrokes) {
- if (oldStrokes > strokes) {
- strokes += 256;
- }
- }
- state.Strokes = (state.Strokes || 0) + strokes - oldStrokes;
- }
-
- switch ((flagStateBF & 0x70) >> 4) {
- case 1: state.State = 'OFF'; break;
- case 2: state.State = 'READY'; resetState(state); break;
- case 3: state.State = 'IN_USE'; break;
- case 4: state.State = 'FINISHED'; break;
- default: delete state.State; break;
- }
- if (flagStateBF & 0x80) {
- // lap
- }
-
- break;
- }
- case 0x17: {
- let strides = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- const cadence = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 4);
- const power = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 5);
- const flagStateBF = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 7);
-
- if (cadence !== 0xFF) {
- state.Cadence = cadence;
- }
-
- if (power !== 0xFFFF) {
- state.InstantaneousPower = power;
- }
-
- if (flagStateBF & 0x01) {
- const oldStrides = (state.Strides || 0) % 256;
- if (strides !== oldStrides) {
- if (oldStrides > strides) {
- strides += 256;
- }
- }
- state.Strides = (state.Strides || 0) + strides - oldStrides;
- }
-
- switch ((flagStateBF & 0x70) >> 4) {
- case 1: state.State = 'OFF'; break;
- case 2: state.State = 'READY'; resetState(state); break;
- case 3: state.State = 'IN_USE'; break;
- case 4: state.State = 'FINISHED'; break;
- default: delete state.State; break;
- }
- if (flagStateBF & 0x80) {
- // lap
- }
-
- break;
- }
- case 0x18: {
- let strides = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- const cadence = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 4);
- const power = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 5);
- const flagStateBF = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 7);
-
- if (cadence !== 0xFF) {
- state.Cadence = cadence;
- }
-
- if (power !== 0xFFFF) {
- state.InstantaneousPower = power;
- }
-
- if (flagStateBF & 0x01) {
- const oldStrides = (state.Strides || 0) % 256;
- if (strides !== oldStrides) {
- if (oldStrides > strides) {
- strides += 256;
- }
- }
- state.Strides = (state.Strides || 0) + strides - oldStrides;
- }
-
- switch ((flagStateBF & 0x70) >> 4) {
- case 1: state.State = 'OFF'; break;
- case 2: state.State = 'READY'; resetState(state); break;
- case 3: state.State = 'IN_USE'; break;
- case 4: state.State = 'FINISHED'; break;
- default: delete state.State; break;
- }
- if (flagStateBF & 0x80) {
- // lap
- }
-
- break;
- }
- case 0x19: {
- const oldEventCount = state._EventCount0x19 || 0;
-
- let eventCount = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1);
- const cadence = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2);
- let accPower = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 3);
- const power = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 5) & 0xFFF;
- const trainerStatus = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 6) >> 4;
- const flagStateBF = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 7);
-
- if (eventCount !== oldEventCount) {
- state._EventCount0x19 = eventCount;
- if (oldEventCount > eventCount) { //Hit rollover value
- eventCount += 255;
- }
- }
-
- if (cadence !== 0xFF) {
- state.Cadence = cadence;
- }
-
- if (power !== 0xFFF) {
- state.InstantaneousPower = power;
-
- const oldAccPower = (state.AccumulatedPower || 0) % 65536;
- if (accPower !== oldAccPower) {
- if (oldAccPower > accPower) {
- accPower += 65536;
- }
- }
- state.AccumulatedPower = (state.AccumulatedPower || 0) + accPower - oldAccPower;
-
- state.AveragePower = (accPower - oldAccPower) / (eventCount - oldEventCount);
- }
-
- state.TrainerStatus = trainerStatus;
-
- switch (flagStateBF & 0x03) {
- case 0: state.TargetStatus = 'OnTarget'; break;
- case 1: state.TargetStatus = 'LowSpeed'; break;
- case 2: state.TargetStatus = 'HighSpeed'; break;
- default: delete state.TargetStatus; break;
- }
-
- switch ((flagStateBF & 0x70) >> 4) {
- case 1: state.State = 'OFF'; break;
- case 2: state.State = 'READY'; resetState(state); break;
- case 3: state.State = 'IN_USE'; break;
- case 4: state.State = 'FINISHED'; break;
- default: delete state.State; break;
- }
- if (flagStateBF & 0x80) {
- // lap
- }
-
- break;
- }
- case 0x1A: {
- const oldEventCount = state._EventCount0x1A || 0;
-
- let eventCount = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1);
- let wheelTicks = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2);
- let accWheelPeriod = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 3);
- let accTorque = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 5);
- const flagStateBF = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 7);
-
- if (eventCount !== oldEventCount) {
- state._EventCount0x1A = eventCount;
- if (oldEventCount > eventCount) { //Hit rollover value
- eventCount += 255;
- }
- }
-
- const oldWheelTicks = (state.WheelTicks || 0) % 256;
- if (wheelTicks !== oldWheelTicks) {
- if (oldWheelTicks > wheelTicks) {
- wheelTicks += 65536;
- }
- }
- state.WheelTicks = (state.WheelTicks || 0) + wheelTicks - oldWheelTicks;
-
- const oldWheelPeriod = (state.WheelPeriod || 0) % 256;
- if (accWheelPeriod !== oldWheelPeriod) {
- if (oldWheelPeriod > accWheelPeriod) {
- accWheelPeriod += 65536;
- }
- }
- state.WheelPeriod = (state.WheelPeriod || 0) + accWheelPeriod - oldWheelPeriod;
-
- const oldTorque = (state.Torque || 0) % 256;
- if (accTorque !== oldTorque) {
- if (oldTorque > accTorque) {
- accTorque += 65536;
- }
- }
- state.Torque = (state.Torque || 0) + accTorque - oldTorque;
-
- switch ((flagStateBF & 0x70) >> 4) {
- case 1: state.State = 'OFF'; break;
- case 2: state.State = 'READY'; resetState(state); break;
- case 3: state.State = 'IN_USE'; break;
- case 4: state.State = 'FINISHED'; break;
- default: delete state.State; break;
- }
- if (flagStateBF & 0x80) {
- // lap
- }
-
- break;
- }
- case 0x50: {
- state.HwVersion = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- state.ManId = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 4);
- state.ModelNum = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 6);
- break;
- }
- case 0x51: {
- const swRevSup = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2);
- const swRevMain = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- const serial = data.readInt32LE(Messages.BUFFER_INDEX_MSG_DATA + 4);
-
- state.SwVersion = swRevMain;
-
- if (swRevSup !== 0xFF) {
- state.SwVersion += swRevSup / 1000;
- }
-
- if (serial !== 0xFFFFFFFF) {
- state.SerialNumber = serial;
- }
-
- break;
- }
- case 0x56: {
- const idx = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1);
- const tot = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2);
- const chState = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- const devId = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 4);
- const trType = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 6);
- const devType = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 7);
-
- if (idx === 0) {
- state.PairedDevices = [];
- }
-
- if (tot > 0) {
- state.PairedDevices.push({ id: devId, type: devType, paired: (chState & 0x80) ? true : false });
- }
-
- break;
- }
- default:
- return;
- }
- sensor.emit('fitnessData', state);
-}
diff --git a/src/heart-rate-sensors.ts b/src/heart-rate-sensors.ts
deleted file mode 100644
index 3264e03..0000000
--- a/src/heart-rate-sensors.ts
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#526_tab
- * Spec sheet: https://www.thisisant.com/resources/heart-rate-monitor/
- */
-
-import { AntPlusSensor, AntPlusScanner, Messages } from './ant';
-
-class HeartRateSensorState {
- constructor(deviceId: number) {
- this.DeviceID = deviceId;
- }
-
- DeviceID: number;
- BeatTime: number;
- BeatCount: number;
- ComputedHeartRate: number;
- OperatingTime?: number;
- ManId?: number;
- SerialNumber?: number;
- HwVersion?: number;
- SwVersion?: number;
- ModelNum?: number;
- PreviousBeat?: number;
-
- IntervalAverage?: number;
- IntervalMax?: number;
- SessionAverage?: number;
- SupportedFeatures?: number;
- EnabledFeatures?: number;
- BatteryLevel?: number;
- BatteryVoltage?: number;
- BatteryStatus?: 'New' | 'Good' | 'Ok' | 'Low' | 'Critical' | 'Invalid';
-}
-
-class HeartRateScannerState extends HeartRateSensorState {
- Rssi: number;
- Threshold: number;
-}
-
-enum PageState { INIT_PAGE, STD_PAGE, EXT_PAGE }
-
-type Page = {
- oldPage: number;
- pageState: PageState // sets the state of the receiver - INIT, STD_PAGE, EXT_PAGE
-};
-
-export class HeartRateSensor extends AntPlusSensor {
- static deviceType = 120;
-
- public attach(channel, deviceID) {
- super.attach(channel, 'receive', deviceID, HeartRateSensor.deviceType, 0, 255, 8070);
- this.state = new HeartRateSensorState(deviceID);
- }
-
- private state: HeartRateSensorState;
-
- private page: Page = {
- oldPage: -1,
- pageState: PageState.INIT_PAGE,
- };
-
- protected updateState(deviceId: number, data: Buffer) {
- this.state.DeviceID = deviceId;
- updateState(this, this.state, this.page, data);
- }
-}
-
-export class HeartRateScanner extends AntPlusScanner {
- protected deviceType() {
- return HeartRateSensor.deviceType;
- }
-
- private states: { [id: number]: HeartRateScannerState } = {};
-
- private pages: { [id: number]: Page } = {};
-
- protected createStateIfNew(deviceId) {
- if (!this.states[deviceId]) {
- this.states[deviceId] = new HeartRateScannerState(deviceId);
- }
-
- if (!this.pages[deviceId]) {
- this.pages[deviceId] = { oldPage: -1, pageState: PageState.INIT_PAGE };
- }
- }
-
- protected updateRssiAndThreshold(deviceId, rssi, threshold) {
- this.states[deviceId].Rssi = rssi;
- this.states[deviceId].Threshold = threshold;
- }
-
- protected updateState(deviceId, data) {
- updateState(this, this.states[deviceId], this.pages[deviceId], data);
- }
-}
-
-const TOGGLE_MASK = 0x80;
-
-function updateState(
- sensor: HeartRateSensor | HeartRateScanner,
- state: HeartRateSensorState | HeartRateScannerState,
- page: Page,
- data: Buffer) {
-
- const pageNum = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA);
- if (page.pageState === PageState.INIT_PAGE) {
- page.pageState = PageState.STD_PAGE; // change the state to STD_PAGE and allow the checking of old and new pages
- // decode with pages if the page byte or toggle bit has changed
- } else if ((pageNum !== page.oldPage) || (page.pageState === PageState.EXT_PAGE)) {
- page.pageState = PageState.EXT_PAGE; // set the state to use the extended page format
- switch (pageNum & ~TOGGLE_MASK) { //check the new pages and remove the toggle bit
- case 1:
- //decode the cumulative operating time
- state.OperatingTime = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1);
- state.OperatingTime |= data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2) << 8;
- state.OperatingTime |= data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3) << 16;
- state.OperatingTime *= 2;
- break;
- case 2:
- //decode the Manufacturer ID
- state.ManId = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1);
- //decode the 4 byte serial number
- state.SerialNumber = state.DeviceID;
- state.SerialNumber |= data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 2) << 16;
- state.SerialNumber >>>= 0;
- break;
- case 3:
- //decode HW version, SW version, and model number
- state.HwVersion = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1);
- state.SwVersion = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2);
- state.ModelNum = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- break;
- case 4:
- //decode the previous heart beat measurement time
- state.PreviousBeat = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 2);
- break;
- case 5:
- state.IntervalAverage = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1);
- state.IntervalMax = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2);
- state.SessionAverage = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- break;
- case 6:
- state.SupportedFeatures = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2);
- state.EnabledFeatures = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- break;
- case 7: {
- const batteryLevel = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1);
- const batteryFrac = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2);
- const batteryStatus = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- if (batteryLevel !== 0xFF) {
- state.BatteryLevel = batteryLevel;
- }
- state.BatteryVoltage = (batteryStatus & 0x0F) + (batteryFrac / 256);
- const batteryFlags = (batteryStatus & 0x70) >>> 4;
- switch (batteryFlags) {
- case 1:
- state.BatteryStatus = 'New';
- break;
- case 2:
- state.BatteryStatus = 'Good';
- break;
- case 3:
- state.BatteryStatus = 'Ok';
- break;
- case 4:
- state.BatteryStatus = 'Low';
- break;
- case 5:
- state.BatteryStatus = 'Critical';
- break;
- default:
- state.BatteryVoltage = undefined;
- state.BatteryStatus = 'Invalid';
- break;
- }
- break;
- }
- default:
- break;
- }
- }
- // decode the last four bytes of the HRM format, the first byte of this message is the channel number
- DecodeDefaultHRM(state, data.slice(Messages.BUFFER_INDEX_MSG_DATA + 4));
- page.oldPage = pageNum;
-
- sensor.emit('hbdata', state);
- sensor.emit('hbData', state);
-}
-
-function DecodeDefaultHRM(state: HeartRateSensorState | HeartRateScannerState, pucPayload: Buffer) {
- // decode the measurement time data (two bytes)
- state.BeatTime = pucPayload.readUInt16LE(0);
- // decode the measurement count data
- state.BeatCount = pucPayload.readUInt8(2);
- // decode the measurement count data
- state.ComputedHeartRate = pucPayload.readUInt8(3);
-}
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 0000000..66bcce7
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,45 @@
+export { Constants } from './Constants';
+export { GarminStick2 } from './GarminStick2';
+export { GarminStick3 } from './GarminStick3';
+export { ICancellationToken } from './ICancellationToken';
+export { Messages } from './Messages';
+export { AntPlusBaseSensor } from './sensors/AntPlusBaseSensor';
+export { AntPlusScanner } from './sensors/AntPlusScanner';
+export { AntPlusSensor } from './sensors/AntPlusSensor';
+export { BaseSensor } from './sensors/BaseSensor';
+export { BicyclePowerScanner } from './sensors/BicyclePowerScanner';
+export { BicyclePowerScanState } from './sensors/BicyclePowerScanState';
+export { BicyclePowerSensor } from './sensors/BicyclePowerSensor';
+export { BicyclePowerSensorState } from './sensors/BicyclePowerSensorState';
+export { CadenceScanner } from './sensors/CadenceScanner';
+export { CadenceScanState } from './sensors/CadenceScanState';
+export { CadenceSensor } from './sensors/CadenceSensor';
+export { CadenceSensorState } from './sensors/CadenceSensorState';
+export { EnvironmentScanner } from './sensors/EnvironmentScanner';
+export { EnvironmentScanState } from './sensors/EnvironmentScanState';
+export { EnvironmentSensor } from './sensors/EnvironmentSensor';
+export { EnvironmentSensorState } from './sensors/EnvironmentSensorState';
+export { FitnessEquipmentScanner } from './sensors/FitnessEquipmentScanner';
+export { FitnessEquipmentScanState } from './sensors/FitnessEquipmentScanState';
+export { FitnessEquipmentSensor } from './sensors/FitnessEquipmentSensor';
+export { FitnessEquipmentSensorState } from './sensors/FitnessEquipmentSensorState';
+export { HeartRateScanner } from './sensors/HeartRateScanner';
+export { HeartRateScanState } from './sensors/HeartRateScanState';
+export { HeartRateSensor } from './sensors/HeartRateSensor';
+export { HeartRateSensorState } from './sensors/HeartRateSensorState';
+export { MuscleOxygenScanner } from './sensors/MuscleOxygenScanner';
+export { MuscleOxygenScanState } from './sensors/MuscleOxygenScanState';
+export { MuscleOxygenSensor } from './sensors/MuscleOxygenSensor';
+export { MuscleOxygenSensorState } from './sensors/MuscleOxygenSensorState';
+export { SpeedCadenceScanner } from './sensors/SpeedCadenceScanner';
+export { SpeedCadenceScanState } from './sensors/SpeedCadenceScanState';
+export { SpeedCadenceSensor } from './sensors/SpeedCadenceSensor';
+export { SpeedCadenceSensorState } from './sensors/SpeedCadenceSensorState';
+export { SpeedScanner } from './sensors/SpeedScanner';
+export { SpeedScanState } from './sensors/SpeedScanState';
+export { SpeedSensor } from './sensors/SpeedSensor';
+export { SpeedSensorState } from './sensors/SpeedSensorState';
+export { StrideSpeedDistanceScanner } from './sensors/StrideSpeedDistanceScanner';
+export { StrideSpeedDistanceScanState } from './sensors/StrideSpeedDistanceScanState';
+export { StrideSpeedDistanceSensor } from './sensors/StrideSpeedDistanceSensor';
+export { StrideSpeedDistanceSensorState } from './sensors/StrideSpeedDistanceSensorState';
diff --git a/src/lib/EventEmitter.ts b/src/lib/EventEmitter.ts
new file mode 100644
index 0000000..a90e42b
--- /dev/null
+++ b/src/lib/EventEmitter.ts
@@ -0,0 +1,233 @@
+/* eslint-disable @typescript-eslint/no-unsafe-call */
+/* eslint-disable @typescript-eslint/no-unsafe-member-access */
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+/* eslint-disable no-restricted-syntax */
+/* eslint-disable @typescript-eslint/ban-types */
+// Folk: https://github.com/deno-library/events/blob/master/mod.ts
+
+export interface WrappedFunction extends Function {
+ listener: Function;
+}
+
+export class EventEmitter {
+ private events: Map> = new Map();
+
+ private maxListeners?: number;
+
+ #defaultMaxListeners = 10;
+
+ get defaultMaxListeners() {
+ return this.#defaultMaxListeners;
+ }
+
+ set defaultMaxListeners(n) {
+ if (Number.isInteger(n) || n < 0) {
+ const error = new RangeError(
+ `The value of "defaultMaxListeners" is out of range. It must be a non-negative integer. Received ${n}.`
+ );
+ throw error;
+ }
+ this.#defaultMaxListeners = n;
+ }
+
+ addListener(eventName: string | symbol, listener: Function) {
+ return this.on(eventName, listener);
+ }
+
+ emit(eventName: string | symbol, ...args: unknown[]) {
+ const listeners = this.events.get(eventName);
+ if (listeners === undefined) {
+ if (eventName === 'error') {
+ const error = args[0];
+
+ if (error instanceof Error) throw error;
+
+ throw new Error('Unhandled error.');
+ }
+ return false;
+ }
+ const copyListeners = [...listeners];
+ for (const listener of copyListeners) {
+ listener.apply(this, args);
+ }
+
+ return true;
+ }
+
+ setMaxListeners(n: number) {
+ if (!Number.isInteger(n) || n < 0) {
+ throw new RangeError(
+ `The value of "n" is out of range. It must be a non-negative integer. Received ${n}.`
+ );
+ }
+ this.maxListeners = n;
+ return this;
+ }
+
+ getMaxListeners() {
+ if (this.maxListeners === undefined) {
+ return this.defaultMaxListeners;
+ }
+ return this.maxListeners;
+ }
+
+ listenerCount(eventName: string | symbol) {
+ const events = this.events.get(eventName);
+ return events === undefined ? 0 : events.length;
+ }
+
+ eventNames() {
+ return Reflect.ownKeys(this.events);
+ }
+
+ listeners(eventName: string | symbol) {
+ const listeners = this.events.get(eventName);
+ return listeners === undefined ? [] : listeners;
+ }
+
+ off(eventName: string | symbol, listener: Function) {
+ return this.removeListener(eventName, listener);
+ }
+
+ on(eventName: string | symbol, listener: Function, prepend?: boolean): this {
+ if (this.events.has(eventName) === false) {
+ this.events.set(eventName, []);
+ }
+ const events = this.events.get(eventName) as any;
+ if (prepend) {
+ events.unshift(listener);
+ } else {
+ events.push(listener);
+ }
+
+ // newListener
+ if (eventName !== 'newListener' && this.events.has('newListener')) {
+ this.emit('newListener', eventName, listener);
+ }
+
+ // warn
+ const maxListener = this.getMaxListeners();
+ const eventLength = events.length;
+ if (maxListener > 0 && eventLength > maxListener && !events.warned) {
+ events.warned = true;
+ const warning = new Error(
+ `Possible EventEmitter memory leak detected.
+ ${this.listenerCount(eventName)} ${eventName.toString()} listeners.
+ Use emitter.setMaxListeners() to increase limit`
+ );
+ warning.name = 'MaxListenersExceededWarning';
+ console.warn(warning);
+ }
+
+ return this;
+ }
+
+ removeAllListeners(eventName: string | symbol) {
+ const { events } = this;
+
+ // Not listening for removeListener, no need to emit
+ if (!events.has('removeListener')) {
+ if (arguments.length === 0) {
+ this.events = new Map();
+ } else if (events.has(eventName)) {
+ events.delete(eventName);
+ }
+ return this;
+ }
+
+ // Emit removeListener for all listeners on all events
+ if (arguments.length === 0) {
+ for (const key of events.keys()) {
+ if (key !== 'removeListener') {
+ this.removeAllListeners(key);
+ }
+ }
+ this.removeAllListeners('removeListener');
+ this.events = new Map();
+ return this;
+ }
+
+ const listeners = events.get(eventName);
+ if (listeners !== undefined) {
+ listeners.forEach((listener) => {
+ this.removeListener(eventName, listener);
+ });
+ }
+
+ return this;
+ }
+
+ removeListener(eventName: string | symbol, listener: Function) {
+ const { events } = this;
+ if (events.size === 0) return this;
+
+ const list = events.get(eventName);
+ if (list === undefined) return this;
+
+ const index = list.findIndex(
+ (item) =>
+ item === listener || (item as WrappedFunction).listener === listener
+ );
+
+ if (index === -1) return this;
+
+ list.splice(index, 1);
+ if (list.length === 0) this.events.delete(eventName);
+
+ if (events.has('removeListener')) {
+ this.emit('removeListener', eventName, listener);
+ }
+
+ return this;
+ }
+
+ once(eventName: string | symbol, listener: Function): this {
+ this.on(eventName, this.onceWrap(eventName, listener));
+ return this;
+ }
+
+ private onceWrap(
+ eventName: string | symbol,
+ listener: Function
+ ): WrappedFunction {
+ const wrapper = function (
+ this: {
+ eventName: string | symbol;
+ listener: Function;
+ wrapedListener: Function;
+ context: EventEmitter;
+ },
+ ...args: any[] // eslint-disable-line @typescript-eslint/no-explicit-any
+ ): void {
+ this.context.removeListener(this.eventName, this.wrapedListener);
+ this.listener.apply(this.context, args);
+ };
+ const wrapperContext = {
+ eventName,
+ listener,
+ wrapedListener: wrapper as unknown as WrappedFunction,
+ context: this,
+ };
+ const wrapped = wrapper.bind(wrapperContext) as unknown as WrappedFunction;
+ wrapperContext.wrapedListener = wrapped;
+ wrapped.listener = listener;
+ return wrapped;
+ }
+
+ prependListener(eventName: string | symbol, listener: Function) {
+ return this.on(eventName, listener, true);
+ }
+
+ prependOnceListener(eventName: string | symbol, listener: Function) {
+ this.prependListener(eventName, this.onceWrap(eventName, listener));
+ return this;
+ }
+
+ rawListeners(eventName: string | symbol) {
+ const { events } = this;
+ if (events === undefined) return [];
+ const listeners = events.get(eventName);
+ if (listeners === undefined) return [];
+ return [...listeners];
+ }
+}
diff --git a/src/lib/UpdateState.ts b/src/lib/UpdateState.ts
new file mode 100644
index 0000000..6a5e51a
--- /dev/null
+++ b/src/lib/UpdateState.ts
@@ -0,0 +1,145 @@
+import { Page } from '../ant';
+import { BicyclePowerScanner } from '../sensors/BicyclePowerScanner';
+import { BicyclePowerScanState } from '../sensors/BicyclePowerScanState';
+import { BicyclePowerSensor } from '../sensors/BicyclePowerSensor';
+import { BicyclePowerSensorState } from '../sensors/BicyclePowerSensorState';
+import { CadenceScanner } from '../sensors/CadenceScanner';
+import { CadenceScanState } from '../sensors/CadenceScanState';
+import { CadenceSensor } from '../sensors/CadenceSensor';
+import { CadenceSensorState } from '../sensors/CadenceSensorState';
+import { EnvironmentScanner } from '../sensors/EnvironmentScanner';
+import { EnvironmentScanState } from '../sensors/EnvironmentScanState';
+import { EnvironmentSensor } from '../sensors/EnvironmentSensor';
+import { EnvironmentSensorState } from '../sensors/EnvironmentSensorState';
+import { FitnessEquipmentScanner } from '../sensors/FitnessEquipmentScanner';
+import { FitnessEquipmentScanState } from '../sensors/FitnessEquipmentScanState';
+import { FitnessEquipmentSensor } from '../sensors/FitnessEquipmentSensor';
+import { FitnessEquipmentSensorState } from '../sensors/FitnessEquipmentSensorState';
+import { HeartRateScanner } from '../sensors/HeartRateScanner';
+import { HeartRateScanState } from '../sensors/HeartRateScanState';
+import { HeartRateSensor } from '../sensors/HeartRateSensor';
+import { HeartRateSensorState } from '../sensors/HeartRateSensorState';
+import { MuscleOxygenScanner } from '../sensors/MuscleOxygenScanner';
+import { MuscleOxygenScanState } from '../sensors/MuscleOxygenScanState';
+import { MuscleOxygenSensor } from '../sensors/MuscleOxygenSensor';
+import { MuscleOxygenSensorState } from '../sensors/MuscleOxygenSensorState';
+import { SpeedCadenceScanner } from '../sensors/SpeedCadenceScanner';
+import { SpeedCadenceScanState } from '../sensors/SpeedCadenceScanState';
+import { SpeedCadenceSensor } from '../sensors/SpeedCadenceSensor';
+import { SpeedCadenceSensorState } from '../sensors/SpeedCadenceSensorState';
+import { SpeedScanner } from '../sensors/SpeedScanner';
+import { SpeedScanState } from '../sensors/SpeedScanState';
+import { SpeedSensor } from '../sensors/SpeedSensor';
+import { SpeedSensorState } from '../sensors/SpeedSensorState';
+import { StrideSpeedDistanceScanner } from '../sensors/StrideSpeedDistanceScanner';
+import { StrideSpeedDistanceScanState } from '../sensors/StrideSpeedDistanceScanState';
+import { StrideSpeedDistanceSensor } from '../sensors/StrideSpeedDistanceSensor';
+import { StrideSpeedDistanceSensorState } from '../sensors/StrideSpeedDistanceSensorState';
+
+export function updateBicyclePowerSensorState(
+ sensor: BicyclePowerSensor | BicyclePowerScanner,
+ state: BicyclePowerSensorState | BicyclePowerScanState,
+ data: DataView
+) {
+ sensor.emit('powerData', state.updateState(data));
+}
+
+export function updateCadenceSensorState(
+ sensor: CadenceSensor | CadenceScanner,
+ state: CadenceSensorState | CadenceScanState,
+ data: DataView
+) {
+ sensor.emit('cadenceData', state.updateState(data));
+}
+
+export function updateEnvironmentSensorState(
+ sensor: EnvironmentSensor | EnvironmentScanner,
+ state: EnvironmentSensorState | EnvironmentScanState,
+ data: DataView
+) {
+ const updatedState = state.updateState(data);
+ sensor.emit('envdata', updatedState);
+ sensor.emit('envData', updatedState);
+}
+
+export function updateFitnessEquipmentSensorState(
+ sensor: FitnessEquipmentSensor | FitnessEquipmentScanner,
+ state: FitnessEquipmentSensorState | FitnessEquipmentScanState,
+ data: DataView
+) {
+ sensor.emit('fitnessData', state.updateState(data));
+}
+
+export function resetFitnessEquipmentSensorState(
+ state: FitnessEquipmentSensorState | FitnessEquipmentScanState
+) {
+ state.resetState();
+}
+
+export function updateHeartRateSensorState(
+ sensor: HeartRateSensor | HeartRateScanner,
+ state: HeartRateSensorState | HeartRateScanState,
+ data: DataView,
+ page: Page
+) {
+ const updatedState = state.updateState(data, page);
+ sensor.emit('hbdata', updatedState);
+ sensor.emit('hbData', updatedState);
+}
+
+export function updateMuscleOxygenSensorState(
+ sensor: MuscleOxygenSensor | MuscleOxygenScanner,
+ state: MuscleOxygenSensorState | MuscleOxygenScanState,
+ data: DataView
+) {
+ const updatedState = state.updateState(data);
+ if (updatedState) {
+ sensor.emit('oxygenData', updatedState);
+ }
+}
+
+export function updateSpeedCadenceSensorState(
+ sensor: SpeedCadenceSensor | SpeedCadenceScanner,
+ state: SpeedCadenceSensorState | SpeedCadenceScanState,
+ data: DataView
+) {
+ const { updatedState, resultType } = state.updateState(
+ data,
+ sensor.wheelCircumference
+ );
+ switch (resultType) {
+ case 'both':
+ sensor.emit('cadenceData', updatedState);
+ sensor.emit('speedData', updatedState);
+ break;
+ case 'cadence':
+ sensor.emit('cadenceData', updatedState);
+ break;
+ case 'speed':
+ sensor.emit('speedData', updatedState);
+ break;
+ default:
+ break;
+ }
+}
+
+export function updateSpeedSensorState(
+ sensor: SpeedSensor | SpeedScanner,
+ state: SpeedSensorState | SpeedScanState,
+ data: DataView
+) {
+ const updatedState = state.updateState(data, sensor.wheelCircumference);
+ if (updatedState) {
+ sensor.emit('speedData', updatedState);
+ }
+}
+
+export function updateStrideSpeedDistanceSensorState(
+ sensor: StrideSpeedDistanceSensor | StrideSpeedDistanceScanner,
+ state: StrideSpeedDistanceSensorState | StrideSpeedDistanceScanState,
+ data: DataView
+) {
+ const updatedState = state.updateState(data);
+ sensor.emit('ssddata', updatedState);
+ sensor.emit('ssdData', updatedState);
+}
diff --git a/src/muscle-oxygen-sensors.ts b/src/muscle-oxygen-sensors.ts
deleted file mode 100644
index 1c53337..0000000
--- a/src/muscle-oxygen-sensors.ts
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#521_tab
- * Spec sheet: https://www.thisisant.com/resources/bicycle-power/
- */
-
-import { Messages, SendCallback, AntPlusSensor, AntPlusScanner } from './ant';
-
-class MuscleOxygenSensorState {
- constructor(deviceID: number) {
- this.DeviceID = deviceID;
- }
-
- _EventCount?: number;
-
- DeviceID: number;
-
- UTCTimeRequired?: boolean;
- SupportANTFS?: boolean;
- MeasurementInterval?: 0.25 | 0.5 | 1 | 2;
- TotalHemoglobinConcentration?: number | 'AmbientLightTooHigh' | 'Invalid';
- PreviousSaturatedHemoglobinPercentage?: number | 'AmbientLightTooHigh' | 'Invalid';
- CurrentSaturatedHemoglobinPercentage?: number | 'AmbientLightTooHigh' | 'Invalid';
-
- HwVersion?: number;
- ManId?: number;
- ModelNum?: number;
-
- SwVersion?: number;
- SerialNumber?: number;
-
- OperatingTime?: number;
- BatteryVoltage?: number;
- BatteryStatus?: 'New' | 'Good' | 'Ok' | 'Low' | 'Critical' | 'Invalid';
-}
-
-class MuscleOxygenScanState extends MuscleOxygenSensorState {
- Rssi: number;
- Threshold: number;
-}
-
-export class MuscleOxygenSensor extends AntPlusSensor {
- static deviceType = 0x1F;
-
- public attach(channel, deviceID): void {
- super.attach(channel, 'receive', deviceID, MuscleOxygenSensor.deviceType, 0, 255, 8192);
- this.state = new MuscleOxygenSensorState(deviceID);
- }
-
- private state: MuscleOxygenSensorState;
-
- protected updateState(deviceId, data) {
- this.state.DeviceID = deviceId;
- updateState(this, this.state, data);
- }
-
- private _sendTimeCmd(cmd: number, cbk?: SendCallback) {
- const now = new Date();
- const utc = Math.round((now.getTime() - Date.UTC(1989, 11, 31, 0, 0, 0, 0)) / 1000);
- const offset = -Math.round(now.getTimezoneOffset() / 15);
- const payload = [0x10, cmd & 0xFF, 0xFF, offset & 0xFF, (utc >> 0) & 0xFF, (utc >> 8) & 0xFF, (utc >> 16) & 0xFF, (utc >> 24) & 0xFF];
- const msg = Messages.acknowledgedData(this.channel, payload);
- this.send(msg, cbk);
- }
-
- public setUTCTime(cbk?: SendCallback) {
- this._sendTimeCmd(0x00, cbk);
- }
-
- public startSession(cbk?: SendCallback) {
- this._sendTimeCmd(0x01, cbk);
- }
-
- public stopSession(cbk?: SendCallback) {
- this._sendTimeCmd(0x02, cbk);
- }
-
- public setLap(cbk?: SendCallback) {
- this._sendTimeCmd(0x03, cbk);
- }
-}
-
-export class MuscleOxygenScanner extends AntPlusScanner {
- protected deviceType() {
- return MuscleOxygenSensor.deviceType;
- }
-
- private states: { [id: number]: MuscleOxygenScanState } = {};
-
- protected createStateIfNew(deviceId) {
- if (!this.states[deviceId]) {
- this.states[deviceId] = new MuscleOxygenScanState(deviceId);
- }
- }
-
- protected updateRssiAndThreshold(deviceId, rssi, threshold) {
- this.states[deviceId].Rssi = rssi;
- this.states[deviceId].Threshold = threshold;
- }
-
- protected updateState(deviceId, data) {
- updateState(this, this.states[deviceId], data);
- }
-}
-
-function updateState(
- sensor: MuscleOxygenSensor | MuscleOxygenScanner,
- state: MuscleOxygenSensorState | MuscleOxygenScanState,
- data: Buffer) {
-
- const oldEventCount = state._EventCount || 0;
-
- const page = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA);
- switch (page) {
- case 0x01: {
-
- let eventCount = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1);
- const notifications = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2);
- const capabilities = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 3);
- const total = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 4) & 0xFFF;
- const previous = (data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 5) >> 4) & 0x3FF;
- const current = (data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 6) >> 6) & 0x3FF;
-
- if (eventCount !== oldEventCount) {
- state._EventCount = eventCount;
- if (oldEventCount > eventCount) { //Hit rollover value
- eventCount += 255;
- }
- }
-
- state.UTCTimeRequired = (notifications & 0x01) === 0x01;
-
- state.SupportANTFS = (capabilities & 0x01) === 0x01;
-
- switch ((capabilities >> 1) & 0x7) {
- case 1: state.MeasurementInterval = 0.25; break;
- case 2: state.MeasurementInterval = 0.5; break;
- case 3: state.MeasurementInterval = 1; break;
- case 4: state.MeasurementInterval = 2; break;
- default: delete state.MeasurementInterval;
- }
-
- switch (total) {
- case 0xFFE: state.TotalHemoglobinConcentration = 'AmbientLightTooHigh'; break;
- case 0xFFF: state.TotalHemoglobinConcentration = 'Invalid'; break;
- default: state.TotalHemoglobinConcentration = total;
- }
-
- switch (previous) {
- case 0x3FE: state.PreviousSaturatedHemoglobinPercentage = 'AmbientLightTooHigh'; break;
- case 0x3FF: state.PreviousSaturatedHemoglobinPercentage = 'Invalid'; break;
- default: state.PreviousSaturatedHemoglobinPercentage = previous;
- }
-
- switch (current) {
- case 0x3FE: state.CurrentSaturatedHemoglobinPercentage = 'AmbientLightTooHigh'; break;
- case 0x3FF: state.CurrentSaturatedHemoglobinPercentage = 'Invalid'; break;
- default: state.CurrentSaturatedHemoglobinPercentage = current;
- }
-
- break;
- }
- case 0x50: {
- state.HwVersion = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- state.ManId = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 4);
- state.ModelNum = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 6);
- break;
- }
- case 0x51: {
- const swRevSup = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2);
- const swRevMain = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- const serial = data.readInt32LE(Messages.BUFFER_INDEX_MSG_DATA + 4);
-
- state.SwVersion = swRevMain;
-
- if (swRevSup !== 0xFF) {
- state.SwVersion += swRevSup / 1000;
- }
-
- if (serial !== 0xFFFFFFFF) {
- state.SerialNumber = serial;
- }
-
- break;
- }
- case 0x52: {
- const batteryId = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2);
- const operatingTime = data.readUInt32LE(Messages.BUFFER_INDEX_MSG_DATA + 3) & 0xFFFFFF;
- const batteryFrac = data.readInt32LE(Messages.BUFFER_INDEX_MSG_DATA + 6);
- const batteryStatus = data.readInt32LE(Messages.BUFFER_INDEX_MSG_DATA + 7);
-
- state.OperatingTime = operatingTime * (((batteryStatus & 0x80) === 0x80) ? 2 : 16);
- state.BatteryVoltage = (batteryStatus & 0x0F) + (batteryFrac / 256);
- const batteryFlags = (batteryStatus & 0x70) >>> 4;
- switch (batteryFlags) {
- case 1:
- state.BatteryStatus = 'New';
- break;
- case 2:
- state.BatteryStatus = 'Good';
- break;
- case 3:
- state.BatteryStatus = 'Ok';
- break;
- case 4:
- state.BatteryStatus = 'Low';
- break;
- case 5:
- state.BatteryStatus = 'Critical';
- break;
- default:
- state.BatteryVoltage = undefined;
- state.BatteryStatus = 'Invalid';
- break;
- }
- break;
- }
- default:
- return;
- }
- if (page !== 0x01 || state._EventCount !== oldEventCount) {
- sensor.emit('oxygenData', state);
- }
-}
diff --git a/src/sensors/AntPlusBaseSensor.ts b/src/sensors/AntPlusBaseSensor.ts
new file mode 100644
index 0000000..267a3cf
--- /dev/null
+++ b/src/sensors/AntPlusBaseSensor.ts
@@ -0,0 +1,14 @@
+import { AttachProps, BaseSensor } from './BaseSensor';
+
+export abstract class AntPlusBaseSensor extends BaseSensor {
+ protected scan(type: string) {
+ return super.scan(type, 57);
+ }
+
+ protected attach(props: AttachProps) {
+ return super.attach({
+ ...props,
+ frequency: 57,
+ });
+ }
+}
diff --git a/src/sensors/AntPlusScanner.ts b/src/sensors/AntPlusScanner.ts
new file mode 100644
index 0000000..21fff25
--- /dev/null
+++ b/src/sensors/AntPlusScanner.ts
@@ -0,0 +1,77 @@
+import { Temporal } from '@js-temporal/polyfill';
+import { Constants } from '../Constants';
+import { GarminStick2 } from '../GarminStick2';
+import { GarminStick3 } from '../GarminStick3';
+import { Messages } from '../Messages';
+import { AntPlusBaseSensor } from './AntPlusBaseSensor';
+
+export abstract class AntPlusScanner extends AntPlusBaseSensor {
+ protected abstract deviceType(): number;
+
+ protected abstract createStateIfNew(deviceId: number): void;
+
+ protected abstract updateRssiAndThreshold(
+ deviceId: number,
+ rssi: number,
+ threshold: number
+ ): void;
+
+ constructor(stick: GarminStick2 | GarminStick3) {
+ super(stick);
+ this.decodeDataCbk = this.decodeData.bind(this);
+ }
+
+ public scan() {
+ return super.scan('receive');
+ }
+
+ protected attach(): Promise {
+ throw new Error('attach unsupported');
+ }
+
+ protected send(): Promise {
+ throw new Error('send unsupported');
+ }
+
+ private decodeData(data: DataView) {
+ if (
+ data.byteLength <= Messages.BUFFER_INDEX_EXT_MSG_BEGIN + 3 ||
+ !(data.getUint8(Messages.BUFFER_INDEX_EXT_MSG_BEGIN) & 0x80)
+ ) {
+ console.warn('wrong message format', data.buffer);
+ return;
+ }
+
+ const deviceId = data.getUint16(
+ Messages.BUFFER_INDEX_EXT_MSG_BEGIN + 1,
+ true
+ );
+ const deviceType = data.getUint8(Messages.BUFFER_INDEX_EXT_MSG_BEGIN + 3);
+
+ if (deviceType !== this.deviceType()) {
+ return;
+ }
+
+ this.createStateIfNew(deviceId);
+
+ if (data.getUint8(Messages.BUFFER_INDEX_EXT_MSG_BEGIN) & 0x40) {
+ if (data.getUint8(Messages.BUFFER_INDEX_EXT_MSG_BEGIN + 5) === 0x20) {
+ this.updateRssiAndThreshold(
+ deviceId,
+ data.getInt8(Messages.BUFFER_INDEX_EXT_MSG_BEGIN + 6),
+ data.getInt8(Messages.BUFFER_INDEX_EXT_MSG_BEGIN + 7)
+ );
+ }
+ }
+
+ switch (data.getUint8(Messages.BUFFER_INDEX_MSG_TYPE)) {
+ case Constants.MESSAGE_CHANNEL_BROADCAST_DATA:
+ case Constants.MESSAGE_CHANNEL_ACKNOWLEDGED_DATA:
+ case Constants.MESSAGE_CHANNEL_BURST_DATA:
+ this.updateState(deviceId, data, Temporal.Now.zonedDateTimeISO());
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/src/sensors/AntPlusSensor.ts b/src/sensors/AntPlusSensor.ts
new file mode 100644
index 0000000..ecb4d51
--- /dev/null
+++ b/src/sensors/AntPlusSensor.ts
@@ -0,0 +1,51 @@
+import { Temporal } from '@js-temporal/polyfill';
+import { Constants } from '../Constants';
+import { GarminStick2 } from '../GarminStick2';
+import { GarminStick3 } from '../GarminStick3';
+import { Messages } from '../Messages';
+import { AntPlusBaseSensor } from './AntPlusBaseSensor';
+import { AttachProps } from './BaseSensor';
+
+export abstract class AntPlusSensor extends AntPlusBaseSensor {
+ constructor(stick: GarminStick2 | GarminStick3) {
+ super(stick);
+ this.decodeDataCbk = this.decodeData.bind(this);
+ }
+
+ protected scan(): Promise {
+ throw 'scanning unsupported';
+ }
+
+ protected attach(props: AttachProps) {
+ return super.attach(props);
+ }
+
+ private decodeData(data: DataView) {
+ switch (data.getUint8(Messages.BUFFER_INDEX_MSG_TYPE)) {
+ case Constants.MESSAGE_CHANNEL_BROADCAST_DATA:
+ case Constants.MESSAGE_CHANNEL_ACKNOWLEDGED_DATA:
+ case Constants.MESSAGE_CHANNEL_BURST_DATA:
+ if (this.channel !== undefined && this.deviceID === 0) {
+ this.write(
+ Messages.requestMessage(this.channel, Constants.MESSAGE_CHANNEL_ID)
+ );
+ }
+ if (this.deviceID !== undefined) {
+ this.updateState(
+ this.deviceID,
+ data,
+ Temporal.Now.zonedDateTimeISO()
+ );
+ }
+ break;
+ case Constants.MESSAGE_CHANNEL_ID:
+ this.deviceID = data.getUint16(Messages.BUFFER_INDEX_MSG_DATA, true);
+ this.transmissionType = data.getUint8(
+ Messages.BUFFER_INDEX_MSG_DATA + 3
+ );
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/src/sensors/BaseSensor.ts b/src/sensors/BaseSensor.ts
new file mode 100644
index 0000000..031d106
--- /dev/null
+++ b/src/sensors/BaseSensor.ts
@@ -0,0 +1,277 @@
+import { Temporal } from '@js-temporal/polyfill';
+import { SendCallback } from '../ant';
+import { Constants } from '../Constants';
+import { EventEmitter } from '../lib/EventEmitter';
+import { Messages } from '../Messages';
+import { USBDriver } from '../USBDriver';
+
+export type AttachProps = {
+ channel: number;
+ deviceID: number;
+ type?: string;
+ deviceType?: number;
+ transmissionType?: number;
+ timeout?: number;
+ period?: number;
+};
+
+export abstract class BaseSensor extends EventEmitter {
+ channel: number | undefined;
+ deviceID: number | undefined;
+ transmissionType: number | undefined;
+
+ private msgQueue: { msg: DataView; cbk?: SendCallback }[] = [];
+
+ protected decodeDataCbk: ((data: DataView) => void) | undefined;
+ protected statusCbk:
+ | ((status: { msg: number; code: number }) => Promise)
+ | undefined;
+
+ protected abstract updateState(
+ deviceId: number,
+ data: DataView,
+ UpdateAt: Temporal.ZonedDateTime
+ ): void;
+
+ constructor(private stick: USBDriver) {
+ super();
+ stick.on('read', this.handleEventMessages.bind(this));
+ }
+
+ protected async scan(type: string, frequency: number) {
+ if (this.channel !== undefined) {
+ throw 'already attached';
+ }
+
+ if (!this.stick.canScan) {
+ throw 'stick cannot scan';
+ }
+
+ const channel = 0;
+ const mc = this.msgQueue.shift();
+ const onStatus = async (status: { msg: number; code: number }) => {
+ switch (status.msg) {
+ case Constants.MESSAGE_RF:
+ switch (status.code) {
+ case Constants.EVENT_CHANNEL_CLOSED:
+ case Constants.EVENT_RX_FAIL_GO_TO_SEARCH:
+ await this.write(Messages.unassignChannel(channel));
+ return true;
+ case Constants.EVENT_TRANSFER_TX_COMPLETED:
+ case Constants.EVENT_TRANSFER_TX_FAILED:
+ case Constants.EVENT_RX_FAIL:
+ case Constants.INVALID_SCAN_TX_CHANNEL:
+ if (mc && mc.cbk) {
+ mc.cbk(status.code === Constants.EVENT_TRANSFER_TX_COMPLETED);
+ }
+ if (this.msgQueue.length) {
+ await this.write(this.msgQueue[0].msg);
+ }
+ return true;
+ default:
+ break;
+ }
+ break;
+ case Constants.MESSAGE_CHANNEL_ASSIGN:
+ await this.write(Messages.setDevice(channel, 0, 0, 0));
+ return true;
+ case Constants.MESSAGE_CHANNEL_ID:
+ await this.write(Messages.setFrequency(channel, frequency));
+ return true;
+ case Constants.MESSAGE_CHANNEL_FREQUENCY:
+ await this.write(Messages.setRxExt());
+ return true;
+ case Constants.MESSAGE_ENABLE_RX_EXT:
+ await this.write(Messages.libConfig(channel, 0xe0));
+ return true;
+ case Constants.MESSAGE_LIB_CONFIG:
+ await this.write(Messages.openRxScan());
+ return true;
+ case Constants.MESSAGE_CHANNEL_OPEN_RX_SCAN:
+ queueMicrotask(() => this.emit('attached'));
+ return true;
+ case Constants.MESSAGE_CHANNEL_CLOSE:
+ return true;
+ case Constants.MESSAGE_CHANNEL_UNASSIGN:
+ this.statusCbk = undefined;
+ this.channel = undefined;
+ queueMicrotask(() => this.emit('detached'));
+ return true;
+ case Constants.MESSAGE_CHANNEL_ACKNOWLEDGED_DATA:
+ return status.code === Constants.TRANSFER_IN_PROGRESS;
+ default:
+ break;
+ }
+ return false;
+ };
+
+ if (this.stick.isScanning()) {
+ this.channel = channel;
+ this.deviceID = 0;
+ this.transmissionType = 0;
+
+ this.statusCbk = onStatus;
+
+ queueMicrotask(() => this.emit('attached'));
+ } else if (this.stick.attach(this, true)) {
+ this.channel = channel;
+ this.deviceID = 0;
+ this.transmissionType = 0;
+
+ this.statusCbk = onStatus;
+
+ await this.write(Messages.assignChannel(channel, type));
+ } else {
+ throw 'cannot attach';
+ }
+ }
+
+ protected async attach(
+ props: AttachProps & {
+ frequency: number;
+ }
+ ) {
+ const {
+ channel,
+ deviceID,
+ type,
+ deviceType,
+ transmissionType,
+ timeout,
+ period,
+ frequency,
+ } = props;
+ if (this.channel !== undefined) {
+ throw 'already attached';
+ }
+ if (!this.stick.attach(this, false)) {
+ throw 'cannot attach';
+ }
+
+ this.channel = channel;
+ this.deviceID = deviceID;
+ this.transmissionType = transmissionType;
+
+ const mc = this.msgQueue.shift();
+ const onStatus = async (status: { msg: number; code: number }) => {
+ switch (status.msg) {
+ case Constants.MESSAGE_RF:
+ switch (status.code) {
+ case Constants.EVENT_CHANNEL_CLOSED:
+ case Constants.EVENT_RX_FAIL_GO_TO_SEARCH:
+ await this.write(Messages.unassignChannel(channel));
+ return true;
+ case Constants.EVENT_TRANSFER_TX_COMPLETED:
+ case Constants.EVENT_TRANSFER_TX_FAILED:
+ case Constants.EVENT_RX_FAIL:
+ case Constants.INVALID_SCAN_TX_CHANNEL:
+ if (mc && mc.cbk) {
+ mc.cbk(status.code === Constants.EVENT_TRANSFER_TX_COMPLETED);
+ }
+ if (this.msgQueue.length) {
+ await this.write(this.msgQueue[0].msg);
+ }
+ return true;
+ default:
+ break;
+ }
+ break;
+ case Constants.MESSAGE_CHANNEL_ASSIGN:
+ if (deviceType === undefined) {
+ throw 'deviceType required';
+ }
+ if (transmissionType === undefined) {
+ throw 'transmissionType required';
+ }
+ await this.write(
+ Messages.setDevice(channel, deviceID, deviceType, transmissionType)
+ );
+ return true;
+ case Constants.MESSAGE_CHANNEL_ID:
+ if (timeout === undefined) {
+ throw 'timeout required';
+ }
+ await this.write(Messages.searchChannel(channel, timeout));
+ return true;
+ case Constants.MESSAGE_CHANNEL_SEARCH_TIMEOUT:
+ await this.write(Messages.setFrequency(channel, frequency));
+ return true;
+ case Constants.MESSAGE_CHANNEL_FREQUENCY:
+ if (period === undefined) {
+ throw 'period required';
+ }
+ await this.write(Messages.setPeriod(channel, period));
+ return true;
+ case Constants.MESSAGE_CHANNEL_PERIOD:
+ await this.write(Messages.libConfig(channel, 0xe0));
+ return true;
+ case Constants.MESSAGE_LIB_CONFIG:
+ await this.write(Messages.openChannel(channel));
+ return true;
+ case Constants.MESSAGE_CHANNEL_OPEN:
+ queueMicrotask(() => this.emit('attached'));
+ return true;
+ case Constants.MESSAGE_CHANNEL_CLOSE:
+ return true;
+ case Constants.MESSAGE_CHANNEL_UNASSIGN:
+ this.statusCbk = undefined;
+ this.channel = undefined;
+ queueMicrotask(() => this.emit('detached'));
+ return true;
+ case Constants.MESSAGE_CHANNEL_ACKNOWLEDGED_DATA:
+ return status.code === Constants.TRANSFER_IN_PROGRESS;
+ default:
+ break;
+ }
+ return false;
+ };
+
+ this.statusCbk = onStatus;
+
+ await this.write(Messages.assignChannel(channel, type));
+ }
+
+ public async detach() {
+ if (this.channel === undefined) {
+ return;
+ }
+ await this.write(Messages.closeChannel(this.channel));
+ if (!this.stick.detach(this)) {
+ throw 'error detaching';
+ }
+ }
+
+ protected async write(data: DataView) {
+ await this.stick.write(data);
+ }
+
+ private async handleEventMessages(data: DataView) {
+ const messageID = data.getUint8(Messages.BUFFER_INDEX_MSG_TYPE);
+ const channel = data.getUint8(Messages.BUFFER_INDEX_CHANNEL_NUM);
+ if (channel === this.channel) {
+ if (messageID === Constants.MESSAGE_CHANNEL_EVENT) {
+ const status = {
+ msg: data.getUint8(Messages.BUFFER_INDEX_MSG_DATA),
+ code: data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 1),
+ };
+
+ const handled = this.statusCbk && (await this.statusCbk(status));
+ if (!handled) {
+ this.emit('eventData', {
+ message: data.getUint8(Messages.BUFFER_INDEX_MSG_DATA),
+ code: data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 1),
+ });
+ }
+ } else if (this.decodeDataCbk) {
+ this.decodeDataCbk(data);
+ }
+ }
+ }
+
+ protected async send(data: DataView, cbk?: SendCallback) {
+ this.msgQueue.push({ msg: data, cbk });
+ if (this.msgQueue.length === 1) {
+ await this.write(data);
+ }
+ }
+}
diff --git a/src/sensors/BicyclePowerScanState.ts b/src/sensors/BicyclePowerScanState.ts
new file mode 100644
index 0000000..a0a6c71
--- /dev/null
+++ b/src/sensors/BicyclePowerScanState.ts
@@ -0,0 +1,18 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#521_tab
+ * Spec sheet: https://www.thisisant.com/resources/bicycle-power/
+ */
+
+import { BicyclePowerSensorState } from './BicyclePowerSensorState';
+
+export class BicyclePowerScanState extends BicyclePowerSensorState {
+ Rssi: number;
+
+ Threshold: number;
+
+ constructor(deviceId: number) {
+ super(deviceId);
+ this.Rssi = 0;
+ this.Threshold = 0;
+ }
+}
diff --git a/src/sensors/BicyclePowerScanner.ts b/src/sensors/BicyclePowerScanner.ts
new file mode 100644
index 0000000..23b8eba
--- /dev/null
+++ b/src/sensors/BicyclePowerScanner.ts
@@ -0,0 +1,36 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#521_tab
+ * Spec sheet: https://www.thisisant.com/resources/bicycle-power/
+ */
+
+import { updateBicyclePowerSensorState } from '../lib/UpdateState';
+import { AntPlusScanner } from './AntPlusScanner';
+import { BicyclePowerScanState } from './BicyclePowerScanState';
+import { BicyclePowerSensor } from './BicyclePowerSensor';
+
+export class BicyclePowerScanner extends AntPlusScanner {
+ protected deviceType() {
+ return BicyclePowerSensor.deviceType;
+ }
+
+ private states: { [id: number]: BicyclePowerScanState } = {};
+
+ protected createStateIfNew(deviceId: number) {
+ if (!this.states[deviceId]) {
+ this.states[deviceId] = new BicyclePowerScanState(deviceId);
+ }
+ }
+
+ protected updateRssiAndThreshold(
+ deviceId: number,
+ rssi: number,
+ threshold: number
+ ) {
+ this.states[deviceId].Rssi = rssi;
+ this.states[deviceId].Threshold = threshold;
+ }
+
+ protected updateState(deviceId: number, data: DataView) {
+ updateBicyclePowerSensorState(this, this.states[deviceId], data);
+ }
+}
diff --git a/src/sensors/BicyclePowerSensor.ts b/src/sensors/BicyclePowerSensor.ts
new file mode 100644
index 0000000..5ec75a5
--- /dev/null
+++ b/src/sensors/BicyclePowerSensor.ts
@@ -0,0 +1,35 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#521_tab
+ * Spec sheet: https://www.thisisant.com/resources/bicycle-power/
+ */
+
+import { updateBicyclePowerSensorState } from '../lib/UpdateState';
+import { AntPlusSensor } from './AntPlusSensor';
+import { BicyclePowerSensorState } from './BicyclePowerSensorState';
+
+export class BicyclePowerSensor extends AntPlusSensor {
+ static deviceType = 0x0b;
+
+ public async attachSensor(channel: number, deviceID: number): Promise {
+ await super.attach({
+ channel,
+ type: 'receive',
+ deviceID,
+ deviceType: BicyclePowerSensor.deviceType,
+ transmissionType: 0,
+ timeout: 255,
+ period: 8182,
+ });
+ this.state = new BicyclePowerSensorState(deviceID);
+ }
+
+ private state?: BicyclePowerSensorState;
+
+ protected updateState(deviceId: number, data: DataView) {
+ if (!this.state) {
+ throw new Error('BicyclePowerSensor: not attached');
+ }
+ this.state.DeviceID = deviceId;
+ updateBicyclePowerSensorState(this, this.state, data);
+ }
+}
diff --git a/src/sensors/BicyclePowerSensorState.ts b/src/sensors/BicyclePowerSensorState.ts
new file mode 100644
index 0000000..a03ceb7
--- /dev/null
+++ b/src/sensors/BicyclePowerSensorState.ts
@@ -0,0 +1,146 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#521_tab
+ * Spec sheet: https://www.thisisant.com/resources/bicycle-power/
+ */
+
+import { Messages } from '../Messages';
+
+export class BicyclePowerSensorState {
+ constructor(deviceID: number) {
+ this.DeviceID = deviceID;
+ }
+
+ DeviceID: number;
+
+ PedalPower?: number;
+
+ RightPedalPower?: number;
+
+ LeftPedalPower?: number;
+
+ Cadence?: number;
+
+ AccumulatedPower?: number;
+
+ Power?: number;
+
+ offset = 0;
+
+ EventCount?: number;
+
+ TimeStamp?: number;
+
+ Slope?: number;
+
+ TorqueTicksStamp?: number;
+
+ CalculatedCadence?: number;
+
+ CalculatedTorque?: number;
+
+ CalculatedPower?: number;
+
+ updateState(data: DataView): BicyclePowerSensorState {
+ const page = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA);
+ switch (page) {
+ case 0x01: {
+ const calID = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 1);
+ if (calID === 0x10) {
+ const calParam = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2);
+ if (calParam === 0x01) {
+ this.offset = data.getUint16(
+ Messages.BUFFER_INDEX_MSG_DATA + 6,
+ true
+ );
+ }
+ }
+ break;
+ }
+ case 0x10: {
+ const pedalPower = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2);
+ if (pedalPower !== 0xff) {
+ if (pedalPower & 0x80) {
+ this.PedalPower = pedalPower & 0x7f;
+ this.RightPedalPower = this.PedalPower;
+ this.LeftPedalPower = 100 - this.RightPedalPower;
+ } else {
+ this.PedalPower = pedalPower & 0x7f;
+ this.RightPedalPower = undefined;
+ this.LeftPedalPower = undefined;
+ }
+ } else {
+ this.PedalPower = undefined;
+ this.RightPedalPower = undefined;
+ this.LeftPedalPower = undefined;
+ }
+ const cadence = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3);
+ if (cadence !== 0xff) {
+ this.Cadence = cadence;
+ } else {
+ this.Cadence = undefined;
+ }
+ this.AccumulatedPower = data.getUint16(
+ Messages.BUFFER_INDEX_MSG_DATA + 4,
+ true
+ );
+ this.Power = data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 6, true);
+ break;
+ }
+ case 0x20: {
+ const oldEventCount = this.EventCount;
+ const oldTimeStamp = this.TimeStamp;
+ const oldTorqueTicksStamp = this.TorqueTicksStamp;
+
+ let eventCount = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 1);
+ const slope = data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 3, true);
+ let timeStamp = data.getUint16(
+ Messages.BUFFER_INDEX_MSG_DATA + 5,
+ true
+ );
+ let torqueTicksStamp = data.getUint16(
+ Messages.BUFFER_INDEX_MSG_DATA + 7,
+ true
+ );
+
+ if (timeStamp !== oldTimeStamp && eventCount !== oldEventCount) {
+ this.EventCount = eventCount;
+ if (oldEventCount && oldEventCount > eventCount) {
+ // Hit rollover value
+ eventCount += 255;
+ }
+
+ this.TimeStamp = timeStamp;
+ if (oldTimeStamp && oldTimeStamp > timeStamp) {
+ // Hit rollover value
+ timeStamp += 65400;
+ }
+
+ this.Slope = slope;
+ this.TorqueTicksStamp = torqueTicksStamp;
+ if (oldTorqueTicksStamp && oldTorqueTicksStamp > torqueTicksStamp) {
+ // Hit rollover value
+ torqueTicksStamp += 65535;
+ }
+
+ const elapsedTime = (timeStamp - (oldTimeStamp || 0)) * 0.0005;
+ const torqueTicks = torqueTicksStamp - (oldTorqueTicksStamp || 0);
+
+ const cadencePeriod =
+ elapsedTime / (eventCount - (oldEventCount || 0)); // s
+ const cadence = Math.round(60 / cadencePeriod); // rpm
+ this.CalculatedCadence = cadence;
+
+ const torqueFrequency = 1 / (elapsedTime / torqueTicks) - this.offset; // Hz
+ const torque = torqueFrequency / (slope / 10); // Nm
+ this.CalculatedTorque = torque;
+
+ this.CalculatedPower = (torque * cadence * Math.PI) / 30; // Watts
+ }
+ break;
+ }
+ default:
+ }
+
+ return this;
+ }
+}
diff --git a/src/sensors/CadenceScanState.ts b/src/sensors/CadenceScanState.ts
new file mode 100644
index 0000000..61115cd
--- /dev/null
+++ b/src/sensors/CadenceScanState.ts
@@ -0,0 +1,12 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#523_tab
+ * Spec sheet: https://www.thisisant.com/resources/bicycle-speed-and-cadence/
+ */
+
+import { CadenceSensorState } from './CadenceSensorState';
+
+export class CadenceScanState extends CadenceSensorState {
+ Rssi?: number;
+
+ Threshold?: number;
+}
diff --git a/src/sensors/CadenceScanner.ts b/src/sensors/CadenceScanner.ts
new file mode 100644
index 0000000..d883654
--- /dev/null
+++ b/src/sensors/CadenceScanner.ts
@@ -0,0 +1,42 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#523_tab
+ * Spec sheet: https://www.thisisant.com/resources/bicycle-speed-and-cadence/
+ */
+
+import { updateCadenceSensorState } from '../lib/UpdateState';
+import { AntPlusScanner } from './AntPlusScanner';
+import { CadenceScanState } from './CadenceScanState';
+import { CadenceSensor } from './CadenceSensor';
+
+export class CadenceScanner extends AntPlusScanner {
+ protected deviceType() {
+ return CadenceSensor.deviceType;
+ }
+
+ wheelCircumference = 2.199; // default 70cm wheel
+
+ public setWheelCircumference(wheelCircumference: number) {
+ this.wheelCircumference = wheelCircumference;
+ }
+
+ private states: { [id: number]: CadenceScanState } = {};
+
+ protected createStateIfNew(deviceId: number) {
+ if (!this.states[deviceId]) {
+ this.states[deviceId] = new CadenceScanState(deviceId);
+ }
+ }
+
+ protected updateRssiAndThreshold(
+ deviceId: number,
+ rssi: number,
+ threshold: number
+ ) {
+ this.states[deviceId].Rssi = rssi;
+ this.states[deviceId].Threshold = threshold;
+ }
+
+ protected updateState(deviceId: number, data: DataView) {
+ updateCadenceSensorState(this, this.states[deviceId], data);
+ }
+}
diff --git a/src/sensors/CadenceSensor.ts b/src/sensors/CadenceSensor.ts
new file mode 100644
index 0000000..cb7d591
--- /dev/null
+++ b/src/sensors/CadenceSensor.ts
@@ -0,0 +1,41 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#523_tab
+ * Spec sheet: https://www.thisisant.com/resources/bicycle-speed-and-cadence/
+ */
+
+import { updateCadenceSensorState } from '../lib/UpdateState';
+import { AntPlusSensor } from './AntPlusSensor';
+import { CadenceSensorState } from './CadenceSensorState';
+
+export class CadenceSensor extends AntPlusSensor {
+ static deviceType = 0x7a;
+
+ wheelCircumference = 2.199; // default 70cm wheel
+
+ public setWheelCircumference(wheelCircumference: number) {
+ this.wheelCircumference = wheelCircumference;
+ }
+
+ public async attachSensor(channel: number, deviceID: number): Promise {
+ await super.attach({
+ channel,
+ type: 'receive',
+ deviceID,
+ deviceType: CadenceSensor.deviceType,
+ transmissionType: 0,
+ timeout: 255,
+ period: 8086,
+ });
+ this.state = new CadenceSensorState(deviceID);
+ }
+
+ private state?: CadenceSensorState;
+
+ protected updateState(deviceId: number, data: DataView) {
+ if (!this.state) {
+ throw new Error('CadenceSensor: not attached');
+ }
+ this.state.DeviceID = deviceId;
+ updateCadenceSensorState(this, this.state, data);
+ }
+}
diff --git a/src/sensors/CadenceSensorState.ts b/src/sensors/CadenceSensorState.ts
new file mode 100644
index 0000000..c572d23
--- /dev/null
+++ b/src/sensors/CadenceSensorState.ts
@@ -0,0 +1,136 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#523_tab
+ * Spec sheet: https://www.thisisant.com/resources/bicycle-speed-and-cadence/
+ */
+
+import { Messages } from '../Messages';
+
+export class CadenceSensorState {
+ constructor(deviceID: number) {
+ this.DeviceID = deviceID;
+ }
+
+ DeviceID: number;
+
+ CadenceEventTime?: number;
+
+ CumulativeCadenceRevolutionCount?: number;
+
+ CalculatedCadence?: number;
+
+ OperatingTime?: number;
+
+ ManId?: number;
+
+ SerialNumber?: number;
+
+ HwVersion?: number;
+
+ SwVersion?: number;
+
+ ModelNum?: number;
+
+ BatteryVoltage?: number;
+
+ BatteryStatus?: 'New' | 'Good' | 'Ok' | 'Low' | 'Critical' | 'Invalid';
+
+ Motion?: boolean;
+
+ updateState(data: DataView): CadenceSensorState {
+ const TOGGLE_MASK = 0x80;
+ const pageNum = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA);
+ switch (
+ pageNum & ~TOGGLE_MASK // check the new pages and remove the toggle bit
+ ) {
+ case 1:
+ // decode the cumulative operating time
+ this.OperatingTime = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 1);
+ this.OperatingTime |=
+ data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2) << 8;
+ this.OperatingTime |=
+ data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3) << 16;
+ this.OperatingTime *= 2;
+ break;
+ case 2:
+ // decode the Manufacturer ID
+ this.ManId = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 1);
+ // decode the 4 byte serial number
+ this.SerialNumber = this.DeviceID;
+ this.SerialNumber |=
+ data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 2, true) << 16;
+ this.SerialNumber >>>= 0;
+ break;
+ case 3:
+ // decode HW version, SW version, and model number
+ this.HwVersion = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 1);
+ this.SwVersion = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2);
+ this.ModelNum = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3);
+ break;
+ case 4: {
+ const batteryFrac = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2);
+ const batteryStatus = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3);
+ this.BatteryVoltage = (batteryStatus & 0x0f) + batteryFrac / 256;
+ const batteryFlags = (batteryStatus & 0x70) >>> 4;
+ switch (batteryFlags) {
+ case 1:
+ this.BatteryStatus = 'New';
+ break;
+ case 2:
+ this.BatteryStatus = 'Good';
+ break;
+ case 3:
+ this.BatteryStatus = 'Ok';
+ break;
+ case 4:
+ this.BatteryStatus = 'Low';
+ break;
+ case 5:
+ this.BatteryStatus = 'Critical';
+ break;
+ default:
+ this.BatteryVoltage = undefined;
+ this.BatteryStatus = 'Invalid';
+ break;
+ }
+ break;
+ }
+ case 5:
+ this.Motion =
+ (data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 1) & 0x01) === 0x01;
+ break;
+ default:
+ break;
+ }
+
+ // get old state for calculating cumulative values
+ const oldCadenceTime = this.CadenceEventTime;
+ const oldCadenceCount = this.CumulativeCadenceRevolutionCount;
+
+ let cadenceTime = data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 4, true);
+ let cadenceCount = data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 6, true);
+
+ if (cadenceTime !== oldCadenceTime) {
+ this.CadenceEventTime = cadenceTime;
+ this.CumulativeCadenceRevolutionCount = cadenceCount;
+
+ if (oldCadenceTime && oldCadenceTime > cadenceTime) {
+ // Hit rollover value
+ cadenceTime += 1024 * 64;
+ }
+
+ if (oldCadenceCount && oldCadenceCount > cadenceCount) {
+ // Hit rollover value
+ cadenceCount += 1024 * 64;
+ }
+
+ const cadence =
+ (60 * (cadenceCount - (oldCadenceCount || 0)) * 1024) /
+ (cadenceTime - (oldCadenceTime || 0));
+ if (!isNaN(cadence)) {
+ this.CalculatedCadence = cadence;
+ }
+ }
+
+ return this;
+ }
+}
diff --git a/src/sensors/EnvironmentScanState.ts b/src/sensors/EnvironmentScanState.ts
new file mode 100644
index 0000000..64cf48c
--- /dev/null
+++ b/src/sensors/EnvironmentScanState.ts
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 Tom Cosgrove
+ * Copyright (c) 2015 Alessandro Vergani
+ *
+ * This file is licensed under the MIT License (MIT):
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+import { EnvironmentSensorState } from './EnvironmentSensorState';
+
+export class EnvironmentScanState extends EnvironmentSensorState {
+ Rssi?: number;
+
+ Threshold?: number;
+}
diff --git a/src/sensors/EnvironmentScanner.ts b/src/sensors/EnvironmentScanner.ts
new file mode 100644
index 0000000..2e545ef
--- /dev/null
+++ b/src/sensors/EnvironmentScanner.ts
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019 Tom Cosgrove
+ * Copyright (c) 2015 Alessandro Vergani
+ *
+ * This file is licensed under the MIT License (MIT):
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+import { updateEnvironmentSensorState } from '../lib/UpdateState';
+import { AntPlusScanner } from './AntPlusScanner';
+import { EnvironmentScanState } from './EnvironmentScanState';
+import { EnvironmentSensor } from './EnvironmentSensor';
+
+export class EnvironmentScanner extends AntPlusScanner {
+ protected deviceType() {
+ return EnvironmentSensor.deviceType;
+ }
+
+ private states: { [id: number]: EnvironmentScanState } = {};
+
+ protected createStateIfNew(deviceId: number) {
+ if (!this.states[deviceId]) {
+ this.states[deviceId] = new EnvironmentScanState(deviceId);
+ }
+ }
+
+ protected updateRssiAndThreshold(
+ deviceId: number,
+ rssi: number,
+ threshold: number
+ ) {
+ this.states[deviceId].Rssi = rssi;
+ this.states[deviceId].Threshold = threshold;
+ }
+
+ protected updateState(deviceId: number, data: DataView) {
+ updateEnvironmentSensorState(this, this.states[deviceId], data);
+ }
+}
diff --git a/src/sensors/EnvironmentSensor.ts b/src/sensors/EnvironmentSensor.ts
new file mode 100644
index 0000000..66357cd
--- /dev/null
+++ b/src/sensors/EnvironmentSensor.ts
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2019 Tom Cosgrove
+ * Copyright (c) 2015 Alessandro Vergani
+ *
+ * This file is licensed under the MIT License (MIT):
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+import { updateEnvironmentSensorState } from '../lib/UpdateState';
+import { AntPlusSensor } from './AntPlusSensor';
+import { EnvironmentSensorState } from './EnvironmentSensorState';
+
+export class EnvironmentSensor extends AntPlusSensor {
+ static deviceType = 25;
+
+ public async attachSensor(channel: any, deviceID: number) {
+ await super.attach({
+ channel,
+ type: 'receive',
+ deviceID,
+ deviceType: EnvironmentSensor.deviceType,
+ transmissionType: 0,
+ timeout: 255,
+ period: 8192,
+ });
+ this.state = new EnvironmentSensorState(deviceID);
+ }
+
+ private state?: EnvironmentSensorState;
+
+ protected updateState(deviceId: number, data: DataView) {
+ if (!this.state) {
+ throw new Error('EnvironmentSensor: not attached');
+ }
+ this.state.DeviceID = deviceId;
+ updateEnvironmentSensorState(this, this.state, data);
+ }
+}
diff --git a/src/sensors/EnvironmentSensorState.ts b/src/sensors/EnvironmentSensorState.ts
new file mode 100644
index 0000000..0d9569d
--- /dev/null
+++ b/src/sensors/EnvironmentSensorState.ts
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019 Tom Cosgrove
+ * Copyright (c) 2015 Alessandro Vergani
+ *
+ * This file is licensed under the MIT License (MIT):
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+import { Messages } from '../Messages';
+
+export class EnvironmentSensorState {
+ constructor(deviceId: number) {
+ this.DeviceID = deviceId;
+ }
+
+ DeviceID: number;
+
+ EventCount?: number;
+
+ Temperature?: number;
+
+ updateState(data: DataView): EnvironmentSensorState {
+ const page = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA);
+ if (page === 1) {
+ this.EventCount = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2);
+ this.Temperature =
+ data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 6, true) / 100;
+ }
+
+ return this;
+ }
+}
diff --git a/src/sensors/FitnessEquipmentScanState.ts b/src/sensors/FitnessEquipmentScanState.ts
new file mode 100644
index 0000000..7980b69
--- /dev/null
+++ b/src/sensors/FitnessEquipmentScanState.ts
@@ -0,0 +1,12 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#525_tab
+ * Spec sheet: https://www.thisisant.com/resources/fitness-equipment-device/
+ */
+
+import { FitnessEquipmentSensorState } from './FitnessEquipmentSensorState';
+
+export class FitnessEquipmentScanState extends FitnessEquipmentSensorState {
+ Rssi?: number;
+
+ Threshold?: number;
+}
diff --git a/src/sensors/FitnessEquipmentScanner.ts b/src/sensors/FitnessEquipmentScanner.ts
new file mode 100644
index 0000000..cf867a8
--- /dev/null
+++ b/src/sensors/FitnessEquipmentScanner.ts
@@ -0,0 +1,36 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#525_tab
+ * Spec sheet: https://www.thisisant.com/resources/fitness-equipment-device/
+ */
+
+import { updateFitnessEquipmentSensorState } from '../lib/UpdateState';
+import { AntPlusScanner } from './AntPlusScanner';
+import { FitnessEquipmentScanState } from './FitnessEquipmentScanState';
+import { FitnessEquipmentSensor } from './FitnessEquipmentSensor';
+
+export class FitnessEquipmentScanner extends AntPlusScanner {
+ protected deviceType() {
+ return FitnessEquipmentSensor.deviceType;
+ }
+
+ private states: { [id: number]: FitnessEquipmentScanState } = {};
+
+ protected createStateIfNew(deviceId: number) {
+ if (!this.states[deviceId]) {
+ this.states[deviceId] = new FitnessEquipmentScanState(deviceId);
+ }
+ }
+
+ protected updateRssiAndThreshold(
+ deviceId: number,
+ rssi: number | undefined,
+ threshold: number | undefined
+ ) {
+ this.states[deviceId].Rssi = rssi;
+ this.states[deviceId].Threshold = threshold;
+ }
+
+ protected updateState(deviceId: number, data: DataView) {
+ updateFitnessEquipmentSensorState(this, this.states[deviceId], data);
+ }
+}
diff --git a/src/sensors/FitnessEquipmentSensor.ts b/src/sensors/FitnessEquipmentSensor.ts
new file mode 100644
index 0000000..c19c038
--- /dev/null
+++ b/src/sensors/FitnessEquipmentSensor.ts
@@ -0,0 +1,327 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#525_tab
+ * Spec sheet: https://www.thisisant.com/resources/fitness-equipment-device/
+ */
+
+import { SendCallback } from '../ant';
+import { updateFitnessEquipmentSensorState } from '../lib/UpdateState';
+import { Messages } from '../Messages';
+import { AntPlusSensor } from './AntPlusSensor';
+import { FitnessEquipmentSensorState } from './FitnessEquipmentSensorState';
+
+export class FitnessEquipmentSensor extends AntPlusSensor {
+ static deviceType = 0x11;
+
+ public async attachSensor(channel: number, deviceID: number): Promise {
+ await super.attach({
+ channel,
+ type: 'receive',
+ deviceID,
+ deviceType: FitnessEquipmentSensor.deviceType,
+ transmissionType: 0,
+ timeout: 255,
+ period: 8192,
+ });
+ this.state = new FitnessEquipmentSensorState(deviceID);
+ }
+
+ private state?: FitnessEquipmentSensorState;
+
+ protected updateState(deviceId: number, data: DataView) {
+ if (!this.state) {
+ throw new Error('FitnessEquipmentSensor: not attached');
+ }
+ this.state.DeviceID = deviceId;
+ updateFitnessEquipmentSensorState(this, this.state, data);
+ }
+
+ private _setUserConfiguration(
+ userWeight?: number,
+ bikeWeight?: number,
+ wheelDiameter?: number,
+ gearRatio?: number,
+ cbk?: SendCallback
+ ) {
+ if (this.channel === undefined) {
+ throw new Error('FitnessEquipmentSensor: not attached');
+ }
+ const m =
+ userWeight === undefined
+ ? 0xffff
+ : Math.max(0, Math.min(65534, Math.round(userWeight * 100)));
+ const df =
+ wheelDiameter === undefined ? 0xff : Math.round(wheelDiameter * 10) % 10;
+ const mb =
+ bikeWeight === undefined
+ ? 0xfff
+ : Math.max(0, Math.min(1000, Math.round(bikeWeight * 20)));
+ const d =
+ wheelDiameter === undefined
+ ? 0xff
+ : Math.max(0, Math.min(254, Math.round(wheelDiameter)));
+ const gr =
+ gearRatio === undefined
+ ? 0x00
+ : Math.max(1, Math.min(255, Math.round(gearRatio / 0.03)));
+ const payload = [
+ 0x37,
+ m & 0xff,
+ (m >> 8) & 0xff,
+ 0xff,
+ (df & 0xf) | ((mb & 0xf) << 4),
+ (mb >> 4) & 0xf,
+ d & 0xff,
+ gr & 0xff,
+ ];
+ const msg = Messages.acknowledgedData(this.channel, payload);
+ this.send(msg, cbk);
+ }
+
+ public setUserConfiguration(cbk: SendCallback): void;
+
+ public setUserConfiguration(userWeight: number, cbk?: SendCallback): void;
+
+ public setUserConfiguration(
+ userWeight: number,
+ bikeWeight: number,
+ cbk?: SendCallback
+ ): void;
+
+ public setUserConfiguration(
+ userWeight: number,
+ bikeWeight: number,
+ wheelDiameter: number,
+ cbk?: SendCallback
+ ): void;
+
+ public setUserConfiguration(
+ userWeight: number,
+ bikeWeight: number,
+ wheelDiameter: number,
+ gearRatio: number,
+ cbk?: SendCallback
+ ): void;
+
+ public setUserConfiguration(
+ userWeight?: number | SendCallback,
+ bikeWeight?: number | SendCallback,
+ wheelDiameter?: number | SendCallback,
+ gearRatio?: number | SendCallback,
+ cbk?: SendCallback
+ ) {
+ if (typeof userWeight === 'function') {
+ return this._setUserConfiguration(
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ userWeight
+ );
+ }
+ if (typeof bikeWeight === 'function') {
+ return this._setUserConfiguration(
+ userWeight,
+ undefined,
+ undefined,
+ undefined,
+ bikeWeight
+ );
+ }
+ if (typeof wheelDiameter === 'function') {
+ return this._setUserConfiguration(
+ userWeight,
+ bikeWeight,
+ undefined,
+ undefined,
+ wheelDiameter
+ );
+ }
+ if (typeof gearRatio === 'function') {
+ return this._setUserConfiguration(
+ userWeight,
+ bikeWeight,
+ wheelDiameter,
+ undefined,
+ gearRatio
+ );
+ }
+ return this._setUserConfiguration(
+ userWeight,
+ bikeWeight,
+ wheelDiameter,
+ gearRatio,
+ cbk
+ );
+ }
+
+ public setBasicResistance(resistance: number, cbk?: SendCallback) {
+ if (this.channel === undefined) {
+ throw new Error('Channel not attached');
+ }
+ const res = Math.max(0, Math.min(200, Math.round(resistance * 2)));
+ const payload = [0x30, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, res & 0xff];
+ const msg = Messages.acknowledgedData(this.channel, payload);
+ this.send(msg, cbk);
+ }
+
+ public setTargetPower(power: number, cbk?: SendCallback) {
+ if (this.channel === undefined) {
+ throw new Error('Channel not attached');
+ }
+ const p = Math.max(0, Math.min(4000, Math.round(power * 4)));
+ const payload = [
+ 0x31,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ p & 0xff,
+ (p >> 8) & 0xff,
+ ];
+ const msg = Messages.acknowledgedData(this.channel, payload);
+ this.send(msg, cbk);
+ }
+
+ private _setWindResistance(
+ windCoeff?: number,
+ windSpeed?: number,
+ draftFactor?: number,
+ cbk?: SendCallback
+ ) {
+ if (this.channel === undefined) {
+ throw new Error('Channel not attached');
+ }
+ const wc =
+ windCoeff === undefined
+ ? 0xff
+ : Math.max(0, Math.min(186, Math.round(windCoeff * 100)));
+ const ws =
+ windSpeed === undefined
+ ? 0xff
+ : Math.max(0, Math.min(254, Math.round(windSpeed + 127)));
+ const df =
+ draftFactor === undefined
+ ? 0xff
+ : Math.max(0, Math.min(100, Math.round(draftFactor * 100)));
+ const payload = [
+ 0x32,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ wc & 0xff,
+ ws & 0xff,
+ df & 0xff,
+ ];
+ const msg = Messages.acknowledgedData(this.channel, payload);
+ this.send(msg, cbk);
+ }
+
+ public setWindResistance(cbk: SendCallback): void;
+
+ public setWindResistance(windCoeff: number, cbk?: SendCallback): void;
+
+ public setWindResistance(
+ windCoeff: number,
+ windSpeed: number,
+ cbk?: SendCallback
+ ): void;
+
+ public setWindResistance(
+ windCoeff: number,
+ windSpeed: number,
+ draftFactor: number,
+ cbk?: SendCallback
+ ): void;
+
+ public setWindResistance(
+ windCoeff?: number | SendCallback,
+ windSpeed?: number | SendCallback,
+ draftFactor?: number | SendCallback,
+ cbk?: SendCallback
+ ) {
+ if (typeof windCoeff === 'function') {
+ return this._setWindResistance(
+ undefined,
+ undefined,
+ undefined,
+ windCoeff
+ );
+ }
+ if (typeof windSpeed === 'function') {
+ return this._setWindResistance(
+ windCoeff,
+ undefined,
+ undefined,
+ windSpeed
+ );
+ }
+ if (typeof draftFactor === 'function') {
+ return this._setWindResistance(
+ windCoeff,
+ windSpeed,
+ undefined,
+ draftFactor
+ );
+ }
+ return this._setWindResistance(windCoeff, windSpeed, draftFactor, cbk);
+ }
+
+ private _setTrackResistance(
+ slope?: number,
+ rollingResistanceCoeff?: number,
+ cbk?: SendCallback
+ ) {
+ if (this.channel === undefined) {
+ throw new Error('Channel not attached');
+ }
+ const s =
+ slope === undefined
+ ? 0xffff
+ : Math.max(0, Math.min(40000, Math.round((slope + 200) * 100)));
+ const rr =
+ rollingResistanceCoeff === undefined
+ ? 0xff
+ : Math.max(
+ 0,
+ Math.min(254, Math.round(rollingResistanceCoeff * 20000))
+ );
+ const payload = [
+ 0x33,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ s & 0xff,
+ (s >> 8) & 0xff,
+ rr & 0xff,
+ ];
+ const msg = Messages.acknowledgedData(this.channel, payload);
+ this.send(msg, cbk);
+ }
+
+ public setTrackResistance(cbk: SendCallback): void;
+
+ public setTrackResistance(slope: number, cbk?: SendCallback): void;
+
+ public setTrackResistance(
+ slope: number,
+ rollingResistanceCoeff: number,
+ cbk?: SendCallback
+ ): void;
+
+ public setTrackResistance(
+ slope?: number | SendCallback,
+ rollingResistanceCoeff?: number | SendCallback,
+ cbk?: SendCallback
+ ) {
+ if (typeof slope === 'function') {
+ return this._setTrackResistance(undefined, undefined, slope);
+ }
+ if (typeof rollingResistanceCoeff === 'function') {
+ return this._setTrackResistance(slope, undefined, rollingResistanceCoeff);
+ }
+ return this._setTrackResistance(slope, rollingResistanceCoeff, cbk);
+ }
+}
diff --git a/src/sensors/FitnessEquipmentSensorState.ts b/src/sensors/FitnessEquipmentSensorState.ts
new file mode 100644
index 0000000..c15d1be
--- /dev/null
+++ b/src/sensors/FitnessEquipmentSensorState.ts
@@ -0,0 +1,809 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#525_tab
+ * Spec sheet: https://www.thisisant.com/resources/fitness-equipment-device/
+ */
+
+import { Messages } from '../Messages';
+
+export class FitnessEquipmentSensorState {
+ constructor(deviceID: number) {
+ this.DeviceID = deviceID;
+ }
+
+ _EventCount0x19?: number;
+
+ _EventCount0x1A?: number;
+
+ DeviceID: number;
+
+ Temperature?: number;
+
+ ZeroOffset?: number;
+
+ SpinDownTime?: number;
+
+ EquipmentType?:
+ | 'Treadmill'
+ | 'Elliptical'
+ | 'Reserved'
+ | 'Rower'
+ | 'Climber'
+ | 'NordicSkier'
+ | 'Trainer/StationaryBike'
+ | 'General';
+
+ ElapsedTime?: number;
+
+ Distance?: number;
+
+ RealSpeed?: number;
+
+ VirtualSpeed?: number;
+
+ HeartRate?: number;
+
+ HeartRateSource?: 'HandContact' | 'EM' | 'ANT+';
+
+ State?: 'OFF' | 'READY' | 'IN_USE' | 'FINISHED';
+
+ CycleLength?: number;
+
+ Incline?: number;
+
+ Resistance?: number;
+
+ METs?: number;
+
+ CaloricBurnRate?: number;
+
+ Calories?: number;
+
+ AscendedDistance?: number;
+
+ DescendedDistance?: number;
+
+ Strides?: number;
+
+ Strokes?: number;
+
+ Cadence?: number;
+
+ AccumulatedPower?: number;
+
+ InstantaneousPower?: number;
+
+ AveragePower?: number;
+
+ TrainerStatus?: number;
+
+ TargetStatus?: 'OnTarget' | 'LowSpeed' | 'HighSpeed';
+
+ WheelTicks?: number;
+
+ WheelPeriod?: number;
+
+ Torque?: number;
+
+ HwVersion?: number;
+
+ ManId?: number;
+
+ ModelNum?: number;
+
+ SwVersion?: number;
+
+ SerialNumber?: number;
+
+ PairedDevices: any[] = [];
+
+ resetState() {
+ delete this.ElapsedTime;
+ delete this.Distance;
+ delete this.RealSpeed;
+ delete this.VirtualSpeed;
+ delete this.HeartRate;
+ delete this.HeartRateSource;
+ delete this.CycleLength;
+ delete this.Incline;
+ delete this.Resistance;
+ delete this.METs;
+ delete this.CaloricBurnRate;
+ delete this.Calories;
+ delete this._EventCount0x19;
+ delete this._EventCount0x1A;
+ delete this.Cadence;
+ delete this.AccumulatedPower;
+ delete this.InstantaneousPower;
+ delete this.AveragePower;
+ delete this.TrainerStatus;
+ delete this.TargetStatus;
+ delete this.AscendedDistance;
+ delete this.DescendedDistance;
+ delete this.Strides;
+ delete this.Strokes;
+ delete this.WheelTicks;
+ delete this.WheelPeriod;
+ delete this.Torque;
+ }
+
+ updateState(data: DataView): FitnessEquipmentSensorState {
+ const page = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA);
+ switch (page) {
+ case 0x01: {
+ const temperature = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3);
+ if (temperature !== 0xff) {
+ this.Temperature = -25 + temperature * 0.5;
+ }
+ const calBF = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 1);
+ if (calBF & 0x40) {
+ this.ZeroOffset = data.getUint16(
+ Messages.BUFFER_INDEX_MSG_DATA + 4,
+ true
+ );
+ }
+ if (calBF & 0x80) {
+ this.SpinDownTime = data.getUint16(
+ Messages.BUFFER_INDEX_MSG_DATA + 6,
+ true
+ );
+ }
+ break;
+ }
+ case 0x10: {
+ const equipmentTypeBF = data.getUint8(
+ Messages.BUFFER_INDEX_MSG_DATA + 1
+ );
+ switch (equipmentTypeBF & 0x1f) {
+ case 19:
+ this.EquipmentType = 'Treadmill';
+ break;
+ case 20:
+ this.EquipmentType = 'Elliptical';
+ break;
+ case 21:
+ this.EquipmentType = 'Reserved';
+ break;
+ case 22:
+ this.EquipmentType = 'Rower';
+ break;
+ case 23:
+ this.EquipmentType = 'Climber';
+ break;
+ case 24:
+ this.EquipmentType = 'NordicSkier';
+ break;
+ case 25:
+ this.EquipmentType = 'Trainer/StationaryBike';
+ break;
+ default:
+ this.EquipmentType = 'General';
+ break;
+ }
+ let elapsedTime = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2);
+ let distance = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3);
+ const speed = data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 4, true);
+ const heartRate = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 6);
+ const capStateBF = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 7);
+ if (heartRate !== 0xff) {
+ switch (capStateBF & 0x03) {
+ case 3: {
+ this.HeartRate = heartRate;
+ this.HeartRateSource = 'HandContact';
+ break;
+ }
+ case 2: {
+ this.HeartRate = heartRate;
+ this.HeartRateSource = 'EM';
+ break;
+ }
+ case 1: {
+ this.HeartRate = heartRate;
+ this.HeartRateSource = 'ANT+';
+ break;
+ }
+ default: {
+ delete this.HeartRate;
+ delete this.HeartRateSource;
+ break;
+ }
+ }
+ }
+
+ elapsedTime /= 4;
+ const oldElapsedTime = (this.ElapsedTime || 0) % 64;
+ if (elapsedTime !== oldElapsedTime) {
+ if (oldElapsedTime > elapsedTime) {
+ // Hit rollover value
+ elapsedTime += 64;
+ }
+ }
+ this.ElapsedTime =
+ (this.ElapsedTime || 0) + elapsedTime - oldElapsedTime;
+
+ if (capStateBF & 0x04) {
+ const oldDistance = (this.Distance || 0) % 256;
+ if (distance !== oldDistance) {
+ if (oldDistance > distance) {
+ // Hit rollover value
+ distance += 256;
+ }
+ }
+ this.Distance = (this.Distance || 0) + distance - oldDistance;
+ } else {
+ delete this.Distance;
+ }
+ if (capStateBF & 0x08) {
+ this.VirtualSpeed = speed / 1000;
+ delete this.RealSpeed;
+ } else {
+ delete this.VirtualSpeed;
+ this.RealSpeed = speed / 1000;
+ }
+ switch ((capStateBF & 0x70) >> 4) {
+ case 1:
+ this.State = 'OFF';
+ break;
+ case 2:
+ this.State = 'READY';
+ this.resetState();
+ break;
+ case 3:
+ this.State = 'IN_USE';
+ break;
+ case 4:
+ this.State = 'FINISHED';
+ break;
+ default:
+ delete this.State;
+ break;
+ }
+ if (capStateBF & 0x80) {
+ // lap
+ }
+ break;
+ }
+ case 0x11: {
+ const cycleLen = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3);
+ const incline = data.getInt16(Messages.BUFFER_INDEX_MSG_DATA + 4, true);
+ const resistance = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 6);
+ const capStateBF = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 7);
+ if (cycleLen !== 0xff) {
+ this.CycleLength = cycleLen / 100;
+ }
+ if (incline >= -10000 && incline <= 10000) {
+ this.Incline = incline / 100;
+ }
+ if (resistance !== 0xff) {
+ this.Resistance = resistance;
+ }
+ switch ((capStateBF & 0x70) >> 4) {
+ case 1:
+ this.State = 'OFF';
+ break;
+ case 2:
+ this.State = 'READY';
+ this.resetState();
+ break;
+ case 3:
+ this.State = 'IN_USE';
+ break;
+ case 4:
+ this.State = 'FINISHED';
+ break;
+ default:
+ delete this.State;
+ break;
+ }
+ if (capStateBF & 0x80) {
+ // lap
+ }
+ break;
+ }
+ case 0x12: {
+ const mets = data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 2, true);
+ const caloricbr = data.getUint16(
+ Messages.BUFFER_INDEX_MSG_DATA + 4,
+ true
+ );
+ const calories = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 6);
+ const capStateBF = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 7);
+ if (mets !== 0xffff) {
+ this.METs = mets / 100;
+ }
+ if (caloricbr !== 0xffff) {
+ this.CaloricBurnRate = caloricbr / 10;
+ }
+ if (capStateBF & 0x01) {
+ this.Calories = calories;
+ }
+ switch ((capStateBF & 0x70) >> 4) {
+ case 1:
+ this.State = 'OFF';
+ break;
+ case 2:
+ this.State = 'READY';
+ this.resetState();
+ break;
+ case 3:
+ this.State = 'IN_USE';
+ break;
+ case 4:
+ this.State = 'FINISHED';
+ break;
+ default:
+ delete this.State;
+ break;
+ }
+ if (capStateBF & 0x80) {
+ // lap
+ }
+ break;
+ }
+ case 0x13: {
+ const cadence = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 4);
+ let negDistance = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 5);
+ let posDistance = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 6);
+ const flagStateBF = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 7);
+
+ if (cadence !== 0xff) {
+ this.Cadence = cadence;
+ }
+
+ if (flagStateBF & 0x02) {
+ const oldNegDistance = (this.DescendedDistance || 0) % 256;
+ if (negDistance !== oldNegDistance) {
+ if (oldNegDistance > negDistance) {
+ negDistance += 256;
+ }
+ }
+ this.DescendedDistance =
+ (this.DescendedDistance || 0) + negDistance - oldNegDistance;
+ }
+
+ if (flagStateBF & 0x01) {
+ const oldPosDistance = (this.AscendedDistance || 0) % 256;
+ if (posDistance !== oldPosDistance) {
+ if (oldPosDistance > posDistance) {
+ posDistance += 256;
+ }
+ }
+ this.AscendedDistance =
+ (this.AscendedDistance || 0) + posDistance - oldPosDistance;
+ }
+
+ switch ((flagStateBF & 0x70) >> 4) {
+ case 1:
+ this.State = 'OFF';
+ break;
+ case 2:
+ this.State = 'READY';
+ this.resetState();
+ break;
+ case 3:
+ this.State = 'IN_USE';
+ break;
+ case 4:
+ this.State = 'FINISHED';
+ break;
+ default:
+ delete this.State;
+ break;
+ }
+ if (flagStateBF & 0x80) {
+ // lap
+ }
+
+ break;
+ }
+ case 0x14: {
+ let posDistance = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2);
+ let strides = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3);
+ const cadence = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 4);
+ const power = data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 5, true);
+ const flagStateBF = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 7);
+
+ if (cadence !== 0xff) {
+ this.Cadence = cadence;
+ }
+
+ if (power !== 0xffff) {
+ this.InstantaneousPower = power;
+ }
+
+ if (flagStateBF & 0x02) {
+ const oldPosDistance = (this.AscendedDistance || 0) % 256;
+ if (posDistance !== oldPosDistance) {
+ if (oldPosDistance > posDistance) {
+ posDistance += 256;
+ }
+ }
+ this.AscendedDistance =
+ (this.AscendedDistance || 0) + posDistance - oldPosDistance;
+ }
+
+ if (flagStateBF & 0x01) {
+ const oldStrides = (this.Strides || 0) % 256;
+ if (strides !== oldStrides) {
+ if (oldStrides > strides) {
+ strides += 256;
+ }
+ }
+ this.Strides = (this.Strides || 0) + strides - oldStrides;
+ }
+
+ switch ((flagStateBF & 0x70) >> 4) {
+ case 1:
+ this.State = 'OFF';
+ break;
+ case 2:
+ this.State = 'READY';
+ this.resetState();
+ break;
+ case 3:
+ this.State = 'IN_USE';
+ break;
+ case 4:
+ this.State = 'FINISHED';
+ break;
+ default:
+ delete this.State;
+ break;
+ }
+ if (flagStateBF & 0x80) {
+ // lap
+ }
+
+ break;
+ }
+ case 0x16: {
+ let strokes = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3);
+ const cadence = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 4);
+ const power = data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 5, true);
+ const flagStateBF = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 7);
+
+ if (cadence !== 0xff) {
+ this.Cadence = cadence;
+ }
+
+ if (power !== 0xffff) {
+ this.InstantaneousPower = power;
+ }
+
+ if (flagStateBF & 0x01) {
+ const oldStrokes = (this.Strokes || 0) % 256;
+ if (strokes !== oldStrokes) {
+ if (oldStrokes > strokes) {
+ strokes += 256;
+ }
+ }
+ this.Strokes = (this.Strokes || 0) + strokes - oldStrokes;
+ }
+
+ switch ((flagStateBF & 0x70) >> 4) {
+ case 1:
+ this.State = 'OFF';
+ break;
+ case 2:
+ this.State = 'READY';
+ this.resetState();
+ break;
+ case 3:
+ this.State = 'IN_USE';
+ break;
+ case 4:
+ this.State = 'FINISHED';
+ break;
+ default:
+ delete this.State;
+ break;
+ }
+ if (flagStateBF & 0x80) {
+ // lap
+ }
+
+ break;
+ }
+ case 0x17: {
+ let strides = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3);
+ const cadence = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 4);
+ const power = data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 5, true);
+ const flagStateBF = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 7);
+
+ if (cadence !== 0xff) {
+ this.Cadence = cadence;
+ }
+
+ if (power !== 0xffff) {
+ this.InstantaneousPower = power;
+ }
+
+ if (flagStateBF & 0x01) {
+ const oldStrides = (this.Strides || 0) % 256;
+ if (strides !== oldStrides) {
+ if (oldStrides > strides) {
+ strides += 256;
+ }
+ }
+ this.Strides = (this.Strides || 0) + strides - oldStrides;
+ }
+
+ switch ((flagStateBF & 0x70) >> 4) {
+ case 1:
+ this.State = 'OFF';
+ break;
+ case 2:
+ this.State = 'READY';
+ this.resetState();
+ break;
+ case 3:
+ this.State = 'IN_USE';
+ break;
+ case 4:
+ this.State = 'FINISHED';
+ break;
+ default:
+ delete this.State;
+ break;
+ }
+ if (flagStateBF & 0x80) {
+ // lap
+ }
+
+ break;
+ }
+ case 0x18: {
+ let strides = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3);
+ const cadence = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 4);
+ const power = data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 5, true);
+ const flagStateBF = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 7);
+
+ if (cadence !== 0xff) {
+ this.Cadence = cadence;
+ }
+
+ if (power !== 0xffff) {
+ this.InstantaneousPower = power;
+ }
+
+ if (flagStateBF & 0x01) {
+ const oldStrides = (this.Strides || 0) % 256;
+ if (strides !== oldStrides) {
+ if (oldStrides > strides) {
+ strides += 256;
+ }
+ }
+ this.Strides = (this.Strides || 0) + strides - oldStrides;
+ }
+
+ switch ((flagStateBF & 0x70) >> 4) {
+ case 1:
+ this.State = 'OFF';
+ break;
+ case 2:
+ this.State = 'READY';
+ this.resetState();
+ break;
+ case 3:
+ this.State = 'IN_USE';
+ break;
+ case 4:
+ this.State = 'FINISHED';
+ break;
+ default:
+ delete this.State;
+ break;
+ }
+ if (flagStateBF & 0x80) {
+ // lap
+ }
+
+ break;
+ }
+ case 0x19: {
+ const oldEventCount = this._EventCount0x19 || 0;
+
+ let eventCount = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 1);
+ const cadence = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2);
+ let accPower = data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 3, true);
+ const power =
+ data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 5, true) & 0xfff;
+ const trainerStatus =
+ data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 6) >> 4;
+ const flagStateBF = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 7);
+
+ if (eventCount !== oldEventCount) {
+ this._EventCount0x19 = eventCount;
+ if (oldEventCount > eventCount) {
+ // Hit rollover value
+ eventCount += 255;
+ }
+ }
+
+ if (cadence !== 0xff) {
+ this.Cadence = cadence;
+ }
+
+ if (power !== 0xfff) {
+ this.InstantaneousPower = power;
+
+ const oldAccPower = (this.AccumulatedPower || 0) % 65536;
+ if (accPower !== oldAccPower) {
+ if (oldAccPower > accPower) {
+ accPower += 65536;
+ }
+ }
+ this.AccumulatedPower =
+ (this.AccumulatedPower || 0) + accPower - oldAccPower;
+
+ this.AveragePower =
+ (accPower - oldAccPower) / (eventCount - oldEventCount);
+ }
+
+ this.TrainerStatus = trainerStatus;
+
+ switch (flagStateBF & 0x03) {
+ case 0:
+ this.TargetStatus = 'OnTarget';
+ break;
+ case 1:
+ this.TargetStatus = 'LowSpeed';
+ break;
+ case 2:
+ this.TargetStatus = 'HighSpeed';
+ break;
+ default:
+ delete this.TargetStatus;
+ break;
+ }
+
+ switch ((flagStateBF & 0x70) >> 4) {
+ case 1:
+ this.State = 'OFF';
+ break;
+ case 2:
+ this.State = 'READY';
+ this.resetState();
+ break;
+ case 3:
+ this.State = 'IN_USE';
+ break;
+ case 4:
+ this.State = 'FINISHED';
+ break;
+ default:
+ delete this.State;
+ break;
+ }
+ if (flagStateBF & 0x80) {
+ // lap
+ }
+
+ break;
+ }
+ case 0x1a: {
+ const oldEventCount = this._EventCount0x1A || 0;
+
+ let eventCount = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 1);
+ let wheelTicks = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2);
+ let accWheelPeriod = data.getUint16(
+ Messages.BUFFER_INDEX_MSG_DATA + 3,
+ true
+ );
+ let accTorque = data.getUint16(
+ Messages.BUFFER_INDEX_MSG_DATA + 5,
+ true
+ );
+ const flagStateBF = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 7);
+
+ if (eventCount !== oldEventCount) {
+ this._EventCount0x1A = eventCount;
+ if (oldEventCount > eventCount) {
+ // Hit rollover value
+ eventCount += 255;
+ }
+ }
+
+ const oldWheelTicks = (this.WheelTicks || 0) % 256;
+ if (wheelTicks !== oldWheelTicks) {
+ if (oldWheelTicks > wheelTicks) {
+ wheelTicks += 65536;
+ }
+ }
+ this.WheelTicks = (this.WheelTicks || 0) + wheelTicks - oldWheelTicks;
+
+ const oldWheelPeriod = (this.WheelPeriod || 0) % 256;
+ if (accWheelPeriod !== oldWheelPeriod) {
+ if (oldWheelPeriod > accWheelPeriod) {
+ accWheelPeriod += 65536;
+ }
+ }
+ this.WheelPeriod =
+ (this.WheelPeriod || 0) + accWheelPeriod - oldWheelPeriod;
+
+ const oldTorque = (this.Torque || 0) % 256;
+ if (accTorque !== oldTorque) {
+ if (oldTorque > accTorque) {
+ accTorque += 65536;
+ }
+ }
+ this.Torque = (this.Torque || 0) + accTorque - oldTorque;
+
+ switch ((flagStateBF & 0x70) >> 4) {
+ case 1:
+ this.State = 'OFF';
+ break;
+ case 2:
+ this.State = 'READY';
+ this.resetState();
+ break;
+ case 3:
+ this.State = 'IN_USE';
+ break;
+ case 4:
+ this.State = 'FINISHED';
+ break;
+ default:
+ delete this.State;
+ break;
+ }
+ if (flagStateBF & 0x80) {
+ // lap
+ }
+
+ break;
+ }
+ case 0x50: {
+ this.HwVersion = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3);
+ this.ManId = data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 4, true);
+ this.ModelNum = data.getUint16(
+ Messages.BUFFER_INDEX_MSG_DATA + 6,
+ true
+ );
+ break;
+ }
+ case 0x51: {
+ const swRevSup = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2);
+ const swRevMain = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3);
+ const serial = data.getInt32(Messages.BUFFER_INDEX_MSG_DATA + 4, true);
+
+ this.SwVersion = swRevMain;
+
+ if (swRevSup !== 0xff) {
+ this.SwVersion += swRevSup / 1000;
+ }
+
+ if (serial !== 0xffffffff) {
+ this.SerialNumber = serial;
+ }
+
+ break;
+ }
+ case 0x56: {
+ const idx = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 1);
+ const tot = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2);
+ const chState = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3);
+ const devId = data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 4, true);
+ const trType = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 6);
+ const devType = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 7);
+
+ if (idx === 0) {
+ this.PairedDevices = [];
+ }
+
+ if (tot > 0) {
+ this.PairedDevices.push({
+ id: devId,
+ type: devType,
+ paired: !!(chState & 0x80),
+ });
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ return this;
+ }
+}
diff --git a/src/sensors/HeartRateScanState.ts b/src/sensors/HeartRateScanState.ts
new file mode 100644
index 0000000..6369e7f
--- /dev/null
+++ b/src/sensors/HeartRateScanState.ts
@@ -0,0 +1,12 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#526_tab
+ * Spec sheet: https://www.thisisant.com/resources/heart-rate-monitor/
+ */
+
+import { HeartRateSensorState } from './HeartRateSensorState';
+
+export class HeartRateScanState extends HeartRateSensorState {
+ Rssi?: number;
+
+ Threshold?: number;
+}
diff --git a/src/sensors/HeartRateScanner.ts b/src/sensors/HeartRateScanner.ts
new file mode 100644
index 0000000..e4c5dbb
--- /dev/null
+++ b/src/sensors/HeartRateScanner.ts
@@ -0,0 +1,48 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#526_tab
+ * Spec sheet: https://www.thisisant.com/resources/heart-rate-monitor/
+ */
+
+import { Page, PageState } from '../ant';
+import { updateHeartRateSensorState } from '../lib/UpdateState';
+import { AntPlusScanner } from './AntPlusScanner';
+import { HeartRateScanState } from './HeartRateScanState';
+import { HeartRateSensor } from './HeartRateSensor';
+
+export class HeartRateScanner extends AntPlusScanner {
+ protected deviceType() {
+ return HeartRateSensor.deviceType;
+ }
+
+ private states: { [id: number]: HeartRateScanState } = {};
+
+ private pages: { [id: number]: Page } = {};
+
+ protected createStateIfNew(deviceId: number) {
+ if (!this.states[deviceId]) {
+ this.states[deviceId] = new HeartRateScanState(deviceId);
+ }
+
+ if (!this.pages[deviceId]) {
+ this.pages[deviceId] = { oldPage: -1, pageState: PageState.INIT_PAGE };
+ }
+ }
+
+ protected updateRssiAndThreshold(
+ deviceId: number,
+ rssi: number | undefined,
+ threshold: number | undefined
+ ) {
+ this.states[deviceId].Rssi = rssi;
+ this.states[deviceId].Threshold = threshold;
+ }
+
+ protected updateState(deviceId: number, data: DataView) {
+ updateHeartRateSensorState(
+ this,
+ this.states[deviceId],
+ data,
+ this.pages[deviceId]
+ );
+ }
+}
diff --git a/src/sensors/HeartRateSensor.ts b/src/sensors/HeartRateSensor.ts
new file mode 100644
index 0000000..46a4d8d
--- /dev/null
+++ b/src/sensors/HeartRateSensor.ts
@@ -0,0 +1,41 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#526_tab
+ * Spec sheet: https://www.thisisant.com/resources/heart-rate-monitor/
+ */
+
+import { Page, PageState } from '../ant';
+import { updateHeartRateSensorState } from '../lib/UpdateState';
+import { AntPlusSensor } from './AntPlusSensor';
+import { HeartRateSensorState } from './HeartRateSensorState';
+
+export class HeartRateSensor extends AntPlusSensor {
+ static deviceType = 120;
+
+ public async attachSensor(channel: number, deviceID: number) {
+ await super.attach({
+ channel,
+ type: 'receive',
+ deviceID,
+ deviceType: HeartRateSensor.deviceType,
+ transmissionType: 0,
+ timeout: 255,
+ period: 8070,
+ });
+ this.state = new HeartRateSensorState(deviceID);
+ }
+
+ private state?: HeartRateSensorState;
+
+ private page: Page = {
+ oldPage: -1,
+ pageState: PageState.INIT_PAGE,
+ };
+
+ protected updateState(deviceId: number, data: DataView) {
+ if (!this.state) {
+ throw new Error('HeartRateSensor: not attached');
+ }
+ this.state.DeviceID = deviceId;
+ updateHeartRateSensorState(this, this.state, data, this.page);
+ }
+}
diff --git a/src/sensors/HeartRateSensorState.ts b/src/sensors/HeartRateSensorState.ts
new file mode 100644
index 0000000..097d11f
--- /dev/null
+++ b/src/sensors/HeartRateSensorState.ts
@@ -0,0 +1,173 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#526_tab
+ * Spec sheet: https://www.thisisant.com/resources/heart-rate-monitor/
+ */
+
+import { Page, PageState } from '../ant';
+import { Messages } from '../Messages';
+
+export class HeartRateSensorState {
+ constructor(deviceId: number) {
+ this.DeviceID = deviceId;
+ }
+
+ DeviceID: number;
+
+ BeatTime?: number;
+
+ BeatCount?: number;
+
+ ComputedHeartRate?: number;
+
+ OperatingTime?: number;
+
+ ManId?: number;
+
+ SerialNumber?: number;
+
+ HwVersion?: number;
+
+ SwVersion?: number;
+
+ ModelNum?: number;
+
+ PreviousBeat?: number;
+
+ IntervalAverage?: number;
+
+ IntervalMax?: number;
+
+ SessionAverage?: number;
+
+ SupportedFeatures?: number;
+
+ EnabledFeatures?: number;
+
+ BatteryLevel?: number;
+
+ BatteryVoltage?: number;
+
+ BatteryStatus?: 'New' | 'Good' | 'Ok' | 'Low' | 'Critical' | 'Invalid';
+
+ updateState(data: DataView, page: Page): HeartRateSensorState {
+ const TOGGLE_MASK = 0x80;
+ const pageNum = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA);
+ if (page.pageState === PageState.INIT_PAGE) {
+ page.pageState = PageState.STD_PAGE; // change the state to STD_PAGE and allow the checking of old and new pages
+ // decode with pages if the page byte or toggle bit has changed
+ } else if (
+ pageNum !== page.oldPage ||
+ page.pageState === PageState.EXT_PAGE
+ ) {
+ page.pageState = PageState.EXT_PAGE; // set the state to use the extended page format
+ switch (
+ pageNum & ~TOGGLE_MASK // check the new pages and remove the toggle bit
+ ) {
+ case 1:
+ // decode the cumulative operating time
+ this.OperatingTime = data.getUint8(
+ Messages.BUFFER_INDEX_MSG_DATA + 1
+ );
+ this.OperatingTime |=
+ data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2) << 8;
+ this.OperatingTime |=
+ data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3) << 16;
+ this.OperatingTime *= 2;
+ break;
+ case 2:
+ // decode the Manufacturer ID
+ this.ManId = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 1);
+ // decode the 4 byte serial number
+ this.SerialNumber = this.DeviceID;
+ this.SerialNumber |=
+ data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 2, true) << 16;
+ this.SerialNumber >>>= 0;
+ break;
+ case 3:
+ // decode HW version, SW version, and model number
+ this.HwVersion = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 1);
+ this.SwVersion = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2);
+ this.ModelNum = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3);
+ break;
+ case 4:
+ // decode the previous heart beat measurement time
+ this.PreviousBeat = data.getUint16(
+ Messages.BUFFER_INDEX_MSG_DATA + 2,
+ true
+ );
+ break;
+ case 5:
+ this.IntervalAverage = data.getUint8(
+ Messages.BUFFER_INDEX_MSG_DATA + 1
+ );
+ this.IntervalMax = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2);
+ this.SessionAverage = data.getUint8(
+ Messages.BUFFER_INDEX_MSG_DATA + 3
+ );
+ break;
+ case 6:
+ this.SupportedFeatures = data.getUint8(
+ Messages.BUFFER_INDEX_MSG_DATA + 2
+ );
+ this.EnabledFeatures = data.getUint8(
+ Messages.BUFFER_INDEX_MSG_DATA + 3
+ );
+ break;
+ case 7: {
+ const batteryLevel = data.getUint8(
+ Messages.BUFFER_INDEX_MSG_DATA + 1
+ );
+ const batteryFrac = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2);
+ const batteryStatus = data.getUint8(
+ Messages.BUFFER_INDEX_MSG_DATA + 3
+ );
+ if (batteryLevel !== 0xff) {
+ this.BatteryLevel = batteryLevel;
+ }
+ this.BatteryVoltage = (batteryStatus & 0x0f) + batteryFrac / 256;
+ const batteryFlags = (batteryStatus & 0x70) >>> 4;
+ switch (batteryFlags) {
+ case 1:
+ this.BatteryStatus = 'New';
+ break;
+ case 2:
+ this.BatteryStatus = 'Good';
+ break;
+ case 3:
+ this.BatteryStatus = 'Ok';
+ break;
+ case 4:
+ this.BatteryStatus = 'Low';
+ break;
+ case 5:
+ this.BatteryStatus = 'Critical';
+ break;
+ default:
+ this.BatteryVoltage = undefined;
+ this.BatteryStatus = 'Invalid';
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ // decode the last four bytes of the HRM format, the first byte of this message is the channel number
+ this.decodeDefaultHRM(
+ new DataView(data.buffer.slice(Messages.BUFFER_INDEX_MSG_DATA + 4))
+ );
+ page.oldPage = pageNum;
+
+ return this;
+ }
+
+ private decodeDefaultHRM(pucPayload: DataView) {
+ // decode the measurement time data (two bytes)
+ this.BeatTime = pucPayload.getUint16(0, true);
+ // decode the measurement count data
+ this.BeatCount = pucPayload.getUint8(2);
+ // decode the measurement count data
+ this.ComputedHeartRate = pucPayload.getUint8(3);
+ }
+}
diff --git a/src/sensors/MuscleOxygenScanState.ts b/src/sensors/MuscleOxygenScanState.ts
new file mode 100644
index 0000000..be2729f
--- /dev/null
+++ b/src/sensors/MuscleOxygenScanState.ts
@@ -0,0 +1,12 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#2343_tab
+ * Spec sheet: https://www.thisisant.com/resources/ant-device-profile-muscle-oxygen/
+ */
+
+import { MuscleOxygenSensorState } from './MuscleOxygenSensorState';
+
+export class MuscleOxygenScanState extends MuscleOxygenSensorState {
+ Rssi?: number;
+
+ Threshold?: number;
+}
diff --git a/src/sensors/MuscleOxygenScanner.ts b/src/sensors/MuscleOxygenScanner.ts
new file mode 100644
index 0000000..233cfc3
--- /dev/null
+++ b/src/sensors/MuscleOxygenScanner.ts
@@ -0,0 +1,36 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#2343_tab
+ * Spec sheet: https://www.thisisant.com/resources/ant-device-profile-muscle-oxygen/
+ */
+
+import { updateMuscleOxygenSensorState } from '../lib/UpdateState';
+import { AntPlusScanner } from './AntPlusScanner';
+import { MuscleOxygenScanState } from './MuscleOxygenScanState';
+import { MuscleOxygenSensor } from './MuscleOxygenSensor';
+
+export class MuscleOxygenScanner extends AntPlusScanner {
+ protected deviceType() {
+ return MuscleOxygenSensor.deviceType;
+ }
+
+ private states: { [id: number]: MuscleOxygenScanState } = {};
+
+ protected createStateIfNew(deviceId: number) {
+ if (!this.states[deviceId]) {
+ this.states[deviceId] = new MuscleOxygenScanState(deviceId);
+ }
+ }
+
+ protected updateRssiAndThreshold(
+ deviceId: number,
+ rssi: number | undefined,
+ threshold: number | undefined
+ ) {
+ this.states[deviceId].Rssi = rssi;
+ this.states[deviceId].Threshold = threshold;
+ }
+
+ protected updateState(deviceId: number, data: DataView) {
+ updateMuscleOxygenSensorState(this, this.states[deviceId], data);
+ }
+}
diff --git a/src/sensors/MuscleOxygenSensor.ts b/src/sensors/MuscleOxygenSensor.ts
new file mode 100644
index 0000000..cad1600
--- /dev/null
+++ b/src/sensors/MuscleOxygenSensor.ts
@@ -0,0 +1,76 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#2343_tab
+ * Spec sheet: https://www.thisisant.com/resources/ant-device-profile-muscle-oxygen/
+ */
+
+import { SendCallback } from '../ant';
+import { updateMuscleOxygenSensorState } from '../lib/UpdateState';
+import { Messages } from '../Messages';
+import { AntPlusSensor } from './AntPlusSensor';
+import { MuscleOxygenSensorState } from './MuscleOxygenSensorState';
+
+export class MuscleOxygenSensor extends AntPlusSensor {
+ static deviceType = 0x1f;
+
+ public async attachSensor(channel: number, deviceID: number): Promise {
+ await super.attach({
+ channel,
+ type: 'receive',
+ deviceID,
+ deviceType: MuscleOxygenSensor.deviceType,
+ transmissionType: 0,
+ timeout: 255,
+ period: 8192,
+ });
+ this.state = new MuscleOxygenSensorState(deviceID);
+ }
+
+ private state?: MuscleOxygenSensorState;
+
+ protected updateState(deviceId: number, data: DataView) {
+ if (!this.state) {
+ throw new Error('MuscleOxygenSensor: not attached');
+ }
+ this.state.DeviceID = deviceId;
+ updateMuscleOxygenSensorState(this, this.state, data);
+ }
+
+ private _sendTimeCmd(cmd: number, cbk?: SendCallback) {
+ if (this.channel === undefined) {
+ throw new Error('MuscleOxygenSensor: not attached');
+ }
+ const now = new Date();
+ const utc = Math.round(
+ (now.getTime() - Date.UTC(1989, 11, 31, 0, 0, 0, 0)) / 1000
+ );
+ const offset = -Math.round(now.getTimezoneOffset() / 15);
+ const payload = [
+ 0x10,
+ cmd & 0xff,
+ 0xff,
+ offset & 0xff,
+ (utc >> 0) & 0xff,
+ (utc >> 8) & 0xff,
+ (utc >> 16) & 0xff,
+ (utc >> 24) & 0xff,
+ ];
+ const msg = Messages.acknowledgedData(this.channel, payload);
+ this.send(msg, cbk);
+ }
+
+ public setUTCTime(cbk?: SendCallback) {
+ this._sendTimeCmd(0x00, cbk);
+ }
+
+ public startSession(cbk?: SendCallback) {
+ this._sendTimeCmd(0x01, cbk);
+ }
+
+ public stopSession(cbk?: SendCallback) {
+ this._sendTimeCmd(0x02, cbk);
+ }
+
+ public setLap(cbk?: SendCallback) {
+ this._sendTimeCmd(0x03, cbk);
+ }
+}
diff --git a/src/sensors/MuscleOxygenSensorState.ts b/src/sensors/MuscleOxygenSensorState.ts
new file mode 100644
index 0000000..33f444d
--- /dev/null
+++ b/src/sensors/MuscleOxygenSensorState.ts
@@ -0,0 +1,211 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#2343_tab
+ * Spec sheet: https://www.thisisant.com/resources/ant-device-profile-muscle-oxygen/
+ */
+
+import { Messages } from '../Messages';
+
+export class MuscleOxygenSensorState {
+ constructor(deviceID: number) {
+ this.DeviceID = deviceID;
+ }
+
+ _EventCount?: number;
+
+ DeviceID: number;
+
+ UTCTimeRequired?: boolean;
+
+ SupportANTFS?: boolean;
+
+ MeasurementInterval?: 0.25 | 0.5 | 1 | 2;
+
+ TotalHemoglobinConcentration?: number | 'AmbientLightTooHigh' | 'Invalid';
+
+ PreviousSaturatedHemoglobinPercentage?:
+ | number
+ | 'AmbientLightTooHigh'
+ | 'Invalid';
+
+ CurrentSaturatedHemoglobinPercentage?:
+ | number
+ | 'AmbientLightTooHigh'
+ | 'Invalid';
+
+ HwVersion?: number;
+
+ ManId?: number;
+
+ ModelNum?: number;
+
+ SwVersion?: number;
+
+ SerialNumber?: number;
+
+ OperatingTime?: number;
+
+ BatteryId?: number;
+
+ BatteryVoltage?: number;
+
+ BatteryStatus?: 'New' | 'Good' | 'Ok' | 'Low' | 'Critical' | 'Invalid';
+
+ updateState(data: DataView): MuscleOxygenSensorState | undefined {
+ const oldEventCount = this._EventCount || 0;
+
+ const page = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA);
+ switch (page) {
+ case 0x01: {
+ let eventCount = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 1);
+ const notifications = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2);
+ const capabilities = data.getUint16(
+ Messages.BUFFER_INDEX_MSG_DATA + 3,
+ true
+ );
+ const total =
+ data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 4, true) & 0xfff;
+ const previous =
+ (data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 5, true) >> 4) &
+ 0x3ff;
+ const current =
+ (data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 6, true) >> 6) &
+ 0x3ff;
+
+ if (eventCount !== oldEventCount) {
+ this._EventCount = eventCount;
+ if (oldEventCount > eventCount) {
+ // Hit rollover value
+ eventCount += 255;
+ }
+ }
+
+ this.UTCTimeRequired = (notifications & 0x01) === 0x01;
+
+ this.SupportANTFS = (capabilities & 0x01) === 0x01;
+
+ switch ((capabilities >> 1) & 0x7) {
+ case 1:
+ this.MeasurementInterval = 0.25;
+ break;
+ case 2:
+ this.MeasurementInterval = 0.5;
+ break;
+ case 3:
+ this.MeasurementInterval = 1;
+ break;
+ case 4:
+ this.MeasurementInterval = 2;
+ break;
+ default:
+ delete this.MeasurementInterval;
+ }
+
+ switch (total) {
+ case 0xffe:
+ this.TotalHemoglobinConcentration = 'AmbientLightTooHigh';
+ break;
+ case 0xfff:
+ this.TotalHemoglobinConcentration = 'Invalid';
+ break;
+ default:
+ this.TotalHemoglobinConcentration = total;
+ }
+
+ switch (previous) {
+ case 0x3fe:
+ this.PreviousSaturatedHemoglobinPercentage = 'AmbientLightTooHigh';
+ break;
+ case 0x3ff:
+ this.PreviousSaturatedHemoglobinPercentage = 'Invalid';
+ break;
+ default:
+ this.PreviousSaturatedHemoglobinPercentage = previous;
+ }
+
+ switch (current) {
+ case 0x3fe:
+ this.CurrentSaturatedHemoglobinPercentage = 'AmbientLightTooHigh';
+ break;
+ case 0x3ff:
+ this.CurrentSaturatedHemoglobinPercentage = 'Invalid';
+ break;
+ default:
+ this.CurrentSaturatedHemoglobinPercentage = current;
+ }
+
+ break;
+ }
+ case 0x50: {
+ this.HwVersion = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3);
+ this.ManId = data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 4, true);
+ this.ModelNum = data.getUint16(
+ Messages.BUFFER_INDEX_MSG_DATA + 6,
+ true
+ );
+ break;
+ }
+ case 0x51: {
+ const swRevSup = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2);
+ const swRevMain = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3);
+ const serial = data.getInt32(Messages.BUFFER_INDEX_MSG_DATA + 4, true);
+
+ this.SwVersion = swRevMain;
+
+ if (swRevSup !== 0xff) {
+ this.SwVersion += swRevSup / 1000;
+ }
+
+ if (serial !== 0xffffffff) {
+ this.SerialNumber = serial;
+ }
+
+ break;
+ }
+ case 0x52: {
+ this.BatteryId = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2);
+ const operatingTime =
+ data.getUint32(Messages.BUFFER_INDEX_MSG_DATA + 3, true) & 0xffffff;
+ const batteryFrac = data.getInt32(
+ Messages.BUFFER_INDEX_MSG_DATA + 6,
+ true
+ );
+ const batteryStatus = data.getInt32(
+ Messages.BUFFER_INDEX_MSG_DATA + 7,
+ true
+ );
+
+ this.OperatingTime =
+ operatingTime * ((batteryStatus & 0x80) === 0x80 ? 2 : 16);
+ this.BatteryVoltage = (batteryStatus & 0x0f) + batteryFrac / 256;
+ const batteryFlags = (batteryStatus & 0x70) >>> 4;
+ switch (batteryFlags) {
+ case 1:
+ this.BatteryStatus = 'New';
+ break;
+ case 2:
+ this.BatteryStatus = 'Good';
+ break;
+ case 3:
+ this.BatteryStatus = 'Ok';
+ break;
+ case 4:
+ this.BatteryStatus = 'Low';
+ break;
+ case 5:
+ this.BatteryStatus = 'Critical';
+ break;
+ default:
+ this.BatteryVoltage = undefined;
+ this.BatteryStatus = 'Invalid';
+ break;
+ }
+ break;
+ }
+ default:
+ return;
+ }
+ if (page !== 0x01 || this._EventCount !== oldEventCount) {
+ return this;
+ }
+ }
+}
diff --git a/src/sensors/SpeedCadenceScanState.ts b/src/sensors/SpeedCadenceScanState.ts
new file mode 100644
index 0000000..e2f8669
--- /dev/null
+++ b/src/sensors/SpeedCadenceScanState.ts
@@ -0,0 +1,12 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#523_tab
+ * Spec sheet: https://www.thisisant.com/resources/bicycle-speed-and-cadence/
+ */
+
+import { SpeedCadenceSensorState } from './SpeedCadenceSensorState';
+
+export class SpeedCadenceScanState extends SpeedCadenceSensorState {
+ Rssi?: number;
+
+ Threshold?: number;
+}
diff --git a/src/sensors/SpeedCadenceScanner.ts b/src/sensors/SpeedCadenceScanner.ts
new file mode 100644
index 0000000..66c2d3a
--- /dev/null
+++ b/src/sensors/SpeedCadenceScanner.ts
@@ -0,0 +1,42 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#523_tab
+ * Spec sheet: https://www.thisisant.com/resources/bicycle-speed-and-cadence/
+ */
+
+import { updateSpeedCadenceSensorState } from '../lib/UpdateState';
+import { AntPlusScanner } from './AntPlusScanner';
+import { SpeedCadenceScanState } from './SpeedCadenceScanState';
+import { SpeedCadenceSensor } from './SpeedCadenceSensor';
+
+export class SpeedCadenceScanner extends AntPlusScanner {
+ protected deviceType() {
+ return SpeedCadenceSensor.deviceType;
+ }
+
+ wheelCircumference = 2.199; // default 70cm wheel
+
+ public setWheelCircumference(wheelCircumference: number) {
+ this.wheelCircumference = wheelCircumference;
+ }
+
+ private states: { [id: number]: SpeedCadenceScanState } = {};
+
+ protected createStateIfNew(deviceId: number) {
+ if (!this.states[deviceId]) {
+ this.states[deviceId] = new SpeedCadenceScanState(deviceId);
+ }
+ }
+
+ protected updateRssiAndThreshold(
+ deviceId: number,
+ rssi: number | undefined,
+ threshold: number | undefined
+ ) {
+ this.states[deviceId].Rssi = rssi;
+ this.states[deviceId].Threshold = threshold;
+ }
+
+ protected updateState(deviceId: number, data: DataView) {
+ updateSpeedCadenceSensorState(this, this.states[deviceId], data);
+ }
+}
diff --git a/src/sensors/SpeedCadenceSensor.ts b/src/sensors/SpeedCadenceSensor.ts
new file mode 100644
index 0000000..da07428
--- /dev/null
+++ b/src/sensors/SpeedCadenceSensor.ts
@@ -0,0 +1,41 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#523_tab
+ * Spec sheet: https://www.thisisant.com/resources/bicycle-speed-and-cadence/
+ */
+
+import { updateSpeedCadenceSensorState } from '../lib/UpdateState';
+import { AntPlusSensor } from './AntPlusSensor';
+import { SpeedCadenceSensorState } from './SpeedCadenceSensorState';
+
+export class SpeedCadenceSensor extends AntPlusSensor {
+ static deviceType = 0x79;
+
+ wheelCircumference = 2.199; // default 70cm wheel
+
+ public setWheelCircumference(wheelCircumference: number) {
+ this.wheelCircumference = wheelCircumference;
+ }
+
+ public async attachSensor(channel: number, deviceID: number): Promise {
+ await super.attach({
+ channel,
+ type: 'receive',
+ deviceID,
+ deviceType: SpeedCadenceSensor.deviceType,
+ transmissionType: 0,
+ timeout: 255,
+ period: 8086,
+ });
+ this.state = new SpeedCadenceSensorState(deviceID);
+ }
+
+ private state?: SpeedCadenceSensorState;
+
+ protected updateState(deviceId: number, data: DataView) {
+ if (!this.state) {
+ throw new Error('SpeedCadenceSensor: not attached');
+ }
+ this.state.DeviceID = deviceId;
+ updateSpeedCadenceSensorState(this, this.state, data);
+ }
+}
diff --git a/src/sensors/SpeedCadenceSensorState.ts b/src/sensors/SpeedCadenceSensorState.ts
new file mode 100644
index 0000000..62eb570
--- /dev/null
+++ b/src/sensors/SpeedCadenceSensorState.ts
@@ -0,0 +1,117 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#523_tab
+ * Spec sheet: https://www.thisisant.com/resources/bicycle-speed-and-cadence/
+ */
+
+import { Messages } from '../Messages';
+
+export class SpeedCadenceSensorState {
+ constructor(deviceID: number) {
+ this.DeviceID = deviceID;
+ }
+
+ DeviceID: number;
+
+ CadenceEventTime?: number;
+
+ CumulativeCadenceRevolutionCount?: number;
+
+ SpeedEventTime?: number;
+
+ CumulativeSpeedRevolutionCount?: number;
+
+ CalculatedCadence?: number;
+
+ CalculatedDistance?: number;
+
+ CalculatedSpeed?: number;
+
+ updateState(
+ data: DataView,
+ wheelCircumference: number
+ ): {
+ updatedState: SpeedCadenceSensorState;
+ resultType: 'cadence' | 'speed' | 'both' | 'none';
+ } {
+ // get old state for calculating cumulative values
+ const oldCadenceTime = this.CadenceEventTime;
+ const oldCadenceCount = this.CumulativeCadenceRevolutionCount;
+ const oldSpeedTime = this.SpeedEventTime;
+ const oldSpeedCount = this.CumulativeSpeedRevolutionCount;
+
+ let cadenceTime = data.getUint16(Messages.BUFFER_INDEX_MSG_DATA, false);
+ let cadenceCount = data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 2, true);
+ let speedEventTime = data.getUint16(
+ Messages.BUFFER_INDEX_MSG_DATA + 4,
+ true
+ );
+ let speedRevolutionCount = data.getUint16(
+ Messages.BUFFER_INDEX_MSG_DATA + 6,
+ true
+ );
+
+ let cadenceDataChanged = false;
+ let speedDataChanged = false;
+
+ if (cadenceTime !== oldCadenceTime) {
+ this.CadenceEventTime = cadenceTime;
+ this.CumulativeCadenceRevolutionCount = cadenceCount;
+
+ if (oldCadenceTime && oldCadenceTime > cadenceTime) {
+ // Hit rollover value
+ cadenceTime += 1024 * 64;
+ }
+
+ if (oldCadenceCount && oldCadenceCount > cadenceCount) {
+ // Hit rollover value
+ cadenceCount += 1024 * 64;
+ }
+
+ const cadence =
+ (60 * (cadenceCount - (oldCadenceCount || 0)) * 1024) /
+ (cadenceTime - (oldCadenceTime || 0));
+ if (!isNaN(cadence)) {
+ this.CalculatedCadence = cadence;
+ cadenceDataChanged = true;
+ }
+ }
+
+ if (speedEventTime !== oldSpeedTime) {
+ this.SpeedEventTime = speedEventTime;
+ this.CumulativeSpeedRevolutionCount = speedRevolutionCount;
+
+ if (oldSpeedTime && oldSpeedTime > speedEventTime) {
+ // Hit rollover value
+ speedEventTime += 1024 * 64;
+ }
+
+ if (oldSpeedCount && oldSpeedCount > speedRevolutionCount) {
+ // Hit rollover value
+ speedRevolutionCount += 1024 * 64;
+ }
+
+ const distance =
+ wheelCircumference * (speedRevolutionCount - (oldSpeedCount || 0));
+ this.CalculatedDistance = distance;
+
+ // speed in m/sec
+ const speed = (distance * 1024) / (speedEventTime - (oldSpeedTime || 0));
+ if (!isNaN(speed)) {
+ this.CalculatedSpeed = speed;
+ speedDataChanged = true;
+ }
+ }
+
+ return {
+ updatedState: this,
+ resultType:
+ cadenceDataChanged && speedDataChanged
+ ? 'both'
+ : cadenceDataChanged
+ ? 'cadence'
+ : speedDataChanged
+ ? 'speed'
+ : 'none',
+ };
+ }
+}
diff --git a/src/sensors/SpeedScanState.ts b/src/sensors/SpeedScanState.ts
new file mode 100644
index 0000000..22a9738
--- /dev/null
+++ b/src/sensors/SpeedScanState.ts
@@ -0,0 +1,12 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#523_tab
+ * Spec sheet: https://www.thisisant.com/resources/bicycle-speed/
+ */
+
+import { SpeedSensorState } from './SpeedSensorState';
+
+export class SpeedScanState extends SpeedSensorState {
+ Rssi?: number;
+
+ Threshold?: number;
+}
diff --git a/src/sensors/SpeedScanner.ts b/src/sensors/SpeedScanner.ts
new file mode 100644
index 0000000..7a826ce
--- /dev/null
+++ b/src/sensors/SpeedScanner.ts
@@ -0,0 +1,42 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#523_tab
+ * Spec sheet: https://www.thisisant.com/resources/bicycle-speed/
+ */
+
+import { updateSpeedSensorState } from '../lib/UpdateState';
+import { AntPlusScanner } from './AntPlusScanner';
+import { SpeedScanState } from './SpeedScanState';
+import { SpeedSensor } from './SpeedSensor';
+
+export class SpeedScanner extends AntPlusScanner {
+ protected deviceType() {
+ return SpeedSensor.deviceType;
+ }
+
+ wheelCircumference = 2.199; // default 70cm wheel
+
+ public setWheelCircumference(wheelCircumference: number) {
+ this.wheelCircumference = wheelCircumference;
+ }
+
+ private states: { [id: number]: SpeedScanState } = {};
+
+ protected createStateIfNew(deviceId: number) {
+ if (!this.states[deviceId]) {
+ this.states[deviceId] = new SpeedScanState(deviceId);
+ }
+ }
+
+ protected updateRssiAndThreshold(
+ deviceId: number,
+ rssi: number | undefined,
+ threshold: number | undefined
+ ) {
+ this.states[deviceId].Rssi = rssi;
+ this.states[deviceId].Threshold = threshold;
+ }
+
+ protected updateState(deviceId: number, data: DataView) {
+ updateSpeedSensorState(this, this.states[deviceId], data);
+ }
+}
diff --git a/src/sensors/SpeedSensor.ts b/src/sensors/SpeedSensor.ts
new file mode 100644
index 0000000..4e4dd87
--- /dev/null
+++ b/src/sensors/SpeedSensor.ts
@@ -0,0 +1,41 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#523_tab
+ * Spec sheet: https://www.thisisant.com/resources/bicycle-speed/
+ */
+
+import { updateSpeedSensorState } from '../lib/UpdateState';
+import { AntPlusSensor } from './AntPlusSensor';
+import { SpeedSensorState } from './SpeedSensorState';
+
+export class SpeedSensor extends AntPlusSensor {
+ static deviceType = 0x7b;
+
+ wheelCircumference = 2.199; // default 70cm wheel
+
+ public setWheelCircumference(wheelCircumference: number) {
+ this.wheelCircumference = wheelCircumference;
+ }
+
+ public async attachSensor(channel: number, deviceID: number): Promise {
+ await super.attach({
+ channel,
+ type: 'receive',
+ deviceID,
+ deviceType: SpeedSensor.deviceType,
+ transmissionType: 0,
+ timeout: 255,
+ period: 8086,
+ });
+ this.state = new SpeedSensorState(deviceID);
+ }
+
+ private state?: SpeedSensorState;
+
+ protected updateState(deviceId: number, data: DataView) {
+ if (!this.state) {
+ throw new Error('SpeedSensor: not attached');
+ }
+ this.state.DeviceID = deviceId;
+ updateSpeedSensorState(this, this.state, data);
+ }
+}
diff --git a/src/sensors/SpeedSensorState.ts b/src/sensors/SpeedSensorState.ts
new file mode 100644
index 0000000..0544b7e
--- /dev/null
+++ b/src/sensors/SpeedSensorState.ts
@@ -0,0 +1,150 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#523_tab
+ * Spec sheet: https://www.thisisant.com/resources/bicycle-speed/
+ */
+
+import { Messages } from '../Messages';
+
+export class SpeedSensorState {
+ constructor(deviceID: number) {
+ this.DeviceID = deviceID;
+ }
+
+ DeviceID: number;
+
+ SpeedEventTime?: number;
+
+ CumulativeSpeedRevolutionCount?: number;
+
+ CalculatedDistance?: number;
+
+ CalculatedSpeed?: number;
+
+ OperatingTime?: number;
+
+ ManId?: number;
+
+ SerialNumber?: number;
+
+ HwVersion?: number;
+
+ SwVersion?: number;
+
+ ModelNum?: number;
+
+ BatteryVoltage?: number;
+
+ BatteryStatus?: 'New' | 'Good' | 'Ok' | 'Low' | 'Critical' | 'Invalid';
+
+ Motion?: boolean;
+
+ updateState(
+ data: DataView,
+ wheelCircumference: number
+ ): SpeedSensorState | undefined {
+ const TOGGLE_MASK = 0x80;
+
+ const pageNum = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA);
+ switch (
+ pageNum & ~TOGGLE_MASK // check the new pages and remove the toggle bit
+ ) {
+ case 1:
+ // decode the cumulative operating time
+ this.OperatingTime = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 1);
+ this.OperatingTime |=
+ data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2) << 8;
+ this.OperatingTime |=
+ data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3) << 16;
+ this.OperatingTime *= 2;
+ break;
+ case 2:
+ // decode the Manufacturer ID
+ this.ManId = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 1);
+ // decode the 4 byte serial number
+ this.SerialNumber = this.DeviceID;
+ this.SerialNumber |=
+ data.getUint16(Messages.BUFFER_INDEX_MSG_DATA + 2, true) << 16;
+ this.SerialNumber >>>= 0;
+ break;
+ case 3:
+ // decode HW version, SW version, and model number
+ this.HwVersion = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 1);
+ this.SwVersion = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2);
+ this.ModelNum = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3);
+ break;
+ case 4: {
+ const batteryFrac = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2);
+ const batteryStatus = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3);
+ this.BatteryVoltage = (batteryStatus & 0x0f) + batteryFrac / 256;
+ const batteryFlags = (batteryStatus & 0x70) >>> 4;
+ switch (batteryFlags) {
+ case 1:
+ this.BatteryStatus = 'New';
+ break;
+ case 2:
+ this.BatteryStatus = 'Good';
+ break;
+ case 3:
+ this.BatteryStatus = 'Ok';
+ break;
+ case 4:
+ this.BatteryStatus = 'Low';
+ break;
+ case 5:
+ this.BatteryStatus = 'Critical';
+ break;
+ default:
+ this.BatteryVoltage = undefined;
+ this.BatteryStatus = 'Invalid';
+ break;
+ }
+ break;
+ }
+ case 5:
+ this.Motion =
+ (data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 1) & 0x01) === 0x01;
+ break;
+ default:
+ break;
+ }
+
+ // get old state for calculating cumulative values
+ const oldSpeedTime = this.SpeedEventTime;
+ const oldSpeedCount = this.CumulativeSpeedRevolutionCount;
+
+ let speedEventTime = data.getUint16(
+ Messages.BUFFER_INDEX_MSG_DATA + 4,
+ true
+ );
+ let speedRevolutionCount = data.getUint16(
+ Messages.BUFFER_INDEX_MSG_DATA + 6,
+ true
+ );
+
+ if (speedEventTime !== oldSpeedTime) {
+ this.SpeedEventTime = speedEventTime;
+ this.CumulativeSpeedRevolutionCount = speedRevolutionCount;
+
+ if (oldSpeedTime && oldSpeedTime > speedEventTime) {
+ // Hit rollover value
+ speedEventTime += 1024 * 64;
+ }
+
+ if (oldSpeedCount && oldSpeedCount > speedRevolutionCount) {
+ // Hit rollover value
+ speedRevolutionCount += 1024 * 64;
+ }
+
+ const distance =
+ wheelCircumference * (speedRevolutionCount - (oldSpeedCount || 0));
+ this.CalculatedDistance = distance;
+
+ // speed in m/sec
+ const speed = (distance * 1024) / (speedEventTime - (oldSpeedTime || 0));
+ if (!isNaN(speed)) {
+ this.CalculatedSpeed = speed;
+ return this;
+ }
+ }
+ }
+}
diff --git a/src/sensors/StrideSpeedDistanceScanState.ts b/src/sensors/StrideSpeedDistanceScanState.ts
new file mode 100644
index 0000000..4b826da
--- /dev/null
+++ b/src/sensors/StrideSpeedDistanceScanState.ts
@@ -0,0 +1,12 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#528_tab
+ * Spec sheet: https://www.thisisant.com/resources/stride-based-speed-and-distance-monitor/
+ */
+
+import { StrideSpeedDistanceSensorState } from './StrideSpeedDistanceSensorState';
+
+export class StrideSpeedDistanceScanState extends StrideSpeedDistanceSensorState {
+ Rssi?: number;
+
+ Threshold?: number;
+}
diff --git a/src/sensors/StrideSpeedDistanceScanner.ts b/src/sensors/StrideSpeedDistanceScanner.ts
new file mode 100644
index 0000000..b8b67bc
--- /dev/null
+++ b/src/sensors/StrideSpeedDistanceScanner.ts
@@ -0,0 +1,36 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#528_tab
+ * Spec sheet: https://www.thisisant.com/resources/stride-based-speed-and-distance-monitor/
+ */
+
+import { updateStrideSpeedDistanceSensorState } from '../lib/UpdateState';
+import { AntPlusScanner } from './AntPlusScanner';
+import { StrideSpeedDistanceScanState } from './StrideSpeedDistanceScanState';
+import { StrideSpeedDistanceSensor } from './StrideSpeedDistanceSensor';
+
+export class StrideSpeedDistanceScanner extends AntPlusScanner {
+ protected deviceType() {
+ return StrideSpeedDistanceSensor.deviceType;
+ }
+
+ private states: { [id: number]: StrideSpeedDistanceScanState } = {};
+
+ protected createStateIfNew(deviceId: number) {
+ if (!this.states[deviceId]) {
+ this.states[deviceId] = new StrideSpeedDistanceScanState(deviceId);
+ }
+ }
+
+ protected updateRssiAndThreshold(
+ deviceId: number,
+ rssi: number | undefined,
+ threshold: number | undefined
+ ) {
+ this.states[deviceId].Rssi = rssi;
+ this.states[deviceId].Threshold = threshold;
+ }
+
+ protected updateState(deviceId: number, data: DataView) {
+ updateStrideSpeedDistanceSensorState(this, this.states[deviceId], data);
+ }
+}
diff --git a/src/sensors/StrideSpeedDistanceSensor.ts b/src/sensors/StrideSpeedDistanceSensor.ts
new file mode 100644
index 0000000..0d05483
--- /dev/null
+++ b/src/sensors/StrideSpeedDistanceSensor.ts
@@ -0,0 +1,35 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#528_tab
+ * Spec sheet: https://www.thisisant.com/resources/stride-based-speed-and-distance-monitor/
+ */
+
+import { updateStrideSpeedDistanceSensorState } from '../lib/UpdateState';
+import { AntPlusSensor } from './AntPlusSensor';
+import { StrideSpeedDistanceSensorState } from './StrideSpeedDistanceSensorState';
+
+export class StrideSpeedDistanceSensor extends AntPlusSensor {
+ static deviceType = 124;
+
+ public async attachSensor(channel: number, deviceID: number) {
+ await super.attach({
+ channel,
+ type: 'receive',
+ deviceID,
+ deviceType: StrideSpeedDistanceSensor.deviceType,
+ transmissionType: 0,
+ timeout: 255,
+ period: 8134,
+ });
+ this.state = new StrideSpeedDistanceSensorState(deviceID);
+ }
+
+ private state?: StrideSpeedDistanceSensorState;
+
+ protected updateState(deviceId: number, data: DataView) {
+ if (!this.state) {
+ throw new Error('StrideSpeedDistanceSensor: not attached');
+ }
+ this.state.DeviceID = deviceId;
+ updateStrideSpeedDistanceSensorState(this, this.state, data);
+ }
+}
diff --git a/src/sensors/StrideSpeedDistanceSensorState.ts b/src/sensors/StrideSpeedDistanceSensorState.ts
new file mode 100644
index 0000000..2578f6c
--- /dev/null
+++ b/src/sensors/StrideSpeedDistanceSensorState.ts
@@ -0,0 +1,71 @@
+/*
+ * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#528_tab
+ * Spec sheet: https://www.thisisant.com/resources/stride-based-speed-and-distance-monitor/
+ */
+
+import { Messages } from '../Messages';
+
+export class StrideSpeedDistanceSensorState {
+ constructor(deviceId: number) {
+ this.DeviceID = deviceId;
+ }
+
+ DeviceID: number;
+
+ TimeFractional?: number;
+
+ TimeInteger?: number;
+
+ DistanceInteger?: number;
+
+ DistanceFractional?: number;
+
+ SpeedInteger?: number;
+
+ SpeedFractional?: number;
+
+ StrideCount?: number;
+
+ UpdateLatency?: number;
+
+ CadenceInteger?: number;
+
+ CadenceFractional?: number;
+
+ Status?: number;
+
+ Calories?: number;
+
+ updateState(data: DataView): StrideSpeedDistanceSensorState {
+ const page = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA);
+ if (page === 1) {
+ this.TimeFractional = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 1);
+ this.TimeInteger = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 2);
+ this.DistanceInteger = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3);
+ this.DistanceFractional =
+ data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 4) >>> 4;
+ this.SpeedInteger =
+ data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 4) & 0x0f;
+ this.SpeedFractional = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 5);
+ this.StrideCount = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 6);
+ this.UpdateLatency = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 7);
+ } else if (page >= 2 && page <= 15) {
+ this.CadenceInteger = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 3);
+ this.CadenceFractional =
+ data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 4) >>> 4;
+ this.SpeedInteger =
+ data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 4) & 0x0f;
+ this.SpeedFractional = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 5);
+ this.Status = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 7);
+
+ switch (page) {
+ case 3:
+ this.Calories = data.getUint8(Messages.BUFFER_INDEX_MSG_DATA + 6);
+ break;
+ default:
+ break;
+ }
+ }
+ return this;
+ }
+}
diff --git a/src/speed-cadence-sensors.ts b/src/speed-cadence-sensors.ts
deleted file mode 100644
index bc03193..0000000
--- a/src/speed-cadence-sensors.ts
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#523_tab
- * Spec sheet: https://www.thisisant.com/resources/bicycle-speed-and-cadence/
- */
-
-import { AntPlusSensor, AntPlusScanner, Messages } from './ant';
-
-class SpeedCadenceSensorState {
- constructor(deviceID: number) {
- this.DeviceID = deviceID;
- }
-
- DeviceID: number;
- CadenceEventTime: number;
- CumulativeCadenceRevolutionCount: number;
- SpeedEventTime: number;
- CumulativeSpeedRevolutionCount: number;
- CalculatedCadence: number;
- CalculatedDistance: number;
- CalculatedSpeed: number;
-}
-
-class SpeedCadenceScanState extends SpeedCadenceSensorState {
- Rssi: number;
- Threshold: number;
-}
-
-export class SpeedCadenceSensor extends AntPlusSensor {
- static deviceType = 0x79;
-
- wheelCircumference: number = 2.199; // default 70cm wheel
-
- public setWheelCircumference(wheelCircumference: number) {
- this.wheelCircumference = wheelCircumference;
- }
-
- public attach(channel, deviceID): void {
- super.attach(channel, 'receive', deviceID, SpeedCadenceSensor.deviceType, 0, 255, 8086);
- this.state = new SpeedCadenceSensorState(deviceID);
- }
-
- private state: SpeedCadenceSensorState;
-
- protected updateState(deviceId, data) {
- this.state.DeviceID = deviceId;
- updateState(this, this.state, data);
- }
-}
-
-export class SpeedCadenceScanner extends AntPlusScanner {
- protected deviceType() {
- return SpeedCadenceSensor.deviceType;
- }
-
- wheelCircumference: number = 2.199; // default 70cm wheel
-
- public setWheelCircumference(wheelCircumference: number) {
- this.wheelCircumference = wheelCircumference;
- }
-
- private states: { [id: number]: SpeedCadenceScanState } = {};
-
- protected createStateIfNew(deviceId) {
- if (!this.states[deviceId]) {
- this.states[deviceId] = new SpeedCadenceScanState(deviceId);
- }
- }
-
- protected updateRssiAndThreshold(deviceId, rssi, threshold) {
- this.states[deviceId].Rssi = rssi;
- this.states[deviceId].Threshold = threshold;
- }
-
- protected updateState(deviceId, data) {
- updateState(this, this.states[deviceId], data);
- }
-}
-
-function updateState(
- sensor: SpeedCadenceSensor | SpeedCadenceScanner,
- state: SpeedCadenceSensorState | SpeedCadenceScanState,
- data: Buffer) {
-
- //get old state for calculating cumulative values
- const oldCadenceTime = state.CadenceEventTime;
- const oldCadenceCount = state.CumulativeCadenceRevolutionCount;
- const oldSpeedTime = state.SpeedEventTime;
- const oldSpeedCount = state.CumulativeSpeedRevolutionCount;
-
- let cadenceTime = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA);
- let cadenceCount = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 2);
- let speedEventTime = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 4);
- let speedRevolutionCount = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 6);
-
- if (cadenceTime !== oldCadenceTime) {
- state.CadenceEventTime = cadenceTime;
- state.CumulativeCadenceRevolutionCount = cadenceCount;
-
- if (oldCadenceTime > cadenceTime) { //Hit rollover value
- cadenceTime += (1024 * 64);
- }
-
- if (oldCadenceCount > cadenceCount) { //Hit rollover value
- cadenceCount += (1024 * 64);
- }
-
- const cadence = ((60 * (cadenceCount - oldCadenceCount) * 1024) / (cadenceTime - oldCadenceTime));
- if (!isNaN(cadence)) {
- state.CalculatedCadence = cadence;
- sensor.emit('cadenceData', state);
- }
- }
-
- if (speedEventTime !== oldSpeedTime) {
- state.SpeedEventTime = speedEventTime;
- state.CumulativeSpeedRevolutionCount = speedRevolutionCount;
-
- if (oldSpeedTime > speedEventTime) { //Hit rollover value
- speedEventTime += (1024 * 64);
- }
-
- if (oldSpeedCount > speedRevolutionCount) { //Hit rollover value
- speedRevolutionCount += (1024 * 64);
- }
-
- const distance = sensor.wheelCircumference * (speedRevolutionCount - oldSpeedCount);
- state.CalculatedDistance = distance;
-
- //speed in m/sec
- const speed = (distance * 1024) / (speedEventTime - oldSpeedTime);
- if (!isNaN(speed)) {
- state.CalculatedSpeed = speed;
- sensor.emit('speedData', state);
- }
- }
-}
diff --git a/src/speed-sensors.ts b/src/speed-sensors.ts
deleted file mode 100644
index 408d71d..0000000
--- a/src/speed-sensors.ts
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#523_tab
- * Spec sheet: https://www.thisisant.com/resources/bicycle-speed-and-cadence/
- */
-
-import { AntPlusSensor, AntPlusScanner, Messages } from './ant';
-
-class SpeedSensorState {
- constructor(deviceID: number) {
- this.DeviceID = deviceID;
- }
-
- DeviceID: number;
- SpeedEventTime: number;
- CumulativeSpeedRevolutionCount: number;
- CalculatedDistance: number;
- CalculatedSpeed: number;
-
- OperatingTime?: number;
- ManId?: number;
- SerialNumber?: number;
- HwVersion?: number;
- SwVersion?: number;
- ModelNum?: number;
- BatteryVoltage?: number;
- BatteryStatus?: 'New' | 'Good' | 'Ok' | 'Low' | 'Critical' | 'Invalid';
- Motion?: boolean;
-}
-
-class SpeedScanState extends SpeedSensorState {
- Rssi: number;
- Threshold: number;
-}
-
-export class SpeedSensor extends AntPlusSensor {
-
- static deviceType = 0x7B;
-
- wheelCircumference: number = 2.199; // default 70cm wheel
-
- public setWheelCircumference(wheelCircumference: number) {
- this.wheelCircumference = wheelCircumference;
- }
-
- public attach(channel, deviceID): void {
- super.attach(channel, 'receive', deviceID, SpeedSensor.deviceType, 0, 255, 8086);
- this.state = new SpeedSensorState(deviceID);
- }
-
- private state: SpeedSensorState;
-
- protected updateState(deviceId, data) {
- this.state.DeviceID = deviceId;
- updateState(this, this.state, data);
- }
-}
-
-export class SpeedScanner extends AntPlusScanner {
- protected deviceType() {
- return SpeedSensor.deviceType;
- }
-
- wheelCircumference: number = 2.199; // default 70cm wheel
-
- public setWheelCircumference(wheelCircumference: number) {
- this.wheelCircumference = wheelCircumference;
- }
-
- private states: { [id: number]: SpeedScanState } = {};
-
- protected createStateIfNew(deviceId) {
- if (!this.states[deviceId]) {
- this.states[deviceId] = new SpeedScanState(deviceId);
- }
- }
-
- protected updateRssiAndThreshold(deviceId, rssi, threshold) {
- this.states[deviceId].Rssi = rssi;
- this.states[deviceId].Threshold = threshold;
- }
-
- protected updateState(deviceId, data) {
- updateState(this, this.states[deviceId], data);
- }
-}
-
-const TOGGLE_MASK = 0x80;
-
-function updateState(sensor: SpeedSensor | SpeedScanner, state: SpeedSensorState | SpeedScanState, data: Buffer) {
- const pageNum = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA);
- switch (pageNum & ~TOGGLE_MASK) { //check the new pages and remove the toggle bit
- case 1:
- //decode the cumulative operating time
- state.OperatingTime = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1);
- state.OperatingTime |= data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2) << 8;
- state.OperatingTime |= data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3) << 16;
- state.OperatingTime *= 2;
- break;
- case 2:
- //decode the Manufacturer ID
- state.ManId = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1);
- //decode the 4 byte serial number
- state.SerialNumber = state.DeviceID;
- state.SerialNumber |= data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 2) << 16;
- state.SerialNumber >>>= 0;
- break;
- case 3:
- //decode HW version, SW version, and model number
- state.HwVersion = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1);
- state.SwVersion = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2);
- state.ModelNum = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- break;
- case 4: {
- const batteryFrac = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2);
- const batteryStatus = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- state.BatteryVoltage = (batteryStatus & 0x0F) + (batteryFrac / 256);
- const batteryFlags = (batteryStatus & 0x70) >>> 4;
- switch (batteryFlags) {
- case 1:
- state.BatteryStatus = 'New';
- break;
- case 2:
- state.BatteryStatus = 'Good';
- break;
- case 3:
- state.BatteryStatus = 'Ok';
- break;
- case 4:
- state.BatteryStatus = 'Low';
- break;
- case 5:
- state.BatteryStatus = 'Critical';
- break;
- default:
- state.BatteryVoltage = undefined;
- state.BatteryStatus = 'Invalid';
- break;
- }
- break;
- }
- case 5:
- state.Motion = (data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1) & 0x01) === 0x01;
- break;
- default:
- break;
- }
-
- //get old state for calculating cumulative values
- const oldSpeedTime = state.SpeedEventTime;
- const oldSpeedCount = state.CumulativeSpeedRevolutionCount;
-
- let speedEventTime = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 4);
- let speedRevolutionCount = data.readUInt16LE(Messages.BUFFER_INDEX_MSG_DATA + 6);
-
- if (speedEventTime !== oldSpeedTime) {
- state.SpeedEventTime = speedEventTime;
- state.CumulativeSpeedRevolutionCount = speedRevolutionCount;
-
- if (oldSpeedTime > speedEventTime) { //Hit rollover value
- speedEventTime += (1024 * 64);
- }
-
- if (oldSpeedCount > speedRevolutionCount) { //Hit rollover value
- speedRevolutionCount += (1024 * 64);
- }
-
- const distance = sensor.wheelCircumference * (speedRevolutionCount - oldSpeedCount);
- state.CalculatedDistance = distance;
-
- //speed in m/sec
- const speed = (distance * 1024) / (speedEventTime - oldSpeedTime);
- if (!isNaN(speed)) {
- state.CalculatedSpeed = speed;
- sensor.emit('speedData', state);
- }
- }
-}
diff --git a/src/stride-speed-distance-sensors.ts b/src/stride-speed-distance-sensors.ts
deleted file mode 100644
index f09de41..0000000
--- a/src/stride-speed-distance-sensors.ts
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * ANT+ profile: https://www.thisisant.com/developer/ant-plus/device-profiles/#528_tab
- * Spec sheet: https://www.thisisant.com/resources/stride-based-speed-and-distance-monitor/
- */
-
-import { AntPlusSensor, AntPlusScanner, Messages } from './ant';
-
-class StrideSpeedDistanceSensorState {
- constructor(deviceId: number) {
- this.DeviceID = deviceId;
- }
-
- DeviceID: number;
- TimeFractional: number;
- TimeInteger: number;
- DistanceInteger: number;
- DistanceFractional: number;
- SpeedInteger: number;
- SpeedFractional: number;
- StrideCount: number;
- UpdateLatency: number;
- CadenceInteger: number;
- CadenceFractional: number;
- Status: number;
- Calories: number;
-}
-
-class StrideSpeedDistanceScanState extends StrideSpeedDistanceSensorState {
- Rssi: number;
- Threshold: number;
-}
-
-export class StrideSpeedDistanceSensor extends AntPlusSensor {
- static deviceType = 124;
-
- public attach(channel, deviceID) {
- super.attach(channel, 'receive', deviceID, StrideSpeedDistanceSensor.deviceType, 0, 255, 8134);
- this.state = new StrideSpeedDistanceSensorState(deviceID);
- }
-
- private state: StrideSpeedDistanceSensorState;
-
- protected updateState(deviceId, data) {
- this.state.DeviceID = deviceId;
- updateState(this, this.state, data);
- }
-}
-
-export class StrideSpeedDistanceScanner extends AntPlusScanner {
- protected deviceType() {
- return StrideSpeedDistanceSensor.deviceType;
- }
-
- private states: { [id: number]: StrideSpeedDistanceScanState } = {};
-
- protected createStateIfNew(deviceId) {
- if (!this.states[deviceId]) {
- this.states[deviceId] = new StrideSpeedDistanceScanState(deviceId);
- }
- }
-
- protected updateRssiAndThreshold(deviceId, rssi, threshold) {
- this.states[deviceId].Rssi = rssi;
- this.states[deviceId].Threshold = threshold;
- }
-
- protected updateState(deviceId, data) {
- updateState(this, this.states[deviceId], data);
- }
-}
-
-function updateState(
- sensor: StrideSpeedDistanceSensor | StrideSpeedDistanceScanner,
- state: StrideSpeedDistanceSensorState | StrideSpeedDistanceScanState,
- data: Buffer) {
-
- const page = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA);
- if (page === 1) {
- state.TimeFractional = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 1);
- state.TimeInteger = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 2);
- state.DistanceInteger = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- state.DistanceFractional = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 4) >>> 4;
- state.SpeedInteger = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 4) & 0x0F;
- state.SpeedFractional = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 5);
- state.StrideCount = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 6);
- state.UpdateLatency = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 7);
- } else if (page >= 2 && page <= 15) {
- state.CadenceInteger = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 3);
- state.CadenceFractional = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 4) >>> 4;
- state.SpeedInteger = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 4) & 0x0F;
- state.SpeedFractional = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 5);
- state.Status = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 7);
-
- switch (page) {
- case 3:
- state.Calories = data.readUInt8(Messages.BUFFER_INDEX_MSG_DATA + 6);
- break;
- default:
- break;
- }
- }
- sensor.emit('ssddata', state);
- sensor.emit('ssdData', state);
-}
diff --git a/tsconfig.json b/tsconfig.json
index 022f4c8..25e8e80 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,9 +1,17 @@
{
- "compilerOptions": {
- "target": "ES5",
- "module": "commonjs",
- "sourceMap": true,
- "outDir": "build",
- "declaration": true
- }
+ "include": ["./src/**/*.ts"],
+ "compilerOptions": {
+ "declaration": true,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "lib": ["DOM", "ESNext"],
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "outDir": "./dist",
+ "skipLibCheck": true,
+ "sourceMap": true,
+ "strict": true,
+ "target": "ES6",
+ "typeRoots": ["./node_modules/@types"]
+ }
}
diff --git a/tslint.json b/tslint.json
deleted file mode 100644
index fe8e6e2..0000000
--- a/tslint.json
+++ /dev/null
@@ -1,121 +0,0 @@
-{
- "rules": {
- "align": [
- true,
- "arguments",
- "statements"
- ],
- "class-name": true,
- "curly": true,
- "eofline": true,
- "forin": true,
- "indent": [
- true,
- "tabs"
- ],
- "interface-name": [
- true,
- "always-prefix"
- ],
- "jsdoc-format": true,
- "label-position": true,
- "label-undefined": true,
- "max-line-length": [
- true,
- 140
- ],
- "new-parens": true,
- "no-angle-bracket-type-assertion": true,
- "no-arg": true,
- "no-conditional-assignment": true,
- "no-construct": true,
- "no-default-export": true,
- "no-duplicate-key": true,
- "no-duplicate-variable": true,
- "no-empty": true,
- "no-eval": true,
- "no-internal-module": true,
- "no-invalid-this": [
- true,
- "check-function-in-method"
- ],
- "no-null-keyword": true,
- "no-shadowed-variable": true,
- "no-string-literal": true,
- "no-switch-case-fall-through": true,
- "no-trailing-whitespace": true,
- "no-unreachable": true,
- "no-unused-expression": true,
- "no-unused-variable": true,
- "no-use-before-declare": true,
- "no-var-keyword": true,
- "no-var-requires": true,
- "one-line": [
- true,
- "check-open-brace",
- "check-catch",
- "check-else",
- "check-finally",
- "check-whitespace"
- ],
- "one-variable-per-declaration": true,
- "quotemark": [
- true,
- "single",
- "avoid-escape"
- ],
- "radix": true,
- "semicolon": [
- true,
- "always"
- ],
- "switch-default": true,
- "trailing-comma": [
- true,
- {
- "multiline": "always",
- "singleline": "never"
- }
- ],
- "triple-equals": true,
- "typedef": true,
- "typedef-whitespace": [
- true,
- {
- "call-signature": "nospace",
- "index-signature": "nospace",
- "parameter": "nospace",
- "property-declaration": "nospace",
- "variable-declaration": "nospace"
- },
- {
- "call-signature": "space",
- "index-signature": "space",
- "parameter": "space",
- "property-declaration": "space",
- "variable-declaration": "space"
- }
- ],
- "use-isnan": true,
- "use-strict": [
- true,
- "check-module",
- "check-function"
- ],
- "variable-name": [
- true,
- "check-format",
- "allow-leading-underscore",
- "allow-pascal-case",
- "ban-keywords"
- ],
- "whitespace": [
- true,
- "check-branch",
- "check-decl",
- "check-operator",
- "check-separator",
- "check-type"
- ]
- }
-}
diff --git a/typings.json b/typings.json
deleted file mode 100644
index 521d533..0000000
--- a/typings.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "name": "ant-plus",
- "dependencies": {},
- "globalDependencies": {
- "node": "registry:dt/node#6.0.0+20160608055420"
- }
-}