├── .gitignore ├── .travis.yml ├── LICENSE.md ├── Makefile ├── README.md ├── READMEnpm.md ├── bower.json ├── example └── index.html ├── glicko2.d.ts ├── glicko2.js ├── package-lock.json ├── package.json ├── test └── glicko2.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | toNPM 2 | *.swp 3 | node_modules/ 4 | doc/glicko2.pdf 5 | *.iml 6 | .idea/ 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 12 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DIRNPM=toNPM 2 | 3 | test: 4 | npm test 5 | 6 | build: 7 | rm -rf $(DIRNPM) 8 | mkdir $(DIRNPM) 9 | cp glicko2.js $(DIRNPM) 10 | cp package.json $(DIRNPM) 11 | cp READMEnpm.md $(DIRNPM)/README.md 12 | cp -R test $(DIRNPM) 13 | 14 | publish: build 15 | cd $(DIRNPM) 16 | npm publish . 17 | cd .. 18 | 19 | .PHONY: test 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Glicko 2 javascript implementation 2 | 3 | [![Build Status](https://travis-ci.org/mmai/glicko2js.png)](https://travis-ci.org/mmai/glicko2js) 4 | 5 | 6 | The Glicko-2 rating system is a method for assessing a player's strength in games of skill, such as chess and go. 7 | The algorithm is explained by its author, Mark E. Glickman, on http://glicko.net/glicko.html. 8 | 9 | Each player begins with a rating, a rating deviation (accuracy of the rating) and a volatility (speed of rating evolution). These values will evolve according to the outcomes of matches with other players. 10 | 11 | ## Installation 12 | 13 | ### As a node.js module 14 | 15 | glicko2.js is available as a [npm module](https://www.npmjs.com/package/glicko2). 16 | 17 | Install with npm: 18 | 19 | ``` shell 20 | npm install glicko2 21 | ``` 22 | 23 | or with yarn: 24 | 25 | ``` shell 26 | yarn add glicko2 27 | ``` 28 | 29 | ### In the browser 30 | 31 | You just need to include the glicko2.js script. 32 | See index.html in the example folder. 33 | 34 | ``` html 35 | 36 | ``` 37 | 38 | ### With [bower](http://bower.io/) 39 | 40 | ``` shell 41 | bower install glicko2 42 | ``` 43 | ``` html 44 | 45 | ``` 46 | 47 | ## Usage 48 | 49 | In node.js, just require the module : 50 | 51 | ``` javascript 52 | import { Glicko2 } from 'glicko2'; 53 | ``` 54 | 55 | or 56 | 57 | ``` javascript 58 | const { Glicko2 } = require('glicko2'); 59 | ``` 60 | 61 | First we initiate a ranking manager and create players with initial ratings, rating deviations and volatilities. 62 | 63 | ``` javascript 64 | const ranking = new Glicko2({ 65 | // tau : "Reasonable choices are between 0.3 and 1.2, though the system should 66 | // be tested to decide which value results in greatest predictive accuracy." 67 | // If not set, default value is 0.5 68 | tau: 0.5, 69 | 70 | // rating : default rating 71 | // If not set, default value is 1500 72 | rating: 1500, 73 | 74 | // rd : Default rating deviation 75 | // small number = good confidence on the rating accuracy 76 | // If not set, default value is 350 77 | rd: 200, 78 | 79 | // vol : Default volatility (expected fluctation on the player rating) 80 | // If not set, default value is 0.06 81 | vol: 0.06, 82 | }); 83 | 84 | // Create players 85 | const Ryan = ranking.makePlayer(); 86 | const Bob = ranking.makePlayer(1400, 30, 0.06); 87 | const John = ranking.makePlayer(1550, 100, 0.06); 88 | const Mary = ranking.makePlayer(1700, 300, 0.06); 89 | ``` 90 | 91 | We can then enter results, calculate the new ratings... 92 | 93 | ``` javascript 94 | const matches = []; 95 | 96 | matches.push([Ryan, Bob, 1]); //Ryan won over Bob 97 | matches.push([Ryan, John, 0]); //Ryan lost against John 98 | matches.push([Ryan, Mary, 0.5]); //A draw between Ryan and Mary 99 | 100 | ranking.updateRatings(matches); 101 | ``` 102 | 103 | ... and get these new ratings. 104 | 105 | ``` javascript 106 | console.log("Ryan new rating: " + Ryan.getRating()); 107 | console.log("Ryan new rating deviation: " + Ryan.getRd()); 108 | console.log("Ryan new volatility: " + Ryan.getVol()); 109 | ``` 110 | 111 | Get players list 112 | 113 | ``` javascript 114 | const players = ranking.getPlayers(); 115 | ``` 116 | 117 | Predict outcome 118 | 119 | ``` javascript 120 | const expected = ranking.predict(Ryan, Bob); // or Ryan.predict(Bob); 121 | console.log("Ryan has " + (expected * 100) + "% chances of winning against Bob in the next match"); 122 | ``` 123 | 124 | ## When to update rankings 125 | 126 | You should not update the ranking after each match. 127 | The typical use of glicko is to calculate the ratings after each tournament (ie collection of matches in a period of time). 128 | A player rating will evolve after a tournament has finished, but not during the tournament. 129 | 130 | Here is what says Mark E. Glickman about the number of matches in a tournament or rating period (cf. ) : 131 | > The Glicko-2 system works best when the number of games in a rating period is moderate to large, say an average of at least 10-15 games per player in a rating period. 132 | 133 | ## How to deal with a big database of players 134 | 135 | If you don't want to load all the players and new matches at once in memory in order to update rankings, here is a strategy you can follow: 136 | 137 | Say you want to update rankings each week, and you have a history of the rankings data (rating, rating deviation, volatility) for each player. 138 | 139 | At the end of the week, for **each** player (even those that did not play): 140 | - load from the database: 141 | - the player ranking data for the current week 142 | - the matches the player has played during the week 143 | - for each match, the player's opponent data for the current week 144 | - add the player and his opponents to a new glicko instance 145 | - add the matches to the glicko instance 146 | - update glicko rankings 147 | - save the player updated ranking data to database as the next week player data 148 | 149 | It is important to update rankings even for players that did not play : this is the way the system can increase their rating deviation over time. 150 | 151 | At the last step, don't overwrite the player current week data, as it will be loaded when calculating its opponents new rankings. 152 | 153 | ### Support for multiple competitors matches (experimental) 154 | 155 | **Note: the glicko2 algorithm was not designed for multiple competitors matches, this is a naive workaround whose results should be taken whith caution.** 156 | 157 | You can enter results from games where multiple competitors play against each other at the same time (ie swimming, racing...). 158 | 159 | First make "Race" objects by entering the results in an array of "positions", where each position is an array of players at this position : 160 | 161 | ```javascript 162 | const race1 = glicko.makeRace( 163 | [ 164 | [Ryan], //Ryan won the race 165 | [Bob, John], //Bob and John ended ex aequo at the 2nd position 166 | [Mary] // Mary 4th position 167 | ] 168 | ); 169 | 170 | const race2 = glicko.makeRace( 171 | [ 172 | [Mary], // won 173 | [Bob], // 2nd 174 | [John], // 3rd 175 | [Ryan], // 4th 176 | ] 177 | ); 178 | 179 | ``` 180 | 181 | Then convert the races to the equivalent matches : 182 | ``` javascript 183 | 184 | const matches1 = race1.getMatches(); 185 | const matches2 = race2.getMatches(); 186 | 187 | const allMatches = matches1.concat(matches2) 188 | 189 | ranking.updateRatings(allMatches); 190 | ``` 191 | 192 | You can also update ratings for one race without converting to matches : 193 | 194 | ``` javascript 195 | ranking.updateRatings(race1); 196 | ``` 197 | 198 | ## Testing 199 | 200 | Run unit tests with: 201 | 202 | ``` shell 203 | npm run test 204 | ``` 205 | 206 | ## License 207 | 208 | This library is under [MIT License](LICENSE.md). 209 | -------------------------------------------------------------------------------- /READMEnpm.md: -------------------------------------------------------------------------------- 1 | # Glicko 2 javascript implementation 2 | 3 | [![Build Status](https://travis-ci.org/mmai/glicko2js.png)](https://travis-ci.org/mmai/glicko2js) 4 | 5 | 6 | The Glicko-2 rating system is a method for assessing a player's strength in games of skill, such as chess and go. 7 | The algorithm is explained by its author, Mark E. Glickman, on http://glicko.net/glicko.html. 8 | 9 | Each player begins with a rating, a rating deviation (accuracy of the rating) and a volatility (speed of rating evolution). These values will evolve according to the outcomes of matches with other players. 10 | 11 | ## Usage 12 | 13 | First we initiate a ranking manager and create players with initial ratings, rating deviations and volatilities. 14 | 15 | ``` javascript 16 | var glicko2 = require('glicko2'); 17 | var settings = { 18 | // tau : "Reasonable choices are between 0.3 and 1.2, though the system should 19 | // be tested to decide which value results in greatest predictive accuracy." 20 | tau : 0.5, 21 | // rating : default rating 22 | rating : 1500, 23 | //rd : Default rating deviation 24 | // small number = good confidence on the rating accuracy 25 | rd : 200, 26 | //vol : Default volatility (expected fluctation on the player rating) 27 | vol : 0.06 28 | }; 29 | var ranking = new glicko2.Glicko2(settings); 30 | 31 | // Create players 32 | var Ryan = ranking.makePlayer(); 33 | var Bob = ranking.makePlayer(1400, 30, 0.06); 34 | var John = ranking.makePlayer(1550, 100, 0.06); 35 | var Mary = ranking.makePlayer(1700, 300, 0.06); 36 | ``` 37 | 38 | We can then enter results, calculate the new ratings... 39 | 40 | ``` javascript 41 | var matches = []; 42 | matches.push([Ryan, Bob, 1]); //Ryan won over Bob 43 | matches.push([Ryan, John, 0]); //Ryan lost against John 44 | matches.push([Ryan, Mary, 0.5]); //A draw between Ryan and Mary 45 | ranking.updateRatings(matches); 46 | ``` 47 | 48 | ... and get these new ratings. 49 | 50 | ``` javascript 51 | console.log("Ryan new rating: " + Ryan.getRating()); 52 | console.log("Ryan new rating deviation: " + Ryan.getRd()); 53 | console.log("Ryan new volatility: " + Ryan.getVol()); 54 | ``` 55 | 56 | Get players list 57 | 58 | ``` javascript 59 | var players = ranking.getPlayers(); 60 | ``` 61 | 62 | Predict outcome 63 | 64 | ``` javascript 65 | var expected = ranking.predict(Ryan, Bob); // or Ryan.predict(Bob); 66 | console.log("Ryan has " + (expected * 100) + "% chances of winning against Bob in the next match"); 67 | ``` 68 | 69 | ## When to update rankings 70 | 71 | You should not update the ranking after each match. 72 | The typical use of glicko is to calculate the ratings after each tournament (ie collection of matches in a period of time). 73 | A player rating will evolve after a tournament has finished, but not during the tournament. 74 | 75 | You can see a client side javascript example using tournaments here : https://github.com/mmai/glicko2js/blob/master/example/index.html 76 | 77 | Here is what says Mark E. Glickman about the number of matches in a tournament or rating period (cf. http://www.glicko.net/glicko/glicko2.pdf ) : 78 | > The Glicko-2 system works best when the number of games in a rating period is moderate to large, say an average of at least 10-15 games per player in a rating period. 79 | 80 | ## How to deal with a big database of players 81 | 82 | If you don't want to load all the players and new matches at once in memory in order to update rankings, here is a strategy you can follow: 83 | 84 | Say you want to update rankings each week, and you have a history of the rankings data (rating, rating deviation, volatility) for each player. 85 | 86 | At the end of the week, for **each** player (even those that did not play): 87 | - load from the database: 88 | - the player ranking data for the current week 89 | - the matches the player has played during the week 90 | - for each match, the player's opponent data for the current week 91 | - add the player and his opponents to a new glicko instance 92 | - add the matches to the glicko instance 93 | - update glicko rankings 94 | - save the player updated ranking data to database as the next week player data 95 | 96 | It is important to update rankings even for players that did not play : this is the way the system can increase their rating deviation over time. 97 | 98 | At the last step, don't overwrite the player current week data, as it will be loaded when calculating its opponents new rankings. 99 | 100 | ### Support for multiple competitors matches (experimental) 101 | 102 | **Note: the glicko2 algorithm was not designed for multiple competitors matches, this is a naive workaround whose results should be taken whith caution.** 103 | 104 | You can enter results from games where multiple competitors play against each other at the same time (ie swimming, racing...). 105 | 106 | First make "Race" objects by entering the results in an array of "positions", where each position is an array of players at this position : 107 | 108 | ```javascript 109 | var race1 = glicko.makeRace( 110 | [ 111 | [Ryan], //Ryan won the race 112 | [Bob, John], //Bob and John ended ex aequo at the 2nd position 113 | [Mary] // Mary 4th position 114 | ] 115 | ); 116 | 117 | var race2 = glicko.makeRace( 118 | [ 119 | [Mary], // won 120 | [Bob], // 2nd 121 | [John], // 3rd 122 | [Ryan], // 4th 123 | ] 124 | ); 125 | 126 | ``` 127 | 128 | Then convert the races to the equivalent matches : 129 | ```javascript 130 | 131 | var matches1 = race1.getMatches(); 132 | var matches2 = race2.getMatches(); 133 | 134 | var allMatches = matches1.concat(matches2) 135 | 136 | ranking.updateRatings(allMatches); 137 | ``` 138 | 139 | You can also update ratings for one race without converting to matches : 140 | 141 | ```javascript 142 | ranking.updateRatings(race1); 143 | ``` 144 | 145 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "glicko2", 3 | "version": "1.2.1", 4 | "homepage": "https://github.com/mmai/glicko2js", 5 | "authors": [ 6 | "Henri Bourcereau " 7 | ], 8 | "description": "glicko2 ranking system", 9 | "main": "./glicko2.js", 10 | "keywords": [ 11 | "glicko", 12 | "utilities", 13 | "ranking" 14 | ], 15 | "license": "MIT", 16 | "ignore": [ 17 | "**/.*", 18 | "example", 19 | "node_modules", 20 | "test", 21 | "toNPM", 22 | "Makefile", 23 | "package.json", 24 | "READMEnpm.md" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 44 | 45 | 46 | 47 |

Initial rankings

48 | 51 | 52 |

Rankings after tournament 1

53 | 63 | 64 |

Rankings after tournament 2

65 | 78 | 79 | 80 | 81 |

Rankings after two races

82 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /glicko2.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'glicko2' { 2 | export class Glicko2 { 3 | /** 4 | * Constructs a new Glicko2 instance with optional settings. 5 | * @param settings - Optional settings to configure the Glicko2 instance. 6 | */ 7 | constructor(settings?: Glicko2Settings); 8 | 9 | /** 10 | * Creates a new Race instance. 11 | * @param results - The results of the race. 12 | * @returns A new Race instance. 13 | */ 14 | makeRace(results: Player[][]): Race; 15 | 16 | /** 17 | * Removes all players from the system. 18 | */ 19 | removePlayers(): void; 20 | 21 | /** 22 | * Gets all players in the system. 23 | * @returns An array of all players. 24 | */ 25 | getPlayers(): Player[]; 26 | 27 | /** 28 | * Cleans previous matches for all players. 29 | */ 30 | cleanPreviousMatches(): void; 31 | 32 | /** 33 | * Calculates the ratings for all players. 34 | */ 35 | calculatePlayersRatings(): void; 36 | 37 | /** 38 | * Adds a match result between two players. 39 | * @param player1 - The first player. 40 | * @param player2 - The second player. 41 | * @param outcome - The outcome of the match: 0 = defeat, 1 = victory, 0.5 = draw. 42 | * @returns An object containing the two players. 43 | */ 44 | addMatch(player1: Player, player2: Player, outcome: number): { pl1: Player, pl2: Player }; 45 | 46 | /** 47 | * Creates a new Player instance. 48 | * @param rating - The rating of the player. 49 | * @param rd - The rating deviation of the player. 50 | * @param vol - The volatility of the player. 51 | * @returns A new Player instance. 52 | */ 53 | makePlayer(rating: number, rd?: number, vol?: number): Player; 54 | 55 | /** 56 | * Adds a result for a player. 57 | * @param player1 - The first player. 58 | * @param player2 - The second player. 59 | * @param outcome - The outcome of the match. 60 | */ 61 | addResult(player1: Player, player2: Player, outcome: number): void; 62 | 63 | /** 64 | * Updates the ratings of players based on the matches. 65 | * @param matches - The matches to be used for updating ratings. 66 | */ 67 | updateRatings(matches: Race | [Player, Player, number][]): void; 68 | 69 | /** 70 | * Predicts the outcome between two players. 71 | * @param player1 - The first player. 72 | * @param player2 - The second player. 73 | * @returns The predicted outcome. 74 | */ 75 | predict(player1: Player, player2: Player): number; 76 | 77 | /** 78 | * Internal method to create a new Player instance. 79 | * @param rating - The rating of the player. 80 | * @param rd - The rating deviation of the player. 81 | * @param vol - The volatility of the player. 82 | * @param id - The ID of the player. 83 | * @returns A new Player instance. 84 | */ 85 | private _createInternalPlayer(rating?: number, rd?: number, vol?: number, id?: number): Player; 86 | } 87 | 88 | /** 89 | * Settings to configure the Glicko2 instance. 90 | */ 91 | export interface Glicko2Settings { 92 | /** 93 | * The system constant which constrains changes in volatility (tau). 94 | * Defaults to 0.5 95 | */ 96 | tau?: number; 97 | 98 | /** 99 | * The initial rating for players. 100 | * Defaults to 1500 101 | */ 102 | rating?: number; 103 | 104 | /** 105 | * The initial rating deviation for players. 106 | * Defaults to 350 107 | */ 108 | rd?: number; 109 | 110 | /** 111 | * The initial volatility for players. 112 | * Defaults to 0.06 113 | */ 114 | vol?: number; 115 | 116 | /** 117 | * The algorithm to use for volatility calculation. 118 | * Defaults to 'newprocedure' 119 | */ 120 | volatility_algorithm?: 'oldprocedure' | 'newprocedure' | 'newprocedure_mod' | 'oldprocedure_simple'; 121 | } 122 | 123 | export class Race { 124 | /** 125 | * Constructs a new Race instance. 126 | * @param results - The results of the race. 127 | */ 128 | constructor(results: Player[][]); 129 | 130 | /** 131 | * Gets the matches for the race. 132 | * @returns An array of matches. 133 | */ 134 | getMatches(): Player[][]; 135 | 136 | /** 137 | * Computes the matches for the race. 138 | * @param results - The results of the race. 139 | * @returns An array of matches. 140 | */ 141 | computeMatches(results: Player[][]): Player[][]; 142 | 143 | /** 144 | * Computes the matches for the race using the v2 algorithm. 145 | * @param results - The results of the race. 146 | * @returns An array of matches. 147 | */ 148 | computeMatches_v2(results: Player[][]): Player[][]; 149 | } 150 | 151 | export class Player { 152 | /** 153 | * Constructs a new Player instance. 154 | * @param rating - The rating of the player. 155 | * @param rd - The rating deviation of the player. 156 | * @param vol - The volatility of the player. 157 | * @param tau - The system constant which constrains changes in volatility (tau). 158 | * @param default_rating - The default rating for new players. 159 | * @param volatility_algorithm - The function for calculating volatility. 160 | * @param id - The ID of the player. 161 | */ 162 | constructor( 163 | rating: number, 164 | rd: number, 165 | vol: number, 166 | tau: number, 167 | default_rating: number, 168 | volatility_algorithm: (v: number, delta: number) => number, 169 | id: number 170 | ); 171 | 172 | /** 173 | * Gets the rating of the player. 174 | * @returns The rating of the player. 175 | */ 176 | getRating(): number; 177 | 178 | /** 179 | * Sets the rating of the player. 180 | * @param rating - The new rating of the player. 181 | */ 182 | setRating(rating: number): void; 183 | 184 | /** 185 | * Gets the rating deviation of the player. 186 | * @returns The rating deviation of the player. 187 | */ 188 | getRd(): number; 189 | 190 | /** 191 | * Sets the rating deviation of the player. 192 | * @param rd - The new rating deviation of the player. 193 | */ 194 | setRd(rd: number): void; 195 | 196 | /** 197 | * Gets the volatility of the player. 198 | * @returns The volatility of the player. 199 | */ 200 | getVol(): number; 201 | 202 | /** 203 | * Sets the volatility of the player. 204 | * @param vol - The new volatility of the player. 205 | */ 206 | setVol(vol: number): void; 207 | 208 | /** 209 | * Adds a result for the player. 210 | * @param opponent - The opponent player. 211 | * @param outcome - The outcome of the match. 212 | */ 213 | addResult(opponent: Player, outcome: number): void; 214 | 215 | /** 216 | * Updates the rank of the player. 217 | */ 218 | update_rank(): void; 219 | 220 | /** 221 | * Checks if the player has played any matches. 222 | * @returns True if the player has played, false otherwise. 223 | */ 224 | hasPlayed(): boolean; 225 | 226 | /** 227 | * Updates the rating deviation for the beginning of a rating period. 228 | */ 229 | _preRatingRD(): void; 230 | 231 | /** 232 | * Calculates the estimated variance of the player's rating based on game outcomes. 233 | * @returns The variance. 234 | */ 235 | _variance(): number; 236 | 237 | /** 238 | * Calculates the expected outcome using the Glicko E function. 239 | * @param p2rating - The rating of the opponent. 240 | * @param p2RD - The rating deviation of the opponent. 241 | * @returns The expected outcome. 242 | */ 243 | _E(p2rating: number, p2RD: number): number; 244 | 245 | /** 246 | * Predicts the outcome of a match against another player. 247 | * @param p2 - The opponent player. 248 | * @returns The predicted outcome. 249 | */ 250 | predict(p2: Player): number; 251 | 252 | /** 253 | * The Glicko2 g(RD) function. 254 | * @param RD - The rating deviation. 255 | * @returns The g(RD) value. 256 | */ 257 | _g(RD: number): number; 258 | 259 | /** 260 | * Calculates the estimated improvement in rating. 261 | * @param v - The variance. 262 | * @returns The delta value. 263 | */ 264 | _delta(v: number): number; 265 | 266 | /** 267 | * Creates a function for volatility calculation. 268 | * @param delta - The delta value. 269 | * @param v - The variance. 270 | * @param a - The a value. 271 | * @returns A function for volatility calculation. 272 | */ 273 | _makef(delta: number, v: number, a: number): (x: number) => number; 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /glicko2.js: -------------------------------------------------------------------------------- 1 | (function(exports){ 2 | var scalingFactor = 173.7178; 3 | 4 | function Race(results){ 5 | this.matches = this.computeMatches(results); 6 | } 7 | Race.prototype.getMatches = function(){ 8 | return this.matches; 9 | }; 10 | Race.prototype.computeMatches = function(results){ 11 | var players = []; 12 | var position = 0; 13 | 14 | results.forEach(function (rank) { 15 | position += 1; 16 | rank.forEach(function (player) { 17 | players.push({"player": player, "position": position}); 18 | }) 19 | }) 20 | 21 | function computeMatches(players){ 22 | if (players.length === 0) return []; 23 | 24 | var player1 = players.shift() 25 | var player1_results = players.map(function(player2){ 26 | return [player1.player, player2.player, (player1.position < player2.position) ? 1 : 0.5]; 27 | }); 28 | 29 | return player1_results.concat(computeMatches(players)); 30 | } 31 | 32 | return computeMatches(players) 33 | } 34 | 35 | // this version uses the algorithm from http://www.tckerrigan.com/Misc/Multiplayer_Elo/ 36 | // but the results are not convincing 37 | Race.prototype.computeMatches_v2 = function(results){ 38 | var players = []; 39 | var position = 0; 40 | 41 | results.forEach(function (rank) { 42 | position += 1; 43 | rank.forEach(function (player) { 44 | players.push({"player": player, "position": position}); 45 | }) 46 | }) 47 | 48 | var matches = []; 49 | while(player = players.shift()){ 50 | if (players.length > 0){ 51 | matches.push([player.player, players[0].player, (player.position < players[0].position) ? 1 : 0.5]); 52 | } 53 | } 54 | 55 | return matches 56 | } 57 | 58 | function Player(rating, rd, vol, tau, default_rating, volatility_algorithm, id) { 59 | this._tau = tau; 60 | this.defaultRating = default_rating; 61 | this.volatility_algorithm = volatility_algorithm; 62 | 63 | this.setRating(rating); 64 | this.setRd(rd); 65 | this.setVol(vol); 66 | 67 | this.id = id 68 | this.adv_ranks = []; 69 | this.adv_rds = []; 70 | this.outcomes = []; 71 | } 72 | 73 | Player.prototype.getRating = function (){ 74 | return this.__rating * scalingFactor + this.defaultRating; 75 | }; 76 | 77 | Player.prototype.setRating = function (rating){ 78 | this.__rating = (rating - this.defaultRating) / scalingFactor; 79 | }; 80 | 81 | Player.prototype.getRd = function(){ 82 | return this.__rd * scalingFactor; 83 | }; 84 | 85 | Player.prototype.setRd = function(rd){ 86 | this.__rd = rd / scalingFactor; 87 | }; 88 | 89 | Player.prototype.getVol = function(){ 90 | return this.__vol; 91 | }; 92 | 93 | Player.prototype.setVol = function(vol){ 94 | this.__vol = vol; 95 | }; 96 | 97 | Player.prototype.addResult = function(opponent, outcome){ 98 | this.adv_ranks.push(opponent.__rating); 99 | this.adv_rds.push(opponent.__rd); 100 | this.outcomes.push(outcome); 101 | }; 102 | 103 | // Calculates the new rating and rating deviation of the player. 104 | // Follows the steps of the algorithm described at http://www.glicko.net/glicko/glicko2.pdf 105 | Player.prototype.update_rank = function(){ 106 | if (!this.hasPlayed()){ 107 | // Applies only the Step 6 of the algorithm 108 | this._preRatingRD(); 109 | return; 110 | } 111 | 112 | //Step 1 : done by Player initialization 113 | //Step 2 : done by setRating and setRd 114 | 115 | //Step 3 116 | var v = this._variance(); 117 | 118 | //Step 4 119 | var delta = this._delta(v); 120 | 121 | //Step 5 122 | this.__vol = this.volatility_algorithm(v, delta); 123 | 124 | //Step 6 125 | this._preRatingRD(); 126 | 127 | //Step 7 128 | this.__rd = 1 / Math.sqrt((1 / Math.pow(this.__rd, 2)) + (1 / v)); 129 | 130 | var tempSum = 0; 131 | for (var i=0,len = this.adv_ranks.length;i< len;i++){ 132 | tempSum += this._g(this.adv_rds[i]) * (this.outcomes[i] - this._E(this.adv_ranks[i], this.adv_rds[i])); 133 | } 134 | this.__rating += Math.pow(this.__rd, 2) * tempSum; 135 | 136 | //Step 8 : done by getRating and getRd 137 | }; 138 | 139 | Player.prototype.hasPlayed = function(){ 140 | return this.outcomes.length > 0; 141 | }; 142 | 143 | // Calculates and updates the player's rating deviation for the beginning of a rating period. 144 | // preRatingRD() -> None 145 | Player.prototype._preRatingRD = function(){ 146 | this.__rd = Math.sqrt(Math.pow(this.__rd, 2) + Math.pow(this.__vol, 2)); 147 | }; 148 | 149 | 150 | // Calculation of the estimated variance of the player's rating based on game outcomes 151 | Player.prototype._variance = function (){ 152 | var tempSum = 0; 153 | for (var i = 0, len = this.adv_ranks.length;i 0 ){ 331 | result = upper; 332 | } else { 333 | x1 = 0; 334 | x2 = x1; 335 | y2 = y1; 336 | x1 = x1 - 1; 337 | y1 = equation(phi, v, x1, a, tau, delta); 338 | while (y1 < 0){ 339 | x2 = x1; 340 | y2 = y1; 341 | x1 = x1 - 1; 342 | y1 = equation(phi, v, x1, a, tau, delta); 343 | } 344 | for (var i = 0; i<21; i++){ 345 | x3 = y1 * (x1 - x2) / (y2 - y1) + x1; 346 | y3 = equation(phi, v, x3, a, tau, delta); 347 | if (y3 > 0 ){ 348 | x1 = x3; 349 | y1 = y3; 350 | } else { 351 | x2 = x3; 352 | y2 = y3; 353 | } 354 | } 355 | if (Math.exp((y1 * (x1 - x2) / (y2 - y1) + x1) / 2) > upper ){ 356 | result = upper; 357 | } else { 358 | result = Math.exp((y1 * (x1 - x2) / (y2 - y1) + x1) / 2); 359 | } 360 | } 361 | return result; 362 | 363 | // 364 | function new_sigma(sigma , phi , v , delta , tau ) { 365 | var a = Math.log(Math.pow(sigma, 2)); 366 | var x = a; 367 | var old_x = 0; 368 | while (x != old_x){ 369 | old_x = x; 370 | var d = Math.pow(phi, 2) + v + Math.exp(old_x); 371 | var h1 = -(old_x - a) / Math.pow(tau, 2) - 0.5 * Math.exp(old_x) / d + 0.5 * Math.exp(old_x) * Math.pow((delta / d), 2); 372 | var h2 = -1 / Math.pow(tau, 2) - 0.5 * Math.exp(old_x) * (Math.pow(phi, 2) + v) / Math.pow(d, 2) + 0.5 * Math.pow(delta, 2) * Math.exp(old_x) * (Math.pow(phi, 2) + v - Math.exp(old_x)) / Math.pow(d, 3); 373 | x = old_x - h1 / h2; 374 | } 375 | return Math.exp(x / 2); 376 | } 377 | 378 | function equation(phi , v , x , a , tau , delta) { 379 | var d = Math.pow(phi, 2) + v + Math.exp(x); 380 | return -(x - a) / Math.pow(tau, 2) - 0.5 * Math.exp(x) / d + 0.5 * Math.exp(x) * Math.pow((delta / d), 2); 381 | } 382 | 383 | function new_sigma_bisection(sigma , phi , v , delta , tau ) { 384 | var a, x1, x2, x3; 385 | a = Math.log(Math.pow(sigma, 2)); 386 | if (equation(phi, v, 0, a, tau, delta) < 0 ){ 387 | x1 = -1; 388 | while (equation(phi, v, x1, a, tau, delta) < 0){ 389 | x1 = x1 - 1; 390 | } 391 | x2 = x1 + 1; 392 | } else { 393 | x2 = 1; 394 | while (equation(phi, v, x2, a, tau, delta) > 0){ 395 | x2 = x2 + 1; 396 | } 397 | x1 = x2 - 1; 398 | } 399 | 400 | for (var i = 0; i < 27; i++) { 401 | x3 = (x1 + x2) / 2; 402 | if (equation(phi, v, x3, a, tau, delta) > 0 ){ 403 | x1 = x3; 404 | } else { 405 | x2 = x3; 406 | } 407 | } 408 | return Math.exp((x1 + x2)/ 4); 409 | } 410 | 411 | function Dequation(phi , v , x , tau , delta) { 412 | d = Math.pow(phi, 2) + v + Math.exp(x); 413 | return -1 / Math.pow(tau, 2) - 0.5 * Math.exp(x) / d + 0.5 * Math.exp(x) * (Math.exp(x) + Math.pow(delta, 2)) / Math.pow(d, 2) - Math.pow(Math.exp(x), 2) * Math.pow(delta, 2) / Math.pow(d, 3); 414 | } 415 | 416 | function find_upper_falsep(phi , v , delta , tau) { 417 | var x1, x2, x3, y1, y2, y3; 418 | y1 = Dequation(phi, v, 0, tau, delta); 419 | if (y1 < 0 ){ 420 | return 1; 421 | } else { 422 | x1 = 0; 423 | x2 = x1; 424 | y2 = y1; 425 | x1 = x1 - 1; 426 | y1 = Dequation(phi, v, x1, tau, delta); 427 | while (y1 > 0){ 428 | x2 = x1; 429 | y2 = y1; 430 | x1 = x1 - 1; 431 | y1 = Dequation(phi, v, x1, tau, delta); 432 | } 433 | for (var i = 0; i < 21 ; i++){ 434 | x3 = y1 * (x1 - x2) / (y2 - y1) + x1; 435 | y3 = Dequation(phi, v, x3, tau, delta); 436 | if (y3 > 0 ){ 437 | x1 = x3; 438 | y1 = y3; 439 | } else { 440 | x2 = x3; 441 | y2 = y3; 442 | } 443 | } 444 | return Math.exp((y1 * (x1 - x2) / (y2 - y1) + x1) / 2); 445 | } 446 | } 447 | }, 448 | newprocedure: function(v, delta){ 449 | //Step 5.1 450 | var A = Math.log(Math.pow(this.__vol, 2)); 451 | var f = this._makef(delta, v, A); 452 | var epsilon = 0.0000001; 453 | 454 | //Step 5.2 455 | var B, k; 456 | if (Math.pow(delta, 2) > Math.pow(this.__rd, 2) + v){ 457 | B = Math.log(Math.pow(delta, 2) - Math.pow(this.__rd, 2) - v); 458 | } else { 459 | k = 1; 460 | while (f(A - k * this._tau) < 0){ 461 | k = k + 1; 462 | } 463 | B = A - k * this._tau; 464 | } 465 | 466 | //Step 5.3 467 | var fA = f(A); 468 | var fB = f(B); 469 | 470 | //Step 5.4 471 | var C, fC; 472 | while (Math.abs(B - A) > epsilon){ 473 | C = A + (A - B) * fA /(fB - fA ); 474 | fC = f(C); 475 | if (fC * fB <= 0){ // March 22, 2022 algorithm update : `<` replaced by `<=` 476 | A = B; 477 | fA = fB; 478 | } else { 479 | fA = fA / 2; 480 | } 481 | B = C; 482 | fB = fC; 483 | } 484 | //Step 5.5 485 | return Math.exp(A/2); 486 | }, 487 | newprocedure_mod: function(v, delta){ 488 | //Step 5.1 489 | var A = Math.log(Math.pow(this.__vol, 2)); 490 | var f = this._makef(delta, v, A); 491 | var epsilon = 0.0000001; 492 | 493 | //Step 5.2 494 | var B, k; 495 | //XXX mod 496 | if (delta > Math.pow(this.__rd, 2) + v){ 497 | //XXX mod 498 | B = Math.log(delta - Math.pow(this.__rd, 2) - v); 499 | } else { 500 | k = 1; 501 | while (f(A - k * this._tau) < 0){ 502 | k = k + 1; 503 | } 504 | B = A - k * this._tau; 505 | } 506 | 507 | //Step 5.3 508 | var fA = f(A); 509 | var fB = f(B); 510 | 511 | //Step 5.4 512 | var C, fC; 513 | while (Math.abs(B - A) > epsilon){ 514 | C = A + (A - B) * fA /(fB - fA ); 515 | fC = f(C); 516 | if (fC * fB < 0){ 517 | A = B; 518 | fA = fB; 519 | } else { 520 | fA = fA / 2; 521 | } 522 | B = C; 523 | fB = fC; 524 | } 525 | //Step 5.5 526 | return Math.exp(A/2); 527 | }, 528 | oldprocedure_simple: function(v, delta){ 529 | var i = 0; 530 | var a = Math.log(Math.pow(this.__vol, 2)); 531 | var tau = this._tau; 532 | var x0 = a; 533 | var x1 = 0; 534 | var d,h1,h2; 535 | 536 | while (Math.abs(x0 - x1) > 0.00000001){ 537 | // New iteration, so x(i) becomes x(i-1) 538 | x0 = x1; 539 | d = Math.pow(this.__rating, 2) + v + Math.exp(x0); 540 | h1 = -(x0 - a) / Math.pow(tau, 2) - 0.5 * Math.exp(x0) / d + 0.5 * Math.exp(x0) * Math.pow(delta / d, 2); 541 | h2 = -1 / Math.pow(tau, 2) - 0.5 * Math.exp(x0) * (Math.pow(this.__rating, 2) + v) / Math.pow(d, 2) + 0.5 * Math.pow(delta, 2) * Math.exp(x0) * (Math.pow(this.__rating, 2) + v - Math.exp(x0)) / Math.pow(d, 3); 542 | x1 = x0 - (h1 / h2); 543 | } 544 | 545 | return Math.exp(x1 / 2); 546 | } 547 | }; 548 | //==== End of volatility algorithms 549 | 550 | exports.Glicko2 = Glicko2; 551 | 552 | })(typeof exports === 'undefined'? this['glicko2']={}: exports); 553 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "glicko2", 3 | "version": "1.2.1", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "glicko2", 9 | "version": "1.2.1", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "mocha": "^10.1.0", 13 | "should": "13.2.x" 14 | } 15 | }, 16 | "node_modules/ansi-colors": { 17 | "version": "4.1.3", 18 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", 19 | "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", 20 | "dev": true, 21 | "engines": { 22 | "node": ">=6" 23 | } 24 | }, 25 | "node_modules/ansi-regex": { 26 | "version": "5.0.1", 27 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 28 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 29 | "dev": true, 30 | "engines": { 31 | "node": ">=8" 32 | } 33 | }, 34 | "node_modules/ansi-styles": { 35 | "version": "4.3.0", 36 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 37 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 38 | "dev": true, 39 | "dependencies": { 40 | "color-convert": "^2.0.1" 41 | }, 42 | "engines": { 43 | "node": ">=8" 44 | }, 45 | "funding": { 46 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 47 | } 48 | }, 49 | "node_modules/anymatch": { 50 | "version": "3.1.2", 51 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", 52 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", 53 | "dev": true, 54 | "dependencies": { 55 | "normalize-path": "^3.0.0", 56 | "picomatch": "^2.0.4" 57 | }, 58 | "engines": { 59 | "node": ">= 8" 60 | } 61 | }, 62 | "node_modules/argparse": { 63 | "version": "2.0.1", 64 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 65 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 66 | "dev": true 67 | }, 68 | "node_modules/balanced-match": { 69 | "version": "1.0.2", 70 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 71 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 72 | "dev": true 73 | }, 74 | "node_modules/binary-extensions": { 75 | "version": "2.2.0", 76 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 77 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 78 | "dev": true, 79 | "engines": { 80 | "node": ">=8" 81 | } 82 | }, 83 | "node_modules/brace-expansion": { 84 | "version": "2.0.1", 85 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 86 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 87 | "dev": true, 88 | "dependencies": { 89 | "balanced-match": "^1.0.0" 90 | } 91 | }, 92 | "node_modules/braces": { 93 | "version": "3.0.3", 94 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", 95 | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 96 | "dev": true, 97 | "dependencies": { 98 | "fill-range": "^7.1.1" 99 | }, 100 | "engines": { 101 | "node": ">=8" 102 | } 103 | }, 104 | "node_modules/browser-stdout": { 105 | "version": "1.3.1", 106 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 107 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 108 | "dev": true 109 | }, 110 | "node_modules/camelcase": { 111 | "version": "6.3.0", 112 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", 113 | "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", 114 | "dev": true, 115 | "engines": { 116 | "node": ">=10" 117 | }, 118 | "funding": { 119 | "url": "https://github.com/sponsors/sindresorhus" 120 | } 121 | }, 122 | "node_modules/chalk": { 123 | "version": "4.1.2", 124 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 125 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 126 | "dev": true, 127 | "dependencies": { 128 | "ansi-styles": "^4.1.0", 129 | "supports-color": "^7.1.0" 130 | }, 131 | "engines": { 132 | "node": ">=10" 133 | }, 134 | "funding": { 135 | "url": "https://github.com/chalk/chalk?sponsor=1" 136 | } 137 | }, 138 | "node_modules/chalk/node_modules/supports-color": { 139 | "version": "7.2.0", 140 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 141 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 142 | "dev": true, 143 | "dependencies": { 144 | "has-flag": "^4.0.0" 145 | }, 146 | "engines": { 147 | "node": ">=8" 148 | } 149 | }, 150 | "node_modules/chokidar": { 151 | "version": "3.5.3", 152 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 153 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 154 | "dev": true, 155 | "funding": [ 156 | { 157 | "type": "individual", 158 | "url": "https://paulmillr.com/funding/" 159 | } 160 | ], 161 | "dependencies": { 162 | "anymatch": "~3.1.2", 163 | "braces": "~3.0.2", 164 | "glob-parent": "~5.1.2", 165 | "is-binary-path": "~2.1.0", 166 | "is-glob": "~4.0.1", 167 | "normalize-path": "~3.0.0", 168 | "readdirp": "~3.6.0" 169 | }, 170 | "engines": { 171 | "node": ">= 8.10.0" 172 | }, 173 | "optionalDependencies": { 174 | "fsevents": "~2.3.2" 175 | } 176 | }, 177 | "node_modules/cliui": { 178 | "version": "7.0.4", 179 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 180 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 181 | "dev": true, 182 | "dependencies": { 183 | "string-width": "^4.2.0", 184 | "strip-ansi": "^6.0.0", 185 | "wrap-ansi": "^7.0.0" 186 | } 187 | }, 188 | "node_modules/color-convert": { 189 | "version": "2.0.1", 190 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 191 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 192 | "dev": true, 193 | "dependencies": { 194 | "color-name": "~1.1.4" 195 | }, 196 | "engines": { 197 | "node": ">=7.0.0" 198 | } 199 | }, 200 | "node_modules/color-name": { 201 | "version": "1.1.4", 202 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 203 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 204 | "dev": true 205 | }, 206 | "node_modules/debug": { 207 | "version": "4.4.0", 208 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", 209 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", 210 | "dev": true, 211 | "dependencies": { 212 | "ms": "^2.1.3" 213 | }, 214 | "engines": { 215 | "node": ">=6.0" 216 | }, 217 | "peerDependenciesMeta": { 218 | "supports-color": { 219 | "optional": true 220 | } 221 | } 222 | }, 223 | "node_modules/decamelize": { 224 | "version": "4.0.0", 225 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", 226 | "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", 227 | "dev": true, 228 | "engines": { 229 | "node": ">=10" 230 | }, 231 | "funding": { 232 | "url": "https://github.com/sponsors/sindresorhus" 233 | } 234 | }, 235 | "node_modules/diff": { 236 | "version": "5.2.0", 237 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", 238 | "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", 239 | "dev": true, 240 | "engines": { 241 | "node": ">=0.3.1" 242 | } 243 | }, 244 | "node_modules/emoji-regex": { 245 | "version": "8.0.0", 246 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 247 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 248 | "dev": true 249 | }, 250 | "node_modules/escalade": { 251 | "version": "3.1.1", 252 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 253 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 254 | "dev": true, 255 | "engines": { 256 | "node": ">=6" 257 | } 258 | }, 259 | "node_modules/escape-string-regexp": { 260 | "version": "4.0.0", 261 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 262 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 263 | "dev": true, 264 | "engines": { 265 | "node": ">=10" 266 | }, 267 | "funding": { 268 | "url": "https://github.com/sponsors/sindresorhus" 269 | } 270 | }, 271 | "node_modules/fill-range": { 272 | "version": "7.1.1", 273 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", 274 | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 275 | "dev": true, 276 | "dependencies": { 277 | "to-regex-range": "^5.0.1" 278 | }, 279 | "engines": { 280 | "node": ">=8" 281 | } 282 | }, 283 | "node_modules/find-up": { 284 | "version": "5.0.0", 285 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 286 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 287 | "dev": true, 288 | "dependencies": { 289 | "locate-path": "^6.0.0", 290 | "path-exists": "^4.0.0" 291 | }, 292 | "engines": { 293 | "node": ">=10" 294 | }, 295 | "funding": { 296 | "url": "https://github.com/sponsors/sindresorhus" 297 | } 298 | }, 299 | "node_modules/flat": { 300 | "version": "5.0.2", 301 | "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", 302 | "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", 303 | "dev": true, 304 | "bin": { 305 | "flat": "cli.js" 306 | } 307 | }, 308 | "node_modules/fs.realpath": { 309 | "version": "1.0.0", 310 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 311 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 312 | "dev": true 313 | }, 314 | "node_modules/fsevents": { 315 | "version": "2.3.2", 316 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 317 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 318 | "dev": true, 319 | "hasInstallScript": true, 320 | "optional": true, 321 | "os": [ 322 | "darwin" 323 | ], 324 | "engines": { 325 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 326 | } 327 | }, 328 | "node_modules/get-caller-file": { 329 | "version": "2.0.5", 330 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 331 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 332 | "dev": true, 333 | "engines": { 334 | "node": "6.* || 8.* || >= 10.*" 335 | } 336 | }, 337 | "node_modules/glob": { 338 | "version": "8.1.0", 339 | "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", 340 | "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", 341 | "deprecated": "Glob versions prior to v9 are no longer supported", 342 | "dev": true, 343 | "dependencies": { 344 | "fs.realpath": "^1.0.0", 345 | "inflight": "^1.0.4", 346 | "inherits": "2", 347 | "minimatch": "^5.0.1", 348 | "once": "^1.3.0" 349 | }, 350 | "engines": { 351 | "node": ">=12" 352 | }, 353 | "funding": { 354 | "url": "https://github.com/sponsors/isaacs" 355 | } 356 | }, 357 | "node_modules/glob-parent": { 358 | "version": "5.1.2", 359 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 360 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 361 | "dev": true, 362 | "dependencies": { 363 | "is-glob": "^4.0.1" 364 | }, 365 | "engines": { 366 | "node": ">= 6" 367 | } 368 | }, 369 | "node_modules/has-flag": { 370 | "version": "4.0.0", 371 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 372 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 373 | "dev": true, 374 | "engines": { 375 | "node": ">=8" 376 | } 377 | }, 378 | "node_modules/he": { 379 | "version": "1.2.0", 380 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 381 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 382 | "dev": true, 383 | "bin": { 384 | "he": "bin/he" 385 | } 386 | }, 387 | "node_modules/inflight": { 388 | "version": "1.0.6", 389 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 390 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 391 | "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", 392 | "dev": true, 393 | "dependencies": { 394 | "once": "^1.3.0", 395 | "wrappy": "1" 396 | } 397 | }, 398 | "node_modules/inherits": { 399 | "version": "2.0.4", 400 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 401 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 402 | "dev": true 403 | }, 404 | "node_modules/is-binary-path": { 405 | "version": "2.1.0", 406 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 407 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 408 | "dev": true, 409 | "dependencies": { 410 | "binary-extensions": "^2.0.0" 411 | }, 412 | "engines": { 413 | "node": ">=8" 414 | } 415 | }, 416 | "node_modules/is-extglob": { 417 | "version": "2.1.1", 418 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 419 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 420 | "dev": true, 421 | "engines": { 422 | "node": ">=0.10.0" 423 | } 424 | }, 425 | "node_modules/is-fullwidth-code-point": { 426 | "version": "3.0.0", 427 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 428 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 429 | "dev": true, 430 | "engines": { 431 | "node": ">=8" 432 | } 433 | }, 434 | "node_modules/is-glob": { 435 | "version": "4.0.3", 436 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 437 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 438 | "dev": true, 439 | "dependencies": { 440 | "is-extglob": "^2.1.1" 441 | }, 442 | "engines": { 443 | "node": ">=0.10.0" 444 | } 445 | }, 446 | "node_modules/is-number": { 447 | "version": "7.0.0", 448 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 449 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 450 | "dev": true, 451 | "engines": { 452 | "node": ">=0.12.0" 453 | } 454 | }, 455 | "node_modules/is-plain-obj": { 456 | "version": "2.1.0", 457 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", 458 | "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", 459 | "dev": true, 460 | "engines": { 461 | "node": ">=8" 462 | } 463 | }, 464 | "node_modules/is-unicode-supported": { 465 | "version": "0.1.0", 466 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", 467 | "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", 468 | "dev": true, 469 | "engines": { 470 | "node": ">=10" 471 | }, 472 | "funding": { 473 | "url": "https://github.com/sponsors/sindresorhus" 474 | } 475 | }, 476 | "node_modules/js-yaml": { 477 | "version": "4.1.0", 478 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 479 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 480 | "dev": true, 481 | "dependencies": { 482 | "argparse": "^2.0.1" 483 | }, 484 | "bin": { 485 | "js-yaml": "bin/js-yaml.js" 486 | } 487 | }, 488 | "node_modules/locate-path": { 489 | "version": "6.0.0", 490 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 491 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 492 | "dev": true, 493 | "dependencies": { 494 | "p-locate": "^5.0.0" 495 | }, 496 | "engines": { 497 | "node": ">=10" 498 | }, 499 | "funding": { 500 | "url": "https://github.com/sponsors/sindresorhus" 501 | } 502 | }, 503 | "node_modules/log-symbols": { 504 | "version": "4.1.0", 505 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", 506 | "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", 507 | "dev": true, 508 | "dependencies": { 509 | "chalk": "^4.1.0", 510 | "is-unicode-supported": "^0.1.0" 511 | }, 512 | "engines": { 513 | "node": ">=10" 514 | }, 515 | "funding": { 516 | "url": "https://github.com/sponsors/sindresorhus" 517 | } 518 | }, 519 | "node_modules/minimatch": { 520 | "version": "5.1.6", 521 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", 522 | "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", 523 | "dev": true, 524 | "dependencies": { 525 | "brace-expansion": "^2.0.1" 526 | }, 527 | "engines": { 528 | "node": ">=10" 529 | } 530 | }, 531 | "node_modules/mocha": { 532 | "version": "10.8.2", 533 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", 534 | "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", 535 | "dev": true, 536 | "dependencies": { 537 | "ansi-colors": "^4.1.3", 538 | "browser-stdout": "^1.3.1", 539 | "chokidar": "^3.5.3", 540 | "debug": "^4.3.5", 541 | "diff": "^5.2.0", 542 | "escape-string-regexp": "^4.0.0", 543 | "find-up": "^5.0.0", 544 | "glob": "^8.1.0", 545 | "he": "^1.2.0", 546 | "js-yaml": "^4.1.0", 547 | "log-symbols": "^4.1.0", 548 | "minimatch": "^5.1.6", 549 | "ms": "^2.1.3", 550 | "serialize-javascript": "^6.0.2", 551 | "strip-json-comments": "^3.1.1", 552 | "supports-color": "^8.1.1", 553 | "workerpool": "^6.5.1", 554 | "yargs": "^16.2.0", 555 | "yargs-parser": "^20.2.9", 556 | "yargs-unparser": "^2.0.0" 557 | }, 558 | "bin": { 559 | "_mocha": "bin/_mocha", 560 | "mocha": "bin/mocha.js" 561 | }, 562 | "engines": { 563 | "node": ">= 14.0.0" 564 | } 565 | }, 566 | "node_modules/ms": { 567 | "version": "2.1.3", 568 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 569 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 570 | "dev": true 571 | }, 572 | "node_modules/normalize-path": { 573 | "version": "3.0.0", 574 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 575 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 576 | "dev": true, 577 | "engines": { 578 | "node": ">=0.10.0" 579 | } 580 | }, 581 | "node_modules/once": { 582 | "version": "1.4.0", 583 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 584 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 585 | "dev": true, 586 | "dependencies": { 587 | "wrappy": "1" 588 | } 589 | }, 590 | "node_modules/p-limit": { 591 | "version": "3.1.0", 592 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 593 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 594 | "dev": true, 595 | "dependencies": { 596 | "yocto-queue": "^0.1.0" 597 | }, 598 | "engines": { 599 | "node": ">=10" 600 | }, 601 | "funding": { 602 | "url": "https://github.com/sponsors/sindresorhus" 603 | } 604 | }, 605 | "node_modules/p-locate": { 606 | "version": "5.0.0", 607 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 608 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 609 | "dev": true, 610 | "dependencies": { 611 | "p-limit": "^3.0.2" 612 | }, 613 | "engines": { 614 | "node": ">=10" 615 | }, 616 | "funding": { 617 | "url": "https://github.com/sponsors/sindresorhus" 618 | } 619 | }, 620 | "node_modules/path-exists": { 621 | "version": "4.0.0", 622 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 623 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 624 | "dev": true, 625 | "engines": { 626 | "node": ">=8" 627 | } 628 | }, 629 | "node_modules/picomatch": { 630 | "version": "2.3.1", 631 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 632 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 633 | "dev": true, 634 | "engines": { 635 | "node": ">=8.6" 636 | }, 637 | "funding": { 638 | "url": "https://github.com/sponsors/jonschlinkert" 639 | } 640 | }, 641 | "node_modules/randombytes": { 642 | "version": "2.1.0", 643 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 644 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 645 | "dev": true, 646 | "dependencies": { 647 | "safe-buffer": "^5.1.0" 648 | } 649 | }, 650 | "node_modules/readdirp": { 651 | "version": "3.6.0", 652 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 653 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 654 | "dev": true, 655 | "dependencies": { 656 | "picomatch": "^2.2.1" 657 | }, 658 | "engines": { 659 | "node": ">=8.10.0" 660 | } 661 | }, 662 | "node_modules/require-directory": { 663 | "version": "2.1.1", 664 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 665 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 666 | "dev": true, 667 | "engines": { 668 | "node": ">=0.10.0" 669 | } 670 | }, 671 | "node_modules/safe-buffer": { 672 | "version": "5.2.1", 673 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 674 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 675 | "dev": true, 676 | "funding": [ 677 | { 678 | "type": "github", 679 | "url": "https://github.com/sponsors/feross" 680 | }, 681 | { 682 | "type": "patreon", 683 | "url": "https://www.patreon.com/feross" 684 | }, 685 | { 686 | "type": "consulting", 687 | "url": "https://feross.org/support" 688 | } 689 | ] 690 | }, 691 | "node_modules/serialize-javascript": { 692 | "version": "6.0.2", 693 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", 694 | "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", 695 | "dev": true, 696 | "dependencies": { 697 | "randombytes": "^2.1.0" 698 | } 699 | }, 700 | "node_modules/should": { 701 | "version": "13.2.3", 702 | "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", 703 | "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", 704 | "dev": true, 705 | "dependencies": { 706 | "should-equal": "^2.0.0", 707 | "should-format": "^3.0.3", 708 | "should-type": "^1.4.0", 709 | "should-type-adaptors": "^1.0.1", 710 | "should-util": "^1.0.0" 711 | } 712 | }, 713 | "node_modules/should-equal": { 714 | "version": "2.0.0", 715 | "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", 716 | "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", 717 | "dev": true, 718 | "dependencies": { 719 | "should-type": "^1.4.0" 720 | } 721 | }, 722 | "node_modules/should-format": { 723 | "version": "3.0.3", 724 | "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", 725 | "integrity": "sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==", 726 | "dev": true, 727 | "dependencies": { 728 | "should-type": "^1.3.0", 729 | "should-type-adaptors": "^1.0.1" 730 | } 731 | }, 732 | "node_modules/should-type": { 733 | "version": "1.4.0", 734 | "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", 735 | "integrity": "sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==", 736 | "dev": true 737 | }, 738 | "node_modules/should-type-adaptors": { 739 | "version": "1.1.0", 740 | "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", 741 | "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", 742 | "dev": true, 743 | "dependencies": { 744 | "should-type": "^1.3.0", 745 | "should-util": "^1.0.0" 746 | } 747 | }, 748 | "node_modules/should-util": { 749 | "version": "1.0.1", 750 | "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", 751 | "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", 752 | "dev": true 753 | }, 754 | "node_modules/string-width": { 755 | "version": "4.2.3", 756 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 757 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 758 | "dev": true, 759 | "dependencies": { 760 | "emoji-regex": "^8.0.0", 761 | "is-fullwidth-code-point": "^3.0.0", 762 | "strip-ansi": "^6.0.1" 763 | }, 764 | "engines": { 765 | "node": ">=8" 766 | } 767 | }, 768 | "node_modules/strip-ansi": { 769 | "version": "6.0.1", 770 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 771 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 772 | "dev": true, 773 | "dependencies": { 774 | "ansi-regex": "^5.0.1" 775 | }, 776 | "engines": { 777 | "node": ">=8" 778 | } 779 | }, 780 | "node_modules/strip-json-comments": { 781 | "version": "3.1.1", 782 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 783 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 784 | "dev": true, 785 | "engines": { 786 | "node": ">=8" 787 | }, 788 | "funding": { 789 | "url": "https://github.com/sponsors/sindresorhus" 790 | } 791 | }, 792 | "node_modules/supports-color": { 793 | "version": "8.1.1", 794 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 795 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 796 | "dev": true, 797 | "dependencies": { 798 | "has-flag": "^4.0.0" 799 | }, 800 | "engines": { 801 | "node": ">=10" 802 | }, 803 | "funding": { 804 | "url": "https://github.com/chalk/supports-color?sponsor=1" 805 | } 806 | }, 807 | "node_modules/to-regex-range": { 808 | "version": "5.0.1", 809 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 810 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 811 | "dev": true, 812 | "dependencies": { 813 | "is-number": "^7.0.0" 814 | }, 815 | "engines": { 816 | "node": ">=8.0" 817 | } 818 | }, 819 | "node_modules/workerpool": { 820 | "version": "6.5.1", 821 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", 822 | "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", 823 | "dev": true 824 | }, 825 | "node_modules/wrap-ansi": { 826 | "version": "7.0.0", 827 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 828 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 829 | "dev": true, 830 | "dependencies": { 831 | "ansi-styles": "^4.0.0", 832 | "string-width": "^4.1.0", 833 | "strip-ansi": "^6.0.0" 834 | }, 835 | "engines": { 836 | "node": ">=10" 837 | }, 838 | "funding": { 839 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 840 | } 841 | }, 842 | "node_modules/wrappy": { 843 | "version": "1.0.2", 844 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 845 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 846 | "dev": true 847 | }, 848 | "node_modules/y18n": { 849 | "version": "5.0.8", 850 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 851 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 852 | "dev": true, 853 | "engines": { 854 | "node": ">=10" 855 | } 856 | }, 857 | "node_modules/yargs": { 858 | "version": "16.2.0", 859 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 860 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 861 | "dev": true, 862 | "dependencies": { 863 | "cliui": "^7.0.2", 864 | "escalade": "^3.1.1", 865 | "get-caller-file": "^2.0.5", 866 | "require-directory": "^2.1.1", 867 | "string-width": "^4.2.0", 868 | "y18n": "^5.0.5", 869 | "yargs-parser": "^20.2.2" 870 | }, 871 | "engines": { 872 | "node": ">=10" 873 | } 874 | }, 875 | "node_modules/yargs-parser": { 876 | "version": "20.2.9", 877 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", 878 | "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", 879 | "dev": true, 880 | "engines": { 881 | "node": ">=10" 882 | } 883 | }, 884 | "node_modules/yargs-unparser": { 885 | "version": "2.0.0", 886 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", 887 | "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", 888 | "dev": true, 889 | "dependencies": { 890 | "camelcase": "^6.0.0", 891 | "decamelize": "^4.0.0", 892 | "flat": "^5.0.2", 893 | "is-plain-obj": "^2.1.0" 894 | }, 895 | "engines": { 896 | "node": ">=10" 897 | } 898 | }, 899 | "node_modules/yocto-queue": { 900 | "version": "0.1.0", 901 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 902 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 903 | "dev": true, 904 | "engines": { 905 | "node": ">=10" 906 | }, 907 | "funding": { 908 | "url": "https://github.com/sponsors/sindresorhus" 909 | } 910 | } 911 | }, 912 | "dependencies": { 913 | "ansi-colors": { 914 | "version": "4.1.3", 915 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", 916 | "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", 917 | "dev": true 918 | }, 919 | "ansi-regex": { 920 | "version": "5.0.1", 921 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 922 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 923 | "dev": true 924 | }, 925 | "ansi-styles": { 926 | "version": "4.3.0", 927 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 928 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 929 | "dev": true, 930 | "requires": { 931 | "color-convert": "^2.0.1" 932 | } 933 | }, 934 | "anymatch": { 935 | "version": "3.1.2", 936 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", 937 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", 938 | "dev": true, 939 | "requires": { 940 | "normalize-path": "^3.0.0", 941 | "picomatch": "^2.0.4" 942 | } 943 | }, 944 | "argparse": { 945 | "version": "2.0.1", 946 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 947 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 948 | "dev": true 949 | }, 950 | "balanced-match": { 951 | "version": "1.0.2", 952 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 953 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 954 | "dev": true 955 | }, 956 | "binary-extensions": { 957 | "version": "2.2.0", 958 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 959 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 960 | "dev": true 961 | }, 962 | "brace-expansion": { 963 | "version": "2.0.1", 964 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 965 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 966 | "dev": true, 967 | "requires": { 968 | "balanced-match": "^1.0.0" 969 | } 970 | }, 971 | "braces": { 972 | "version": "3.0.3", 973 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", 974 | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 975 | "dev": true, 976 | "requires": { 977 | "fill-range": "^7.1.1" 978 | } 979 | }, 980 | "browser-stdout": { 981 | "version": "1.3.1", 982 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 983 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 984 | "dev": true 985 | }, 986 | "camelcase": { 987 | "version": "6.3.0", 988 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", 989 | "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", 990 | "dev": true 991 | }, 992 | "chalk": { 993 | "version": "4.1.2", 994 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 995 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 996 | "dev": true, 997 | "requires": { 998 | "ansi-styles": "^4.1.0", 999 | "supports-color": "^7.1.0" 1000 | }, 1001 | "dependencies": { 1002 | "supports-color": { 1003 | "version": "7.2.0", 1004 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1005 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1006 | "dev": true, 1007 | "requires": { 1008 | "has-flag": "^4.0.0" 1009 | } 1010 | } 1011 | } 1012 | }, 1013 | "chokidar": { 1014 | "version": "3.5.3", 1015 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 1016 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 1017 | "dev": true, 1018 | "requires": { 1019 | "anymatch": "~3.1.2", 1020 | "braces": "~3.0.2", 1021 | "fsevents": "~2.3.2", 1022 | "glob-parent": "~5.1.2", 1023 | "is-binary-path": "~2.1.0", 1024 | "is-glob": "~4.0.1", 1025 | "normalize-path": "~3.0.0", 1026 | "readdirp": "~3.6.0" 1027 | } 1028 | }, 1029 | "cliui": { 1030 | "version": "7.0.4", 1031 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 1032 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 1033 | "dev": true, 1034 | "requires": { 1035 | "string-width": "^4.2.0", 1036 | "strip-ansi": "^6.0.0", 1037 | "wrap-ansi": "^7.0.0" 1038 | } 1039 | }, 1040 | "color-convert": { 1041 | "version": "2.0.1", 1042 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1043 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1044 | "dev": true, 1045 | "requires": { 1046 | "color-name": "~1.1.4" 1047 | } 1048 | }, 1049 | "color-name": { 1050 | "version": "1.1.4", 1051 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1052 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1053 | "dev": true 1054 | }, 1055 | "debug": { 1056 | "version": "4.4.0", 1057 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", 1058 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", 1059 | "dev": true, 1060 | "requires": { 1061 | "ms": "^2.1.3" 1062 | } 1063 | }, 1064 | "decamelize": { 1065 | "version": "4.0.0", 1066 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", 1067 | "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", 1068 | "dev": true 1069 | }, 1070 | "diff": { 1071 | "version": "5.2.0", 1072 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", 1073 | "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", 1074 | "dev": true 1075 | }, 1076 | "emoji-regex": { 1077 | "version": "8.0.0", 1078 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 1079 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 1080 | "dev": true 1081 | }, 1082 | "escalade": { 1083 | "version": "3.1.1", 1084 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 1085 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 1086 | "dev": true 1087 | }, 1088 | "escape-string-regexp": { 1089 | "version": "4.0.0", 1090 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 1091 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 1092 | "dev": true 1093 | }, 1094 | "fill-range": { 1095 | "version": "7.1.1", 1096 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", 1097 | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 1098 | "dev": true, 1099 | "requires": { 1100 | "to-regex-range": "^5.0.1" 1101 | } 1102 | }, 1103 | "find-up": { 1104 | "version": "5.0.0", 1105 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 1106 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 1107 | "dev": true, 1108 | "requires": { 1109 | "locate-path": "^6.0.0", 1110 | "path-exists": "^4.0.0" 1111 | } 1112 | }, 1113 | "flat": { 1114 | "version": "5.0.2", 1115 | "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", 1116 | "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", 1117 | "dev": true 1118 | }, 1119 | "fs.realpath": { 1120 | "version": "1.0.0", 1121 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1122 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 1123 | "dev": true 1124 | }, 1125 | "fsevents": { 1126 | "version": "2.3.2", 1127 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 1128 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 1129 | "dev": true, 1130 | "optional": true 1131 | }, 1132 | "get-caller-file": { 1133 | "version": "2.0.5", 1134 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 1135 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 1136 | "dev": true 1137 | }, 1138 | "glob": { 1139 | "version": "8.1.0", 1140 | "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", 1141 | "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", 1142 | "dev": true, 1143 | "requires": { 1144 | "fs.realpath": "^1.0.0", 1145 | "inflight": "^1.0.4", 1146 | "inherits": "2", 1147 | "minimatch": "^5.0.1", 1148 | "once": "^1.3.0" 1149 | } 1150 | }, 1151 | "glob-parent": { 1152 | "version": "5.1.2", 1153 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 1154 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 1155 | "dev": true, 1156 | "requires": { 1157 | "is-glob": "^4.0.1" 1158 | } 1159 | }, 1160 | "has-flag": { 1161 | "version": "4.0.0", 1162 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1163 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1164 | "dev": true 1165 | }, 1166 | "he": { 1167 | "version": "1.2.0", 1168 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 1169 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 1170 | "dev": true 1171 | }, 1172 | "inflight": { 1173 | "version": "1.0.6", 1174 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1175 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 1176 | "dev": true, 1177 | "requires": { 1178 | "once": "^1.3.0", 1179 | "wrappy": "1" 1180 | } 1181 | }, 1182 | "inherits": { 1183 | "version": "2.0.4", 1184 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1185 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 1186 | "dev": true 1187 | }, 1188 | "is-binary-path": { 1189 | "version": "2.1.0", 1190 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 1191 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 1192 | "dev": true, 1193 | "requires": { 1194 | "binary-extensions": "^2.0.0" 1195 | } 1196 | }, 1197 | "is-extglob": { 1198 | "version": "2.1.1", 1199 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1200 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 1201 | "dev": true 1202 | }, 1203 | "is-fullwidth-code-point": { 1204 | "version": "3.0.0", 1205 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1206 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 1207 | "dev": true 1208 | }, 1209 | "is-glob": { 1210 | "version": "4.0.3", 1211 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1212 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1213 | "dev": true, 1214 | "requires": { 1215 | "is-extglob": "^2.1.1" 1216 | } 1217 | }, 1218 | "is-number": { 1219 | "version": "7.0.0", 1220 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1221 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1222 | "dev": true 1223 | }, 1224 | "is-plain-obj": { 1225 | "version": "2.1.0", 1226 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", 1227 | "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", 1228 | "dev": true 1229 | }, 1230 | "is-unicode-supported": { 1231 | "version": "0.1.0", 1232 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", 1233 | "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", 1234 | "dev": true 1235 | }, 1236 | "js-yaml": { 1237 | "version": "4.1.0", 1238 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 1239 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 1240 | "dev": true, 1241 | "requires": { 1242 | "argparse": "^2.0.1" 1243 | } 1244 | }, 1245 | "locate-path": { 1246 | "version": "6.0.0", 1247 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 1248 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 1249 | "dev": true, 1250 | "requires": { 1251 | "p-locate": "^5.0.0" 1252 | } 1253 | }, 1254 | "log-symbols": { 1255 | "version": "4.1.0", 1256 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", 1257 | "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", 1258 | "dev": true, 1259 | "requires": { 1260 | "chalk": "^4.1.0", 1261 | "is-unicode-supported": "^0.1.0" 1262 | } 1263 | }, 1264 | "minimatch": { 1265 | "version": "5.1.6", 1266 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", 1267 | "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", 1268 | "dev": true, 1269 | "requires": { 1270 | "brace-expansion": "^2.0.1" 1271 | } 1272 | }, 1273 | "mocha": { 1274 | "version": "10.8.2", 1275 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", 1276 | "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", 1277 | "dev": true, 1278 | "requires": { 1279 | "ansi-colors": "^4.1.3", 1280 | "browser-stdout": "^1.3.1", 1281 | "chokidar": "^3.5.3", 1282 | "debug": "^4.3.5", 1283 | "diff": "^5.2.0", 1284 | "escape-string-regexp": "^4.0.0", 1285 | "find-up": "^5.0.0", 1286 | "glob": "^8.1.0", 1287 | "he": "^1.2.0", 1288 | "js-yaml": "^4.1.0", 1289 | "log-symbols": "^4.1.0", 1290 | "minimatch": "^5.1.6", 1291 | "ms": "^2.1.3", 1292 | "serialize-javascript": "^6.0.2", 1293 | "strip-json-comments": "^3.1.1", 1294 | "supports-color": "^8.1.1", 1295 | "workerpool": "^6.5.1", 1296 | "yargs": "^16.2.0", 1297 | "yargs-parser": "^20.2.9", 1298 | "yargs-unparser": "^2.0.0" 1299 | } 1300 | }, 1301 | "ms": { 1302 | "version": "2.1.3", 1303 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1304 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 1305 | "dev": true 1306 | }, 1307 | "normalize-path": { 1308 | "version": "3.0.0", 1309 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1310 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1311 | "dev": true 1312 | }, 1313 | "once": { 1314 | "version": "1.4.0", 1315 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1316 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1317 | "dev": true, 1318 | "requires": { 1319 | "wrappy": "1" 1320 | } 1321 | }, 1322 | "p-limit": { 1323 | "version": "3.1.0", 1324 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 1325 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 1326 | "dev": true, 1327 | "requires": { 1328 | "yocto-queue": "^0.1.0" 1329 | } 1330 | }, 1331 | "p-locate": { 1332 | "version": "5.0.0", 1333 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 1334 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 1335 | "dev": true, 1336 | "requires": { 1337 | "p-limit": "^3.0.2" 1338 | } 1339 | }, 1340 | "path-exists": { 1341 | "version": "4.0.0", 1342 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1343 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1344 | "dev": true 1345 | }, 1346 | "picomatch": { 1347 | "version": "2.3.1", 1348 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1349 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1350 | "dev": true 1351 | }, 1352 | "randombytes": { 1353 | "version": "2.1.0", 1354 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 1355 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 1356 | "dev": true, 1357 | "requires": { 1358 | "safe-buffer": "^5.1.0" 1359 | } 1360 | }, 1361 | "readdirp": { 1362 | "version": "3.6.0", 1363 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 1364 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1365 | "dev": true, 1366 | "requires": { 1367 | "picomatch": "^2.2.1" 1368 | } 1369 | }, 1370 | "require-directory": { 1371 | "version": "2.1.1", 1372 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1373 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 1374 | "dev": true 1375 | }, 1376 | "safe-buffer": { 1377 | "version": "5.2.1", 1378 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1379 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1380 | "dev": true 1381 | }, 1382 | "serialize-javascript": { 1383 | "version": "6.0.2", 1384 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", 1385 | "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", 1386 | "dev": true, 1387 | "requires": { 1388 | "randombytes": "^2.1.0" 1389 | } 1390 | }, 1391 | "should": { 1392 | "version": "13.2.3", 1393 | "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", 1394 | "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", 1395 | "dev": true, 1396 | "requires": { 1397 | "should-equal": "^2.0.0", 1398 | "should-format": "^3.0.3", 1399 | "should-type": "^1.4.0", 1400 | "should-type-adaptors": "^1.0.1", 1401 | "should-util": "^1.0.0" 1402 | } 1403 | }, 1404 | "should-equal": { 1405 | "version": "2.0.0", 1406 | "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", 1407 | "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", 1408 | "dev": true, 1409 | "requires": { 1410 | "should-type": "^1.4.0" 1411 | } 1412 | }, 1413 | "should-format": { 1414 | "version": "3.0.3", 1415 | "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", 1416 | "integrity": "sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==", 1417 | "dev": true, 1418 | "requires": { 1419 | "should-type": "^1.3.0", 1420 | "should-type-adaptors": "^1.0.1" 1421 | } 1422 | }, 1423 | "should-type": { 1424 | "version": "1.4.0", 1425 | "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", 1426 | "integrity": "sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==", 1427 | "dev": true 1428 | }, 1429 | "should-type-adaptors": { 1430 | "version": "1.1.0", 1431 | "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", 1432 | "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", 1433 | "dev": true, 1434 | "requires": { 1435 | "should-type": "^1.3.0", 1436 | "should-util": "^1.0.0" 1437 | } 1438 | }, 1439 | "should-util": { 1440 | "version": "1.0.1", 1441 | "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", 1442 | "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", 1443 | "dev": true 1444 | }, 1445 | "string-width": { 1446 | "version": "4.2.3", 1447 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1448 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1449 | "dev": true, 1450 | "requires": { 1451 | "emoji-regex": "^8.0.0", 1452 | "is-fullwidth-code-point": "^3.0.0", 1453 | "strip-ansi": "^6.0.1" 1454 | } 1455 | }, 1456 | "strip-ansi": { 1457 | "version": "6.0.1", 1458 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1459 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1460 | "dev": true, 1461 | "requires": { 1462 | "ansi-regex": "^5.0.1" 1463 | } 1464 | }, 1465 | "strip-json-comments": { 1466 | "version": "3.1.1", 1467 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 1468 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 1469 | "dev": true 1470 | }, 1471 | "supports-color": { 1472 | "version": "8.1.1", 1473 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 1474 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 1475 | "dev": true, 1476 | "requires": { 1477 | "has-flag": "^4.0.0" 1478 | } 1479 | }, 1480 | "to-regex-range": { 1481 | "version": "5.0.1", 1482 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1483 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1484 | "dev": true, 1485 | "requires": { 1486 | "is-number": "^7.0.0" 1487 | } 1488 | }, 1489 | "workerpool": { 1490 | "version": "6.5.1", 1491 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", 1492 | "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", 1493 | "dev": true 1494 | }, 1495 | "wrap-ansi": { 1496 | "version": "7.0.0", 1497 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 1498 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 1499 | "dev": true, 1500 | "requires": { 1501 | "ansi-styles": "^4.0.0", 1502 | "string-width": "^4.1.0", 1503 | "strip-ansi": "^6.0.0" 1504 | } 1505 | }, 1506 | "wrappy": { 1507 | "version": "1.0.2", 1508 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1509 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 1510 | "dev": true 1511 | }, 1512 | "y18n": { 1513 | "version": "5.0.8", 1514 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 1515 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 1516 | "dev": true 1517 | }, 1518 | "yargs": { 1519 | "version": "16.2.0", 1520 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 1521 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 1522 | "dev": true, 1523 | "requires": { 1524 | "cliui": "^7.0.2", 1525 | "escalade": "^3.1.1", 1526 | "get-caller-file": "^2.0.5", 1527 | "require-directory": "^2.1.1", 1528 | "string-width": "^4.2.0", 1529 | "y18n": "^5.0.5", 1530 | "yargs-parser": "^20.2.2" 1531 | } 1532 | }, 1533 | "yargs-parser": { 1534 | "version": "20.2.9", 1535 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", 1536 | "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", 1537 | "dev": true 1538 | }, 1539 | "yargs-unparser": { 1540 | "version": "2.0.0", 1541 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", 1542 | "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", 1543 | "dev": true, 1544 | "requires": { 1545 | "camelcase": "^6.0.0", 1546 | "decamelize": "^4.0.0", 1547 | "flat": "^5.0.2", 1548 | "is-plain-obj": "^2.1.0" 1549 | } 1550 | }, 1551 | "yocto-queue": { 1552 | "version": "0.1.0", 1553 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 1554 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 1555 | "dev": true 1556 | } 1557 | } 1558 | } 1559 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "glicko2", 3 | "license": "MIT", 4 | "description": "glicko2 ranking system", 5 | "version": "1.2.1", 6 | "author": "Henri Bourcereau", 7 | "keywords": [ 8 | "glicko", 9 | "utilities", 10 | "ranking" 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "git://github.com/mmai/glicko2js.git" 15 | }, 16 | "main": "./glicko2.js", 17 | "devDependencies": { 18 | "mocha": "^10.1.0", 19 | "should": "13.2.x" 20 | }, 21 | "scripts": { 22 | "test": "mocha --require should --reporter list" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/glicko2.js: -------------------------------------------------------------------------------- 1 | const { Glicko2 } = require('../glicko2'); 2 | 3 | // Generate normally distributed random numbers 4 | // cf. https://mika-s.github.io/javascript/random/normal-distributed/2019/05/15/generating-normally-distributed-random-numbers-in-javascript.html 5 | function boxMullerTransform() { 6 | const u1 = Math.random(); 7 | const u2 = Math.random(); 8 | const z0 = Math.sqrt(-2.0 * Math.log(u1)) * Math.cos(2.0 * Math.PI * u2); 9 | const z1 = Math.sqrt(-2.0 * Math.log(u1)) * Math.sin(2.0 * Math.PI * u2); 10 | return { z0, z1 }; 11 | } 12 | 13 | function getNormallyDistributedRandomNumber(mean, stddev) { 14 | const { z0, _ } = boxMullerTransform(); 15 | return z0 * stddev + mean; 16 | } 17 | 18 | describe('Glicko2', function() { 19 | describe('makePlayer()', function() { 20 | it('should make a default player when passed no settings', function() { 21 | // const glicko = new Glicko2(); 22 | const glicko = new Glicko2(); 23 | const player = glicko.makePlayer(); 24 | player.getRating().should.equal(1500); 25 | player.getRd().should.equal(350); 26 | player.getVol().should.equal(0.06); 27 | }); 28 | it('should support setting individual settings', function() { 29 | const glicko = new Glicko2({ 30 | rating: 1600 31 | }); 32 | const player = glicko.makePlayer(); 33 | player.getRating().should.equal(1600); 34 | player.getRd().should.equal(350); 35 | player.getVol().should.equal(0.06); 36 | }); 37 | it('should not be affected by newer instances of Glicko2', function() { 38 | const glicko = new Glicko2({ 39 | rating: 1400 40 | }); 41 | const player = glicko.makePlayer(); 42 | 43 | const newerGlicko = new Glicko2({ 44 | rating: 1600 45 | }); 46 | 47 | player.getRating().should.equal(1400); 48 | }); 49 | }); 50 | describe('getPlayers()', function() { 51 | it('should retrieve all players with ids', function() { 52 | const settings = { 53 | tau: 0.5, 54 | rpd: 604800, 55 | rating: 1500, 56 | rd: 200, 57 | vol: 0.06 58 | }; 59 | const glicko = new Glicko2(settings); 60 | // const glicko = new Glicko2(settings); 61 | const player = glicko.makePlayer(); 62 | const pl1 = glicko.makePlayer(1400, 30, 0.06); 63 | const pl3 = glicko.makePlayer(1550, 100, 0.06); 64 | const players = glicko.getPlayers(); 65 | players.length.should.equal(3); 66 | players[1].id.should.equal(1); 67 | players[1].getRating().should.equal(1400); 68 | }); 69 | }); 70 | describe('updateRatings()', function() { 71 | it('should calculate new ratings', function() { 72 | // Following the example at: 73 | // http://math.bu.edu/people/mg/glicko/glicko2.doc/example.html 74 | // Pretend Ryan (of rating 1500 and rating deviation 350) plays players of ratings 1400, 1550 and 1700 75 | // and rating deviations 30, 100 and 300 respectively with outcomes 1, 0 and 0. 76 | const settings = { 77 | tau: 0.5, 78 | rpd: 604800, 79 | rating: 1500, 80 | rd: 200, 81 | vol: 0.06 82 | }; 83 | const glicko = new Glicko2(settings); 84 | const Ryan = glicko.makePlayer(); 85 | const Bob = glicko.makePlayer(1400, 30, 0.06); 86 | const John = glicko.makePlayer(1550, 100, 0.06); 87 | const Mary = glicko.makePlayer(1700, 300, 0.06); 88 | 89 | const matches = []; 90 | matches.push([Ryan, Bob, 1]); //Ryan won over Bob 91 | matches.push([Ryan, John, 0]); //Ryan lost against John 92 | matches.push([Ryan, Mary, 0]); //Ryan lost against Mary 93 | 94 | /*Perfs testing 95 | const players = [Bob, John, Mary]; 96 | 97 | const ind = 0; 98 | while (ind++ < 50){ 99 | players.push(glicko.makePlayer()); 100 | } 101 | 102 | const nbpl = players.length; 103 | const pl1, pl2; 104 | for (const i=0; i<1000;i++){ 105 | pl1 = players[Math.floor(Math.random() * nbpl)]; 106 | pl2 = players[Math.floor(Math.random() * nbpl)]; 107 | matches.push([pl1, pl2, Math.floor(Math.random() * 3) / 2]); 108 | } 109 | //End perfs 110 | */ 111 | 112 | glicko.updateRatings(matches); 113 | (Math.abs(Ryan.getRating() - 1464.06) < 0.01).should.equal(true); 114 | (Math.abs(Ryan.getRd() - 151.52) < 0.01).should.equal(true); 115 | (Math.abs(Ryan.getVol() - 0.05999) < 0.00001).should.equal(true); 116 | }); 117 | it('should allow to be called multiple times', function() { 118 | const settings = { 119 | tau: 0.5, 120 | rpd: 604800, 121 | rating: 1500, 122 | rd: 200, 123 | vol: 0.06 124 | }; 125 | const glicko = new Glicko2(settings); 126 | const Ryan = glicko.makePlayer(); 127 | const Bob = glicko.makePlayer(1400, 30, 0.06); 128 | const John = glicko.makePlayer(1550, 100, 0.06); 129 | const Mary = glicko.makePlayer(1700, 300, 0.06); 130 | 131 | let matches = []; 132 | matches.push([Ryan, Bob, 1]); //Ryan won over Bob 133 | matches.push([Ryan, John, 0]); //Ryan lost against John 134 | matches.push([Ryan, Mary, 0]); //Ryan lost against Mary 135 | glicko.updateRatings(matches); 136 | 137 | 138 | //We initiate a new ranking instance with the actual values of the first one 139 | const glicko_new = new Glicko2(settings); 140 | const Ryan_new = glicko_new.makePlayer(Ryan.getRating(), Ryan.getRd(), Ryan.getVol()); 141 | const Bob_new = glicko_new.makePlayer(Bob.getRating(), Bob.getRd(), Bob.getVol()); 142 | const John_new = glicko_new.makePlayer(John.getRating(), John.getRd(), John.getVol()); 143 | const Mary_new = glicko_new.makePlayer(Mary.getRating(), Mary.getRd(), Mary.getVol()); 144 | 145 | //Second tournament for the first ranking instance 146 | matches = []; 147 | matches.push([Ryan, Bob, 0]); 148 | matches.push([Ryan, John, 1]); 149 | matches.push([Mary, Bob, 1]); 150 | glicko.updateRatings(matches); 151 | //console.log('nb players: ' + glicko.getPlayers().length); 152 | 153 | //Fist tournament for the second ranking instance, with the same matches 154 | const matches_new = []; 155 | matches_new.push([Ryan_new, Bob_new, 0]); 156 | matches_new.push([Ryan_new, John_new, 1]); 157 | matches_new.push([Mary_new, Bob_new, 1]); 158 | glicko_new.updateRatings(matches_new); 159 | 160 | //The ratings in both systems should be the same 161 | (Math.abs(Ryan.getRating() - Ryan_new.getRating()) < 0.1).should.equal(true); 162 | (Math.abs(Ryan.getRd() - Ryan_new.getRd()) < 0.1).should.equal(true); 163 | (Math.abs(Ryan.getVol() - Ryan_new.getVol()) < 0.00001).should.equal(true); 164 | }); 165 | it('should be able to update ratings when a player did not play', function() { 166 | const settings = { 167 | tau: 0.5, 168 | rpd: 604800, 169 | rating: 1500, 170 | rd: 200, 171 | vol: 0.06 172 | }; 173 | const glicko = new Glicko2(settings); 174 | const Ryan = glicko.makePlayer(); 175 | const matches = []; 176 | glicko.updateRatings(matches); 177 | }); 178 | it('should accept Race objects', function() { 179 | const settings = { 180 | tau: 0.5, 181 | rpd: 604800, 182 | rating: 1500, 183 | rd: 200, 184 | vol: 0.06 185 | }; 186 | const glicko = new Glicko2(settings); 187 | const Ryan = glicko.makePlayer(); 188 | const Bob = glicko.makePlayer(1400, 30, 0.06); 189 | const John = glicko.makePlayer(1550, 100, 0.06); 190 | const Mary = glicko.makePlayer(1700, 300, 0.06); 191 | 192 | const race = glicko.makeRace( 193 | [ 194 | [Ryan], //Ryan won the race 195 | [Bob, John], //Bob and John 2nd position ex-aequo 196 | [Mary] // Mary 4th position 197 | ] 198 | ); 199 | 200 | glicko.updateRatings(race); 201 | 202 | // console.log(Ryan.getRating(), Ryan.getRd(), Ryan.getVol()); 203 | //v1 204 | (Math.abs(Ryan.getRating() - 1685.7) < 0.1).should.equal(true); 205 | (Math.abs(Ryan.getRd() - 151.52) < 0.01).should.equal(true); 206 | (Math.abs(Ryan.getVol() - 0.06000) < 0.00001).should.equal(true); 207 | //v2 208 | // (Math.abs(Ryan.getRating() - 1563.6) < 0.1).should.equal(true); 209 | // (Math.abs(Ryan.getRd() - 175.40) < 0.01).should.equal(true); 210 | // (Math.abs(Ryan.getVol() - 0.06) < 0.00001).should.equal(true); 211 | }); 212 | }); 213 | describe('predict()', function() { 214 | it('should calculate expected outcome', function() { 215 | const settings = { 216 | tau: 0.5, 217 | rpd: 604800, 218 | rating: 1500, 219 | rd: 200, 220 | vol: 0.06 221 | }; 222 | const glicko = new Glicko2(settings); 223 | const p1 = glicko.makePlayer(1500, 100, 0.06); 224 | const p2 = glicko.makePlayer(1300, 100, 0.06); 225 | const expected = p1.predict(p2); 226 | 227 | (Math.abs(expected - 0.74) < 0.001).should.equal(true); 228 | 229 | const gexpected = glicko.predict(p1, p2); 230 | gexpected.should.equal(expected); 231 | }); 232 | }); 233 | }); 234 | 235 | describe("Race", function() { 236 | describe("getMatches", function() { 237 | it("Should convert a race to a list of matches", function() { 238 | const settings = { 239 | tau: 0.5, 240 | rpd: 604800, 241 | rating: 1500, 242 | rd: 200, 243 | vol: 0.06 244 | }; 245 | const glicko = new Glicko2(settings); 246 | const Ryan = glicko.makePlayer(); 247 | const Bob = glicko.makePlayer(1400, 30, 0.06); 248 | const John = glicko.makePlayer(1550, 100, 0.06); 249 | const Mary = glicko.makePlayer(1700, 300, 0.06); 250 | 251 | const race = glicko.makeRace( 252 | [ 253 | [Ryan], //Ryan won the race 254 | [Bob, John], //Bob and John 2nd position ex-aequo 255 | [Mary] // Mary 4th position 256 | ] 257 | ); 258 | 259 | const matches = race.getMatches(); 260 | //v1 261 | matches.should.eql([ 262 | [Ryan, Bob, 1], 263 | [Ryan, John, 1], 264 | [Ryan, Mary, 1], 265 | [Bob, John, 0.5], 266 | [Bob, Mary, 1], 267 | [John, Mary, 1] 268 | ]); 269 | //v2 270 | // matches.should.eql([ 271 | // [Ryan, Bob, 1], 272 | // [Bob, John, 0.5], 273 | // [John, Mary, 1] 274 | // ]); 275 | }) 276 | }); 277 | 278 | function calculatePrecision(scoreRatings) { 279 | let predictionOk = 0; 280 | let predictionCount = 0; 281 | while (scoreRating = scoreRatings.pop()) { 282 | scoreRatings.forEach(adversary => { 283 | predictedWin = scoreRating.rating > adversary.rating; 284 | realWin = scoreRating.score > adversary.score; 285 | if (predictedWin == realWin) { 286 | predictionOk += 1; 287 | } 288 | predictionCount += 1; 289 | }); 290 | } 291 | return predictionOk / predictionCount; 292 | } 293 | 294 | function makeRound(ranking, players, realRatings) { 295 | let playerScores = realRatings.map(function(real, i) { 296 | let score = Math.ceil(getNormallyDistributedRandomNumber(real, 150)); 297 | return { 298 | real, 299 | score, 300 | player: players[i] 301 | }; 302 | }).sort((a, b) => b.score - a.score); 303 | 304 | let scoreRatings = playerScores.map(pscore => ({ 305 | score: pscore.score, 306 | rating: pscore.player.getRating(), 307 | })); 308 | 309 | let idealRatings = playerScores.map(pscore => ({ 310 | score: pscore.score, 311 | rating: pscore.real, 312 | })); 313 | 314 | let realPrecision = calculatePrecision(idealRatings); 315 | let rankingPrecision = calculatePrecision(scoreRatings); 316 | 317 | let raceResults = []; 318 | let current = []; 319 | playerScores.forEach(function(pscore) { 320 | if (current.length && current[0].score == pscore.score) { 321 | current.push(pscore); 322 | } else { 323 | raceResults.push(current.map((currScore) => currScore.player)); 324 | current = [pscore]; 325 | } 326 | }); 327 | raceResults.push(current.map((currScore) => currScore.player)); 328 | 329 | const race = ranking.makeRace(raceResults); 330 | ranking.updateRatings(race); 331 | 332 | return { 333 | realPrecision, 334 | rankingPrecision 335 | }; 336 | } 337 | 338 | // XXX : trying to replicate http://www.tckerrigan.com/Misc/Multiplayer_Elo/ procedure to test accuracy of the algorithm 339 | describe("evaluateAlgorithm", function() { 340 | it("Should approximate reality", function() { 341 | const settings = { 342 | tau: 0.5, 343 | rpd: 604800, 344 | rating: 1500, 345 | rd: 200, 346 | vol: 0.06 347 | }; 348 | const ranking = new Glicko2(settings); 349 | 350 | let nbPlayers = 10; 351 | const players = []; 352 | const realRatings = []; 353 | let diff = 1; // add or substract 1 alternatively 354 | for (let idx = 0; idx < nbPlayers; idx++) { 355 | realRatings.push(1100 + 100 * idx); 356 | players.push(ranking.makePlayer(1500 + diff, 200, 0.06)); 357 | diff = 0 - diff; 358 | } 359 | 360 | let precisions = []; 361 | for (let numRound = 1; numRound < 50; numRound++) { 362 | precisions = makeRound(ranking, players, realRatings); 363 | // console.log(precisions.realPrecision, precisions.rankingPrecision); 364 | } 365 | // (Math.abs(precisions.realPrecision - precisions.rankingPrecision) < 0.01).should.equal(true); 366 | }); 367 | }) 368 | 369 | }) 370 | 371 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | ansi-colors@^4.1.3: 6 | version "4.1.3" 7 | resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" 8 | 9 | ansi-regex@^5.0.1: 10 | version "5.0.1" 11 | resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" 12 | 13 | ansi-styles@^4.0.0, ansi-styles@^4.1.0: 14 | version "4.3.0" 15 | resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" 16 | dependencies: 17 | color-convert "^2.0.1" 18 | 19 | anymatch@~3.1.2: 20 | version "3.1.2" 21 | resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz" 22 | dependencies: 23 | normalize-path "^3.0.0" 24 | picomatch "^2.0.4" 25 | 26 | argparse@^2.0.1: 27 | version "2.0.1" 28 | resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" 29 | 30 | balanced-match@^1.0.0: 31 | version "1.0.2" 32 | resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" 33 | 34 | binary-extensions@^2.0.0: 35 | version "2.2.0" 36 | resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" 37 | 38 | brace-expansion@^2.0.1: 39 | version "2.0.1" 40 | resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz" 41 | dependencies: 42 | balanced-match "^1.0.0" 43 | 44 | braces@~3.0.2: 45 | version "3.0.3" 46 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" 47 | dependencies: 48 | fill-range "^7.1.1" 49 | 50 | browser-stdout@^1.3.1: 51 | version "1.3.1" 52 | resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" 53 | 54 | camelcase@^6.0.0: 55 | version "6.2.0" 56 | resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz" 57 | 58 | chalk@^4.1.0: 59 | version "4.1.2" 60 | resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" 61 | dependencies: 62 | ansi-styles "^4.1.0" 63 | supports-color "^7.1.0" 64 | 65 | chokidar@^3.5.3: 66 | version "3.6.0" 67 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" 68 | dependencies: 69 | anymatch "~3.1.2" 70 | braces "~3.0.2" 71 | glob-parent "~5.1.2" 72 | is-binary-path "~2.1.0" 73 | is-glob "~4.0.1" 74 | normalize-path "~3.0.0" 75 | readdirp "~3.6.0" 76 | optionalDependencies: 77 | fsevents "~2.3.2" 78 | 79 | cliui@^7.0.2: 80 | version "7.0.4" 81 | resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz" 82 | dependencies: 83 | string-width "^4.2.0" 84 | strip-ansi "^6.0.0" 85 | wrap-ansi "^7.0.0" 86 | 87 | color-convert@^2.0.1: 88 | version "2.0.1" 89 | resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" 90 | dependencies: 91 | color-name "~1.1.4" 92 | 93 | color-name@~1.1.4: 94 | version "1.1.4" 95 | resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" 96 | 97 | debug@^4.3.5: 98 | version "4.4.0" 99 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" 100 | dependencies: 101 | ms "^2.1.3" 102 | 103 | decamelize@^4.0.0: 104 | version "4.0.0" 105 | resolved "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz" 106 | 107 | diff@^5.2.0: 108 | version "5.2.0" 109 | resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" 110 | 111 | emoji-regex@^8.0.0: 112 | version "8.0.0" 113 | resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" 114 | 115 | escalade@^3.1.1: 116 | version "3.1.1" 117 | resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" 118 | 119 | escape-string-regexp@^4.0.0: 120 | version "4.0.0" 121 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" 122 | 123 | fill-range@^7.1.1: 124 | version "7.1.1" 125 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" 126 | dependencies: 127 | to-regex-range "^5.0.1" 128 | 129 | find-up@^5.0.0: 130 | version "5.0.0" 131 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" 132 | dependencies: 133 | locate-path "^6.0.0" 134 | path-exists "^4.0.0" 135 | 136 | flat@^5.0.2: 137 | version "5.0.2" 138 | resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" 139 | 140 | fs.realpath@^1.0.0: 141 | version "1.0.0" 142 | resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" 143 | 144 | fsevents@~2.3.2: 145 | version "2.3.3" 146 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" 147 | 148 | get-caller-file@^2.0.5: 149 | version "2.0.5" 150 | resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" 151 | 152 | glob-parent@~5.1.2: 153 | version "5.1.2" 154 | resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" 155 | dependencies: 156 | is-glob "^4.0.1" 157 | 158 | glob@^8.1.0: 159 | version "8.1.0" 160 | resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" 161 | dependencies: 162 | fs.realpath "^1.0.0" 163 | inflight "^1.0.4" 164 | inherits "2" 165 | minimatch "^5.0.1" 166 | once "^1.3.0" 167 | 168 | has-flag@^4.0.0: 169 | version "4.0.0" 170 | resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" 171 | 172 | he@^1.2.0: 173 | version "1.2.0" 174 | resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" 175 | 176 | inflight@^1.0.4: 177 | version "1.0.6" 178 | resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" 179 | dependencies: 180 | once "^1.3.0" 181 | wrappy "1" 182 | 183 | inherits@2: 184 | version "2.0.4" 185 | resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" 186 | 187 | is-binary-path@~2.1.0: 188 | version "2.1.0" 189 | resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" 190 | dependencies: 191 | binary-extensions "^2.0.0" 192 | 193 | is-extglob@^2.1.1: 194 | version "2.1.1" 195 | resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" 196 | 197 | is-fullwidth-code-point@^3.0.0: 198 | version "3.0.0" 199 | resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" 200 | 201 | is-glob@^4.0.1, is-glob@~4.0.1: 202 | version "4.0.3" 203 | resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" 204 | dependencies: 205 | is-extglob "^2.1.1" 206 | 207 | is-number@^7.0.0: 208 | version "7.0.0" 209 | resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" 210 | 211 | is-plain-obj@^2.1.0: 212 | version "2.1.0" 213 | resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz" 214 | 215 | is-unicode-supported@^0.1.0: 216 | version "0.1.0" 217 | resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" 218 | 219 | js-yaml@^4.1.0: 220 | version "4.1.0" 221 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" 222 | dependencies: 223 | argparse "^2.0.1" 224 | 225 | locate-path@^6.0.0: 226 | version "6.0.0" 227 | resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" 228 | dependencies: 229 | p-locate "^5.0.0" 230 | 231 | log-symbols@^4.1.0: 232 | version "4.1.0" 233 | resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" 234 | dependencies: 235 | chalk "^4.1.0" 236 | is-unicode-supported "^0.1.0" 237 | 238 | minimatch@^5.0.1, minimatch@^5.1.6: 239 | version "5.1.6" 240 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" 241 | dependencies: 242 | brace-expansion "^2.0.1" 243 | 244 | mocha@^10.1.0: 245 | version "10.8.2" 246 | resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.8.2.tgz#8d8342d016ed411b12a429eb731b825f961afb96" 247 | dependencies: 248 | ansi-colors "^4.1.3" 249 | browser-stdout "^1.3.1" 250 | chokidar "^3.5.3" 251 | debug "^4.3.5" 252 | diff "^5.2.0" 253 | escape-string-regexp "^4.0.0" 254 | find-up "^5.0.0" 255 | glob "^8.1.0" 256 | he "^1.2.0" 257 | js-yaml "^4.1.0" 258 | log-symbols "^4.1.0" 259 | minimatch "^5.1.6" 260 | ms "^2.1.3" 261 | serialize-javascript "^6.0.2" 262 | strip-json-comments "^3.1.1" 263 | supports-color "^8.1.1" 264 | workerpool "^6.5.1" 265 | yargs "^16.2.0" 266 | yargs-parser "^20.2.9" 267 | yargs-unparser "^2.0.0" 268 | 269 | ms@^2.1.3: 270 | version "2.1.3" 271 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" 272 | 273 | normalize-path@^3.0.0, normalize-path@~3.0.0: 274 | version "3.0.0" 275 | resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" 276 | 277 | once@^1.3.0: 278 | version "1.4.0" 279 | resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" 280 | dependencies: 281 | wrappy "1" 282 | 283 | p-limit@^3.0.2: 284 | version "3.1.0" 285 | resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" 286 | dependencies: 287 | yocto-queue "^0.1.0" 288 | 289 | p-locate@^5.0.0: 290 | version "5.0.0" 291 | resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" 292 | dependencies: 293 | p-limit "^3.0.2" 294 | 295 | path-exists@^4.0.0: 296 | version "4.0.0" 297 | resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" 298 | 299 | picomatch@^2.0.4, picomatch@^2.2.1: 300 | version "2.3.1" 301 | resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" 302 | 303 | randombytes@^2.1.0: 304 | version "2.1.0" 305 | resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" 306 | dependencies: 307 | safe-buffer "^5.1.0" 308 | 309 | readdirp@~3.6.0: 310 | version "3.6.0" 311 | resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" 312 | dependencies: 313 | picomatch "^2.2.1" 314 | 315 | require-directory@^2.1.1: 316 | version "2.1.1" 317 | resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" 318 | 319 | safe-buffer@^5.1.0: 320 | version "5.2.1" 321 | resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" 322 | 323 | serialize-javascript@^6.0.2: 324 | version "6.0.2" 325 | resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" 326 | dependencies: 327 | randombytes "^2.1.0" 328 | 329 | should-equal@^2.0.0: 330 | version "2.0.0" 331 | resolved "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz" 332 | dependencies: 333 | should-type "^1.4.0" 334 | 335 | should-format@^3.0.3: 336 | version "3.0.3" 337 | resolved "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz" 338 | dependencies: 339 | should-type "^1.3.0" 340 | should-type-adaptors "^1.0.1" 341 | 342 | should-type-adaptors@^1.0.1: 343 | version "1.1.0" 344 | resolved "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz" 345 | dependencies: 346 | should-type "^1.3.0" 347 | should-util "^1.0.0" 348 | 349 | should-type@^1.3.0, should-type@^1.4.0: 350 | version "1.4.0" 351 | resolved "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz" 352 | 353 | should-util@^1.0.0: 354 | version "1.0.1" 355 | resolved "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz" 356 | 357 | should@13.2.x: 358 | version "13.2.3" 359 | resolved "https://registry.npmjs.org/should/-/should-13.2.3.tgz" 360 | dependencies: 361 | should-equal "^2.0.0" 362 | should-format "^3.0.3" 363 | should-type "^1.4.0" 364 | should-type-adaptors "^1.0.1" 365 | should-util "^1.0.0" 366 | 367 | string-width@^4.1.0, string-width@^4.2.0: 368 | version "4.2.3" 369 | resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" 370 | dependencies: 371 | emoji-regex "^8.0.0" 372 | is-fullwidth-code-point "^3.0.0" 373 | strip-ansi "^6.0.1" 374 | 375 | strip-ansi@^6.0.0, strip-ansi@^6.0.1: 376 | version "6.0.1" 377 | resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" 378 | dependencies: 379 | ansi-regex "^5.0.1" 380 | 381 | strip-json-comments@^3.1.1: 382 | version "3.1.1" 383 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" 384 | 385 | supports-color@^7.1.0: 386 | version "7.2.0" 387 | resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" 388 | dependencies: 389 | has-flag "^4.0.0" 390 | 391 | supports-color@^8.1.1: 392 | version "8.1.1" 393 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" 394 | dependencies: 395 | has-flag "^4.0.0" 396 | 397 | to-regex-range@^5.0.1: 398 | version "5.0.1" 399 | resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" 400 | dependencies: 401 | is-number "^7.0.0" 402 | 403 | workerpool@^6.5.1: 404 | version "6.5.1" 405 | resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" 406 | 407 | wrap-ansi@^7.0.0: 408 | version "7.0.0" 409 | resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" 410 | dependencies: 411 | ansi-styles "^4.0.0" 412 | string-width "^4.1.0" 413 | strip-ansi "^6.0.0" 414 | 415 | wrappy@1: 416 | version "1.0.2" 417 | resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" 418 | 419 | y18n@^5.0.5: 420 | version "5.0.8" 421 | resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" 422 | 423 | yargs-parser@^20.2.2, yargs-parser@^20.2.9: 424 | version "20.2.9" 425 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" 426 | 427 | yargs-unparser@^2.0.0: 428 | version "2.0.0" 429 | resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" 430 | dependencies: 431 | camelcase "^6.0.0" 432 | decamelize "^4.0.0" 433 | flat "^5.0.2" 434 | is-plain-obj "^2.1.0" 435 | 436 | yargs@^16.2.0: 437 | version "16.2.0" 438 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" 439 | dependencies: 440 | cliui "^7.0.2" 441 | escalade "^3.1.1" 442 | get-caller-file "^2.0.5" 443 | require-directory "^2.1.1" 444 | string-width "^4.2.0" 445 | y18n "^5.0.5" 446 | yargs-parser "^20.2.2" 447 | 448 | yocto-queue@^0.1.0: 449 | version "0.1.0" 450 | resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" 451 | --------------------------------------------------------------------------------