├── .editorconfig ├── .eslintrc.json ├── .github └── workflows │ └── default.yml ├── .gitignore ├── .prettierrc.json ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src ├── browserFileReader.ts ├── genres.ts ├── id3.ts ├── id3Frame.ts ├── id3Tag.ts ├── localReader.ts ├── reader.ts ├── remoteReader.ts └── util.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | indent_size = 2 6 | indent_style = space 7 | trim_trailing_whitespace = true 8 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended", 4 | "google", 5 | "plugin:@typescript-eslint/eslint-recommended", 6 | "plugin:@typescript-eslint/recommended" 7 | ], 8 | "parser": "@typescript-eslint/parser", 9 | "parserOptions": { 10 | "ecmaVersion": 2017, 11 | "sourceType": "module" 12 | }, 13 | "plugins": [ 14 | "@typescript-eslint" 15 | ], 16 | "rules": { 17 | "no-unused-vars": "off", 18 | "indent": "off", 19 | "operator-linebreak": "off", 20 | "comma-dangle": ["error", "never"], 21 | "@typescript-eslint/no-unused-vars": "off", 22 | "@typescript-eslint/indent": "off", 23 | "@typescript-eslint/interface-name-prefix": "off", 24 | "@typescript-eslint/explicit-function-return-type": ["error", { 25 | "allowExpressions": true 26 | }], 27 | "@typescript-eslint/no-inferrable-types": ["error", { 28 | "ignoreProperties": true, 29 | "ignoreParameters": true 30 | }] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.github/workflows/default.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [10.x, 14.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v1 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | - run: npm ci 24 | - run: npm run build 25 | - run: npm run lint 26 | - run: npm test 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib/ 2 | node_modules/ 3 | *.swp 4 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "bracketSpacing": false, 3 | "printWidth": 80, 4 | "semi": true, 5 | "singleQuote": true, 6 | "tabWidth": 2, 7 | "trailingComma": "none", 8 | "useTabs": false, 9 | "arrowParens": "always" 10 | } 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 43081j 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## id3.js - Javascript ID3 tag parser 2 | 3 | **id3.js** is a JavaScript library for reading and parsing ID3 tags of MP3 4 | files. 5 | 6 | It can parse both ID3v1 and ID3v2 tags within a browser or within Node. 7 | 8 | Files can be read from the local disk (Node only), same-origin URLs 9 | and `File` instances (HTML5 File API). 10 | 11 | ## Usage 12 | 13 | Install: 14 | 15 | ``` 16 | $ npm i -S id3js 17 | ``` 18 | 19 | ### AJAX 20 | 21 | You may parse ID3 tags of a remote MP3 by URL: 22 | 23 | ```html 24 | 31 | ``` 32 | 33 | This works by sending a `HEAD` request for the file and, based on the response, 34 | sending subsequent `Range` requests for the ID3 tags. 35 | 36 | This is rather efficient as there is no need for the entire file to be 37 | downloaded. 38 | 39 | ### Local Files 40 | 41 | You may parse ID3 tags of a local file in Node: 42 | 43 | ```ts 44 | import * as id3 from 'id3js'; 45 | 46 | id3.fromPath('./test.mp3').then((tags) => { 47 | // tags now contains v1, v2 and merged tags 48 | }); 49 | ``` 50 | 51 | **Keep in mind, Node must be run with `--experimental-modules` 52 | for this to be imported and it cannot be used with `require`.** 53 | 54 | ### File inputs (HTML5) 55 | 56 | You may parse ID3 tags of a file input: 57 | 58 | ```html 59 | 60 | 61 | 71 | ``` 72 | 73 | This will read the data from the File instance using slices, 74 | so the entire file is not loaded into memory but rather only the tags. 75 | 76 | ## Images 77 | 78 | An MP3 may have images embedded in the ID3 tags. If this is the case, 79 | they can be accessed through the `tag.images` property and will 80 | look like so: 81 | 82 | ```json 83 | { 84 | "type": "cover-front", 85 | "mime": "image/jpeg", 86 | "description": null, 87 | "data": ArrayBuffer 88 | } 89 | ``` 90 | 91 | As you can see, the data is provided as an `ArrayBuffer`. 92 | To access it, you may use a `DataView` or typed array such 93 | as `Uint8Array`. 94 | 95 | ## License 96 | 97 | MIT 98 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "id3js", 3 | "version": "2.1.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.12.11", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", 10 | "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "^7.10.4" 14 | } 15 | }, 16 | "@babel/helper-validator-identifier": { 17 | "version": "7.12.11", 18 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", 19 | "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", 20 | "dev": true 21 | }, 22 | "@babel/highlight": { 23 | "version": "7.13.10", 24 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", 25 | "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", 26 | "dev": true, 27 | "requires": { 28 | "@babel/helper-validator-identifier": "^7.12.11", 29 | "chalk": "^2.0.0", 30 | "js-tokens": "^4.0.0" 31 | }, 32 | "dependencies": { 33 | "chalk": { 34 | "version": "2.4.2", 35 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 36 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 37 | "dev": true, 38 | "requires": { 39 | "ansi-styles": "^3.2.1", 40 | "escape-string-regexp": "^1.0.5", 41 | "supports-color": "^5.3.0" 42 | } 43 | } 44 | } 45 | }, 46 | "@eslint/eslintrc": { 47 | "version": "0.4.0", 48 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", 49 | "integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==", 50 | "dev": true, 51 | "requires": { 52 | "ajv": "^6.12.4", 53 | "debug": "^4.1.1", 54 | "espree": "^7.3.0", 55 | "globals": "^12.1.0", 56 | "ignore": "^4.0.6", 57 | "import-fresh": "^3.2.1", 58 | "js-yaml": "^3.13.1", 59 | "minimatch": "^3.0.4", 60 | "strip-json-comments": "^3.1.1" 61 | }, 62 | "dependencies": { 63 | "globals": { 64 | "version": "12.4.0", 65 | "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", 66 | "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", 67 | "dev": true, 68 | "requires": { 69 | "type-fest": "^0.8.1" 70 | } 71 | }, 72 | "ignore": { 73 | "version": "4.0.6", 74 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", 75 | "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", 76 | "dev": true 77 | } 78 | } 79 | }, 80 | "@nodelib/fs.scandir": { 81 | "version": "2.1.4", 82 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", 83 | "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", 84 | "dev": true, 85 | "requires": { 86 | "@nodelib/fs.stat": "2.0.4", 87 | "run-parallel": "^1.1.9" 88 | } 89 | }, 90 | "@nodelib/fs.stat": { 91 | "version": "2.0.4", 92 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", 93 | "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", 94 | "dev": true 95 | }, 96 | "@nodelib/fs.walk": { 97 | "version": "1.2.6", 98 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", 99 | "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", 100 | "dev": true, 101 | "requires": { 102 | "@nodelib/fs.scandir": "2.1.4", 103 | "fastq": "^1.6.0" 104 | } 105 | }, 106 | "@types/json-schema": { 107 | "version": "7.0.7", 108 | "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", 109 | "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", 110 | "dev": true 111 | }, 112 | "@types/node": { 113 | "version": "14.14.37", 114 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.37.tgz", 115 | "integrity": "sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw==", 116 | "dev": true 117 | }, 118 | "@typescript-eslint/eslint-plugin": { 119 | "version": "4.20.0", 120 | "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.20.0.tgz", 121 | "integrity": "sha512-sw+3HO5aehYqn5w177z2D82ZQlqHCwcKSMboueo7oE4KU9QiC0SAgfS/D4z9xXvpTc8Bt41Raa9fBR8T2tIhoQ==", 122 | "dev": true, 123 | "requires": { 124 | "@typescript-eslint/experimental-utils": "4.20.0", 125 | "@typescript-eslint/scope-manager": "4.20.0", 126 | "debug": "^4.1.1", 127 | "functional-red-black-tree": "^1.0.1", 128 | "lodash": "^4.17.15", 129 | "regexpp": "^3.0.0", 130 | "semver": "^7.3.2", 131 | "tsutils": "^3.17.1" 132 | } 133 | }, 134 | "@typescript-eslint/experimental-utils": { 135 | "version": "4.20.0", 136 | "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.20.0.tgz", 137 | "integrity": "sha512-sQNlf6rjLq2yB5lELl3gOE7OuoA/6IVXJUJ+Vs7emrQMva14CkOwyQwD7CW+TkmOJ4Q/YGmoDLmbfFrpGmbKng==", 138 | "dev": true, 139 | "requires": { 140 | "@types/json-schema": "^7.0.3", 141 | "@typescript-eslint/scope-manager": "4.20.0", 142 | "@typescript-eslint/types": "4.20.0", 143 | "@typescript-eslint/typescript-estree": "4.20.0", 144 | "eslint-scope": "^5.0.0", 145 | "eslint-utils": "^2.0.0" 146 | } 147 | }, 148 | "@typescript-eslint/parser": { 149 | "version": "4.20.0", 150 | "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.20.0.tgz", 151 | "integrity": "sha512-m6vDtgL9EABdjMtKVw5rr6DdeMCH3OA1vFb0dAyuZSa3e5yw1YRzlwFnm9knma9Lz6b2GPvoNSa8vOXrqsaglA==", 152 | "dev": true, 153 | "requires": { 154 | "@typescript-eslint/scope-manager": "4.20.0", 155 | "@typescript-eslint/types": "4.20.0", 156 | "@typescript-eslint/typescript-estree": "4.20.0", 157 | "debug": "^4.1.1" 158 | } 159 | }, 160 | "@typescript-eslint/scope-manager": { 161 | "version": "4.20.0", 162 | "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.20.0.tgz", 163 | "integrity": "sha512-/zm6WR6iclD5HhGpcwl/GOYDTzrTHmvf8LLLkwKqqPKG6+KZt/CfSgPCiybshmck66M2L5fWSF/MKNuCwtKQSQ==", 164 | "dev": true, 165 | "requires": { 166 | "@typescript-eslint/types": "4.20.0", 167 | "@typescript-eslint/visitor-keys": "4.20.0" 168 | } 169 | }, 170 | "@typescript-eslint/types": { 171 | "version": "4.20.0", 172 | "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.20.0.tgz", 173 | "integrity": "sha512-cYY+1PIjei1nk49JAPnH1VEnu7OYdWRdJhYI5wiKOUMhLTG1qsx5cQxCUTuwWCmQoyriadz3Ni8HZmGSofeC+w==", 174 | "dev": true 175 | }, 176 | "@typescript-eslint/typescript-estree": { 177 | "version": "4.20.0", 178 | "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.20.0.tgz", 179 | "integrity": "sha512-Knpp0reOd4ZsyoEJdW8i/sK3mtZ47Ls7ZHvD8WVABNx5Xnn7KhenMTRGegoyMTx6TiXlOVgMz9r0pDgXTEEIHA==", 180 | "dev": true, 181 | "requires": { 182 | "@typescript-eslint/types": "4.20.0", 183 | "@typescript-eslint/visitor-keys": "4.20.0", 184 | "debug": "^4.1.1", 185 | "globby": "^11.0.1", 186 | "is-glob": "^4.0.1", 187 | "semver": "^7.3.2", 188 | "tsutils": "^3.17.1" 189 | } 190 | }, 191 | "@typescript-eslint/visitor-keys": { 192 | "version": "4.20.0", 193 | "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.20.0.tgz", 194 | "integrity": "sha512-NXKRM3oOVQL8yNFDNCZuieRIwZ5UtjNLYtmMx2PacEAGmbaEYtGgVHUHVyZvU/0rYZcizdrWjDo+WBtRPSgq+A==", 195 | "dev": true, 196 | "requires": { 197 | "@typescript-eslint/types": "4.20.0", 198 | "eslint-visitor-keys": "^2.0.0" 199 | } 200 | }, 201 | "acorn": { 202 | "version": "7.4.1", 203 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", 204 | "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", 205 | "dev": true 206 | }, 207 | "acorn-jsx": { 208 | "version": "5.3.1", 209 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", 210 | "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", 211 | "dev": true 212 | }, 213 | "ajv": { 214 | "version": "6.12.6", 215 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 216 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 217 | "dev": true, 218 | "requires": { 219 | "fast-deep-equal": "^3.1.1", 220 | "fast-json-stable-stringify": "^2.0.0", 221 | "json-schema-traverse": "^0.4.1", 222 | "uri-js": "^4.2.2" 223 | } 224 | }, 225 | "ansi-colors": { 226 | "version": "4.1.1", 227 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 228 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 229 | "dev": true 230 | }, 231 | "ansi-regex": { 232 | "version": "5.0.0", 233 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 234 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", 235 | "dev": true 236 | }, 237 | "ansi-styles": { 238 | "version": "3.2.1", 239 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 240 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 241 | "dev": true, 242 | "requires": { 243 | "color-convert": "^1.9.0" 244 | } 245 | }, 246 | "argparse": { 247 | "version": "1.0.10", 248 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 249 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 250 | "dev": true, 251 | "requires": { 252 | "sprintf-js": "~1.0.2" 253 | } 254 | }, 255 | "array-union": { 256 | "version": "2.1.0", 257 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", 258 | "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", 259 | "dev": true 260 | }, 261 | "astral-regex": { 262 | "version": "2.0.0", 263 | "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", 264 | "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", 265 | "dev": true 266 | }, 267 | "balanced-match": { 268 | "version": "1.0.0", 269 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 270 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 271 | "dev": true 272 | }, 273 | "brace-expansion": { 274 | "version": "1.1.11", 275 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 276 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 277 | "dev": true, 278 | "requires": { 279 | "balanced-match": "^1.0.0", 280 | "concat-map": "0.0.1" 281 | } 282 | }, 283 | "braces": { 284 | "version": "3.0.2", 285 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 286 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 287 | "dev": true, 288 | "requires": { 289 | "fill-range": "^7.0.1" 290 | } 291 | }, 292 | "call-bind": { 293 | "version": "1.0.2", 294 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 295 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 296 | "dev": true, 297 | "requires": { 298 | "function-bind": "^1.1.1", 299 | "get-intrinsic": "^1.0.2" 300 | } 301 | }, 302 | "callsites": { 303 | "version": "3.1.0", 304 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 305 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 306 | "dev": true 307 | }, 308 | "chalk": { 309 | "version": "4.1.0", 310 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", 311 | "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", 312 | "dev": true, 313 | "requires": { 314 | "ansi-styles": "^4.1.0", 315 | "supports-color": "^7.1.0" 316 | }, 317 | "dependencies": { 318 | "ansi-styles": { 319 | "version": "4.3.0", 320 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 321 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 322 | "dev": true, 323 | "requires": { 324 | "color-convert": "^2.0.1" 325 | } 326 | }, 327 | "color-convert": { 328 | "version": "2.0.1", 329 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 330 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 331 | "dev": true, 332 | "requires": { 333 | "color-name": "~1.1.4" 334 | } 335 | }, 336 | "color-name": { 337 | "version": "1.1.4", 338 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 339 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 340 | "dev": true 341 | }, 342 | "has-flag": { 343 | "version": "4.0.0", 344 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 345 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 346 | "dev": true 347 | }, 348 | "supports-color": { 349 | "version": "7.2.0", 350 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 351 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 352 | "dev": true, 353 | "requires": { 354 | "has-flag": "^4.0.0" 355 | } 356 | } 357 | } 358 | }, 359 | "color-convert": { 360 | "version": "1.9.3", 361 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 362 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 363 | "dev": true, 364 | "requires": { 365 | "color-name": "1.1.3" 366 | } 367 | }, 368 | "color-name": { 369 | "version": "1.1.3", 370 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 371 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 372 | "dev": true 373 | }, 374 | "concat-map": { 375 | "version": "0.0.1", 376 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 377 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 378 | "dev": true 379 | }, 380 | "cross-spawn": { 381 | "version": "7.0.3", 382 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 383 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 384 | "dev": true, 385 | "requires": { 386 | "path-key": "^3.1.0", 387 | "shebang-command": "^2.0.0", 388 | "which": "^2.0.1" 389 | } 390 | }, 391 | "debug": { 392 | "version": "4.3.1", 393 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", 394 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", 395 | "dev": true, 396 | "requires": { 397 | "ms": "2.1.2" 398 | } 399 | }, 400 | "deep-is": { 401 | "version": "0.1.3", 402 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 403 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 404 | "dev": true 405 | }, 406 | "dir-glob": { 407 | "version": "3.0.1", 408 | "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", 409 | "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", 410 | "dev": true, 411 | "requires": { 412 | "path-type": "^4.0.0" 413 | } 414 | }, 415 | "doctrine": { 416 | "version": "3.0.0", 417 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 418 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", 419 | "dev": true, 420 | "requires": { 421 | "esutils": "^2.0.2" 422 | } 423 | }, 424 | "emoji-regex": { 425 | "version": "8.0.0", 426 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 427 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 428 | "dev": true 429 | }, 430 | "enquirer": { 431 | "version": "2.3.6", 432 | "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", 433 | "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", 434 | "dev": true, 435 | "requires": { 436 | "ansi-colors": "^4.1.1" 437 | } 438 | }, 439 | "escape-string-regexp": { 440 | "version": "1.0.5", 441 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 442 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 443 | "dev": true 444 | }, 445 | "eslint": { 446 | "version": "7.23.0", 447 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.23.0.tgz", 448 | "integrity": "sha512-kqvNVbdkjzpFy0XOszNwjkKzZ+6TcwCQ/h+ozlcIWwaimBBuhlQ4nN6kbiM2L+OjDcznkTJxzYfRFH92sx4a0Q==", 449 | "dev": true, 450 | "requires": { 451 | "@babel/code-frame": "7.12.11", 452 | "@eslint/eslintrc": "^0.4.0", 453 | "ajv": "^6.10.0", 454 | "chalk": "^4.0.0", 455 | "cross-spawn": "^7.0.2", 456 | "debug": "^4.0.1", 457 | "doctrine": "^3.0.0", 458 | "enquirer": "^2.3.5", 459 | "eslint-scope": "^5.1.1", 460 | "eslint-utils": "^2.1.0", 461 | "eslint-visitor-keys": "^2.0.0", 462 | "espree": "^7.3.1", 463 | "esquery": "^1.4.0", 464 | "esutils": "^2.0.2", 465 | "file-entry-cache": "^6.0.1", 466 | "functional-red-black-tree": "^1.0.1", 467 | "glob-parent": "^5.0.0", 468 | "globals": "^13.6.0", 469 | "ignore": "^4.0.6", 470 | "import-fresh": "^3.0.0", 471 | "imurmurhash": "^0.1.4", 472 | "is-glob": "^4.0.0", 473 | "js-yaml": "^3.13.1", 474 | "json-stable-stringify-without-jsonify": "^1.0.1", 475 | "levn": "^0.4.1", 476 | "lodash": "^4.17.21", 477 | "minimatch": "^3.0.4", 478 | "natural-compare": "^1.4.0", 479 | "optionator": "^0.9.1", 480 | "progress": "^2.0.0", 481 | "regexpp": "^3.1.0", 482 | "semver": "^7.2.1", 483 | "strip-ansi": "^6.0.0", 484 | "strip-json-comments": "^3.1.0", 485 | "table": "^6.0.4", 486 | "text-table": "^0.2.0", 487 | "v8-compile-cache": "^2.0.3" 488 | }, 489 | "dependencies": { 490 | "ignore": { 491 | "version": "4.0.6", 492 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", 493 | "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", 494 | "dev": true 495 | } 496 | } 497 | }, 498 | "eslint-config-google": { 499 | "version": "0.14.0", 500 | "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.14.0.tgz", 501 | "integrity": "sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==", 502 | "dev": true 503 | }, 504 | "eslint-scope": { 505 | "version": "5.1.1", 506 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", 507 | "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", 508 | "dev": true, 509 | "requires": { 510 | "esrecurse": "^4.3.0", 511 | "estraverse": "^4.1.1" 512 | } 513 | }, 514 | "eslint-utils": { 515 | "version": "2.1.0", 516 | "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", 517 | "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", 518 | "dev": true, 519 | "requires": { 520 | "eslint-visitor-keys": "^1.1.0" 521 | }, 522 | "dependencies": { 523 | "eslint-visitor-keys": { 524 | "version": "1.3.0", 525 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", 526 | "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", 527 | "dev": true 528 | } 529 | } 530 | }, 531 | "eslint-visitor-keys": { 532 | "version": "2.0.0", 533 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", 534 | "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", 535 | "dev": true 536 | }, 537 | "espree": { 538 | "version": "7.3.1", 539 | "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", 540 | "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", 541 | "dev": true, 542 | "requires": { 543 | "acorn": "^7.4.0", 544 | "acorn-jsx": "^5.3.1", 545 | "eslint-visitor-keys": "^1.3.0" 546 | }, 547 | "dependencies": { 548 | "eslint-visitor-keys": { 549 | "version": "1.3.0", 550 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", 551 | "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", 552 | "dev": true 553 | } 554 | } 555 | }, 556 | "esprima": { 557 | "version": "4.0.1", 558 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 559 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 560 | "dev": true 561 | }, 562 | "esquery": { 563 | "version": "1.4.0", 564 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", 565 | "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", 566 | "dev": true, 567 | "requires": { 568 | "estraverse": "^5.1.0" 569 | }, 570 | "dependencies": { 571 | "estraverse": { 572 | "version": "5.2.0", 573 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", 574 | "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", 575 | "dev": true 576 | } 577 | } 578 | }, 579 | "esrecurse": { 580 | "version": "4.3.0", 581 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 582 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 583 | "dev": true, 584 | "requires": { 585 | "estraverse": "^5.2.0" 586 | }, 587 | "dependencies": { 588 | "estraverse": { 589 | "version": "5.2.0", 590 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", 591 | "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", 592 | "dev": true 593 | } 594 | } 595 | }, 596 | "estraverse": { 597 | "version": "4.3.0", 598 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 599 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", 600 | "dev": true 601 | }, 602 | "esutils": { 603 | "version": "2.0.3", 604 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 605 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 606 | "dev": true 607 | }, 608 | "fast-deep-equal": { 609 | "version": "3.1.3", 610 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 611 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 612 | "dev": true 613 | }, 614 | "fast-glob": { 615 | "version": "3.2.5", 616 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", 617 | "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", 618 | "dev": true, 619 | "requires": { 620 | "@nodelib/fs.stat": "^2.0.2", 621 | "@nodelib/fs.walk": "^1.2.3", 622 | "glob-parent": "^5.1.0", 623 | "merge2": "^1.3.0", 624 | "micromatch": "^4.0.2", 625 | "picomatch": "^2.2.1" 626 | } 627 | }, 628 | "fast-json-stable-stringify": { 629 | "version": "2.1.0", 630 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 631 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 632 | "dev": true 633 | }, 634 | "fast-levenshtein": { 635 | "version": "2.0.6", 636 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 637 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 638 | "dev": true 639 | }, 640 | "fastq": { 641 | "version": "1.11.0", 642 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", 643 | "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", 644 | "dev": true, 645 | "requires": { 646 | "reusify": "^1.0.4" 647 | } 648 | }, 649 | "file-entry-cache": { 650 | "version": "6.0.1", 651 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", 652 | "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", 653 | "dev": true, 654 | "requires": { 655 | "flat-cache": "^3.0.4" 656 | } 657 | }, 658 | "fill-range": { 659 | "version": "7.0.1", 660 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 661 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 662 | "dev": true, 663 | "requires": { 664 | "to-regex-range": "^5.0.1" 665 | } 666 | }, 667 | "flat-cache": { 668 | "version": "3.0.4", 669 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", 670 | "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", 671 | "dev": true, 672 | "requires": { 673 | "flatted": "^3.1.0", 674 | "rimraf": "^3.0.2" 675 | } 676 | }, 677 | "flatted": { 678 | "version": "3.1.1", 679 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", 680 | "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", 681 | "dev": true 682 | }, 683 | "fs.realpath": { 684 | "version": "1.0.0", 685 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 686 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 687 | "dev": true 688 | }, 689 | "function-bind": { 690 | "version": "1.1.1", 691 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 692 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 693 | "dev": true 694 | }, 695 | "functional-red-black-tree": { 696 | "version": "1.0.1", 697 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 698 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", 699 | "dev": true 700 | }, 701 | "get-intrinsic": { 702 | "version": "1.1.1", 703 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", 704 | "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", 705 | "dev": true, 706 | "requires": { 707 | "function-bind": "^1.1.1", 708 | "has": "^1.0.3", 709 | "has-symbols": "^1.0.1" 710 | } 711 | }, 712 | "glob": { 713 | "version": "7.1.6", 714 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 715 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 716 | "dev": true, 717 | "requires": { 718 | "fs.realpath": "^1.0.0", 719 | "inflight": "^1.0.4", 720 | "inherits": "2", 721 | "minimatch": "^3.0.4", 722 | "once": "^1.3.0", 723 | "path-is-absolute": "^1.0.0" 724 | } 725 | }, 726 | "glob-parent": { 727 | "version": "5.1.2", 728 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 729 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 730 | "dev": true, 731 | "requires": { 732 | "is-glob": "^4.0.1" 733 | } 734 | }, 735 | "globals": { 736 | "version": "13.7.0", 737 | "resolved": "https://registry.npmjs.org/globals/-/globals-13.7.0.tgz", 738 | "integrity": "sha512-Aipsz6ZKRxa/xQkZhNg0qIWXT6x6rD46f6x/PCnBomlttdIyAPak4YD9jTmKpZ72uROSMU87qJtcgpgHaVchiA==", 739 | "dev": true, 740 | "requires": { 741 | "type-fest": "^0.20.2" 742 | }, 743 | "dependencies": { 744 | "type-fest": { 745 | "version": "0.20.2", 746 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", 747 | "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", 748 | "dev": true 749 | } 750 | } 751 | }, 752 | "globby": { 753 | "version": "11.0.3", 754 | "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", 755 | "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", 756 | "dev": true, 757 | "requires": { 758 | "array-union": "^2.1.0", 759 | "dir-glob": "^3.0.1", 760 | "fast-glob": "^3.1.1", 761 | "ignore": "^5.1.4", 762 | "merge2": "^1.3.0", 763 | "slash": "^3.0.0" 764 | } 765 | }, 766 | "has": { 767 | "version": "1.0.3", 768 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 769 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 770 | "dev": true, 771 | "requires": { 772 | "function-bind": "^1.1.1" 773 | } 774 | }, 775 | "has-flag": { 776 | "version": "3.0.0", 777 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 778 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 779 | "dev": true 780 | }, 781 | "has-symbols": { 782 | "version": "1.0.2", 783 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", 784 | "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", 785 | "dev": true 786 | }, 787 | "ignore": { 788 | "version": "5.1.8", 789 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", 790 | "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", 791 | "dev": true 792 | }, 793 | "import-fresh": { 794 | "version": "3.3.0", 795 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", 796 | "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", 797 | "dev": true, 798 | "requires": { 799 | "parent-module": "^1.0.0", 800 | "resolve-from": "^4.0.0" 801 | } 802 | }, 803 | "imurmurhash": { 804 | "version": "0.1.4", 805 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 806 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 807 | "dev": true 808 | }, 809 | "inflight": { 810 | "version": "1.0.6", 811 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 812 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 813 | "dev": true, 814 | "requires": { 815 | "once": "^1.3.0", 816 | "wrappy": "1" 817 | } 818 | }, 819 | "inherits": { 820 | "version": "2.0.4", 821 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 822 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 823 | "dev": true 824 | }, 825 | "is-boolean-object": { 826 | "version": "1.1.0", 827 | "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", 828 | "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", 829 | "dev": true, 830 | "requires": { 831 | "call-bind": "^1.0.0" 832 | } 833 | }, 834 | "is-extglob": { 835 | "version": "2.1.1", 836 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 837 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 838 | "dev": true 839 | }, 840 | "is-fullwidth-code-point": { 841 | "version": "3.0.0", 842 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 843 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 844 | "dev": true 845 | }, 846 | "is-glob": { 847 | "version": "4.0.1", 848 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 849 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 850 | "dev": true, 851 | "requires": { 852 | "is-extglob": "^2.1.1" 853 | } 854 | }, 855 | "is-number": { 856 | "version": "7.0.0", 857 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 858 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 859 | "dev": true 860 | }, 861 | "is-number-object": { 862 | "version": "1.0.4", 863 | "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", 864 | "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", 865 | "dev": true 866 | }, 867 | "is-string": { 868 | "version": "1.0.5", 869 | "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", 870 | "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", 871 | "dev": true 872 | }, 873 | "isexe": { 874 | "version": "2.0.0", 875 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 876 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 877 | "dev": true 878 | }, 879 | "js-tokens": { 880 | "version": "4.0.0", 881 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 882 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 883 | "dev": true 884 | }, 885 | "js-yaml": { 886 | "version": "3.14.1", 887 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", 888 | "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", 889 | "dev": true, 890 | "requires": { 891 | "argparse": "^1.0.7", 892 | "esprima": "^4.0.0" 893 | } 894 | }, 895 | "json-schema-traverse": { 896 | "version": "0.4.1", 897 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 898 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 899 | "dev": true 900 | }, 901 | "json-stable-stringify-without-jsonify": { 902 | "version": "1.0.1", 903 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 904 | "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", 905 | "dev": true 906 | }, 907 | "levn": { 908 | "version": "0.4.1", 909 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 910 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 911 | "dev": true, 912 | "requires": { 913 | "prelude-ls": "^1.2.1", 914 | "type-check": "~0.4.0" 915 | } 916 | }, 917 | "lodash": { 918 | "version": "4.17.21", 919 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 920 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", 921 | "dev": true 922 | }, 923 | "lodash.clonedeep": { 924 | "version": "4.5.0", 925 | "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", 926 | "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", 927 | "dev": true 928 | }, 929 | "lodash.flatten": { 930 | "version": "4.4.0", 931 | "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", 932 | "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", 933 | "dev": true 934 | }, 935 | "lodash.truncate": { 936 | "version": "4.4.2", 937 | "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", 938 | "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", 939 | "dev": true 940 | }, 941 | "lru-cache": { 942 | "version": "6.0.0", 943 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 944 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 945 | "dev": true, 946 | "requires": { 947 | "yallist": "^4.0.0" 948 | } 949 | }, 950 | "merge2": { 951 | "version": "1.4.1", 952 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 953 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 954 | "dev": true 955 | }, 956 | "micromatch": { 957 | "version": "4.0.2", 958 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", 959 | "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", 960 | "dev": true, 961 | "requires": { 962 | "braces": "^3.0.1", 963 | "picomatch": "^2.0.5" 964 | } 965 | }, 966 | "minimatch": { 967 | "version": "3.0.4", 968 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 969 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 970 | "dev": true, 971 | "requires": { 972 | "brace-expansion": "^1.1.7" 973 | } 974 | }, 975 | "ms": { 976 | "version": "2.1.2", 977 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 978 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 979 | "dev": true 980 | }, 981 | "natural-compare": { 982 | "version": "1.4.0", 983 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 984 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 985 | "dev": true 986 | }, 987 | "once": { 988 | "version": "1.4.0", 989 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 990 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 991 | "dev": true, 992 | "requires": { 993 | "wrappy": "1" 994 | } 995 | }, 996 | "optionator": { 997 | "version": "0.9.1", 998 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", 999 | "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", 1000 | "dev": true, 1001 | "requires": { 1002 | "deep-is": "^0.1.3", 1003 | "fast-levenshtein": "^2.0.6", 1004 | "levn": "^0.4.1", 1005 | "prelude-ls": "^1.2.1", 1006 | "type-check": "^0.4.0", 1007 | "word-wrap": "^1.2.3" 1008 | } 1009 | }, 1010 | "parent-module": { 1011 | "version": "1.0.1", 1012 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 1013 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 1014 | "dev": true, 1015 | "requires": { 1016 | "callsites": "^3.0.0" 1017 | } 1018 | }, 1019 | "path-is-absolute": { 1020 | "version": "1.0.1", 1021 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1022 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1023 | "dev": true 1024 | }, 1025 | "path-key": { 1026 | "version": "3.1.1", 1027 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1028 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 1029 | "dev": true 1030 | }, 1031 | "path-type": { 1032 | "version": "4.0.0", 1033 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", 1034 | "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", 1035 | "dev": true 1036 | }, 1037 | "picomatch": { 1038 | "version": "2.2.2", 1039 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", 1040 | "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", 1041 | "dev": true 1042 | }, 1043 | "prelude-ls": { 1044 | "version": "1.2.1", 1045 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 1046 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 1047 | "dev": true 1048 | }, 1049 | "prettier": { 1050 | "version": "2.2.1", 1051 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", 1052 | "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", 1053 | "dev": true 1054 | }, 1055 | "progress": { 1056 | "version": "2.0.3", 1057 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", 1058 | "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", 1059 | "dev": true 1060 | }, 1061 | "punycode": { 1062 | "version": "2.1.1", 1063 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 1064 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 1065 | "dev": true 1066 | }, 1067 | "queue-microtask": { 1068 | "version": "1.2.3", 1069 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 1070 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 1071 | "dev": true 1072 | }, 1073 | "regexpp": { 1074 | "version": "3.1.0", 1075 | "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", 1076 | "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", 1077 | "dev": true 1078 | }, 1079 | "require-from-string": { 1080 | "version": "2.0.2", 1081 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", 1082 | "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", 1083 | "dev": true 1084 | }, 1085 | "resolve-from": { 1086 | "version": "4.0.0", 1087 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 1088 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 1089 | "dev": true 1090 | }, 1091 | "reusify": { 1092 | "version": "1.0.4", 1093 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 1094 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 1095 | "dev": true 1096 | }, 1097 | "rimraf": { 1098 | "version": "3.0.2", 1099 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 1100 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 1101 | "dev": true, 1102 | "requires": { 1103 | "glob": "^7.1.3" 1104 | } 1105 | }, 1106 | "run-parallel": { 1107 | "version": "1.2.0", 1108 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 1109 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 1110 | "dev": true, 1111 | "requires": { 1112 | "queue-microtask": "^1.2.2" 1113 | } 1114 | }, 1115 | "semver": { 1116 | "version": "7.3.5", 1117 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", 1118 | "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", 1119 | "dev": true, 1120 | "requires": { 1121 | "lru-cache": "^6.0.0" 1122 | } 1123 | }, 1124 | "shebang-command": { 1125 | "version": "2.0.0", 1126 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1127 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1128 | "dev": true, 1129 | "requires": { 1130 | "shebang-regex": "^3.0.0" 1131 | } 1132 | }, 1133 | "shebang-regex": { 1134 | "version": "3.0.0", 1135 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1136 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 1137 | "dev": true 1138 | }, 1139 | "slash": { 1140 | "version": "3.0.0", 1141 | "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", 1142 | "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", 1143 | "dev": true 1144 | }, 1145 | "slice-ansi": { 1146 | "version": "4.0.0", 1147 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", 1148 | "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", 1149 | "dev": true, 1150 | "requires": { 1151 | "ansi-styles": "^4.0.0", 1152 | "astral-regex": "^2.0.0", 1153 | "is-fullwidth-code-point": "^3.0.0" 1154 | }, 1155 | "dependencies": { 1156 | "ansi-styles": { 1157 | "version": "4.3.0", 1158 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1159 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1160 | "dev": true, 1161 | "requires": { 1162 | "color-convert": "^2.0.1" 1163 | } 1164 | }, 1165 | "color-convert": { 1166 | "version": "2.0.1", 1167 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1168 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1169 | "dev": true, 1170 | "requires": { 1171 | "color-name": "~1.1.4" 1172 | } 1173 | }, 1174 | "color-name": { 1175 | "version": "1.1.4", 1176 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1177 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1178 | "dev": true 1179 | } 1180 | } 1181 | }, 1182 | "sprintf-js": { 1183 | "version": "1.0.3", 1184 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1185 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1186 | "dev": true 1187 | }, 1188 | "string-width": { 1189 | "version": "4.2.2", 1190 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", 1191 | "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", 1192 | "dev": true, 1193 | "requires": { 1194 | "emoji-regex": "^8.0.0", 1195 | "is-fullwidth-code-point": "^3.0.0", 1196 | "strip-ansi": "^6.0.0" 1197 | } 1198 | }, 1199 | "strip-ansi": { 1200 | "version": "6.0.0", 1201 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 1202 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 1203 | "dev": true, 1204 | "requires": { 1205 | "ansi-regex": "^5.0.0" 1206 | } 1207 | }, 1208 | "strip-json-comments": { 1209 | "version": "3.1.1", 1210 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 1211 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 1212 | "dev": true 1213 | }, 1214 | "supports-color": { 1215 | "version": "5.5.0", 1216 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 1217 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 1218 | "dev": true, 1219 | "requires": { 1220 | "has-flag": "^3.0.0" 1221 | } 1222 | }, 1223 | "table": { 1224 | "version": "6.0.9", 1225 | "resolved": "https://registry.npmjs.org/table/-/table-6.0.9.tgz", 1226 | "integrity": "sha512-F3cLs9a3hL1Z7N4+EkSscsel3z55XT950AvB05bwayrNg5T1/gykXtigioTAjbltvbMSJvvhFCbnf6mX+ntnJQ==", 1227 | "dev": true, 1228 | "requires": { 1229 | "ajv": "^8.0.1", 1230 | "is-boolean-object": "^1.1.0", 1231 | "is-number-object": "^1.0.4", 1232 | "is-string": "^1.0.5", 1233 | "lodash.clonedeep": "^4.5.0", 1234 | "lodash.flatten": "^4.4.0", 1235 | "lodash.truncate": "^4.4.2", 1236 | "slice-ansi": "^4.0.0", 1237 | "string-width": "^4.2.0" 1238 | }, 1239 | "dependencies": { 1240 | "ajv": { 1241 | "version": "8.0.5", 1242 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.0.5.tgz", 1243 | "integrity": "sha512-RkiLa/AeJx7+9OvniQ/qeWu0w74A8DiPPBclQ6ji3ZQkv5KamO+QGpqmi7O4JIw3rHGUXZ6CoP9tsAkn3gyazg==", 1244 | "dev": true, 1245 | "requires": { 1246 | "fast-deep-equal": "^3.1.1", 1247 | "json-schema-traverse": "^1.0.0", 1248 | "require-from-string": "^2.0.2", 1249 | "uri-js": "^4.2.2" 1250 | } 1251 | }, 1252 | "json-schema-traverse": { 1253 | "version": "1.0.0", 1254 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", 1255 | "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", 1256 | "dev": true 1257 | } 1258 | } 1259 | }, 1260 | "text-table": { 1261 | "version": "0.2.0", 1262 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 1263 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 1264 | "dev": true 1265 | }, 1266 | "to-regex-range": { 1267 | "version": "5.0.1", 1268 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1269 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1270 | "dev": true, 1271 | "requires": { 1272 | "is-number": "^7.0.0" 1273 | } 1274 | }, 1275 | "tslib": { 1276 | "version": "1.14.1", 1277 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 1278 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", 1279 | "dev": true 1280 | }, 1281 | "tsutils": { 1282 | "version": "3.21.0", 1283 | "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", 1284 | "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", 1285 | "dev": true, 1286 | "requires": { 1287 | "tslib": "^1.8.1" 1288 | } 1289 | }, 1290 | "type-check": { 1291 | "version": "0.4.0", 1292 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 1293 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 1294 | "dev": true, 1295 | "requires": { 1296 | "prelude-ls": "^1.2.1" 1297 | } 1298 | }, 1299 | "type-fest": { 1300 | "version": "0.8.1", 1301 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", 1302 | "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", 1303 | "dev": true 1304 | }, 1305 | "typescript": { 1306 | "version": "4.2.3", 1307 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz", 1308 | "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==", 1309 | "dev": true 1310 | }, 1311 | "uri-js": { 1312 | "version": "4.4.1", 1313 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 1314 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 1315 | "dev": true, 1316 | "requires": { 1317 | "punycode": "^2.1.0" 1318 | } 1319 | }, 1320 | "v8-compile-cache": { 1321 | "version": "2.3.0", 1322 | "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", 1323 | "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", 1324 | "dev": true 1325 | }, 1326 | "which": { 1327 | "version": "2.0.2", 1328 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1329 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1330 | "dev": true, 1331 | "requires": { 1332 | "isexe": "^2.0.0" 1333 | } 1334 | }, 1335 | "word-wrap": { 1336 | "version": "1.2.3", 1337 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", 1338 | "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", 1339 | "dev": true 1340 | }, 1341 | "wrappy": { 1342 | "version": "1.0.2", 1343 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1344 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1345 | "dev": true 1346 | }, 1347 | "yallist": { 1348 | "version": "4.0.0", 1349 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 1350 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", 1351 | "dev": true 1352 | } 1353 | } 1354 | } 1355 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "id3js", 3 | "version": "2.1.1", 4 | "author": "43081j", 5 | "description": "A modern ID3 parser written completely in JavaScript, making use of typed arrays and the HTML5 File API", 6 | "main": "./lib/id3.js", 7 | "types": "./lib/id3.d.ts", 8 | "type": "module", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/43081j/id3.git" 12 | }, 13 | "keywords": [ 14 | "id3", 15 | "mp3", 16 | "parser" 17 | ], 18 | "files": [ 19 | "id3.js", 20 | "lib/**/*.js", 21 | "lib/**/*.d.ts" 22 | ], 23 | "license": "MIT", 24 | "devDependencies": { 25 | "@types/node": "^14.14.37", 26 | "@typescript-eslint/eslint-plugin": "^4.20.0", 27 | "@typescript-eslint/parser": "^4.20.0", 28 | "eslint": "^7.23.0", 29 | "eslint-config-google": "^0.14.0", 30 | "prettier": "^2.2.1", 31 | "rimraf": "^3.0.2", 32 | "typescript": "^4.2.3" 33 | }, 34 | "scripts": { 35 | "clean": "rimraf ./lib", 36 | "prebuild": "npm run clean", 37 | "lint": "eslint \"src/**/*.ts\"", 38 | "build": "npm run lint && tsc", 39 | "prepare": "npm run build", 40 | "prepublishOnly": "npm run lint", 41 | "format": "prettier --write \"src/**/*.ts\"" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/browserFileReader.ts: -------------------------------------------------------------------------------- 1 | import {Reader} from './reader.js'; 2 | 3 | /** 4 | * Reads a `File` instance 5 | */ 6 | export class BrowserFileReader extends Reader { 7 | protected _file: File; 8 | 9 | /** 10 | * @param {File} file File to read 11 | */ 12 | public constructor(file: File) { 13 | super(); 14 | 15 | this._file = file; 16 | } 17 | 18 | /** @inheritdoc */ 19 | public async open(): Promise { 20 | this.size = this._file.size; 21 | } 22 | 23 | /** @inheritdoc */ 24 | public async read(length: number, position: number): Promise { 25 | const slice = this._file.slice(position, position + length); 26 | 27 | return new Promise((resolve, reject) => { 28 | const fr = new FileReader(); 29 | 30 | fr.onload = () => { 31 | resolve(fr.result as ArrayBuffer); 32 | }; 33 | 34 | fr.onerror = () => { 35 | reject(new Error('File read failed')); 36 | }; 37 | 38 | fr.readAsArrayBuffer(slice); 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/genres.ts: -------------------------------------------------------------------------------- 1 | const genres = [ 2 | 'Blues', 3 | 'Classic Rock', 4 | 'Country', 5 | 'Dance', 6 | 'Disco', 7 | 'Funk', 8 | 'Grunge', 9 | 'Hip-Hop', 10 | 'Jazz', 11 | 'Metal', 12 | 'New Age', 13 | 'Oldies', 14 | 'Other', 15 | 'Pop', 16 | 'R&B', 17 | 'Rap', 18 | 'Reggae', 19 | 'Rock', 20 | 'Techno', 21 | 'Industrial', 22 | 'Alternative', 23 | 'Ska', 24 | 'Death Metal', 25 | 'Pranks', 26 | 'Soundtrack', 27 | 'Euro-Techno', 28 | 'Ambient', 29 | 'Trip-Hop', 30 | 'Vocal', 31 | 'Jazz+Funk', 32 | 'Fusion', 33 | 'Trance', 34 | 'Classical', 35 | 'Instrumental', 36 | 'Acid', 37 | 'House', 38 | 'Game', 39 | 'Sound Clip', 40 | 'Gospel', 41 | 'Noise', 42 | 'AlternRock', 43 | 'Bass', 44 | 'Soul', 45 | 'Punk', 46 | 'Space', 47 | 'Meditative', 48 | 'Instrumental Pop', 49 | 'Instrumental Rock', 50 | 'Ethnic', 51 | 'Gothic', 52 | 'Darkwave', 53 | 'Techno-Industrial', 54 | 'Electronic', 55 | 'Pop-Folk', 56 | 'Eurodance', 57 | 'Dream', 58 | 'Southern Rock', 59 | 'Comedy', 60 | 'Cult', 61 | 'Gangsta Rap', 62 | 'Top 40', 63 | 'Christian Rap', 64 | 'Pop / Funk', 65 | 'Jungle', 66 | 'Native American', 67 | 'Cabaret', 68 | 'New Wave', 69 | 'Psychedelic', 70 | 'Rave', 71 | 'Showtunes', 72 | 'Trailer', 73 | 'Lo-Fi', 74 | 'Tribal', 75 | 'Acid Punk', 76 | 'Acid Jazz', 77 | 'Polka', 78 | 'Retro', 79 | 'Musical', 80 | 'Rock & Roll', 81 | 'Hard Rock', 82 | 'Folk', 83 | 'Folk-Rock', 84 | 'National Folk', 85 | 'Swing', 86 | 'Fast Fusion', 87 | 'Bebob', 88 | 'Latin', 89 | 'Revival', 90 | 'Celtic', 91 | 'Bluegrass', 92 | 'Avantgarde', 93 | 'Gothic Rock', 94 | 'Progressive Rock', 95 | 'Psychedelic Rock', 96 | 'Symphonic Rock', 97 | 'Slow Rock', 98 | 'Big Band', 99 | 'Chorus', 100 | 'Easy Listening', 101 | 'Acoustic', 102 | 'Humour', 103 | 'Speech', 104 | 'Chanson', 105 | 'Opera', 106 | 'Chamber Music', 107 | 'Sonata', 108 | 'Symphony', 109 | 'Booty Bass', 110 | 'Primus', 111 | 'Porn Groove', 112 | 'Satire', 113 | 'Slow Jam', 114 | 'Club', 115 | 'Tango', 116 | 'Samba', 117 | 'Folklore', 118 | 'Ballad', 119 | 'Power Ballad', 120 | 'Rhythmic Soul', 121 | 'Freestyle', 122 | 'Duet', 123 | 'Punk Rock', 124 | 'Drum Solo', 125 | 'A Cappella', 126 | 'Euro-House', 127 | 'Dance Hall', 128 | 'Goa', 129 | 'Drum & Bass', 130 | 'Club-House', 131 | 'Hardcore', 132 | 'Terror', 133 | 'Indie', 134 | 'BritPop', 135 | 'Negerpunk', 136 | 'Polsk Punk', 137 | 'Beat', 138 | 'Christian Gangsta Rap', 139 | 'Heavy Metal', 140 | 'Black Metal', 141 | 'Crossover', 142 | 'Contemporary Christian', 143 | 'Christian Rock', 144 | 'Merengue', 145 | 'Salsa', 146 | 'Thrash Metal', 147 | 'Anime', 148 | 'JPop', 149 | 'Synthpop', 150 | 'Rock/Pop' 151 | ]; 152 | 153 | export default genres; 154 | -------------------------------------------------------------------------------- /src/id3.ts: -------------------------------------------------------------------------------- 1 | import {ID3Tag, parse} from './id3Tag.js'; 2 | import {BrowserFileReader} from './browserFileReader.js'; 3 | import {RemoteReader} from './remoteReader.js'; 4 | import {Reader} from './reader.js'; 5 | 6 | const SUPPORTS_FILE = 7 | typeof window !== 'undefined' && 8 | 'File' in window && 9 | 'FileReader' in window && 10 | typeof ArrayBuffer !== 'undefined'; 11 | 12 | /** 13 | * Parses ID3 tags from a given reader 14 | * @param {Reader} reader Reader to use 15 | * @return {Promise} 16 | */ 17 | export async function fromReader(reader: Reader): Promise { 18 | await reader.open(); 19 | 20 | const tags = await parse(reader); 21 | 22 | await reader.close(); 23 | 24 | return tags; 25 | } 26 | 27 | /** 28 | * Parses ID3 tags from a local path 29 | * @param {string} path Path to file 30 | * @return {Promise} 31 | */ 32 | export async function fromPath(path: string): Promise { 33 | const mod = await import('./localReader.js'); 34 | return fromReader(new mod.LocalReader(path)); 35 | } 36 | 37 | /** 38 | * Parses ID3 tags from a specified URL 39 | * @param {string} url URL to retrieve data from 40 | * @return {Promise} 41 | */ 42 | export function fromUrl(url: string): Promise { 43 | return fromReader(new RemoteReader(url)); 44 | } 45 | 46 | /** 47 | * Parses ID3 tags from a File instance 48 | * @param {File} file File to parse 49 | * @return {Promise} 50 | */ 51 | export function fromFile(file: File): Promise { 52 | if (!SUPPORTS_FILE) { 53 | throw new Error( 54 | 'Browser does not have support for the File API and/or ' + 'ArrayBuffers' 55 | ); 56 | } 57 | 58 | return fromReader(new BrowserFileReader(file)); 59 | } 60 | -------------------------------------------------------------------------------- /src/id3Frame.ts: -------------------------------------------------------------------------------- 1 | import genres from './genres.js'; 2 | import {getString, getUint24, getUint32Synch, getStringUtf16} from './util.js'; 3 | 4 | export interface ID3Frame { 5 | tag: string | null; 6 | value: unknown | null; 7 | id: string | null; 8 | } 9 | 10 | export interface ImageValue { 11 | type: null | string; 12 | mime: null | string; 13 | description: null | string; 14 | data: null | ArrayBuffer; 15 | } 16 | 17 | export const types: ReadonlyMap = new Map([ 18 | /* 19 | * Textual frames 20 | */ 21 | ['TALB', 'album'], 22 | ['TBPM', 'bpm'], 23 | ['TCOM', 'composer'], 24 | ['TCON', 'genre'], 25 | ['TCOP', 'copyright'], 26 | ['TDEN', 'encoding-time'], 27 | ['TDLY', 'playlist-delay'], 28 | ['TDOR', 'original-release-time'], 29 | ['TDRC', 'recording-time'], 30 | ['TDRL', 'release-time'], 31 | ['TDTG', 'tagging-time'], 32 | ['TENC', 'encoder'], 33 | ['TEXT', 'writer'], 34 | ['TFLT', 'file-type'], 35 | ['TIPL', 'involved-people'], 36 | ['TIT1', 'content-group'], 37 | ['TIT2', 'title'], 38 | ['TIT3', 'subtitle'], 39 | ['TKEY', 'initial-key'], 40 | ['TLAN', 'language'], 41 | ['TLEN', 'length'], 42 | ['TMCL', 'credits'], 43 | ['TMED', 'media-type'], 44 | ['TMOO', 'mood'], 45 | ['TOAL', 'original-album'], 46 | ['TOFN', 'original-filename'], 47 | ['TOLY', 'original-writer'], 48 | ['TOPE', 'original-artist'], 49 | ['TOWN', 'owner'], 50 | ['TPE1', 'artist'], 51 | ['TPE2', 'band'], 52 | ['TPE3', 'conductor'], 53 | ['TPE4', 'remixer'], 54 | ['TPOS', 'set-part'], 55 | ['TPRO', 'produced-notice'], 56 | ['TPUB', 'publisher'], 57 | ['TRCK', 'track'], 58 | ['TRSN', 'radio-name'], 59 | ['TRSO', 'radio-owner'], 60 | ['TSOA', 'album-sort'], 61 | ['TSOP', 'performer-sort'], 62 | ['TSOT', 'title-sort'], 63 | ['TSRC', 'isrc'], 64 | ['TSSE', 'encoder-settings'], 65 | ['TSST', 'set-subtitle'], 66 | ['TYER', 'year'], 67 | /* 68 | * Textual frames (<=2.2) 69 | */ 70 | ['TAL', 'album'], 71 | ['TBP', 'bpm'], 72 | ['TCM', 'composer'], 73 | ['TCO', 'genre'], 74 | ['TCR', 'copyright'], 75 | ['TDY', 'playlist-delay'], 76 | ['TEN', 'encoder'], 77 | ['TFT', 'file-type'], 78 | ['TKE', 'initial-key'], 79 | ['TLA', 'language'], 80 | ['TLE', 'length'], 81 | ['TMT', 'media-type'], 82 | ['TOA', 'original-artist'], 83 | ['TOF', 'original-filename'], 84 | ['TOL', 'original-writer'], 85 | ['TOT', 'original-album'], 86 | ['TP1', 'artist'], 87 | ['TP2', 'band'], 88 | ['TP3', 'conductor'], 89 | ['TP4', 'remixer'], 90 | ['TPA', 'set-part'], 91 | ['TPB', 'publisher'], 92 | ['TRC', 'isrc'], 93 | ['TRK', 'track'], 94 | ['TSS', 'encoder-settings'], 95 | ['TT1', 'content-group'], 96 | ['TT2', 'title'], 97 | ['TT3', 'subtitle'], 98 | ['TXT', 'writer'], 99 | ['TYE', 'year'], 100 | /* 101 | * URL frames 102 | */ 103 | ['WCOM', 'url-commercial'], 104 | ['WCOP', 'url-legal'], 105 | ['WOAF', 'url-file'], 106 | ['WOAR', 'url-artist'], 107 | ['WOAS', 'url-source'], 108 | ['WORS', 'url-radio'], 109 | ['WPAY', 'url-payment'], 110 | ['WPUB', 'url-publisher'], 111 | /* 112 | * URL frames (<=2.2) 113 | */ 114 | ['WAF', 'url-file'], 115 | ['WAR', 'url-artist'], 116 | ['WAS', 'url-source'], 117 | ['WCM', 'url-commercial'], 118 | ['WCP', 'url-copyright'], 119 | ['WPB', 'url-publisher'], 120 | /* 121 | * Comment frame 122 | */ 123 | ['COMM', 'comments'], 124 | /* 125 | * Image frame 126 | */ 127 | ['APIC', 'image'], 128 | ['PIC', 'image'], 129 | /* 130 | * Private frames 131 | */ 132 | ['PRIV', 'private'] 133 | ]); 134 | 135 | export const imageTypes = [ 136 | 'other', 137 | 'file-icon', 138 | 'icon', 139 | 'cover-front', 140 | 'cover-back', 141 | 'leaflet', 142 | 'media', 143 | 'artist-lead', 144 | 'artist', 145 | 'conductor', 146 | 'band', 147 | 'composer', 148 | 'writer', 149 | 'location', 150 | 'during-recording', 151 | 'during-performance', 152 | 'screen', 153 | 'fish', 154 | 'illustration', 155 | 'logo-band', 156 | 'logo-publisher' 157 | ]; 158 | 159 | /** 160 | * Parses legacy frames for ID3 v2.2 and earlier 161 | * @param {ArrayBuffer} buffer Buffer to read 162 | * @return {ID3Frame|null} 163 | */ 164 | export function parseLegacy(buffer: ArrayBuffer): ID3Frame | null { 165 | const result: ID3Frame = { 166 | id: null, 167 | tag: null, 168 | value: null 169 | }; 170 | const dv = new DataView(buffer); 171 | const header = { 172 | id: getString(dv, 3), 173 | type: getString(dv, 1), 174 | size: getUint24(dv, 3) 175 | }; 176 | 177 | const matchedType = types.get(header.id); 178 | 179 | if (!matchedType) { 180 | return null; 181 | } 182 | 183 | result.id = header.id; 184 | result.tag = matchedType; 185 | 186 | if (header.type === 'T') { 187 | /* 188 | * TODO: Implement UTF-8, UTF-16 and UTF-16 with BOM properly? 189 | */ 190 | let val = getString(dv, -7, 7); 191 | 192 | if (header.id === 'TCO' && !!parseInt(val)) { 193 | val = genres[parseInt(val)]; 194 | } 195 | 196 | result.value = val; 197 | } else if (header.type === 'W') { 198 | result.value = getString(dv, -7, 7); 199 | } else if (header.id === 'COM') { 200 | /* 201 | * TODO: Implement UTF-8, UTF-16 and UTF-16 with BOM properly? 202 | */ 203 | let val = getString(dv, -10, 10); 204 | 205 | if (val.indexOf('\x00') !== -1) { 206 | val = val.substr(val.indexOf('\x00') + 1); 207 | } 208 | 209 | result.value = val; 210 | } else if (header.id === 'PIC') { 211 | const image: ImageValue = { 212 | type: null, 213 | mime: 'image/' + getString(dv, 3, 7).toLowerCase(), 214 | description: null, 215 | data: null 216 | }; 217 | 218 | image.type = imageTypes[dv.getUint8(11)] || 'other'; 219 | 220 | const variableStart = 11; 221 | let variableLength = 0; 222 | 223 | for (let i = variableStart; ; i++) { 224 | if (dv.getUint8(i) === 0x00) { 225 | variableLength = i - variableStart; 226 | break; 227 | } 228 | } 229 | 230 | image.description = 231 | variableLength === 0 232 | ? null 233 | : getString(dv, variableLength, variableStart); 234 | image.data = buffer.slice(variableStart + 1); 235 | 236 | result.value = image; 237 | } 238 | 239 | return result.tag ? result : null; 240 | } 241 | 242 | /** 243 | * Parses a given buffer into an ID3 frame 244 | * @param {ArrayBuffer} buffer Buffer to read data from 245 | * @param {number} major Major version of ID3 246 | * @param {number} minor Minor version of ID3 247 | * @return {ID3Frame|null} 248 | */ 249 | export function parse( 250 | buffer: ArrayBuffer, 251 | major: number, 252 | minor: number 253 | ): ID3Frame | null { 254 | minor = minor || 0; 255 | major = major || 4; 256 | 257 | const result: ID3Frame = {id: null, tag: null, value: null}; 258 | const dv = new DataView(buffer); 259 | 260 | if (major < 3) { 261 | return parseLegacy(buffer); 262 | } 263 | 264 | const header = { 265 | id: getString(dv, 4), 266 | type: getString(dv, 1), 267 | size: getUint32Synch(dv, 4), 268 | flags: [dv.getUint8(8), dv.getUint8(9)] 269 | }; 270 | 271 | /* 272 | * No support for compressed, unsychronised, etc frames 273 | */ 274 | if (header.flags[1] !== 0) { 275 | return null; 276 | } 277 | 278 | const matchedType = types.get(header.id); 279 | 280 | if (!matchedType) { 281 | return null; 282 | } 283 | 284 | result.tag = matchedType; 285 | result.id = header.id; 286 | 287 | if (header.type === 'T') { 288 | const encoding = dv.getUint8(10); 289 | let val: string | null = null; 290 | 291 | /* 292 | * TODO: Implement UTF-8, UTF-16 and UTF-16 with BOM properly? 293 | */ 294 | if (encoding === 0 || encoding === 3) { 295 | val = getString(dv, -11, 11); 296 | } else if (encoding === 1) { 297 | val = getStringUtf16(dv, -11, 11, true); 298 | } else if (encoding === 2) { 299 | val = getStringUtf16(dv, -11, 11); 300 | } 301 | 302 | if (header.id === 'TCON' && val !== null && !!parseInt(val)) { 303 | val = genres[parseInt(val)]; 304 | } 305 | 306 | result.value = val; 307 | } else if (header.type === 'W') { 308 | result.value = getString(dv, -10, 10); 309 | } else if (header.id === 'PRIV') { 310 | const variableStart = 10; 311 | let variableLength = 0; 312 | 313 | for (let i = 0; ; i++) { 314 | if (dv.getUint8(i) === 0x00) { 315 | variableLength = i - variableStart; 316 | break; 317 | } 318 | } 319 | 320 | result.value = { 321 | identifier: 322 | variableLength === 0 323 | ? null 324 | : getString(dv, variableLength, variableStart), 325 | data: buffer.slice(variableLength + variableStart + 1) 326 | }; 327 | } else if (header.id === 'COMM') { 328 | /* 329 | * TODO: Implement UTF-8, UTF-16 and UTF-16 with BOM properly? 330 | */ 331 | const encoding = dv.getUint8(10); 332 | let variableStart = 14; 333 | 334 | /* 335 | * Skip the comment description and retrieve only the comment its self 336 | */ 337 | for (let i = variableStart; ; i++) { 338 | if (encoding === 1 || encoding === 2) { 339 | if (dv.getUint16(i) === 0x0000) { 340 | variableStart = i + 2; 341 | break; 342 | } 343 | i++; 344 | } else { 345 | if (dv.getUint8(i) === 0x00) { 346 | variableStart = i + 1; 347 | break; 348 | } 349 | } 350 | } 351 | 352 | if (encoding === 0 || encoding === 3) { 353 | result.value = getString(dv, -1 * variableStart, variableStart); 354 | } else if (encoding === 1) { 355 | result.value = getStringUtf16( 356 | dv, 357 | -1 * variableStart, 358 | variableStart, 359 | true 360 | ); 361 | } else if (encoding === 2) { 362 | result.value = getStringUtf16(dv, -1 * variableStart, variableStart); 363 | } 364 | } else if (header.id === 'APIC') { 365 | const encoding = dv.getUint8(10); 366 | const image: ImageValue = { 367 | type: null, 368 | mime: null, 369 | description: null, 370 | data: null 371 | }; 372 | let variableStart = 11; 373 | let variableLength = 0; 374 | 375 | for (let i = variableStart; ; i++) { 376 | if (dv.getUint8(i) === 0x00) { 377 | variableLength = i - variableStart; 378 | break; 379 | } 380 | } 381 | 382 | image.mime = getString(dv, variableLength, variableStart); 383 | image.type = 384 | imageTypes[dv.getUint8(variableStart + variableLength + 1)] || 'other'; 385 | variableStart += variableLength + 2; 386 | variableLength = 0; 387 | 388 | for (let i = variableStart; ; i++) { 389 | if (dv.getUint8(i) === 0x00) { 390 | variableLength = i - variableStart; 391 | break; 392 | } 393 | } 394 | 395 | if (variableLength !== 0) { 396 | if (encoding === 0 || encoding === 3) { 397 | image.description = getString(dv, variableLength, variableStart); 398 | } else if (encoding === 1) { 399 | image.description = getStringUtf16( 400 | dv, 401 | variableLength, 402 | variableStart, 403 | true 404 | ); 405 | } else if (encoding === 2) { 406 | image.description = getStringUtf16(dv, variableLength, variableStart); 407 | } 408 | // advancing the read pointer after reading the picture description 409 | variableStart += variableLength; 410 | } 411 | 412 | image.data = buffer.slice(variableStart + 1); 413 | 414 | result.value = image; 415 | } 416 | 417 | return result.tag ? result : null; 418 | } 419 | -------------------------------------------------------------------------------- /src/id3Tag.ts: -------------------------------------------------------------------------------- 1 | import {Reader} from './reader.js'; 2 | import {parse as parseFrame, ID3Frame, ImageValue} from './id3Frame.js'; 3 | import {getString, getUint32Synch, getUint24} from './util.js'; 4 | import genres from './genres.js'; 5 | 6 | export interface ID3Tag { 7 | title: string | null; 8 | album: string | null; 9 | artist: string | null; 10 | year: string | null; 11 | [key: string]: unknown; 12 | } 13 | 14 | export interface ID3TagV1 extends ID3Tag { 15 | kind: 'v1'; 16 | comment: string | null; 17 | track: string | null; 18 | genre: string | null; 19 | version: number; 20 | } 21 | 22 | export interface ID3TagV2 extends ID3Tag { 23 | kind: 'v2'; 24 | version: [number, number]; 25 | frames: ID3Frame[]; 26 | images: ImageValue[]; 27 | } 28 | 29 | /** 30 | * Parses a given resource into an ID3 tag 31 | * @param {Reader} handle Reader to use for reading the resource 32 | * @return {Promise} 33 | */ 34 | export async function parse(handle: Reader): Promise { 35 | let tag: ID3Tag | null = null; 36 | 37 | /* 38 | * Read the last 128 bytes (ID3v1) 39 | */ 40 | const v1HeaderBuf = await handle.read(128, handle.size - 128); 41 | const v1Header = new DataView(v1HeaderBuf); 42 | 43 | if ( 44 | v1HeaderBuf.byteLength === 128 && 45 | getString(v1Header, 3, undefined, true) === 'TAG' 46 | ) { 47 | tag = { 48 | kind: 'v1', 49 | title: getString(v1Header, 30, 3).trim() || null, 50 | album: getString(v1Header, 30, 63).trim() || null, 51 | artist: getString(v1Header, 30, 33).trim() || null, 52 | year: getString(v1Header, 4, 93).trim() || null, 53 | genre: null, 54 | comment: null, 55 | track: null 56 | }; 57 | 58 | /* 59 | * If there is a zero byte at [125], the comment is 28 bytes and the 60 | * remaining 2 are [0, trackno] 61 | */ 62 | if (v1Header.getUint8(125) === 0) { 63 | tag.comment = getString(v1Header, 28, 97); 64 | tag.version = 1.1; 65 | tag.track = v1Header.getUint8(126); 66 | } else { 67 | tag.comment = getString(v1Header, 30, 97); 68 | } 69 | 70 | /* 71 | * Lookup the genre index in the predefined genres array 72 | */ 73 | tag.genre = genres[v1Header.getUint8(127)] || null; 74 | } 75 | 76 | /* 77 | * Read 14 bytes (10 for ID3v2 header, 4 for possible extended header size) 78 | * Assuming the ID3v2 tag is prepended 79 | */ 80 | const v2PrefixBuf = await handle.read(14, 0); 81 | const v2Prefix = new DataView(v2PrefixBuf); 82 | 83 | /* 84 | * Be sure that the buffer is at least the size of an id3v2 header 85 | * Assume incompatibility if a major version of > 4 is used 86 | */ 87 | if ( 88 | v2PrefixBuf.byteLength === 14 && 89 | getString(v2Prefix, 3, undefined, true) === 'ID3' && 90 | v2Prefix.getUint8(3) <= 4 91 | ) { 92 | let headerSize = 10; 93 | let tagSize = 0; 94 | const version = [v2Prefix.getUint8(3), v2Prefix.getUint8(4)]; 95 | const tagFlags = v2Prefix.getUint8(5); 96 | 97 | /* 98 | * Do not support unsynchronisation 99 | */ 100 | if ((tagFlags & 0x80) === 0) { 101 | tag = { 102 | kind: 'v2', 103 | title: tag ? tag.title : null, 104 | album: tag ? tag.album : null, 105 | artist: tag ? tag.artist : null, 106 | year: tag ? tag.year : null, 107 | version: version, 108 | frames: [], 109 | images: [] 110 | }; 111 | 112 | /* 113 | * Increment the header size to offset by if an extended header exists 114 | */ 115 | if ((tagFlags & 0x40) !== 0) { 116 | headerSize += getUint32Synch(v2Prefix, 11); 117 | } 118 | 119 | /* 120 | * Calculate the tag size to be read 121 | */ 122 | tagSize += getUint32Synch(v2Prefix, 6); 123 | 124 | const v2TagBuf = await handle.read(tagSize, headerSize); 125 | const v2Tag = new DataView(v2TagBuf); 126 | let position = 0; 127 | 128 | while (position < v2TagBuf.byteLength) { 129 | let slice; 130 | let isFrame = true; 131 | 132 | for (let i = 0; i < 3; i++) { 133 | const frameBit = v2Tag.getUint8(position + i); 134 | 135 | if ( 136 | (frameBit < 0x41 || frameBit > 0x5a) && 137 | (frameBit < 0x30 || frameBit > 0x39) 138 | ) { 139 | isFrame = false; 140 | } 141 | } 142 | 143 | if (!isFrame) { 144 | break; 145 | } 146 | 147 | /* 148 | * < v2.3, frame ID is 3 chars, size is 3 bytes making a total 149 | * size of 6 bytes. 150 | * >= v2.3, frame ID is 4 chars, size is 4 bytes, flags are 2 bytes, 151 | * total 10 bytes. 152 | */ 153 | if (version[0] < 3) { 154 | slice = v2TagBuf.slice( 155 | position, 156 | position + 6 + getUint24(v2Tag, position + 3) 157 | ); 158 | } else if (version[0] === 3) { 159 | slice = v2TagBuf.slice( 160 | position, 161 | position + 10 + v2Tag.getUint32(position + 4) 162 | ); 163 | } else { 164 | slice = v2TagBuf.slice( 165 | position, 166 | position + 10 + getUint32Synch(v2Tag, position + 4) 167 | ); 168 | } 169 | 170 | const frame = await parseFrame(slice, version[0], version[1]); 171 | 172 | if (frame && frame.tag) { 173 | const tagAsV2 = tag as ID3TagV2; 174 | 175 | tagAsV2.frames.push(frame); 176 | 177 | if (frame.tag === 'image') { 178 | tagAsV2.images.push(frame.value as ImageValue); 179 | } else { 180 | tag[frame.tag] = frame.value; 181 | } 182 | } 183 | 184 | position += slice.byteLength; 185 | } 186 | } 187 | } 188 | 189 | return tag; 190 | } 191 | -------------------------------------------------------------------------------- /src/localReader.ts: -------------------------------------------------------------------------------- 1 | import {Reader} from './reader.js'; 2 | import * as fs from 'fs'; 3 | 4 | /** 5 | * Provides read access to the local file system 6 | */ 7 | export class LocalReader extends Reader { 8 | protected _path: string; 9 | protected _fd?: number; 10 | 11 | /** 12 | * @param {string} path Path of the local file 13 | */ 14 | public constructor(path: string) { 15 | super(); 16 | 17 | this._path = path; 18 | } 19 | 20 | /** @inheritdoc */ 21 | public async open(): Promise { 22 | return new Promise((resolve, reject): void => { 23 | fs.stat(this._path, (err, stat): void => { 24 | if (err) { 25 | reject(err); 26 | return; 27 | } 28 | 29 | this.size = stat.size; 30 | 31 | fs.open(this._path, 'r', (openErr, fd) => { 32 | if (openErr) { 33 | reject(err); 34 | return; 35 | } 36 | 37 | this._fd = fd; 38 | resolve(); 39 | }); 40 | }); 41 | }); 42 | } 43 | 44 | /** @inheritdoc */ 45 | public async close(): Promise { 46 | return new Promise((resolve, reject) => { 47 | if (this._fd === undefined) { 48 | reject(new Error('Resource not yet open')); 49 | return; 50 | } 51 | 52 | fs.close(this._fd, (err) => { 53 | if (err) { 54 | reject(err); 55 | } else { 56 | resolve(); 57 | } 58 | }); 59 | }); 60 | } 61 | 62 | /** @inheritdoc */ 63 | public async read(length: number, position: number): Promise { 64 | const buffer = Buffer.alloc(length); 65 | 66 | return new Promise((resolve, reject) => { 67 | if (this._fd === undefined) { 68 | reject(new Error('Resource not yet open')); 69 | return; 70 | } 71 | 72 | fs.read( 73 | this._fd, 74 | buffer, 75 | 0, 76 | length, 77 | position, 78 | (err, _bytesRead, buffer) => { 79 | if (err) { 80 | return reject(err); 81 | } 82 | 83 | const ab = new ArrayBuffer(buffer.length); 84 | const view = new Uint8Array(ab); 85 | 86 | for (let i = 0; i < buffer.length; i++) { 87 | view[i] = buffer[i]; 88 | } 89 | 90 | resolve(ab); 91 | } 92 | ); 93 | }); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/reader.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Provides read access to a given resource 3 | */ 4 | export abstract class Reader { 5 | /** 6 | * Size of the resource 7 | */ 8 | public size: number = 0; 9 | 10 | /** 11 | * Opens the resource for reading 12 | * @return {Promise} 13 | */ 14 | public abstract open(): Promise; 15 | 16 | /** 17 | * Closes the resource 18 | * @return {Promise} 19 | */ 20 | public async close(): Promise { 21 | return; 22 | } 23 | 24 | /** 25 | * Reads a specified range of the resource 26 | * @param {number} length Number of bytes to read 27 | * @param {number} position Position to begin from 28 | * @return {Promise} 29 | */ 30 | public abstract read(length: number, position: number): Promise; 31 | 32 | /** 33 | * Reads a specified range into a Blob 34 | * @param {number} length Number of bytes to read 35 | * @param {number} position Position to begin from 36 | * @param {string=} type Type of data to return 37 | * @return {Promise} 38 | */ 39 | public async readBlob( 40 | length: number, 41 | position: number = 0, 42 | type: string = 'application/octet-stream' 43 | ): Promise { 44 | const data = await this.read(length, position); 45 | return new Blob([data], {type: type}); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/remoteReader.ts: -------------------------------------------------------------------------------- 1 | import {Reader} from './reader.js'; 2 | 3 | /** 4 | * Reads a remote URL 5 | */ 6 | export class RemoteReader extends Reader { 7 | protected _url: string; 8 | 9 | /** 10 | * @param {string} url URL to retrieve 11 | */ 12 | public constructor(url: string) { 13 | super(); 14 | 15 | this._url = url; 16 | } 17 | 18 | /** @inheritdoc */ 19 | public async open(): Promise { 20 | const resp = await fetch(this._url, { 21 | method: 'HEAD' 22 | }); 23 | 24 | const contentLength = resp.headers.get('Content-Length'); 25 | 26 | this.size = contentLength ? Number(contentLength) : 0; 27 | } 28 | 29 | /** @inheritdoc */ 30 | public async read(length: number, position: number): Promise { 31 | const resp = await fetch(this._url, { 32 | method: 'GET', 33 | headers: { 34 | Range: `bytes=${position}-${position + length - 1}` 35 | } 36 | }); 37 | 38 | return await resp.arrayBuffer(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Retrieves a string from a specific offset of a data view 3 | * @param {DataView} view View to retrieve string from 4 | * @param {number|null} length Bytes to read 5 | * @param {number=} offset Offset to read from 6 | * @param {boolean=} raw Whether to return the raw string or not 7 | * @return {string} 8 | */ 9 | export function getString( 10 | view: DataView, 11 | length: number | undefined, 12 | offset: number = 0, 13 | raw?: boolean 14 | ): string { 15 | let len = length || view.byteLength - offset; 16 | const useBuffer = typeof Buffer !== 'undefined'; 17 | 18 | if (len < 0) { 19 | len += view.byteLength; 20 | } 21 | 22 | const data: number[] = []; 23 | const limit = offset + len; 24 | 25 | for (let i = offset; i < limit; i++) { 26 | const current = view.getUint8(i); 27 | if (current === 0) { 28 | break; 29 | } 30 | data.push(current); 31 | } 32 | 33 | if (useBuffer) { 34 | return Buffer.from(data).toString(); 35 | } 36 | 37 | const str = data.map((chr) => String.fromCharCode(chr)).join(''); 38 | 39 | if (raw) { 40 | return str; 41 | } 42 | 43 | return decodeURIComponent(escape(str)); 44 | } 45 | 46 | /** 47 | * Retrieves a UTF16 string from a specific offset of a data view 48 | * @param {DataView} view View to retrieve string from 49 | * @param {number|null} length Bytes to read 50 | * @param {number=} offset Offset to read from 51 | * @param {boolean=} bom Whether to use BOM or not 52 | * @return {string} 53 | */ 54 | export function getStringUtf16( 55 | view: DataView, 56 | length: number | null, 57 | offset: number = 0, 58 | bom?: boolean 59 | ): string { 60 | let littleEndian = false; 61 | let len = length || view.byteLength - offset; 62 | const str: number[] = []; 63 | const useBuffer = typeof Buffer !== 'undefined'; 64 | 65 | if (len < 0) { 66 | len += view.byteLength; 67 | } 68 | 69 | if (offset + 1 > view.byteLength) { 70 | return ''; 71 | } 72 | 73 | if (bom) { 74 | const bomInt = view.getUint16(offset); 75 | 76 | if (bomInt === 0xfffe) { 77 | littleEndian = true; 78 | } 79 | 80 | offset += 2; 81 | len -= 2; 82 | } 83 | 84 | const limit = offset + len; 85 | 86 | for (let i = offset; i < limit; i += 2) { 87 | let ch = view.getUint16(i, littleEndian); 88 | 89 | if ( 90 | i < limit - 1 && 91 | ch === 0 && 92 | view.getUint16(i + 1, littleEndian) === 0 93 | ) { 94 | break; 95 | } 96 | 97 | if ((ch >= 0 && ch <= 0xd7ff) || (ch >= 0xe000 && ch <= 0xffff)) { 98 | str.push(ch); 99 | } else if (ch >= 0x10000 && ch <= 0x10ffff) { 100 | ch -= 0x10000; 101 | 102 | str.push(((0xffc00 & ch) >> 10) + 0xd800); 103 | str.push((0x3ff & ch) + 0xdc00); 104 | } 105 | } 106 | 107 | if (useBuffer) { 108 | const buf = Buffer.alloc(str.length * 2); 109 | for (let i = 0; i < str.length; i++) { 110 | const chr = str[i]; 111 | 112 | if (littleEndian) { 113 | buf.writeUInt16LE(chr, i * 2); 114 | } else { 115 | buf.writeUInt16BE(chr, i * 2); 116 | } 117 | } 118 | return buf.toString(); 119 | } 120 | 121 | return String.fromCharCode.apply( 122 | null, 123 | (new Uint16Array(str) as unknown) as number[] 124 | ); 125 | } 126 | 127 | /** 128 | * Gets the "synch" representation of a number 129 | * @param {number} num Number to convert 130 | * @return {number} 131 | */ 132 | export function getSynch(num: number): number { 133 | let out = 0; 134 | let mask = 0x7f000000; 135 | 136 | while (mask) { 137 | out >>= 1; 138 | out |= num & mask; 139 | mask >>= 8; 140 | } 141 | 142 | return out; 143 | } 144 | 145 | /** 146 | * Gets a "synch2 uint8 from a view 147 | * @param {DataView} view View to read 148 | * @param {number=} offset Offset to read from 149 | * @return {number} 150 | */ 151 | export function getUint8Synch(view: DataView, offset: number = 0): number { 152 | return getSynch(view.getUint8(offset)); 153 | } 154 | 155 | /** 156 | * Gets a "synch2 uint32 from a view 157 | * @param {DataView} view View to read 158 | * @param {number=} offset Offset to read from 159 | * @return {number} 160 | */ 161 | export function getUint32Synch(view: DataView, offset: number = 0): number { 162 | return getSynch(view.getUint32(offset)); 163 | } 164 | 165 | /** 166 | * Gets a uint24 from a view 167 | * @param {DataView} view View to read 168 | * @param {number=} offset Offset to read from 169 | * @param {boolean=} littleEndian Whether to use little endian or not 170 | * @return {number} 171 | */ 172 | export function getUint24( 173 | view: DataView, 174 | offset: number = 0, 175 | littleEndian?: boolean 176 | ): number { 177 | if (littleEndian) { 178 | return ( 179 | view.getUint8(offset) + 180 | (view.getUint8(offset + 1) << 8) + 181 | (view.getUint8(offset + 2) << 16) 182 | ); 183 | } 184 | return ( 185 | view.getUint8(offset + 2) + 186 | (view.getUint8(offset + 1) << 8) + 187 | (view.getUint8(offset) << 16) 188 | ); 189 | } 190 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "module": "esnext", 5 | "declaration": true, 6 | "declarationMap": true, 7 | "outDir": "./lib", 8 | "strict": true, 9 | "noImplicitAny": true, 10 | "noImplicitThis": true, 11 | "noUnusedLocals": true, 12 | "noUnusedParameters": true, 13 | "noImplicitReturns": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "alwaysStrict": true, 16 | "moduleResolution": "node" 17 | }, 18 | "include": [ 19 | "src/**/*.ts" 20 | ] 21 | } 22 | --------------------------------------------------------------------------------