├── .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 |
--------------------------------------------------------------------------------