(.+?)<\/div>/s', $Section, $UrlMatches ) !== 1 )
35 | {
36 | continue;
37 | }
38 |
39 | $Url = htmlspecialchars_decode( $UrlMatches[ 1 ] );
40 | $Url = explode( ' ', trim( preg_replace( '/[\n\r ]+/', ' ', $Url ) ) );
41 |
42 | if( count( $Url ) < 2 )
43 | {
44 | continue;
45 | }
46 |
47 | $UrlPath = parse_url( $Url[ 1 ], PHP_URL_PATH );
48 |
49 | if( empty( $UrlPath ) )
50 | {
51 | continue;
52 | }
53 |
54 | $Parameters = [];
55 | $HttpMethod = $Url[ 0 ];
56 | $HttpPath = explode( '/', $UrlPath );
57 |
58 | if( count( $HttpPath ) < 4 )
59 | {
60 | continue;
61 | }
62 |
63 | $ApiService = $HttpPath[ 1 ];
64 | $ApiMethod = $HttpPath[ 2 ];
65 | $ApiVersion = (int)str_replace( 'v', '', $HttpPath[ 3 ] );
66 |
67 | $Method =
68 | [
69 | 'name' => $ApiMethod,
70 | 'version' => $ApiVersion,
71 | 'httpmethod' => $HttpMethod,
72 | 'parameters' => [],
73 | ];
74 |
75 | $Section = explode( '', $Section, 2 ); // Can be multiple tables (like response)
76 |
77 | preg_match_all( '/
(.*?)<\/tr>/s', $Section[ 0 ], $Matches );
78 |
79 | foreach( $Matches[ 1 ] as $ParamsHtml )
80 | {
81 | if( preg_match_all( '/(.*?)<\/td>/s', $ParamsHtml, $ParamsMatches ) === 0 )
82 | {
83 | continue;
84 | }
85 |
86 | $Method[ 'parameters' ][] =
87 | [
88 | 'name' => trim( strip_tags( $ParamsMatches[ 1 ][ 0 ] ) ),
89 | 'type' => trim( strip_tags( $ParamsMatches[ 1 ][ 1 ] ) ),
90 | 'optional' => $ParamsMatches[ 1 ][ 2 ] !== '✔',
91 | 'description' => trim( strip_tags( $ParamsMatches[ 1 ][ 3 ] ) ),
92 | ];
93 | }
94 |
95 | if( !isset( $Methods[ $ApiService ] ) )
96 | {
97 | $Methods[ $ApiService ] = [];
98 | }
99 |
100 | $Methods[ $ApiService ][] = $Method;
101 | }
102 | }
103 |
104 | $FoundServices = [];
105 |
106 | foreach( $Methods as $ServiceName => $FoundMethods )
107 | {
108 | $FoundServices[] =
109 | [
110 | 'name' => $ServiceName,
111 | 'methods' => $FoundMethods,
112 | ];
113 | }
114 |
115 | file_put_contents(
116 | __DIR__ . DIRECTORY_SEPARATOR . 'api_from_docs.json',
117 | json_encode( $FoundServices, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ) . PHP_EOL
118 | );
119 |
--------------------------------------------------------------------------------
/generate_api_from_protos.php:
--------------------------------------------------------------------------------
1 | isFile() )
45 | {
46 | echo $fileInfo . ' does not exist' . PHP_EOL;
47 | return '';
48 | }
49 |
50 | $proto = file_get_contents( $fileInfo );
51 |
52 | if( $proto === false )
53 | {
54 | throw new Exception( "Failed to read $fileInfo" );
55 | }
56 |
57 | return preg_replace_callback( '/^import "(?.+\.proto)";/m', function( array $matches ) use ( $fileInfo ) : string
58 | {
59 | $path = $fileInfo->getPath() . '/';
60 |
61 | if( str_starts_with( $matches[ 'file' ], 'google/' ) )
62 | {
63 | $path .= '../' . $matches[ 'file' ];
64 | }
65 | else if( str_starts_with( $matches[ 'file' ], 'steammessages' ) && str_contains( $path, 'webui' ) )
66 | {
67 | $path .= '../steam/' . $matches[ 'file' ];
68 | }
69 | else
70 | {
71 | $path .= $matches[ 'file' ];
72 | }
73 |
74 | return ProcessFile( new SplFileInfo( $path ) );
75 | }, $proto ) ?? '';
76 | }
77 |
78 | function ParseTypeToRequestParameters( string $request, string $proto, int $level = 0 )
79 | {
80 | if( !preg_match( "/message " . $request . " {(.+?)^}/ms", $proto, $message ) )
81 | {
82 | echo $request . ' not found' . PHP_EOL;
83 | return [];
84 | }
85 |
86 | $parameters = [];
87 |
88 | if( preg_match_all(
89 | "/^\t(?optional|repeated|required) (?[\w\.]+) (?\w+).+?(?:\(description\) = \"(?.+?)\".+?)?;$/m",
90 | $message[ 1 ] ?? '',
91 | $fields
92 | ) > 0 )
93 | {
94 | for( $y = 0; $y < count( $fields[ 0 ] ); $y++ )
95 | {
96 | $name = $fields[ 'name' ][ $y ];
97 | $extra = null;
98 |
99 | if( strpos( $fields[ 'type' ][ $y ], '.' ) !== false )
100 | {
101 | $type = substr( $fields[ 'type' ][ $y ], 1 );
102 | // TODO: Support inner types with dots in middle
103 |
104 | if( $type !== $request && $level < 5 )
105 | {
106 | $extra = ParseTypeToRequestParameters( $type, $proto, $level + 1 );
107 | }
108 | }
109 | else
110 | {
111 | $type = $fields[ 'type' ][ $y ];
112 | }
113 |
114 | if( $fields[ 'rule' ][ $y ] === 'repeated' )
115 | {
116 | $name .= '[0]';
117 | $type .= '[]';
118 | }
119 |
120 | if( !empty( $parameters[ $name ] ) )
121 | {
122 | if( empty( $parameters[ $name ][ 'description' ] ) )
123 | {
124 | $parameters[ $name ][ 'description' ] = trim( $fields[ 'description' ][ $y ] );
125 | }
126 |
127 | continue;
128 | }
129 |
130 | $parameters[ $name ] =
131 | [
132 | 'name' => $name,
133 | 'type' => $type,
134 | 'optional' => true,
135 | 'description' => trim( $fields[ 'description' ][ $y ] ),
136 | ];
137 |
138 | if( !empty( $extra ) )
139 | {
140 | $parameters[ $name ][ 'extra' ] = array_values( $extra );
141 | }
142 | }
143 | }
144 |
145 | return $parameters;
146 | }
147 |
148 | foreach( $allProtos as $fileInfo )
149 | {
150 | if( strpos( $fileInfo, '.git' ) !== false || $fileInfo->getExtension() !== 'proto' )
151 | {
152 | continue;
153 | }
154 |
155 | $proto = ProcessFile( $fileInfo );
156 |
157 | preg_match_all( "/service (.+?)\s{/", $proto, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE );
158 |
159 | $matches[] =
160 | [
161 | [],
162 | [
163 | '',
164 | strlen( $proto )
165 | ]
166 | ];
167 |
168 | for( $i = count( $matches ) - 1; $i > 0; $i-- )
169 | {
170 | $serviceName = $matches[ $i - 1 ][ 1 ][ 0 ];
171 |
172 | if( $serviceName === 'RemoteClientSteamClient' )
173 | {
174 | $serviceName = 'RemoteClient';
175 | }
176 |
177 | $serviceName = 'I' . $serviceName . 'Service';
178 |
179 | $service = substr( $proto, $matches[ $i - 1 ][ 1 ][ 1 ], $matches[ $i ][ 1 ][ 1 ] - $matches[ $i - 1 ][ 1 ][ 1 ] );
180 |
181 | preg_match_all( "/rpc (.+?) \(\.?(.+?)\) returns \(\.?(?:.+?)\)\s*(?:;|{}|\{\s*option \(method_description\) = \"(.+?)\";)$/m", $service, $rpcs );
182 |
183 | $generatedMethods = [];
184 |
185 | for( $x = 0; $x < count( $rpcs[ 0 ] ); $x++ )
186 | {
187 | $methodName = $rpcs[ 1 ][ $x ];
188 | $request = $rpcs[ 2 ][ $x ];
189 |
190 | $methodPath = $serviceName . '/' . $methodName;
191 |
192 | if( empty( $methodDescriptions[ $methodPath ] ) )
193 | {
194 | $methodDescriptions[ $methodPath ] = trim( $rpcs[ 3 ][ $x ] );
195 | }
196 |
197 | if( empty( $methodParameters[ $methodPath ] ) )
198 | {
199 | $methodParameters[ $methodPath ] =
200 | [
201 | 'key' =>
202 | [
203 | "name" => "key",
204 | "type" => "string",
205 | "optional" => false,
206 | "description" => "Access key",
207 | ]
208 | ];
209 | }
210 |
211 | $methodParameters[ $methodPath ] += ParseTypeToRequestParameters( $request, $proto );
212 |
213 | $generatedMethods[ $methodName ] = true;
214 | }
215 |
216 | if( empty( $generatedServices[ $serviceName ] ) )
217 | {
218 | $generatedServices[ $serviceName ] = [];
219 | }
220 |
221 | $generatedServices[ $serviceName ] += $generatedMethods;
222 | }
223 | }
224 |
225 | $c = curl_init( );
226 |
227 | curl_setopt_array( $c, [
228 | CURLOPT_USERAGENT => '',
229 | CURLOPT_RETURNTRANSFER => true,
230 | CURLOPT_TIMEOUT => 5,
231 | CURLOPT_CONNECTTIMEOUT => 5,
232 | CURLOPT_URL => 'https://api.steampowered.com/ISteamWebAPIUtil/GetSupportedAPIList/v1/?format=json&key=' . $PublisherApiKey,
233 | ] );
234 |
235 | $knownServices = [];
236 |
237 | $response = curl_exec( $c );
238 | $response = json_decode( $response, true, 512, JSON_THROW_ON_ERROR );
239 |
240 | foreach( $response[ 'apilist' ][ 'interfaces' ] as $service )
241 | {
242 | foreach( $service[ 'methods' ] as $method )
243 | {
244 | $knownServices[ $service[ 'name' ] . '/' . $method[ 'name' ] ] = true;
245 | }
246 | }
247 |
248 | $notFound = "\n\n404 Not Found \n\n\nNot Found \n\n";
249 | $notFoundNoInterface = "Not Found Not Found Interface '%s' not found";
250 | $notFoundMethodInInterface = "Not Found Not Found Method '%s' not found in interface '%s'";
251 | $mustBePost = "Method Not Allowed Method Not Allowed This API must be called with a HTTP POST request";
252 |
253 | $foundServices = [];
254 |
255 | ksort( $generatedServices );
256 |
257 | foreach( $generatedServices as $serviceName => $methods )
258 | {
259 | ksort( $methods );
260 |
261 | $foundMethods = [];
262 |
263 | foreach( $methods as $methodName => $trash )
264 | {
265 | $path = $serviceName . '/' . $methodName;
266 |
267 | if( isset( $knownServices[ $path ] ) )
268 | {
269 | continue;
270 | }
271 |
272 | echo 'Checking ' . $path . '...';
273 |
274 | curl_setopt( $c, CURLOPT_URL, "https://api.steampowered.com/{$path}/v1/" );
275 | $response = curl_exec( $c );
276 |
277 | if( $response === $notFound )
278 | {
279 | echo " \033[1;31mnothing\033[0m" . PHP_EOL;
280 | continue;
281 | }
282 |
283 | if( $response === sprintf( $notFoundNoInterface, $serviceName ) )
284 | {
285 | echo " \033[1;33minterface not found\033[0m" . PHP_EOL;
286 | continue;
287 | }
288 |
289 | if( $response === sprintf( $notFoundMethodInInterface, $methodName, $serviceName ) )
290 | {
291 | echo " \033[1;31mmethod not found\033[0m" . PHP_EOL;
292 | continue;
293 | }
294 |
295 | echo " \033[0;32mFOUND!\033[0m" . PHP_EOL;
296 |
297 | $method =
298 | [
299 | 'name' => $methodName,
300 | 'version' => 1,
301 | ];
302 |
303 | if( $response === $mustBePost )
304 | {
305 | $method[ 'httpmethod' ] = 'POST';
306 | }
307 |
308 | if( !empty( $methodDescriptions[ $path ] ) )
309 | {
310 | $method[ 'description' ] = $methodDescriptions[ $path ];
311 | }
312 |
313 | if( !empty( $methodParameters[ $path ] ) )
314 | {
315 | $method[ 'parameters' ] = array_values( $methodParameters[ $path ] );
316 | }
317 |
318 | $foundMethods[] = $method;
319 | }
320 |
321 | if( empty( $foundMethods ) )
322 | {
323 | continue;
324 | }
325 |
326 | $foundServices[] =
327 | [
328 | 'name' => $serviceName,
329 | 'methods' => $foundMethods,
330 | ];
331 | }
332 |
333 | curl_close( $c );
334 |
335 | file_put_contents(
336 | __DIR__ . DIRECTORY_SEPARATOR . 'api_from_protos.json',
337 | json_encode( $foundServices, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ) . PHP_EOL
338 | );
339 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "SteamWebAPIDocumentation",
3 | "lockfileVersion": 3,
4 | "requires": true,
5 | "packages": {
6 | "": {
7 | "devDependencies": {
8 | "@biomejs/biome": "^2.0.0-beta.1",
9 | "@chialab/esbuild-plugin-html": "^0.18.2",
10 | "@vue/tsconfig": "^0.7.0",
11 | "bootstrap": "^5.2.2",
12 | "esbuild": "^0.25.0",
13 | "esbuild-plugin-vue3": "^0.4.2",
14 | "htmlnano": "^2.1.1",
15 | "typescript": "^5.2.2",
16 | "vue": "^3.2.40"
17 | }
18 | },
19 | "node_modules/@babel/code-frame": {
20 | "version": "7.27.1",
21 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
22 | "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
23 | "dev": true,
24 | "license": "MIT",
25 | "dependencies": {
26 | "@babel/helper-validator-identifier": "^7.27.1",
27 | "js-tokens": "^4.0.0",
28 | "picocolors": "^1.1.1"
29 | },
30 | "engines": {
31 | "node": ">=6.9.0"
32 | }
33 | },
34 | "node_modules/@babel/helper-string-parser": {
35 | "version": "7.27.1",
36 | "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
37 | "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
38 | "dev": true,
39 | "license": "MIT",
40 | "engines": {
41 | "node": ">=6.9.0"
42 | }
43 | },
44 | "node_modules/@babel/helper-validator-identifier": {
45 | "version": "7.27.1",
46 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
47 | "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
48 | "dev": true,
49 | "license": "MIT",
50 | "engines": {
51 | "node": ">=6.9.0"
52 | }
53 | },
54 | "node_modules/@babel/parser": {
55 | "version": "7.27.5",
56 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz",
57 | "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==",
58 | "dev": true,
59 | "license": "MIT",
60 | "dependencies": {
61 | "@babel/types": "^7.27.3"
62 | },
63 | "bin": {
64 | "parser": "bin/babel-parser.js"
65 | },
66 | "engines": {
67 | "node": ">=6.0.0"
68 | }
69 | },
70 | "node_modules/@babel/types": {
71 | "version": "7.27.6",
72 | "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz",
73 | "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==",
74 | "dev": true,
75 | "license": "MIT",
76 | "dependencies": {
77 | "@babel/helper-string-parser": "^7.27.1",
78 | "@babel/helper-validator-identifier": "^7.27.1"
79 | },
80 | "engines": {
81 | "node": ">=6.9.0"
82 | }
83 | },
84 | "node_modules/@biomejs/biome": {
85 | "version": "2.0.4",
86 | "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.0.4.tgz",
87 | "integrity": "sha512-DNA++xe+E7UugTvI/HhzSFl6OwrVgU8SIV0Mb2fPtWPk2/oTr4eOSA5xy1JECrvgJeYxurmUBOS49qxv/OUkrQ==",
88 | "dev": true,
89 | "license": "MIT OR Apache-2.0",
90 | "bin": {
91 | "biome": "bin/biome"
92 | },
93 | "engines": {
94 | "node": ">=14.21.3"
95 | },
96 | "funding": {
97 | "type": "opencollective",
98 | "url": "https://opencollective.com/biome"
99 | },
100 | "optionalDependencies": {
101 | "@biomejs/cli-darwin-arm64": "2.0.4",
102 | "@biomejs/cli-darwin-x64": "2.0.4",
103 | "@biomejs/cli-linux-arm64": "2.0.4",
104 | "@biomejs/cli-linux-arm64-musl": "2.0.4",
105 | "@biomejs/cli-linux-x64": "2.0.4",
106 | "@biomejs/cli-linux-x64-musl": "2.0.4",
107 | "@biomejs/cli-win32-arm64": "2.0.4",
108 | "@biomejs/cli-win32-x64": "2.0.4"
109 | }
110 | },
111 | "node_modules/@biomejs/cli-darwin-arm64": {
112 | "version": "2.0.4",
113 | "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.0.4.tgz",
114 | "integrity": "sha512-r5McIUMMiedwJ2rltuXhj0+w0W7IJLpkOS+OGCVZQQOOcrGY9gUSUmOo7O6Z7P0vlv5YYZkPbi+qR9MDDWRBSw==",
115 | "cpu": [
116 | "arm64"
117 | ],
118 | "dev": true,
119 | "license": "MIT OR Apache-2.0",
120 | "optional": true,
121 | "os": [
122 | "darwin"
123 | ],
124 | "engines": {
125 | "node": ">=14.21.3"
126 | }
127 | },
128 | "node_modules/@biomejs/cli-darwin-x64": {
129 | "version": "2.0.4",
130 | "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.0.4.tgz",
131 | "integrity": "sha512-aV5Zc/3E3aXFbrjK1IgCMEQc+6PCkBL+NS+vtjoNM2VPFeM5OL5Q82BI4YZyPnebj+k42BPIoYtz0jJ95PGRRg==",
132 | "cpu": [
133 | "x64"
134 | ],
135 | "dev": true,
136 | "license": "MIT OR Apache-2.0",
137 | "optional": true,
138 | "os": [
139 | "darwin"
140 | ],
141 | "engines": {
142 | "node": ">=14.21.3"
143 | }
144 | },
145 | "node_modules/@biomejs/cli-linux-arm64": {
146 | "version": "2.0.4",
147 | "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.0.4.tgz",
148 | "integrity": "sha512-nlJhf7DyuajMj+S7Ygum59cbrHvI/nSRvedfJcEIx4X7SsiZjpRUiC5XtEn77kg7NIKq/KqG5roQIHkmjuFHCw==",
149 | "cpu": [
150 | "arm64"
151 | ],
152 | "dev": true,
153 | "license": "MIT OR Apache-2.0",
154 | "optional": true,
155 | "os": [
156 | "linux"
157 | ],
158 | "engines": {
159 | "node": ">=14.21.3"
160 | }
161 | },
162 | "node_modules/@biomejs/cli-linux-arm64-musl": {
163 | "version": "2.0.4",
164 | "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.0.4.tgz",
165 | "integrity": "sha512-cNukq2PthoOa7quqaKoEFz4Zd1pDPJGfTR5jVyk9Z9iFHEm6TI7+7eeIs3aYcEuuJPNFR9xhJ4Uj3E2iUWkV3A==",
166 | "cpu": [
167 | "arm64"
168 | ],
169 | "dev": true,
170 | "license": "MIT OR Apache-2.0",
171 | "optional": true,
172 | "os": [
173 | "linux"
174 | ],
175 | "engines": {
176 | "node": ">=14.21.3"
177 | }
178 | },
179 | "node_modules/@biomejs/cli-linux-x64": {
180 | "version": "2.0.4",
181 | "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.0.4.tgz",
182 | "integrity": "sha512-jlzrNZ+OzN9wvp2RL3cl5Y4NiV7xSU+QV5A8bWXke1on3jKy7QbXajybSjVQ6aFw1gdrqkO/W8xV5HODhIMT4g==",
183 | "cpu": [
184 | "x64"
185 | ],
186 | "dev": true,
187 | "license": "MIT OR Apache-2.0",
188 | "optional": true,
189 | "os": [
190 | "linux"
191 | ],
192 | "engines": {
193 | "node": ">=14.21.3"
194 | }
195 | },
196 | "node_modules/@biomejs/cli-linux-x64-musl": {
197 | "version": "2.0.4",
198 | "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.0.4.tgz",
199 | "integrity": "sha512-oWQALSbp8xF0t/wiHU2zdkZOpIHyaI9QxQv0Ytty9GAKsCGP6pczp8qyKD/P49iGJdDozHp5KiuQPxs33APhyA==",
200 | "cpu": [
201 | "x64"
202 | ],
203 | "dev": true,
204 | "license": "MIT OR Apache-2.0",
205 | "optional": true,
206 | "os": [
207 | "linux"
208 | ],
209 | "engines": {
210 | "node": ">=14.21.3"
211 | }
212 | },
213 | "node_modules/@biomejs/cli-win32-arm64": {
214 | "version": "2.0.4",
215 | "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.0.4.tgz",
216 | "integrity": "sha512-/PbNhMJo9ONja7hOxLlifM/qgeHpRD9bF2flTz5KIrXnQqpuegaRuwP/HYdJ9TFkTKFjHkPLoE4onOz3HIT5CQ==",
217 | "cpu": [
218 | "arm64"
219 | ],
220 | "dev": true,
221 | "license": "MIT OR Apache-2.0",
222 | "optional": true,
223 | "os": [
224 | "win32"
225 | ],
226 | "engines": {
227 | "node": ">=14.21.3"
228 | }
229 | },
230 | "node_modules/@biomejs/cli-win32-x64": {
231 | "version": "2.0.4",
232 | "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.0.4.tgz",
233 | "integrity": "sha512-dIM4SgO4/Rmsb4X7fwKtciQ682SZDSC1lm42uSM9gt8zNqBIeTaqsMc6eO1DpxYWMlAb/n2SML9+HUHmCib7NA==",
234 | "cpu": [
235 | "x64"
236 | ],
237 | "dev": true,
238 | "license": "MIT OR Apache-2.0",
239 | "optional": true,
240 | "os": [
241 | "win32"
242 | ],
243 | "engines": {
244 | "node": ">=14.21.3"
245 | }
246 | },
247 | "node_modules/@chialab/esbuild-plugin-html": {
248 | "version": "0.18.2",
249 | "resolved": "https://registry.npmjs.org/@chialab/esbuild-plugin-html/-/esbuild-plugin-html-0.18.2.tgz",
250 | "integrity": "sha512-8dBqIZIL1tkLYyMko+HY8b80QoDOIGlydbIQO8ALLv4e59UO0MGQjFG3RCD0V5sMbpjeNrJ3MCKz29wGL16Wrg==",
251 | "dev": true,
252 | "license": "MIT",
253 | "dependencies": {
254 | "@chialab/esbuild-rna": "^0.18.1"
255 | },
256 | "engines": {
257 | "node": ">=18"
258 | },
259 | "peerDependencies": {
260 | "htmlnano": "^2.0.0"
261 | },
262 | "peerDependenciesMeta": {
263 | "htmlnano": {
264 | "optional": true
265 | }
266 | }
267 | },
268 | "node_modules/@chialab/esbuild-rna": {
269 | "version": "0.18.2",
270 | "resolved": "https://registry.npmjs.org/@chialab/esbuild-rna/-/esbuild-rna-0.18.2.tgz",
271 | "integrity": "sha512-ckzskez7bxstVQ4c5cxbx0DRP2teldzrcSGQl2KPh1VJGdO2ZmRrb6vNkBBD5K3dx9tgTyvskWp4dV+Fbg07Ag==",
272 | "dev": true,
273 | "license": "MIT",
274 | "dependencies": {
275 | "@chialab/estransform": "^0.18.0",
276 | "@chialab/node-resolve": "^0.18.0"
277 | },
278 | "engines": {
279 | "node": ">=18"
280 | }
281 | },
282 | "node_modules/@chialab/estransform": {
283 | "version": "0.18.1",
284 | "resolved": "https://registry.npmjs.org/@chialab/estransform/-/estransform-0.18.1.tgz",
285 | "integrity": "sha512-W/WmjpQL2hndD0/XfR0FcPBAUj+aLNeoAVehOjV/Q9bSnioz0GVSAXXhzp59S33ZynxJBBfn8DNiMTVNJmk4Aw==",
286 | "dev": true,
287 | "license": "MIT",
288 | "dependencies": {
289 | "@parcel/source-map": "^2.0.0"
290 | },
291 | "engines": {
292 | "node": ">=18"
293 | }
294 | },
295 | "node_modules/@chialab/node-resolve": {
296 | "version": "0.18.0",
297 | "resolved": "https://registry.npmjs.org/@chialab/node-resolve/-/node-resolve-0.18.0.tgz",
298 | "integrity": "sha512-eV1m70Qn9pLY9xwFmZ2FlcOzwiaUywsJ7NB/ud8VB7DouvCQtIHkQ3Om7uPX0ojXGEG1LCyO96kZkvbNTxNu0Q==",
299 | "dev": true,
300 | "license": "MIT",
301 | "engines": {
302 | "node": ">=18"
303 | }
304 | },
305 | "node_modules/@esbuild/aix-ppc64": {
306 | "version": "0.25.5",
307 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
308 | "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
309 | "cpu": [
310 | "ppc64"
311 | ],
312 | "dev": true,
313 | "license": "MIT",
314 | "optional": true,
315 | "os": [
316 | "aix"
317 | ],
318 | "engines": {
319 | "node": ">=18"
320 | }
321 | },
322 | "node_modules/@esbuild/android-arm": {
323 | "version": "0.25.5",
324 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
325 | "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
326 | "cpu": [
327 | "arm"
328 | ],
329 | "dev": true,
330 | "license": "MIT",
331 | "optional": true,
332 | "os": [
333 | "android"
334 | ],
335 | "engines": {
336 | "node": ">=18"
337 | }
338 | },
339 | "node_modules/@esbuild/android-arm64": {
340 | "version": "0.25.5",
341 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz",
342 | "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
343 | "cpu": [
344 | "arm64"
345 | ],
346 | "dev": true,
347 | "license": "MIT",
348 | "optional": true,
349 | "os": [
350 | "android"
351 | ],
352 | "engines": {
353 | "node": ">=18"
354 | }
355 | },
356 | "node_modules/@esbuild/android-x64": {
357 | "version": "0.25.5",
358 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz",
359 | "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
360 | "cpu": [
361 | "x64"
362 | ],
363 | "dev": true,
364 | "license": "MIT",
365 | "optional": true,
366 | "os": [
367 | "android"
368 | ],
369 | "engines": {
370 | "node": ">=18"
371 | }
372 | },
373 | "node_modules/@esbuild/darwin-arm64": {
374 | "version": "0.25.5",
375 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
376 | "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
377 | "cpu": [
378 | "arm64"
379 | ],
380 | "dev": true,
381 | "license": "MIT",
382 | "optional": true,
383 | "os": [
384 | "darwin"
385 | ],
386 | "engines": {
387 | "node": ">=18"
388 | }
389 | },
390 | "node_modules/@esbuild/darwin-x64": {
391 | "version": "0.25.5",
392 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz",
393 | "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
394 | "cpu": [
395 | "x64"
396 | ],
397 | "dev": true,
398 | "license": "MIT",
399 | "optional": true,
400 | "os": [
401 | "darwin"
402 | ],
403 | "engines": {
404 | "node": ">=18"
405 | }
406 | },
407 | "node_modules/@esbuild/freebsd-arm64": {
408 | "version": "0.25.5",
409 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz",
410 | "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
411 | "cpu": [
412 | "arm64"
413 | ],
414 | "dev": true,
415 | "license": "MIT",
416 | "optional": true,
417 | "os": [
418 | "freebsd"
419 | ],
420 | "engines": {
421 | "node": ">=18"
422 | }
423 | },
424 | "node_modules/@esbuild/freebsd-x64": {
425 | "version": "0.25.5",
426 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz",
427 | "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
428 | "cpu": [
429 | "x64"
430 | ],
431 | "dev": true,
432 | "license": "MIT",
433 | "optional": true,
434 | "os": [
435 | "freebsd"
436 | ],
437 | "engines": {
438 | "node": ">=18"
439 | }
440 | },
441 | "node_modules/@esbuild/linux-arm": {
442 | "version": "0.25.5",
443 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
444 | "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
445 | "cpu": [
446 | "arm"
447 | ],
448 | "dev": true,
449 | "license": "MIT",
450 | "optional": true,
451 | "os": [
452 | "linux"
453 | ],
454 | "engines": {
455 | "node": ">=18"
456 | }
457 | },
458 | "node_modules/@esbuild/linux-arm64": {
459 | "version": "0.25.5",
460 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz",
461 | "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
462 | "cpu": [
463 | "arm64"
464 | ],
465 | "dev": true,
466 | "license": "MIT",
467 | "optional": true,
468 | "os": [
469 | "linux"
470 | ],
471 | "engines": {
472 | "node": ">=18"
473 | }
474 | },
475 | "node_modules/@esbuild/linux-ia32": {
476 | "version": "0.25.5",
477 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz",
478 | "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
479 | "cpu": [
480 | "ia32"
481 | ],
482 | "dev": true,
483 | "license": "MIT",
484 | "optional": true,
485 | "os": [
486 | "linux"
487 | ],
488 | "engines": {
489 | "node": ">=18"
490 | }
491 | },
492 | "node_modules/@esbuild/linux-loong64": {
493 | "version": "0.25.5",
494 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
495 | "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
496 | "cpu": [
497 | "loong64"
498 | ],
499 | "dev": true,
500 | "license": "MIT",
501 | "optional": true,
502 | "os": [
503 | "linux"
504 | ],
505 | "engines": {
506 | "node": ">=18"
507 | }
508 | },
509 | "node_modules/@esbuild/linux-mips64el": {
510 | "version": "0.25.5",
511 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz",
512 | "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
513 | "cpu": [
514 | "mips64el"
515 | ],
516 | "dev": true,
517 | "license": "MIT",
518 | "optional": true,
519 | "os": [
520 | "linux"
521 | ],
522 | "engines": {
523 | "node": ">=18"
524 | }
525 | },
526 | "node_modules/@esbuild/linux-ppc64": {
527 | "version": "0.25.5",
528 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz",
529 | "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
530 | "cpu": [
531 | "ppc64"
532 | ],
533 | "dev": true,
534 | "license": "MIT",
535 | "optional": true,
536 | "os": [
537 | "linux"
538 | ],
539 | "engines": {
540 | "node": ">=18"
541 | }
542 | },
543 | "node_modules/@esbuild/linux-riscv64": {
544 | "version": "0.25.5",
545 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz",
546 | "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
547 | "cpu": [
548 | "riscv64"
549 | ],
550 | "dev": true,
551 | "license": "MIT",
552 | "optional": true,
553 | "os": [
554 | "linux"
555 | ],
556 | "engines": {
557 | "node": ">=18"
558 | }
559 | },
560 | "node_modules/@esbuild/linux-s390x": {
561 | "version": "0.25.5",
562 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
563 | "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
564 | "cpu": [
565 | "s390x"
566 | ],
567 | "dev": true,
568 | "license": "MIT",
569 | "optional": true,
570 | "os": [
571 | "linux"
572 | ],
573 | "engines": {
574 | "node": ">=18"
575 | }
576 | },
577 | "node_modules/@esbuild/linux-x64": {
578 | "version": "0.25.5",
579 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
580 | "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
581 | "cpu": [
582 | "x64"
583 | ],
584 | "dev": true,
585 | "license": "MIT",
586 | "optional": true,
587 | "os": [
588 | "linux"
589 | ],
590 | "engines": {
591 | "node": ">=18"
592 | }
593 | },
594 | "node_modules/@esbuild/netbsd-arm64": {
595 | "version": "0.25.5",
596 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz",
597 | "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
598 | "cpu": [
599 | "arm64"
600 | ],
601 | "dev": true,
602 | "license": "MIT",
603 | "optional": true,
604 | "os": [
605 | "netbsd"
606 | ],
607 | "engines": {
608 | "node": ">=18"
609 | }
610 | },
611 | "node_modules/@esbuild/netbsd-x64": {
612 | "version": "0.25.5",
613 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz",
614 | "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
615 | "cpu": [
616 | "x64"
617 | ],
618 | "dev": true,
619 | "license": "MIT",
620 | "optional": true,
621 | "os": [
622 | "netbsd"
623 | ],
624 | "engines": {
625 | "node": ">=18"
626 | }
627 | },
628 | "node_modules/@esbuild/openbsd-arm64": {
629 | "version": "0.25.5",
630 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz",
631 | "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
632 | "cpu": [
633 | "arm64"
634 | ],
635 | "dev": true,
636 | "license": "MIT",
637 | "optional": true,
638 | "os": [
639 | "openbsd"
640 | ],
641 | "engines": {
642 | "node": ">=18"
643 | }
644 | },
645 | "node_modules/@esbuild/openbsd-x64": {
646 | "version": "0.25.5",
647 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz",
648 | "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
649 | "cpu": [
650 | "x64"
651 | ],
652 | "dev": true,
653 | "license": "MIT",
654 | "optional": true,
655 | "os": [
656 | "openbsd"
657 | ],
658 | "engines": {
659 | "node": ">=18"
660 | }
661 | },
662 | "node_modules/@esbuild/sunos-x64": {
663 | "version": "0.25.5",
664 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz",
665 | "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
666 | "cpu": [
667 | "x64"
668 | ],
669 | "dev": true,
670 | "license": "MIT",
671 | "optional": true,
672 | "os": [
673 | "sunos"
674 | ],
675 | "engines": {
676 | "node": ">=18"
677 | }
678 | },
679 | "node_modules/@esbuild/win32-arm64": {
680 | "version": "0.25.5",
681 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz",
682 | "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
683 | "cpu": [
684 | "arm64"
685 | ],
686 | "dev": true,
687 | "license": "MIT",
688 | "optional": true,
689 | "os": [
690 | "win32"
691 | ],
692 | "engines": {
693 | "node": ">=18"
694 | }
695 | },
696 | "node_modules/@esbuild/win32-ia32": {
697 | "version": "0.25.5",
698 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
699 | "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
700 | "cpu": [
701 | "ia32"
702 | ],
703 | "dev": true,
704 | "license": "MIT",
705 | "optional": true,
706 | "os": [
707 | "win32"
708 | ],
709 | "engines": {
710 | "node": ">=18"
711 | }
712 | },
713 | "node_modules/@esbuild/win32-x64": {
714 | "version": "0.25.5",
715 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz",
716 | "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
717 | "cpu": [
718 | "x64"
719 | ],
720 | "dev": true,
721 | "license": "MIT",
722 | "optional": true,
723 | "os": [
724 | "win32"
725 | ],
726 | "engines": {
727 | "node": ">=18"
728 | }
729 | },
730 | "node_modules/@jridgewell/sourcemap-codec": {
731 | "version": "1.5.0",
732 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
733 | "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
734 | "dev": true,
735 | "license": "MIT"
736 | },
737 | "node_modules/@parcel/source-map": {
738 | "version": "2.1.1",
739 | "resolved": "https://registry.npmjs.org/@parcel/source-map/-/source-map-2.1.1.tgz",
740 | "integrity": "sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew==",
741 | "dev": true,
742 | "license": "MIT",
743 | "dependencies": {
744 | "detect-libc": "^1.0.3"
745 | },
746 | "engines": {
747 | "node": "^12.18.3 || >=14"
748 | }
749 | },
750 | "node_modules/@popperjs/core": {
751 | "version": "2.11.8",
752 | "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
753 | "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
754 | "dev": true,
755 | "license": "MIT",
756 | "peer": true,
757 | "funding": {
758 | "type": "opencollective",
759 | "url": "https://opencollective.com/popperjs"
760 | }
761 | },
762 | "node_modules/@vue/compiler-core": {
763 | "version": "3.5.17",
764 | "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.17.tgz",
765 | "integrity": "sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==",
766 | "dev": true,
767 | "license": "MIT",
768 | "dependencies": {
769 | "@babel/parser": "^7.27.5",
770 | "@vue/shared": "3.5.17",
771 | "entities": "^4.5.0",
772 | "estree-walker": "^2.0.2",
773 | "source-map-js": "^1.2.1"
774 | }
775 | },
776 | "node_modules/@vue/compiler-core/node_modules/entities": {
777 | "version": "4.5.0",
778 | "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
779 | "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
780 | "dev": true,
781 | "license": "BSD-2-Clause",
782 | "engines": {
783 | "node": ">=0.12"
784 | },
785 | "funding": {
786 | "url": "https://github.com/fb55/entities?sponsor=1"
787 | }
788 | },
789 | "node_modules/@vue/compiler-dom": {
790 | "version": "3.5.17",
791 | "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.17.tgz",
792 | "integrity": "sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==",
793 | "dev": true,
794 | "license": "MIT",
795 | "dependencies": {
796 | "@vue/compiler-core": "3.5.17",
797 | "@vue/shared": "3.5.17"
798 | }
799 | },
800 | "node_modules/@vue/compiler-sfc": {
801 | "version": "3.5.17",
802 | "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.17.tgz",
803 | "integrity": "sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==",
804 | "dev": true,
805 | "license": "MIT",
806 | "dependencies": {
807 | "@babel/parser": "^7.27.5",
808 | "@vue/compiler-core": "3.5.17",
809 | "@vue/compiler-dom": "3.5.17",
810 | "@vue/compiler-ssr": "3.5.17",
811 | "@vue/shared": "3.5.17",
812 | "estree-walker": "^2.0.2",
813 | "magic-string": "^0.30.17",
814 | "postcss": "^8.5.6",
815 | "source-map-js": "^1.2.1"
816 | }
817 | },
818 | "node_modules/@vue/compiler-ssr": {
819 | "version": "3.5.17",
820 | "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.17.tgz",
821 | "integrity": "sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==",
822 | "dev": true,
823 | "license": "MIT",
824 | "dependencies": {
825 | "@vue/compiler-dom": "3.5.17",
826 | "@vue/shared": "3.5.17"
827 | }
828 | },
829 | "node_modules/@vue/reactivity": {
830 | "version": "3.5.17",
831 | "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.17.tgz",
832 | "integrity": "sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==",
833 | "dev": true,
834 | "license": "MIT",
835 | "dependencies": {
836 | "@vue/shared": "3.5.17"
837 | }
838 | },
839 | "node_modules/@vue/runtime-core": {
840 | "version": "3.5.17",
841 | "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.17.tgz",
842 | "integrity": "sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==",
843 | "dev": true,
844 | "license": "MIT",
845 | "dependencies": {
846 | "@vue/reactivity": "3.5.17",
847 | "@vue/shared": "3.5.17"
848 | }
849 | },
850 | "node_modules/@vue/runtime-dom": {
851 | "version": "3.5.17",
852 | "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.17.tgz",
853 | "integrity": "sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==",
854 | "dev": true,
855 | "license": "MIT",
856 | "dependencies": {
857 | "@vue/reactivity": "3.5.17",
858 | "@vue/runtime-core": "3.5.17",
859 | "@vue/shared": "3.5.17",
860 | "csstype": "^3.1.3"
861 | }
862 | },
863 | "node_modules/@vue/server-renderer": {
864 | "version": "3.5.17",
865 | "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.17.tgz",
866 | "integrity": "sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==",
867 | "dev": true,
868 | "license": "MIT",
869 | "dependencies": {
870 | "@vue/compiler-ssr": "3.5.17",
871 | "@vue/shared": "3.5.17"
872 | },
873 | "peerDependencies": {
874 | "vue": "3.5.17"
875 | }
876 | },
877 | "node_modules/@vue/shared": {
878 | "version": "3.5.17",
879 | "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.17.tgz",
880 | "integrity": "sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==",
881 | "dev": true,
882 | "license": "MIT"
883 | },
884 | "node_modules/@vue/tsconfig": {
885 | "version": "0.7.0",
886 | "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.7.0.tgz",
887 | "integrity": "sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==",
888 | "dev": true,
889 | "license": "MIT",
890 | "peerDependencies": {
891 | "typescript": "5.x",
892 | "vue": "^3.4.0"
893 | },
894 | "peerDependenciesMeta": {
895 | "typescript": {
896 | "optional": true
897 | },
898 | "vue": {
899 | "optional": true
900 | }
901 | }
902 | },
903 | "node_modules/argparse": {
904 | "version": "2.0.1",
905 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
906 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
907 | "dev": true,
908 | "license": "Python-2.0"
909 | },
910 | "node_modules/bootstrap": {
911 | "version": "5.3.7",
912 | "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.7.tgz",
913 | "integrity": "sha512-7KgiD8UHjfcPBHEpDNg+zGz8L3LqR3GVwqZiBRFX04a1BCArZOz1r2kjly2HQ0WokqTO0v1nF+QAt8dsW4lKlw==",
914 | "dev": true,
915 | "funding": [
916 | {
917 | "type": "github",
918 | "url": "https://github.com/sponsors/twbs"
919 | },
920 | {
921 | "type": "opencollective",
922 | "url": "https://opencollective.com/bootstrap"
923 | }
924 | ],
925 | "license": "MIT",
926 | "peerDependencies": {
927 | "@popperjs/core": "^2.11.8"
928 | }
929 | },
930 | "node_modules/callsites": {
931 | "version": "3.1.0",
932 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
933 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
934 | "dev": true,
935 | "license": "MIT",
936 | "engines": {
937 | "node": ">=6"
938 | }
939 | },
940 | "node_modules/cosmiconfig": {
941 | "version": "9.0.0",
942 | "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
943 | "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
944 | "dev": true,
945 | "license": "MIT",
946 | "dependencies": {
947 | "env-paths": "^2.2.1",
948 | "import-fresh": "^3.3.0",
949 | "js-yaml": "^4.1.0",
950 | "parse-json": "^5.2.0"
951 | },
952 | "engines": {
953 | "node": ">=14"
954 | },
955 | "funding": {
956 | "url": "https://github.com/sponsors/d-fischer"
957 | },
958 | "peerDependencies": {
959 | "typescript": ">=4.9.5"
960 | },
961 | "peerDependenciesMeta": {
962 | "typescript": {
963 | "optional": true
964 | }
965 | }
966 | },
967 | "node_modules/csstype": {
968 | "version": "3.1.3",
969 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
970 | "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
971 | "dev": true,
972 | "license": "MIT"
973 | },
974 | "node_modules/detect-libc": {
975 | "version": "1.0.3",
976 | "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
977 | "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
978 | "dev": true,
979 | "license": "Apache-2.0",
980 | "bin": {
981 | "detect-libc": "bin/detect-libc.js"
982 | },
983 | "engines": {
984 | "node": ">=0.10"
985 | }
986 | },
987 | "node_modules/dom-serializer": {
988 | "version": "1.4.1",
989 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
990 | "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
991 | "dev": true,
992 | "license": "MIT",
993 | "dependencies": {
994 | "domelementtype": "^2.0.1",
995 | "domhandler": "^4.2.0",
996 | "entities": "^2.0.0"
997 | },
998 | "funding": {
999 | "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
1000 | }
1001 | },
1002 | "node_modules/dom-serializer/node_modules/entities": {
1003 | "version": "2.2.0",
1004 | "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
1005 | "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
1006 | "dev": true,
1007 | "license": "BSD-2-Clause",
1008 | "funding": {
1009 | "url": "https://github.com/fb55/entities?sponsor=1"
1010 | }
1011 | },
1012 | "node_modules/domelementtype": {
1013 | "version": "2.3.0",
1014 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
1015 | "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
1016 | "dev": true,
1017 | "funding": [
1018 | {
1019 | "type": "github",
1020 | "url": "https://github.com/sponsors/fb55"
1021 | }
1022 | ],
1023 | "license": "BSD-2-Clause"
1024 | },
1025 | "node_modules/domhandler": {
1026 | "version": "4.3.1",
1027 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
1028 | "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
1029 | "dev": true,
1030 | "license": "BSD-2-Clause",
1031 | "dependencies": {
1032 | "domelementtype": "^2.2.0"
1033 | },
1034 | "engines": {
1035 | "node": ">= 4"
1036 | },
1037 | "funding": {
1038 | "url": "https://github.com/fb55/domhandler?sponsor=1"
1039 | }
1040 | },
1041 | "node_modules/domutils": {
1042 | "version": "2.8.0",
1043 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
1044 | "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
1045 | "dev": true,
1046 | "license": "BSD-2-Clause",
1047 | "dependencies": {
1048 | "dom-serializer": "^1.0.1",
1049 | "domelementtype": "^2.2.0",
1050 | "domhandler": "^4.2.0"
1051 | },
1052 | "funding": {
1053 | "url": "https://github.com/fb55/domutils?sponsor=1"
1054 | }
1055 | },
1056 | "node_modules/entities": {
1057 | "version": "3.0.1",
1058 | "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
1059 | "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
1060 | "dev": true,
1061 | "license": "BSD-2-Clause",
1062 | "engines": {
1063 | "node": ">=0.12"
1064 | },
1065 | "funding": {
1066 | "url": "https://github.com/fb55/entities?sponsor=1"
1067 | }
1068 | },
1069 | "node_modules/env-paths": {
1070 | "version": "2.2.1",
1071 | "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
1072 | "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
1073 | "dev": true,
1074 | "license": "MIT",
1075 | "engines": {
1076 | "node": ">=6"
1077 | }
1078 | },
1079 | "node_modules/error-ex": {
1080 | "version": "1.3.2",
1081 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
1082 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
1083 | "dev": true,
1084 | "license": "MIT",
1085 | "dependencies": {
1086 | "is-arrayish": "^0.2.1"
1087 | }
1088 | },
1089 | "node_modules/esbuild": {
1090 | "version": "0.25.5",
1091 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz",
1092 | "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
1093 | "dev": true,
1094 | "hasInstallScript": true,
1095 | "license": "MIT",
1096 | "bin": {
1097 | "esbuild": "bin/esbuild"
1098 | },
1099 | "engines": {
1100 | "node": ">=18"
1101 | },
1102 | "optionalDependencies": {
1103 | "@esbuild/aix-ppc64": "0.25.5",
1104 | "@esbuild/android-arm": "0.25.5",
1105 | "@esbuild/android-arm64": "0.25.5",
1106 | "@esbuild/android-x64": "0.25.5",
1107 | "@esbuild/darwin-arm64": "0.25.5",
1108 | "@esbuild/darwin-x64": "0.25.5",
1109 | "@esbuild/freebsd-arm64": "0.25.5",
1110 | "@esbuild/freebsd-x64": "0.25.5",
1111 | "@esbuild/linux-arm": "0.25.5",
1112 | "@esbuild/linux-arm64": "0.25.5",
1113 | "@esbuild/linux-ia32": "0.25.5",
1114 | "@esbuild/linux-loong64": "0.25.5",
1115 | "@esbuild/linux-mips64el": "0.25.5",
1116 | "@esbuild/linux-ppc64": "0.25.5",
1117 | "@esbuild/linux-riscv64": "0.25.5",
1118 | "@esbuild/linux-s390x": "0.25.5",
1119 | "@esbuild/linux-x64": "0.25.5",
1120 | "@esbuild/netbsd-arm64": "0.25.5",
1121 | "@esbuild/netbsd-x64": "0.25.5",
1122 | "@esbuild/openbsd-arm64": "0.25.5",
1123 | "@esbuild/openbsd-x64": "0.25.5",
1124 | "@esbuild/sunos-x64": "0.25.5",
1125 | "@esbuild/win32-arm64": "0.25.5",
1126 | "@esbuild/win32-ia32": "0.25.5",
1127 | "@esbuild/win32-x64": "0.25.5"
1128 | }
1129 | },
1130 | "node_modules/esbuild-android-64": {
1131 | "version": "0.14.54",
1132 | "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz",
1133 | "integrity": "sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==",
1134 | "cpu": [
1135 | "x64"
1136 | ],
1137 | "dev": true,
1138 | "license": "MIT",
1139 | "optional": true,
1140 | "os": [
1141 | "android"
1142 | ],
1143 | "engines": {
1144 | "node": ">=12"
1145 | }
1146 | },
1147 | "node_modules/esbuild-android-arm64": {
1148 | "version": "0.14.54",
1149 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz",
1150 | "integrity": "sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==",
1151 | "cpu": [
1152 | "arm64"
1153 | ],
1154 | "dev": true,
1155 | "license": "MIT",
1156 | "optional": true,
1157 | "os": [
1158 | "android"
1159 | ],
1160 | "engines": {
1161 | "node": ">=12"
1162 | }
1163 | },
1164 | "node_modules/esbuild-darwin-64": {
1165 | "version": "0.14.54",
1166 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz",
1167 | "integrity": "sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==",
1168 | "cpu": [
1169 | "x64"
1170 | ],
1171 | "dev": true,
1172 | "license": "MIT",
1173 | "optional": true,
1174 | "os": [
1175 | "darwin"
1176 | ],
1177 | "engines": {
1178 | "node": ">=12"
1179 | }
1180 | },
1181 | "node_modules/esbuild-darwin-arm64": {
1182 | "version": "0.14.54",
1183 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz",
1184 | "integrity": "sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==",
1185 | "cpu": [
1186 | "arm64"
1187 | ],
1188 | "dev": true,
1189 | "license": "MIT",
1190 | "optional": true,
1191 | "os": [
1192 | "darwin"
1193 | ],
1194 | "engines": {
1195 | "node": ">=12"
1196 | }
1197 | },
1198 | "node_modules/esbuild-freebsd-64": {
1199 | "version": "0.14.54",
1200 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz",
1201 | "integrity": "sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==",
1202 | "cpu": [
1203 | "x64"
1204 | ],
1205 | "dev": true,
1206 | "license": "MIT",
1207 | "optional": true,
1208 | "os": [
1209 | "freebsd"
1210 | ],
1211 | "engines": {
1212 | "node": ">=12"
1213 | }
1214 | },
1215 | "node_modules/esbuild-freebsd-arm64": {
1216 | "version": "0.14.54",
1217 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz",
1218 | "integrity": "sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==",
1219 | "cpu": [
1220 | "arm64"
1221 | ],
1222 | "dev": true,
1223 | "license": "MIT",
1224 | "optional": true,
1225 | "os": [
1226 | "freebsd"
1227 | ],
1228 | "engines": {
1229 | "node": ">=12"
1230 | }
1231 | },
1232 | "node_modules/esbuild-linux-32": {
1233 | "version": "0.14.54",
1234 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz",
1235 | "integrity": "sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==",
1236 | "cpu": [
1237 | "ia32"
1238 | ],
1239 | "dev": true,
1240 | "license": "MIT",
1241 | "optional": true,
1242 | "os": [
1243 | "linux"
1244 | ],
1245 | "engines": {
1246 | "node": ">=12"
1247 | }
1248 | },
1249 | "node_modules/esbuild-linux-64": {
1250 | "version": "0.14.54",
1251 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz",
1252 | "integrity": "sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==",
1253 | "cpu": [
1254 | "x64"
1255 | ],
1256 | "dev": true,
1257 | "license": "MIT",
1258 | "optional": true,
1259 | "os": [
1260 | "linux"
1261 | ],
1262 | "engines": {
1263 | "node": ">=12"
1264 | }
1265 | },
1266 | "node_modules/esbuild-linux-arm": {
1267 | "version": "0.14.54",
1268 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz",
1269 | "integrity": "sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==",
1270 | "cpu": [
1271 | "arm"
1272 | ],
1273 | "dev": true,
1274 | "license": "MIT",
1275 | "optional": true,
1276 | "os": [
1277 | "linux"
1278 | ],
1279 | "engines": {
1280 | "node": ">=12"
1281 | }
1282 | },
1283 | "node_modules/esbuild-linux-arm64": {
1284 | "version": "0.14.54",
1285 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz",
1286 | "integrity": "sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==",
1287 | "cpu": [
1288 | "arm64"
1289 | ],
1290 | "dev": true,
1291 | "license": "MIT",
1292 | "optional": true,
1293 | "os": [
1294 | "linux"
1295 | ],
1296 | "engines": {
1297 | "node": ">=12"
1298 | }
1299 | },
1300 | "node_modules/esbuild-linux-mips64le": {
1301 | "version": "0.14.54",
1302 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz",
1303 | "integrity": "sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==",
1304 | "cpu": [
1305 | "mips64el"
1306 | ],
1307 | "dev": true,
1308 | "license": "MIT",
1309 | "optional": true,
1310 | "os": [
1311 | "linux"
1312 | ],
1313 | "engines": {
1314 | "node": ">=12"
1315 | }
1316 | },
1317 | "node_modules/esbuild-linux-ppc64le": {
1318 | "version": "0.14.54",
1319 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz",
1320 | "integrity": "sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==",
1321 | "cpu": [
1322 | "ppc64"
1323 | ],
1324 | "dev": true,
1325 | "license": "MIT",
1326 | "optional": true,
1327 | "os": [
1328 | "linux"
1329 | ],
1330 | "engines": {
1331 | "node": ">=12"
1332 | }
1333 | },
1334 | "node_modules/esbuild-linux-riscv64": {
1335 | "version": "0.14.54",
1336 | "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz",
1337 | "integrity": "sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==",
1338 | "cpu": [
1339 | "riscv64"
1340 | ],
1341 | "dev": true,
1342 | "license": "MIT",
1343 | "optional": true,
1344 | "os": [
1345 | "linux"
1346 | ],
1347 | "engines": {
1348 | "node": ">=12"
1349 | }
1350 | },
1351 | "node_modules/esbuild-linux-s390x": {
1352 | "version": "0.14.54",
1353 | "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz",
1354 | "integrity": "sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==",
1355 | "cpu": [
1356 | "s390x"
1357 | ],
1358 | "dev": true,
1359 | "license": "MIT",
1360 | "optional": true,
1361 | "os": [
1362 | "linux"
1363 | ],
1364 | "engines": {
1365 | "node": ">=12"
1366 | }
1367 | },
1368 | "node_modules/esbuild-netbsd-64": {
1369 | "version": "0.14.54",
1370 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz",
1371 | "integrity": "sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==",
1372 | "cpu": [
1373 | "x64"
1374 | ],
1375 | "dev": true,
1376 | "license": "MIT",
1377 | "optional": true,
1378 | "os": [
1379 | "netbsd"
1380 | ],
1381 | "engines": {
1382 | "node": ">=12"
1383 | }
1384 | },
1385 | "node_modules/esbuild-openbsd-64": {
1386 | "version": "0.14.54",
1387 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz",
1388 | "integrity": "sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==",
1389 | "cpu": [
1390 | "x64"
1391 | ],
1392 | "dev": true,
1393 | "license": "MIT",
1394 | "optional": true,
1395 | "os": [
1396 | "openbsd"
1397 | ],
1398 | "engines": {
1399 | "node": ">=12"
1400 | }
1401 | },
1402 | "node_modules/esbuild-plugin-vue3": {
1403 | "version": "0.4.2",
1404 | "resolved": "https://registry.npmjs.org/esbuild-plugin-vue3/-/esbuild-plugin-vue3-0.4.2.tgz",
1405 | "integrity": "sha512-edaghOAJY+26uIJVywkT0cyUxWu/oi2+dGe2KePyHAJ9y6hAB4fqNnY5SFpZY9G6knERZo4Nykp/YOcKML06rA==",
1406 | "dev": true,
1407 | "license": "MIT",
1408 | "dependencies": {
1409 | "esbuild": "^0.14.8",
1410 | "typescript": "^4.7.4"
1411 | },
1412 | "peerDependencies": {
1413 | "cheerio": "^1.0.0-rc.10",
1414 | "html-minifier": "^4.0.0",
1415 | "pug": "^3.0.2",
1416 | "sass": "^1.35.2",
1417 | "vue": "^3.4.15"
1418 | },
1419 | "peerDependenciesMeta": {
1420 | "cheerio": {
1421 | "optional": true
1422 | },
1423 | "html-minifier": {
1424 | "optional": true
1425 | },
1426 | "pug": {
1427 | "optional": true
1428 | },
1429 | "sass": {
1430 | "optional": true
1431 | }
1432 | }
1433 | },
1434 | "node_modules/esbuild-plugin-vue3/node_modules/@esbuild/linux-loong64": {
1435 | "version": "0.14.54",
1436 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz",
1437 | "integrity": "sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==",
1438 | "cpu": [
1439 | "loong64"
1440 | ],
1441 | "dev": true,
1442 | "license": "MIT",
1443 | "optional": true,
1444 | "os": [
1445 | "linux"
1446 | ],
1447 | "engines": {
1448 | "node": ">=12"
1449 | }
1450 | },
1451 | "node_modules/esbuild-plugin-vue3/node_modules/esbuild": {
1452 | "version": "0.14.54",
1453 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.54.tgz",
1454 | "integrity": "sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==",
1455 | "dev": true,
1456 | "hasInstallScript": true,
1457 | "license": "MIT",
1458 | "bin": {
1459 | "esbuild": "bin/esbuild"
1460 | },
1461 | "engines": {
1462 | "node": ">=12"
1463 | },
1464 | "optionalDependencies": {
1465 | "@esbuild/linux-loong64": "0.14.54",
1466 | "esbuild-android-64": "0.14.54",
1467 | "esbuild-android-arm64": "0.14.54",
1468 | "esbuild-darwin-64": "0.14.54",
1469 | "esbuild-darwin-arm64": "0.14.54",
1470 | "esbuild-freebsd-64": "0.14.54",
1471 | "esbuild-freebsd-arm64": "0.14.54",
1472 | "esbuild-linux-32": "0.14.54",
1473 | "esbuild-linux-64": "0.14.54",
1474 | "esbuild-linux-arm": "0.14.54",
1475 | "esbuild-linux-arm64": "0.14.54",
1476 | "esbuild-linux-mips64le": "0.14.54",
1477 | "esbuild-linux-ppc64le": "0.14.54",
1478 | "esbuild-linux-riscv64": "0.14.54",
1479 | "esbuild-linux-s390x": "0.14.54",
1480 | "esbuild-netbsd-64": "0.14.54",
1481 | "esbuild-openbsd-64": "0.14.54",
1482 | "esbuild-sunos-64": "0.14.54",
1483 | "esbuild-windows-32": "0.14.54",
1484 | "esbuild-windows-64": "0.14.54",
1485 | "esbuild-windows-arm64": "0.14.54"
1486 | }
1487 | },
1488 | "node_modules/esbuild-plugin-vue3/node_modules/typescript": {
1489 | "version": "4.9.5",
1490 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
1491 | "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
1492 | "dev": true,
1493 | "license": "Apache-2.0",
1494 | "bin": {
1495 | "tsc": "bin/tsc",
1496 | "tsserver": "bin/tsserver"
1497 | },
1498 | "engines": {
1499 | "node": ">=4.2.0"
1500 | }
1501 | },
1502 | "node_modules/esbuild-sunos-64": {
1503 | "version": "0.14.54",
1504 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz",
1505 | "integrity": "sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==",
1506 | "cpu": [
1507 | "x64"
1508 | ],
1509 | "dev": true,
1510 | "license": "MIT",
1511 | "optional": true,
1512 | "os": [
1513 | "sunos"
1514 | ],
1515 | "engines": {
1516 | "node": ">=12"
1517 | }
1518 | },
1519 | "node_modules/esbuild-windows-32": {
1520 | "version": "0.14.54",
1521 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz",
1522 | "integrity": "sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==",
1523 | "cpu": [
1524 | "ia32"
1525 | ],
1526 | "dev": true,
1527 | "license": "MIT",
1528 | "optional": true,
1529 | "os": [
1530 | "win32"
1531 | ],
1532 | "engines": {
1533 | "node": ">=12"
1534 | }
1535 | },
1536 | "node_modules/esbuild-windows-64": {
1537 | "version": "0.14.54",
1538 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz",
1539 | "integrity": "sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==",
1540 | "cpu": [
1541 | "x64"
1542 | ],
1543 | "dev": true,
1544 | "license": "MIT",
1545 | "optional": true,
1546 | "os": [
1547 | "win32"
1548 | ],
1549 | "engines": {
1550 | "node": ">=12"
1551 | }
1552 | },
1553 | "node_modules/esbuild-windows-arm64": {
1554 | "version": "0.14.54",
1555 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz",
1556 | "integrity": "sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==",
1557 | "cpu": [
1558 | "arm64"
1559 | ],
1560 | "dev": true,
1561 | "license": "MIT",
1562 | "optional": true,
1563 | "os": [
1564 | "win32"
1565 | ],
1566 | "engines": {
1567 | "node": ">=12"
1568 | }
1569 | },
1570 | "node_modules/estree-walker": {
1571 | "version": "2.0.2",
1572 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
1573 | "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
1574 | "dev": true,
1575 | "license": "MIT"
1576 | },
1577 | "node_modules/htmlnano": {
1578 | "version": "2.1.2",
1579 | "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-2.1.2.tgz",
1580 | "integrity": "sha512-8Fst+0bhAfU362S6oHVb4wtJj/UYEFr0qiCLAEi8zioqmp1JYBQx5crZAADlFVX0Ly/6s/IQz6G7PL9/hgoJaQ==",
1581 | "dev": true,
1582 | "license": "MIT",
1583 | "dependencies": {
1584 | "cosmiconfig": "^9.0.0",
1585 | "posthtml": "^0.16.5"
1586 | },
1587 | "peerDependencies": {
1588 | "cssnano": "^7.0.0",
1589 | "postcss": "^8.3.11",
1590 | "purgecss": "^7.0.2",
1591 | "relateurl": "^0.2.7",
1592 | "srcset": "5.0.1",
1593 | "svgo": "^3.0.2",
1594 | "terser": "^5.10.0",
1595 | "uncss": "^0.17.3"
1596 | },
1597 | "peerDependenciesMeta": {
1598 | "cssnano": {
1599 | "optional": true
1600 | },
1601 | "postcss": {
1602 | "optional": true
1603 | },
1604 | "purgecss": {
1605 | "optional": true
1606 | },
1607 | "relateurl": {
1608 | "optional": true
1609 | },
1610 | "srcset": {
1611 | "optional": true
1612 | },
1613 | "svgo": {
1614 | "optional": true
1615 | },
1616 | "terser": {
1617 | "optional": true
1618 | },
1619 | "uncss": {
1620 | "optional": true
1621 | }
1622 | }
1623 | },
1624 | "node_modules/htmlparser2": {
1625 | "version": "7.2.0",
1626 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz",
1627 | "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==",
1628 | "dev": true,
1629 | "funding": [
1630 | "https://github.com/fb55/htmlparser2?sponsor=1",
1631 | {
1632 | "type": "github",
1633 | "url": "https://github.com/sponsors/fb55"
1634 | }
1635 | ],
1636 | "license": "MIT",
1637 | "dependencies": {
1638 | "domelementtype": "^2.0.1",
1639 | "domhandler": "^4.2.2",
1640 | "domutils": "^2.8.0",
1641 | "entities": "^3.0.1"
1642 | }
1643 | },
1644 | "node_modules/import-fresh": {
1645 | "version": "3.3.1",
1646 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
1647 | "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
1648 | "dev": true,
1649 | "license": "MIT",
1650 | "dependencies": {
1651 | "parent-module": "^1.0.0",
1652 | "resolve-from": "^4.0.0"
1653 | },
1654 | "engines": {
1655 | "node": ">=6"
1656 | },
1657 | "funding": {
1658 | "url": "https://github.com/sponsors/sindresorhus"
1659 | }
1660 | },
1661 | "node_modules/is-arrayish": {
1662 | "version": "0.2.1",
1663 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
1664 | "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
1665 | "dev": true,
1666 | "license": "MIT"
1667 | },
1668 | "node_modules/is-json": {
1669 | "version": "2.0.1",
1670 | "resolved": "https://registry.npmjs.org/is-json/-/is-json-2.0.1.tgz",
1671 | "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==",
1672 | "dev": true,
1673 | "license": "ISC"
1674 | },
1675 | "node_modules/js-tokens": {
1676 | "version": "4.0.0",
1677 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
1678 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
1679 | "dev": true,
1680 | "license": "MIT"
1681 | },
1682 | "node_modules/js-yaml": {
1683 | "version": "4.1.0",
1684 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
1685 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
1686 | "dev": true,
1687 | "license": "MIT",
1688 | "dependencies": {
1689 | "argparse": "^2.0.1"
1690 | },
1691 | "bin": {
1692 | "js-yaml": "bin/js-yaml.js"
1693 | }
1694 | },
1695 | "node_modules/json-parse-even-better-errors": {
1696 | "version": "2.3.1",
1697 | "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
1698 | "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
1699 | "dev": true,
1700 | "license": "MIT"
1701 | },
1702 | "node_modules/lines-and-columns": {
1703 | "version": "1.2.4",
1704 | "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
1705 | "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
1706 | "dev": true,
1707 | "license": "MIT"
1708 | },
1709 | "node_modules/magic-string": {
1710 | "version": "0.30.17",
1711 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
1712 | "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
1713 | "dev": true,
1714 | "license": "MIT",
1715 | "dependencies": {
1716 | "@jridgewell/sourcemap-codec": "^1.5.0"
1717 | }
1718 | },
1719 | "node_modules/nanoid": {
1720 | "version": "3.3.11",
1721 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
1722 | "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
1723 | "dev": true,
1724 | "funding": [
1725 | {
1726 | "type": "github",
1727 | "url": "https://github.com/sponsors/ai"
1728 | }
1729 | ],
1730 | "license": "MIT",
1731 | "bin": {
1732 | "nanoid": "bin/nanoid.cjs"
1733 | },
1734 | "engines": {
1735 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1736 | }
1737 | },
1738 | "node_modules/parent-module": {
1739 | "version": "1.0.1",
1740 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
1741 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
1742 | "dev": true,
1743 | "license": "MIT",
1744 | "dependencies": {
1745 | "callsites": "^3.0.0"
1746 | },
1747 | "engines": {
1748 | "node": ">=6"
1749 | }
1750 | },
1751 | "node_modules/parse-json": {
1752 | "version": "5.2.0",
1753 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
1754 | "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
1755 | "dev": true,
1756 | "license": "MIT",
1757 | "dependencies": {
1758 | "@babel/code-frame": "^7.0.0",
1759 | "error-ex": "^1.3.1",
1760 | "json-parse-even-better-errors": "^2.3.0",
1761 | "lines-and-columns": "^1.1.6"
1762 | },
1763 | "engines": {
1764 | "node": ">=8"
1765 | },
1766 | "funding": {
1767 | "url": "https://github.com/sponsors/sindresorhus"
1768 | }
1769 | },
1770 | "node_modules/picocolors": {
1771 | "version": "1.1.1",
1772 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
1773 | "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
1774 | "dev": true,
1775 | "license": "ISC"
1776 | },
1777 | "node_modules/postcss": {
1778 | "version": "8.5.6",
1779 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
1780 | "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
1781 | "dev": true,
1782 | "funding": [
1783 | {
1784 | "type": "opencollective",
1785 | "url": "https://opencollective.com/postcss/"
1786 | },
1787 | {
1788 | "type": "tidelift",
1789 | "url": "https://tidelift.com/funding/github/npm/postcss"
1790 | },
1791 | {
1792 | "type": "github",
1793 | "url": "https://github.com/sponsors/ai"
1794 | }
1795 | ],
1796 | "license": "MIT",
1797 | "dependencies": {
1798 | "nanoid": "^3.3.11",
1799 | "picocolors": "^1.1.1",
1800 | "source-map-js": "^1.2.1"
1801 | },
1802 | "engines": {
1803 | "node": "^10 || ^12 || >=14"
1804 | }
1805 | },
1806 | "node_modules/posthtml": {
1807 | "version": "0.16.6",
1808 | "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz",
1809 | "integrity": "sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==",
1810 | "dev": true,
1811 | "license": "MIT",
1812 | "dependencies": {
1813 | "posthtml-parser": "^0.11.0",
1814 | "posthtml-render": "^3.0.0"
1815 | },
1816 | "engines": {
1817 | "node": ">=12.0.0"
1818 | }
1819 | },
1820 | "node_modules/posthtml-parser": {
1821 | "version": "0.11.0",
1822 | "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz",
1823 | "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==",
1824 | "dev": true,
1825 | "license": "MIT",
1826 | "dependencies": {
1827 | "htmlparser2": "^7.1.1"
1828 | },
1829 | "engines": {
1830 | "node": ">=12"
1831 | }
1832 | },
1833 | "node_modules/posthtml-render": {
1834 | "version": "3.0.0",
1835 | "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz",
1836 | "integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==",
1837 | "dev": true,
1838 | "license": "MIT",
1839 | "dependencies": {
1840 | "is-json": "^2.0.1"
1841 | },
1842 | "engines": {
1843 | "node": ">=12"
1844 | }
1845 | },
1846 | "node_modules/resolve-from": {
1847 | "version": "4.0.0",
1848 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
1849 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
1850 | "dev": true,
1851 | "license": "MIT",
1852 | "engines": {
1853 | "node": ">=4"
1854 | }
1855 | },
1856 | "node_modules/source-map-js": {
1857 | "version": "1.2.1",
1858 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
1859 | "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
1860 | "dev": true,
1861 | "license": "BSD-3-Clause",
1862 | "engines": {
1863 | "node": ">=0.10.0"
1864 | }
1865 | },
1866 | "node_modules/typescript": {
1867 | "version": "5.8.3",
1868 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
1869 | "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
1870 | "dev": true,
1871 | "license": "Apache-2.0",
1872 | "bin": {
1873 | "tsc": "bin/tsc",
1874 | "tsserver": "bin/tsserver"
1875 | },
1876 | "engines": {
1877 | "node": ">=14.17"
1878 | }
1879 | },
1880 | "node_modules/vue": {
1881 | "version": "3.5.17",
1882 | "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.17.tgz",
1883 | "integrity": "sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==",
1884 | "dev": true,
1885 | "license": "MIT",
1886 | "dependencies": {
1887 | "@vue/compiler-dom": "3.5.17",
1888 | "@vue/compiler-sfc": "3.5.17",
1889 | "@vue/runtime-dom": "3.5.17",
1890 | "@vue/server-renderer": "3.5.17",
1891 | "@vue/shared": "3.5.17"
1892 | },
1893 | "peerDependencies": {
1894 | "typescript": "*"
1895 | },
1896 | "peerDependenciesMeta": {
1897 | "typescript": {
1898 | "optional": true
1899 | }
1900 | }
1901 | }
1902 | }
1903 | }
1904 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "build": "node build.mjs",
4 | "dev": "node build.mjs --dev",
5 | "test": "tsc && biome check",
6 | "fix": "biome check --write"
7 | },
8 | "devDependencies": {
9 | "@biomejs/biome": "^2.0.0-beta.1",
10 | "@chialab/esbuild-plugin-html": "^0.18.2",
11 | "@vue/tsconfig": "^0.7.0",
12 | "bootstrap": "^5.2.2",
13 | "esbuild": "^0.25.0",
14 | "esbuild-plugin-vue3": "^0.4.2",
15 | "htmlnano": "^2.1.1",
16 | "typescript": "^5.2.2",
17 | "vue": "^3.2.40"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/public/192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xPaw/SteamWebAPIDocumentation/7f9352c3b34d1a200f54eb298369477d0a2e701b/public/192.png
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | 404 · Page not found
10 |
11 |
12 | 404 Not Found
13 |
14 |
15 |
--------------------------------------------------------------------------------
/public/512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xPaw/SteamWebAPIDocumentation/7f9352c3b34d1a200f54eb298369477d0a2e701b/public/512.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xPaw/SteamWebAPIDocumentation/7f9352c3b34d1a200f54eb298369477d0a2e701b/public/favicon.ico
--------------------------------------------------------------------------------
/public/icons/artifact.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xPaw/SteamWebAPIDocumentation/7f9352c3b34d1a200f54eb298369477d0a2e701b/public/icons/artifact.jpg
--------------------------------------------------------------------------------
/public/icons/cs2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xPaw/SteamWebAPIDocumentation/7f9352c3b34d1a200f54eb298369477d0a2e701b/public/icons/cs2.jpg
--------------------------------------------------------------------------------
/public/icons/deadlock.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xPaw/SteamWebAPIDocumentation/7f9352c3b34d1a200f54eb298369477d0a2e701b/public/icons/deadlock.jpg
--------------------------------------------------------------------------------
/public/icons/dota.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xPaw/SteamWebAPIDocumentation/7f9352c3b34d1a200f54eb298369477d0a2e701b/public/icons/dota.jpg
--------------------------------------------------------------------------------
/public/icons/portal2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xPaw/SteamWebAPIDocumentation/7f9352c3b34d1a200f54eb298369477d0a2e701b/public/icons/portal2.jpg
--------------------------------------------------------------------------------
/public/icons/steam.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xPaw/SteamWebAPIDocumentation/7f9352c3b34d1a200f54eb298369477d0a2e701b/public/icons/steam.jpg
--------------------------------------------------------------------------------
/public/icons/tf.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xPaw/SteamWebAPIDocumentation/7f9352c3b34d1a200f54eb298369477d0a2e701b/public/icons/tf.jpg
--------------------------------------------------------------------------------
/public/icons/underlords.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xPaw/SteamWebAPIDocumentation/7f9352c3b34d1a200f54eb298369477d0a2e701b/public/icons/underlords.jpg
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Steam Web API Documentation",
3 | "short_name": "Steam Web API",
4 | "description": "An automatically generated list of Steam Web API interfaces, methods and parameters. Allows you to craft requests in the browser.",
5 | "start_url": "/",
6 | "display": "standalone",
7 | "theme_color": "#0d0d0d",
8 | "background_color": "#0d0d0d",
9 | "icons": [
10 | {
11 | "src": "favicon.ico",
12 | "sizes": "16x16 32x32 64x64"
13 | },
14 | {
15 | "src": "192.png",
16 | "sizes": "192x192"
17 | },
18 | {
19 | "src": "512.png",
20 | "sizes": "512x512"
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Allow: /
3 |
--------------------------------------------------------------------------------
/public/serviceworker.js:
--------------------------------------------------------------------------------
1 | self.addEventListener('install', () => self.skipWaiting());
2 | self.addEventListener('activate', (event) => event.waitUntil(self.clients.claim()));
3 |
4 | self.addEventListener('fetch', (event) => {
5 | if (event.request.method !== 'GET') {
6 | return;
7 | }
8 |
9 | event.respondWith(networkOrCache(event));
10 | });
11 |
12 | async function putInCache(event, response) {
13 | const cache = await caches.open('steamwebapi-cache');
14 | await cache.put(event.request, response);
15 | }
16 |
17 | async function networkOrCache(event) {
18 | try {
19 | const response = await fetch(event.request, { cache: 'no-cache' });
20 |
21 | if (response.ok) {
22 | event.waitUntil(putInCache(event, response));
23 |
24 | return response.clone();
25 | }
26 |
27 | return response;
28 | } catch (e) {
29 | const cache = await caches.open('steamwebapi-cache');
30 | const matching = await cache.match(event.request);
31 |
32 | return matching || Promise.reject(e);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/ApiParameter.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ↳
5 | {{ parameter.name }}
6 | +
7 |
8 |
9 |
10 | click to change
11 | click to set
12 |
13 |
14 |
20 |
28 | ×
29 |
30 |
38 |
39 | {{ parameter.type }}
40 | No
41 |
42 |
43 | Yes
44 |
45 | {{ parameter.description }}
46 |
47 |
48 |
58 |
59 |
60 |
76 |
--------------------------------------------------------------------------------
/src/App.ts:
--------------------------------------------------------------------------------
1 | import { defineComponent, markRaw, ref } from 'vue';
2 | import interfacesJson from '../api.json';
3 | import ApiParameter from './ApiParameter.vue';
4 | import HighlightedSearchMethod from './HighlightedSearchMethod';
5 | import type { ApiInterface, ApiMethod, ApiMethodParameter, ApiServices, SidebarGroupData } from './interfaces';
6 | import { ApiSearcher } from './search';
7 |
8 | const sidebar = ref(null);
9 | const inputSearch = ref(null);
10 | const inputApiKey = ref(null);
11 | const inputAccessToken = ref(null);
12 |
13 | export default defineComponent({
14 | components: {
15 | ApiParameter,
16 | HighlightedSearchMethod,
17 | },
18 | data() {
19 | // @ts-ignore
20 | const interfaces = interfacesJson as ApiServices;
21 |
22 | const groupsMap = new Map();
23 | const groupsData = new Map(
24 | // biome-ignore format: too verbose
25 | [
26 | // Order of apps here defines the order in the sidebar
27 | [0, { name: 'Steam', icon: 'steam.jpg', open: true, methods: {} }],
28 | [730, { name: 'Counter-Strike 2', icon: 'cs2.jpg', open: true, methods: {} }],
29 | [570, { name: 'Dota 2', icon: 'dota.jpg', open: true, methods: {} }],
30 | [440, { name: 'Team Fortress 2', icon: 'tf.jpg', open: true, methods: {} }],
31 | [1422450, { name: 'Deadlock', icon: 'deadlock.jpg', open: true, methods: {} }],
32 | [620, { name: 'Portal 2', icon: 'portal2.jpg', open: false, methods: {} }],
33 | [1046930, { name: 'Dota Underlords', icon: 'underlords.jpg', open: false, methods: {} }],
34 | [583950, { name: 'Artifact Classic', icon: 'artifact.jpg', open: false, methods: {} }],
35 | [1269260, { name: 'Artifact Foundry', icon: 'artifact.jpg', open: false, methods: {} }],
36 |
37 | // Beta apps
38 | [247040, { name: 'Dota 2 Experimental', icon: 'dota.jpg', open: false, methods: {} }],
39 | [2305270, { name: 'Dota 2 Staging', icon: 'dota.jpg', open: false, methods: {} }],
40 | [3488080, { name: 'Deadlock Experimental', icon: 'deadlock.jpg', open: false, methods: {} }],
41 | [3781850, { name: 'Deadlock Unknown', icon: 'deadlock.jpg', open: false, methods: {} }],
42 | ],
43 | );
44 |
45 | const steamGroup = groupsData.get(0)!;
46 |
47 | for (const interfaceName in interfaces) {
48 | const interfaceAppid = interfaceName.match(/_(?[0-9]+)$/);
49 |
50 | if (interfaceAppid) {
51 | const appid = parseInt(interfaceAppid.groups!.appid, 10);
52 |
53 | groupsMap.set(interfaceName, appid);
54 |
55 | let group = groupsData.get(appid);
56 |
57 | if (!group) {
58 | group = {
59 | name: `App ${appid}`,
60 | icon: 'steam.jpg',
61 | open: false,
62 | methods: {},
63 | };
64 |
65 | groupsData.set(appid, group);
66 | }
67 |
68 | group.methods[interfaceName] = interfaces[interfaceName];
69 | } else {
70 | steamGroup.methods[interfaceName] = interfaces[interfaceName];
71 | }
72 |
73 | for (const methodName in interfaces[interfaceName]) {
74 | const method = interfaces[interfaceName][methodName];
75 |
76 | for (const parameter of method.parameters) {
77 | parameter._value = '';
78 |
79 | if (parameter.type === 'bool') {
80 | parameter.manuallyToggled = false;
81 | }
82 | }
83 | }
84 | }
85 |
86 | return {
87 | userData: {
88 | webapi_key: '',
89 | access_token: '',
90 | steamid: '',
91 | format: 'json',
92 | favorites: new Set(),
93 | },
94 | skipNextHashChange: false,
95 | keyInputType: 'password',
96 | hasValidWebApiKey: false,
97 | hasValidAccessToken: false,
98 | accessTokenExpiration: 0,
99 | accessTokenSteamId: null,
100 | accessTokenAudience: [],
101 | accessTokenVisible: false,
102 | currentFilter: '',
103 | currentInterface: '',
104 | search: markRaw(new ApiSearcher(interfaces)),
105 | interfaces,
106 | groupsMap,
107 | groupsData,
108 | };
109 | },
110 | setup() {
111 | return {
112 | sidebar,
113 | inputSearch,
114 | inputApiKey,
115 | inputAccessToken,
116 | };
117 | },
118 | watch: {
119 | 'userData.format'(value: string): void {
120 | localStorage.setItem('format', value);
121 | },
122 | 'userData.webapi_key'(value: string): void {
123 | if (this.isFieldValid('webapi_key')) {
124 | localStorage.setItem('webapi_key', value);
125 | } else {
126 | localStorage.removeItem('webapi_key');
127 | }
128 | },
129 | 'userData.access_token'(value: string): void {
130 | try {
131 | if (value.length > 2 && value[0] === '{' && value[value.length - 1] === '}') {
132 | const obj = JSON.parse(value);
133 |
134 | if (obj.data?.webapi_token) {
135 | this.userData.access_token = obj.data.webapi_token;
136 | return;
137 | }
138 | }
139 | if (/^[\w-]+\.[\w-]+\.[\w-]+$/.test(value)) {
140 | const jwt = value.split('.');
141 | const token = JSON.parse(atob(jwt[1]));
142 |
143 | this.accessTokenExpiration = token.exp * 1000;
144 | this.accessTokenAudience = token.aud;
145 | this.accessTokenSteamId = token.sub;
146 |
147 | if (token.sub && !this.userData.steamid) {
148 | this.userData.steamid = token.sub;
149 | }
150 | } else {
151 | throw new Error('Invalid token format (or empty)');
152 | }
153 | } catch (e) {
154 | console.log((e as Error).message);
155 | this.accessTokenExpiration = 0;
156 | this.accessTokenSteamId = null;
157 | this.accessTokenAudience = [];
158 | }
159 |
160 | if (this.isFieldValid('access_token')) {
161 | localStorage.setItem('access_token', value);
162 | } else {
163 | localStorage.removeItem('access_token');
164 | }
165 | },
166 | 'userData.steamid'(value: string): void {
167 | if (this.isFieldValid('steamid')) {
168 | localStorage.setItem('steamid', value);
169 |
170 | this.fillSteamidParameter();
171 | } else {
172 | localStorage.removeItem('steamid');
173 | }
174 | },
175 | currentFilter(newFilter: string, oldFilter: string): void {
176 | if (!newFilter) {
177 | this.$nextTick(this.scrollInterfaceIntoView);
178 |
179 | if (oldFilter) {
180 | this.sidebar!.scrollTop = 0;
181 | }
182 | } else {
183 | this.setInterface('');
184 |
185 | if (!oldFilter) {
186 | this.sidebar!.scrollTop = 0;
187 | }
188 | }
189 | },
190 | },
191 | mounted(): void {
192 | try {
193 | this.userData.format = localStorage.getItem('format') || 'json';
194 | this.userData.steamid = localStorage.getItem('steamid') || '';
195 | this.userData.webapi_key = localStorage.getItem('webapi_key') || '';
196 | this.userData.access_token = localStorage.getItem('access_token') || '';
197 |
198 | const favoriteStrings = JSON.parse(localStorage.getItem('favorites') || '[]');
199 |
200 | for (const favorite of favoriteStrings) {
201 | const [favoriteInterface, favoriteMethod] = favorite.split('/', 2);
202 |
203 | if (
204 | Object.hasOwn(this.interfaces, favoriteInterface) &&
205 | Object.hasOwn(this.interfaces[favoriteInterface], favoriteMethod)
206 | ) {
207 | this.interfaces[favoriteInterface][favoriteMethod].isFavorite = true;
208 |
209 | this.userData.favorites.add(favorite);
210 | }
211 | }
212 | } catch (e) {
213 | console.error(e);
214 | }
215 |
216 | if (location.hash.startsWith('#')) {
217 | this.setInterface(location.hash.substring(1), true);
218 | }
219 |
220 | window.addEventListener(
221 | 'hashchange',
222 | () => {
223 | if (this.skipNextHashChange) {
224 | this.skipNextHashChange = false;
225 | return;
226 | }
227 |
228 | this.setInterface(location.hash.substring(1));
229 | },
230 | false,
231 | );
232 |
233 | this.bindGlobalKeybind();
234 | },
235 | computed: {
236 | sidebarInterfaces(): Map {
237 | const interfaces = this.filteredInterfaces;
238 |
239 | if (this.currentFilter) {
240 | return new Map([
241 | [
242 | -1,
243 | {
244 | name: 'Search results',
245 | icon: '',
246 | open: true,
247 | methods: interfaces,
248 | },
249 | ],
250 | ]);
251 | }
252 |
253 | return this.groupsData;
254 | },
255 | filteredInterfaces(): ApiServices {
256 | if (!this.currentFilter) {
257 | return this.interfaces;
258 | }
259 |
260 | const matchedInterfaces: ApiServices = {};
261 | const hits = this.search.search(this.currentFilter);
262 |
263 | for (const match of hits) {
264 | if (!matchedInterfaces[match.interface]) {
265 | matchedInterfaces[match.interface] = {};
266 | }
267 |
268 | const method = this.interfaces[match.interface][match.method];
269 | method.highlight = match.indices;
270 | matchedInterfaces[match.interface][match.method] = method;
271 | }
272 |
273 | return matchedInterfaces;
274 | },
275 | currentInterfaceMethods(): ApiInterface {
276 | return this.interfaces[this.currentInterface];
277 | },
278 | uriDelimeterBeforeKey() {
279 | return this.hasValidAccessToken || this.hasValidWebApiKey ? '?' : '';
280 | },
281 | formatAccessTokenExpirationDate(): string {
282 | const formatter = new Intl.DateTimeFormat('en-US', {
283 | hourCycle: 'h23',
284 | dateStyle: 'medium',
285 | timeStyle: 'short',
286 | });
287 |
288 | return formatter.format(this.accessTokenExpiration);
289 | },
290 | },
291 | methods: {
292 | setInterface(interfaceAndMethod: string, setFromUrl = false): void {
293 | const split = interfaceAndMethod.split('/', 2);
294 | let currentInterface: string | null = split[0];
295 | let currentMethod: string | null = split.length > 1 ? split[1] : null;
296 |
297 | if (!Object.hasOwn(this.interfaces, currentInterface)) {
298 | currentInterface = null;
299 | currentMethod = null;
300 | } else if (currentMethod !== null && !Object.hasOwn(this.interfaces[currentInterface], currentMethod)) {
301 | currentMethod = null;
302 | }
303 |
304 | this.currentInterface = currentInterface || '';
305 |
306 | if (currentInterface) {
307 | document.title = `${currentInterface} – Steam Web API Documentation`;
308 | } else {
309 | document.title = `Steam Web API Documentation`;
310 | }
311 |
312 | // Since we won't scroll to a method, scroll to top (as there is no element with just interface id)
313 | if (document.scrollingElement && !currentMethod) {
314 | document.scrollingElement.scrollTop = 0;
315 | }
316 |
317 | if (setFromUrl) {
318 | return;
319 | }
320 |
321 | this.$nextTick(() => {
322 | this.skipNextHashChange = true;
323 |
324 | if (currentMethod) {
325 | location.hash = `#${currentInterface}/${currentMethod}`;
326 | } else if (currentInterface) {
327 | location.hash = `#${currentInterface}`;
328 | } else {
329 | location.hash = '';
330 | }
331 | });
332 | },
333 | fillSteamidParameter(): void {
334 | if (!this.userData.steamid) {
335 | return;
336 | }
337 |
338 | for (const interfaceName in this.interfaces) {
339 | for (const methodName in this.interfaces[interfaceName]) {
340 | for (const parameter of this.interfaces[interfaceName][methodName].parameters) {
341 | if (!parameter._value && parameter.name.includes('steamid')) {
342 | parameter._value = this.userData.steamid;
343 | }
344 | }
345 | }
346 | }
347 | },
348 | isFieldValid(field: string): boolean {
349 | switch (field) {
350 | case 'access_token':
351 | this.hasValidAccessToken = this.accessTokenExpiration > Date.now();
352 | return this.hasValidAccessToken;
353 |
354 | case 'webapi_key':
355 | this.hasValidWebApiKey = /^[0-9a-f]{32}$/i.test(this.userData[field]);
356 | return this.hasValidWebApiKey;
357 |
358 | case 'steamid':
359 | return /^[0-9]{17}$/.test(this.userData[field]);
360 | }
361 |
362 | return false;
363 | },
364 | renderUri(methodName: string, method: ApiMethod): string {
365 | let host = 'https://api.steampowered.com/';
366 |
367 | if (method._type === 'publisher_only') {
368 | host = 'https://partner.steam-api.com/';
369 | }
370 |
371 | return `${host}${this.currentInterface}/${methodName}/v${method.version}/`;
372 | },
373 | renderApiKey(): string {
374 | const parameters = new URLSearchParams();
375 |
376 | if (this.hasValidAccessToken) {
377 | parameters.set('access_token', this.userData.access_token);
378 | } else if (this.hasValidWebApiKey) {
379 | parameters.set('key', this.userData.webapi_key);
380 | }
381 |
382 | return parameters.toString();
383 | },
384 | renderParameters(method: ApiMethod): string {
385 | const parameters = new URLSearchParams();
386 |
387 | if (this.userData.format !== 'json') {
388 | parameters.set('format', this.userData.format);
389 | }
390 |
391 | let hasArrays = false;
392 | const inputJson = {} as any;
393 |
394 | for (const parameter of method.parameters) {
395 | if (parameter.extra) {
396 | const arr = this.getInnerParameters(parameter);
397 |
398 | if (Object.keys(arr).length > 0) {
399 | hasArrays = true;
400 |
401 | if (parameter.type?.endsWith('[]')) {
402 | const paramName = parameter.name.substring(0, parameter.name.length - 3);
403 |
404 | if (!Object.hasOwn(inputJson, paramName)) {
405 | inputJson[paramName] = [];
406 | }
407 |
408 | inputJson[paramName].push(arr);
409 | } else {
410 | inputJson[parameter.name] = arr;
411 | }
412 | } else if (parameter._value) {
413 | parameters.set(parameter.name, parameter._value);
414 | }
415 |
416 | continue;
417 | }
418 |
419 | if (!parameter._value && !parameter.manuallyToggled) {
420 | continue;
421 | }
422 |
423 | parameters.set(parameter.name, parameter._value ?? '');
424 | }
425 |
426 | if (hasArrays) {
427 | method.hasArrays = true;
428 | parameters.set('input_json', JSON.stringify(inputJson));
429 | }
430 |
431 | const str = parameters.toString();
432 |
433 | if (str.length === 0) {
434 | return '';
435 | }
436 |
437 | if (this.uriDelimeterBeforeKey) {
438 | return `&${str}`;
439 | }
440 |
441 | return `?${str}`;
442 | },
443 | getInnerParameters(parameterParent: ApiMethodParameter) {
444 | const arr = {} as any;
445 |
446 | for (const parameter of parameterParent.extra!) {
447 | if (parameter.extra) {
448 | const result = this.getInnerParameters(parameter);
449 |
450 | if (Object.keys(result).length > 0) {
451 | if (parameter.type?.endsWith('[]')) {
452 | const paramName = parameter.name.substring(0, parameter.name.length - 3);
453 |
454 | if (!Object.hasOwn(arr, paramName)) {
455 | arr[paramName] = [];
456 | }
457 |
458 | arr[paramName].push(result);
459 | } else {
460 | arr[parameter.name] = result;
461 | }
462 | }
463 |
464 | continue;
465 | }
466 |
467 | if (!parameter._value && !parameter.manuallyToggled) {
468 | continue;
469 | }
470 |
471 | if (parameter.type?.endsWith('[]')) {
472 | const paramName = parameter.name.substring(0, parameter.name.length - 3);
473 |
474 | if (!Object.hasOwn(arr, paramName)) {
475 | arr[paramName] = [];
476 | }
477 |
478 | arr[paramName].push(parameter._value || '');
479 | } else {
480 | arr[parameter.name] = parameter._value || '';
481 | }
482 | }
483 |
484 | return arr;
485 | },
486 | useThisMethod(event: SubmitEvent, method: ApiMethod): void {
487 | const form = event.target as HTMLFormElement;
488 |
489 | if (method.hasArrays) {
490 | event.preventDefault();
491 |
492 | if (method.httpmethod === 'POST') {
493 | alert('Executing POST requests with input_json is not yet supported.');
494 | return;
495 | }
496 |
497 | const url = [form.action, this.uriDelimeterBeforeKey, this.renderApiKey(), this.renderParameters(method)].join(
498 | '',
499 | );
500 |
501 | try {
502 | window.open(url, '_blank');
503 | } catch {
504 | alert('Failed to open window');
505 | }
506 |
507 | return;
508 | }
509 |
510 | if (
511 | method.httpmethod === 'POST' &&
512 | !confirm(
513 | 'Executing POST requests could be potentially disastrous.\n\n' +
514 | 'Author is not responsible for any damage done.\n\n' +
515 | 'Are you sure you want to continue?',
516 | )
517 | ) {
518 | event.preventDefault();
519 | }
520 |
521 | for (const field of form.elements) {
522 | if (!(field instanceof HTMLInputElement)) {
523 | continue;
524 | }
525 |
526 | if (!field.value && !field.disabled && field.tagName === 'INPUT') {
527 | field.disabled = true;
528 |
529 | setTimeout(() => {
530 | field.disabled = false;
531 | }, 0);
532 | }
533 | }
534 | },
535 | addParamArray(method: ApiMethod, parameter: ApiMethodParameter): void {
536 | if (!parameter._counter) {
537 | parameter._counter = 1;
538 | } else {
539 | parameter._counter++;
540 | }
541 |
542 | const newParameter: ApiMethodParameter = {
543 | name: `${parameter.name.substring(0, parameter.name.length - 3)}[${parameter._counter}]`,
544 | type: parameter.type,
545 | optional: true,
546 | };
547 |
548 | if (parameter.extra) {
549 | newParameter.extra = [];
550 |
551 | for (const parameter2 of parameter.extra!) {
552 | newParameter.extra.push({
553 | name: parameter2.name,
554 | type: parameter2.type,
555 | optional: true,
556 | });
557 | }
558 | }
559 |
560 | const parameterIndex = method.parameters.findIndex((param) => param.name === parameter.name);
561 | method.parameters.splice(parameterIndex + parameter._counter, 0, newParameter);
562 | },
563 | scrollInterfaceIntoView(): void {
564 | const element = document.querySelector(`.interface-list a[href="#${this.currentInterface}"]`);
565 |
566 | if (element instanceof HTMLElement) {
567 | element.scrollIntoView();
568 | }
569 | },
570 | copyUrl(event: MouseEvent): void {
571 | const button = event.target as Element;
572 | const element = button.closest('.input-group')!.querySelector('.form-control')!;
573 |
574 | navigator.clipboard.writeText(element.textContent || '').then(
575 | () => {
576 | button.classList.add('bg-success');
577 |
578 | setTimeout(() => button.classList.remove('bg-success'), 500);
579 | },
580 | () => {
581 | // write fail
582 | },
583 | );
584 | },
585 | favoriteMethod(method: ApiMethod, methodName: string): void {
586 | const name = `${this.currentInterface}/${methodName}`;
587 |
588 | method.isFavorite = !method.isFavorite;
589 |
590 | if (method.isFavorite) {
591 | this.userData.favorites.add(name);
592 | } else {
593 | this.userData.favorites.delete(name);
594 | }
595 |
596 | localStorage.setItem('favorites', JSON.stringify([...this.userData.favorites]));
597 | },
598 | navigateSidebar(direction: number): void {
599 | const entries = Object.entries(this.filteredInterfaces);
600 | const index = entries.findIndex((x) => x[0] === this.currentInterface) + direction;
601 | const size = entries.length;
602 | const [interfaceName, methods] = entries[((index % size) + size) % size];
603 | const firstMethodName = Object.keys(methods)[0];
604 |
605 | this.setInterface(`${interfaceName}/${firstMethodName}`);
606 | this.scrollInterfaceIntoView();
607 |
608 | // This is trash, but the focus gets lost because of location.hash change
609 | this.$nextTick(() => {
610 | this.inputSearch?.focus();
611 | });
612 | },
613 | focusApiKey(): void {
614 | this.currentFilter = '';
615 | this.setInterface('');
616 |
617 | this.$nextTick(() => {
618 | const element = this.hasValidAccessToken ? this.inputAccessToken : this.inputApiKey;
619 |
620 | if (element) {
621 | element.focus();
622 | }
623 | });
624 | },
625 | onSearchInput(e: Event) {
626 | requestAnimationFrame(() => {
627 | this.currentFilter = (e.target as HTMLInputElement).value;
628 | });
629 | },
630 | bindGlobalKeybind() {
631 | document.addEventListener('keydown', (e: KeyboardEvent) => {
632 | if (e.ctrlKey || e.metaKey) {
633 | return;
634 | }
635 |
636 | const target = e.target as HTMLElement;
637 |
638 | if (['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON'].includes(target.tagName)) {
639 | return;
640 | }
641 |
642 | if (e.key === '/' || e.key === 's') {
643 | e.preventDefault();
644 | this.inputSearch?.focus();
645 | }
646 | });
647 | },
648 | },
649 | });
650 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
27 |
28 |
29 |
30 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
112 |
113 |
How to get it
114 |
115 | Access token
116 | expires on {{formatAccessTokenExpirationDate}}
117 |
118 |
131 |
132 |
133 | Preferred output format
134 |
135 | JSON
136 | VDF
137 | XML (not recommended)
138 |
139 |
140 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
Some APIs work with access tokens, if you have one you can provide it here and it will be preferred over the webapi key.
155 |
Currently entered token is for {{ aud }} with steamid {{ accessTokenSteamId }} and expires on {{ formatAccessTokenExpirationDate }}.
156 |
Here's how to get a store token:
157 |
161 |
162 |
Here's how to get a community token:
163 |
164 | Open https://steamcommunity.com/my/edit/info
165 | Run the following script: JSON.parse(application_config.dataset.loyalty_webapi_token)
(or manually copy data-loyalty_webapi_token from application_config element)
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
This is a static page that is automatically generated from GetSupportedAPIList using public and publisher keys. Additionally service methods are parsed from Steam client's protobuf files.
174 |
If you specify the web api key above, it will be stored in your browser, and will only be sent to Valve's API servers if you chose to do so.
175 |
Type a value in the value field and click the execute button to perform an API request in your browser.
176 |
177 |
This website is created and maintained by xPaw . Use any APIs listed here at your own risk. I do not know how all of them work, this page is simply a reference.
178 |
If you know of an API that is not listed here, make a pull request to the file of undocumented APIs .
179 |
Source code for this page is also available on GitHub .
180 |
181 |
182 |
183 |
184 |
185 |
186 |
The public Steamworks Web API is hosted on https://api.steampowered.com
or https://community.steam-api.com
.
187 |
188 | The public Web API host is accessible via HTTP (port 80) and HTTPS (port 443). Note that any requests using your publisher Web API key should be made over HTTPS.
189 | This service is behind Akamai's edge cache, so the actual IP addresses you will see for the name will vary based on your location and on ongoing service changes.
190 | The IPs can change rapidly and fluidly, so if your Web API calls are made through a firewall on outbound requests, read on.
191 |
192 |
193 | Steam also provides a partner-only Web API server hosted on https://partner.steam-api.com
. The intent of this service is to have higher availability than
194 | the public host; you should use this service for all requests made from your secure servers. This host has some different properties than the public host:
195 |
196 |
197 | This host is only accessible via HTTPS.
198 | This host is not behind Akamai's edge cache.
199 | Every request to this host must be made with your publisher Web API key, even requests which would ordinarily not need a key.
200 | Requests made without a valid publisher key will return a 403 error code.
201 | Requests generating 403 status codes will incur strict rate limits for the connecting IP. This is in an effort to ensure
202 | high availability. If you generate a sufficient number of requests within a certain time interval that return 403 status codes —
203 | either during testing, or by using a regular Web API key instead of your publisher key — the host will put your IP on a deny
204 | list for a while.
205 |
206 |
You should not connect to the Web API servers by IP; use the DNS name.
207 |
208 |
209 |
210 |
211 |
212 |
213 |
Similiar to the Steamworks C++ API, the Web API has been divided into multiple interfaces that contain related methods. The URI format of each API request is: https://api.steampowered.com/<interface>/<method>/<method_version>/
214 |
215 |
Steam supports returning Web API responses in multiple formats. By default, all responses are returned JSON encoded. However, each request can optionally contain a format
parameter to specify the desired response format. The following values can be passed for this parameter: xml
, json
, and vdf
.
216 |
A flexible solution should be used to parse Web API results as each method may return results in an arbitrary order.
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
225 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
226 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
227 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
228 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
229 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
230 | SOFTWARE.
231 |
232 |
233 |
234 |
235 |
253 |
254 |
255 |
256 | This page is just a reference of all the known Steam APIs, I do not know how they work. Please do not email me with questions.
257 |
258 |
262 |
263 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
--------------------------------------------------------------------------------
/src/HighlightedSearchMethod.ts:
--------------------------------------------------------------------------------
1 | import { defineComponent, h, type VNode } from 'vue';
2 |
3 | export default defineComponent({
4 | props: {
5 | method: {
6 | type: String,
7 | default: '',
8 | },
9 | indices: {
10 | type: Array as () => number[],
11 | default: () => [],
12 | },
13 | },
14 | setup(props) {
15 | return () => highlightMethod(props.method, props.indices);
16 | },
17 | });
18 |
19 | function highlightMethod(text: string, indices: number[]) {
20 | // Group consecutive indices for proper highlighting spans
21 | const spans: { start: number; end: number }[] = [];
22 | let currentSpan: { start: number; end: number } | null = null;
23 |
24 | for (let i = 0; i < indices.length; i++) {
25 | const index = indices[i];
26 |
27 | if (currentSpan === null) {
28 | currentSpan = { start: index, end: index };
29 | } else if (index === currentSpan.end + 1) {
30 | currentSpan.end = index;
31 | } else {
32 | spans.push(currentSpan);
33 | currentSpan = { start: index, end: index };
34 | }
35 | }
36 |
37 | if (currentSpan !== null) {
38 | spans.push(currentSpan);
39 | }
40 |
41 | // Build the highlighted text with VNodes
42 | const nodes: (VNode | string)[] = [];
43 | let lastIndex = 0;
44 |
45 | for (const span of spans) {
46 | // Add text before the span
47 | if (span.start > lastIndex) {
48 | nodes.push(text.substring(lastIndex, span.start));
49 | }
50 |
51 | // Add highlighted span
52 | nodes.push(h('b', {}, text.substring(span.start, span.end + 1)));
53 |
54 | lastIndex = span.end + 1;
55 | }
56 |
57 | // Add remaining text
58 | if (lastIndex < text.length) {
59 | nodes.push(text.substring(lastIndex));
60 | }
61 |
62 | return nodes;
63 | }
64 |
--------------------------------------------------------------------------------
/src/documentation.ts:
--------------------------------------------------------------------------------
1 | import './index.ts';
2 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Steam Web API Documentation and Tester
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { createSSRApp } from 'vue';
2 | import App from './App.vue';
3 |
4 | if ('serviceWorker' in navigator && !('DEV_MODE' in window)) {
5 | navigator.serviceWorker.register('serviceworker.js', { scope: './' });
6 | }
7 |
8 | createSSRApp(App).mount('#app');
9 |
--------------------------------------------------------------------------------
/src/interfaces.ts:
--------------------------------------------------------------------------------
1 | export interface SidebarGroupData {
2 | name: string;
3 | icon: string;
4 | open: boolean;
5 | methods: ApiServices;
6 | }
7 |
8 | export interface ApiServices {
9 | [x: string]: ApiInterface;
10 | }
11 |
12 | export interface ApiInterface {
13 | [x: string]: ApiMethod;
14 | }
15 |
16 | export interface ApiMethod {
17 | _type: 'protobufs' | 'publisher_only' | 'undocumented' | null;
18 | httpmethod: 'GET' | 'POST' | null;
19 | version: number;
20 | description?: string;
21 | highlight?: number[];
22 | parameters: ApiMethodParameter[];
23 | isFavorite: boolean;
24 | hasArrays: boolean;
25 | }
26 |
27 | export interface ApiMethodParameter {
28 | _value?: string;
29 | _counter?: number;
30 | manuallyToggled?: boolean;
31 |
32 | name: string;
33 | type?: string;
34 | description?: string;
35 | optional: boolean;
36 | extra?: ApiMethodParameter[];
37 | }
38 |
--------------------------------------------------------------------------------
/src/search.ts:
--------------------------------------------------------------------------------
1 | import type { ApiServices } from './interfaces';
2 |
3 | export interface SearchResult {
4 | interface: string;
5 | method: string;
6 | indices: number[];
7 | score: number;
8 | }
9 |
10 | interface ScoreResult {
11 | score: number;
12 | indices: number[];
13 | }
14 |
15 | /**
16 | * A high-performance fuzzy search implementation for API methods
17 | *
18 | * Design Goals:
19 | * 1. Performance: Optimized for handling thousands of methods with minimal latency
20 | * 2. Relevance:
21 | * - Method names weighted higher than interface names
22 | * - Results sorted by calculated relevance score
23 | * - Exact substring matches prioritized over subsequence matches
24 | * - Start-of-word matches given higher scores
25 | * 3. Match Types:
26 | * - Substring matching (e.g. "tag" finds "GetTagList")
27 | * - Subsequence matching (e.g. "getcurrent" finds "GetNumberOfCurrentPlayers")
28 | * 4. Usability Features:
29 | * - Matched portions highlighted with tags
30 | * - Interface names with numeric IDs receive lower scores
31 | * - Adaptive thresholds based on query length
32 | * 5. Implementation:
33 | * - Prefix and trigram indexes for efficient candidate selection
34 | * - Pre-cached lowercase versions of strings for performance
35 | * - No regex usage for critical path operations
36 | * - Clean, maintainable code structure
37 | * - Limited to top 100 most relevant results
38 | */
39 | export class ApiSearcher {
40 | private methodsList: {
41 | interface: string;
42 | method: string;
43 | interfaceLower: string;
44 | methodLower: string;
45 | hasAppIdSuffix: boolean;
46 | }[] = [];
47 | private prefixMap: Map = new Map();
48 | private trigrams: Map = new Map();
49 | private interfacePrefixMap: Map = new Map();
50 | private interfaceTrigrams: Map = new Map();
51 |
52 | constructor(apiServices: ApiServices) {
53 | // Build flat list of methods with pre-cached lowercase versions
54 | for (const interfaceName in apiServices) {
55 | const apiInterface = apiServices[interfaceName];
56 | const interfaceLower = interfaceName.toLowerCase();
57 |
58 | // Pre-check if interface matches the app ID pattern
59 | const hasAppIdSuffix = /_(?[0-9]+)$/.test(interfaceName);
60 |
61 | for (const methodName in apiInterface) {
62 | const methodLower = methodName.toLowerCase();
63 | this.methodsList.push({
64 | interface: interfaceName,
65 | method: methodName,
66 | interfaceLower,
67 | methodLower,
68 | hasAppIdSuffix,
69 | });
70 | }
71 | }
72 |
73 | // Build indexes for both methods and interfaces
74 | this.methodsList.forEach((item, index) => {
75 | // Method indexes
76 | this.indexString(item.methodLower, index, this.prefixMap, this.trigrams);
77 |
78 | // Interface indexes
79 | this.indexString(item.interfaceLower, index, this.interfacePrefixMap, this.interfaceTrigrams);
80 | });
81 | }
82 |
83 | private indexString(
84 | str: string,
85 | index: number,
86 | prefixMap: Map,
87 | trigramMap: Map,
88 | ): void {
89 | // Add each prefix of the string to the map
90 | for (let i = 1; i <= Math.min(str.length, 4); i++) {
91 | const prefix = str.substring(0, i);
92 | let arr = prefixMap.get(prefix);
93 | if (!arr) {
94 | arr = [];
95 | prefixMap.set(prefix, arr);
96 | }
97 | arr.push(index);
98 | }
99 |
100 | // Add trigrams for substring matching acceleration
101 | if (str.length >= 3) {
102 | for (let i = 0; i <= str.length - 3; i++) {
103 | const trigram = str.substring(i, i + 3);
104 | let arr = trigramMap.get(trigram);
105 | if (!arr) {
106 | arr = [];
107 | trigramMap.set(trigram, arr);
108 | }
109 | arr.push(index);
110 | }
111 | }
112 | }
113 |
114 | public search(query: string): SearchResult[] {
115 | if (!query || query.length === 0) {
116 | return [];
117 | }
118 |
119 | const normalizedQuery = query.toLowerCase().replace(/\s/g, '');
120 |
121 | // Get method candidates
122 | const methodCandidates = this.getMethodCandidates(normalizedQuery);
123 |
124 | // Find matching interfaces
125 | const matchingInterfaces = this.getMatchingInterfaces(normalizedQuery);
126 |
127 | // Process candidates and build results
128 | const results: SearchResult[] = [];
129 | const processedItems = new Set();
130 |
131 | // First process direct method matches
132 | for (const index of methodCandidates) {
133 | const item = this.methodsList[index];
134 |
135 | // Only process if it's an actual match
136 | if (!this.hasSubstringOrSubsequence(item.methodLower, normalizedQuery)) {
137 | continue;
138 | }
139 |
140 | processedItems.add(index);
141 |
142 | const methodResult = this.getScore(item.methodLower, normalizedQuery, 3);
143 | let totalScore = methodResult.score;
144 |
145 | // Apply penalty for interfaces with app ID suffix
146 | if (item.hasAppIdSuffix) {
147 | totalScore *= 0.5;
148 | }
149 |
150 | // For very short queries, apply a stricter threshold
151 | const minScoreThreshold = normalizedQuery.length < 3 ? 0.7 : 0.3;
152 |
153 | // Apply stricter scoring for common terms
154 | if (totalScore > minScoreThreshold) {
155 | results.push({
156 | interface: item.interface,
157 | method: item.method,
158 | indices: methodResult.indices,
159 | score: totalScore,
160 | });
161 | }
162 | }
163 |
164 | // Then add methods from matching interfaces (if not already added)
165 | for (const interfaceName of matchingInterfaces) {
166 | // Find all methods belonging to this interface
167 | for (let i = 0; i < this.methodsList.length; i++) {
168 | if (this.methodsList[i].interfaceLower === interfaceName) {
169 | // Skip if already processed
170 | if (processedItems.has(i)) continue;
171 |
172 | const item = this.methodsList[i];
173 | processedItems.add(i);
174 |
175 | // Check if method itself also matches (for highlighting)
176 | const methodResult = this.hasSubstringOrSubsequence(item.methodLower, normalizedQuery)
177 | ? this.getScore(item.methodLower, normalizedQuery, 3)
178 | : { score: 0, indices: [] };
179 |
180 | // Interface matches get a modest base score
181 | let interfaceScore = 0.5;
182 |
183 | // Apply penalty for interfaces with app ID suffix
184 | if (item.hasAppIdSuffix) {
185 | interfaceScore *= 0.9;
186 | }
187 |
188 | const totalScore = interfaceScore + methodResult.score;
189 |
190 | // For very short queries, we need a higher threshold for interface matches
191 | const minScoreThreshold = normalizedQuery.length < 3 ? 0.5 : 0.25;
192 |
193 | if (totalScore > minScoreThreshold) {
194 | results.push({
195 | interface: item.interface,
196 | method: item.method,
197 | indices: methodResult.indices,
198 | score: totalScore,
199 | });
200 | }
201 | }
202 | }
203 | }
204 |
205 | // Sort by score in descending order
206 | results.sort((a, b) => b.score - a.score);
207 |
208 | return results.slice(0, 100);
209 | }
210 |
211 | private getMethodCandidates(query: string): Set {
212 | const candidateSet = new Set();
213 |
214 | // Try prefix matching for methods (fastest)
215 | this.addCandidatesByPrefix(query, this.prefixMap, candidateSet);
216 |
217 | // Return early if we have enough candidates
218 | if (candidateSet.size >= 100) {
219 | return candidateSet;
220 | }
221 |
222 | // If query is long enough, try trigram matching
223 | if (query.length >= 3) {
224 | this.addCandidatesByTrigram(query, this.trigrams, candidateSet);
225 |
226 | // Return early if we have enough candidates
227 | if (candidateSet.size >= 100) {
228 | return candidateSet;
229 | }
230 | }
231 |
232 | // If still no candidates, include methods with first letter match
233 | if (candidateSet.size === 0 && query.length > 0) {
234 | const firstChar = query[0];
235 | for (let i = 0; i < this.methodsList.length; i++) {
236 | if (this.methodsList[i].methodLower.includes(firstChar)) {
237 | candidateSet.add(i);
238 | }
239 | }
240 | }
241 |
242 | return candidateSet;
243 | }
244 |
245 | private getMatchingInterfaces(query: string): Set {
246 | const matchingInterfaces = new Set();
247 |
248 | // Find matching interfaces using the candidate approach
249 | const candidateSet = new Set();
250 |
251 | // Try prefix matching for interfaces
252 | this.addCandidatesByPrefix(query, this.interfacePrefixMap, candidateSet);
253 |
254 | // If needed, try trigram matching
255 | if (candidateSet.size < 50 && query.length >= 3) {
256 | this.addCandidatesByTrigram(query, this.interfaceTrigrams, candidateSet);
257 | }
258 |
259 | // Check candidates
260 | for (const index of candidateSet) {
261 | const interfaceName = this.methodsList[index].interfaceLower;
262 | if (this.hasSubstringOrSubsequence(interfaceName, query)) {
263 | matchingInterfaces.add(interfaceName);
264 | }
265 | }
266 |
267 | return matchingInterfaces;
268 | }
269 |
270 | private addCandidatesByPrefix(query: string, prefixMap: Map, candidateSet: Set): void {
271 | const prefix = query.substring(0, Math.min(query.length, 4));
272 | const indices = prefixMap.get(prefix);
273 |
274 | if (indices) {
275 | indices.forEach((index) => candidateSet.add(index));
276 | }
277 | }
278 |
279 | private addCandidatesByTrigram(query: string, trigramMap: Map, candidateSet: Set): void {
280 | for (let i = 0; i <= query.length - 3; i++) {
281 | const trigram = query.substring(i, i + 3);
282 | const indices = trigramMap.get(trigram);
283 |
284 | if (indices) {
285 | indices.forEach((index) => candidateSet.add(index));
286 | }
287 | }
288 | }
289 |
290 | private hasSubstringOrSubsequence(text: string, query: string): boolean {
291 | // Quick substring check
292 | if (text.includes(query)) {
293 | return true;
294 | }
295 |
296 | // Quick subsequence check
297 | let j = 0;
298 | for (let i = 0; i < text.length && j < query.length; i++) {
299 | if (text[i] === query[j]) {
300 | j++;
301 | }
302 | }
303 | return j === query.length;
304 | }
305 |
306 | private getScore(text: string, query: string, weight: number): ScoreResult {
307 | // Substring match (highest score)
308 | const substringIndex = text.indexOf(query);
309 | if (substringIndex !== -1) {
310 | // Score based on position (earlier matches get higher scores)
311 | let positionScore = 1 - substringIndex / text.length;
312 |
313 | // Bonus for matches at start of words (camelCase detection)
314 | if (
315 | substringIndex === 0 ||
316 | (text[substringIndex - 1] >= 'a' &&
317 | text[substringIndex - 1] <= 'z' &&
318 | text[substringIndex] >= 'A' &&
319 | text[substringIndex] <= 'Z')
320 | ) {
321 | positionScore += 0.5;
322 | }
323 |
324 | // Apply length-based adjustment for short queries
325 | const lengthFactor = Math.min(1, query.length / 5);
326 |
327 | const indices = Array.from({ length: query.length }, (_, i) => substringIndex + i);
328 | return {
329 | score: weight * (1.0 + positionScore * 0.5) * lengthFactor,
330 | indices,
331 | };
332 | }
333 |
334 | // Subsequence match - only compute if length is manageable and query isn't too short
335 | if (query.length >= 2 && query.length <= text.length) {
336 | return this.getSubsequenceScore(text, query, weight);
337 | }
338 |
339 | return { score: 0, indices: [] };
340 | }
341 |
342 | private getSubsequenceScore(text: string, query: string, weight: number): ScoreResult {
343 | const matchIndices: number[] = [];
344 | let i = 0;
345 | let j = 0;
346 |
347 | // Try to find a subsequence match
348 | while (i < text.length && j < query.length) {
349 | if (text[i] === query[j]) {
350 | matchIndices.push(i);
351 | j++;
352 | }
353 | i++;
354 | }
355 |
356 | // If we didn't match all query characters
357 | if (j < query.length) {
358 | return { score: 0, indices: [] };
359 | }
360 |
361 | // Calculate score based on matches and gaps
362 | const textLength = text.length;
363 |
364 | // Calculate gap penalties
365 | let gapPenalty = 0;
366 | for (let k = 1; k < matchIndices.length; k++) {
367 | const gap = matchIndices[k] - matchIndices[k - 1] - 1;
368 | gapPenalty += gap / textLength;
369 | }
370 |
371 | // Normalize gap penalty
372 | gapPenalty = matchIndices.length > 1 ? gapPenalty / (matchIndices.length - 1) : 0;
373 |
374 | // Position bonus (earlier matches are better)
375 | const positionBonus = 1 - matchIndices[0] / textLength;
376 |
377 | // Calculate density bonus (denser matches are better)
378 | const densityRange = matchIndices[matchIndices.length - 1] - matchIndices[0] + 1;
379 | const density = densityRange > 0 ? matchIndices.length / densityRange : 1;
380 |
381 | // Length factor (longer queries are better)
382 | const lengthFactor = Math.min(1, query.length / 5);
383 |
384 | const score = weight * 0.4 * (1 - gapPenalty) * (1 + 0.3 * positionBonus + 0.3 * density) * lengthFactor;
385 |
386 | return { score, indices: matchIndices };
387 | }
388 | }
389 |
--------------------------------------------------------------------------------
/src/ssr.ts:
--------------------------------------------------------------------------------
1 | import { createSSRApp } from 'vue';
2 | import { renderToString } from 'vue/server-renderer';
3 | import App from './App.vue';
4 |
5 | const app = createSSRApp(App);
6 |
7 | renderToString(app).then((html) => {
8 | console.log(html);
9 | });
10 |
--------------------------------------------------------------------------------
/src/style.css:
--------------------------------------------------------------------------------
1 | @import "bootstrap/dist/css/bootstrap.css";
2 |
3 | a {
4 | color: #1f8bff;
5 | text-decoration: none;
6 | }
7 |
8 | a:hover {
9 | color: #99caff;
10 | text-decoration: underline;
11 | }
12 |
13 | code {
14 | color: #ec5fa1;
15 | }
16 |
17 | .card-body {
18 | padding-bottom: 0;
19 | }
20 |
21 | .card .table {
22 | font-size: 14px;
23 | margin-bottom: 0;
24 | }
25 |
26 | .card .table td:first-child,
27 | .card .table th:first-child {
28 | border-left: 0;
29 | }
30 |
31 | .card .table td:last-child,
32 | .card .table th:last-child {
33 | border-right: 0;
34 | }
35 |
36 | .card .btn-sm {
37 | padding-top: 0;
38 | padding-bottom: 0;
39 | line-height: 1.4;
40 | }
41 |
42 | .interface-list {
43 | padding: 0;
44 | list-style: none;
45 | }
46 |
47 | .method-list {
48 | padding: 0;
49 | list-style: none;
50 | font-size: 0.9rem;
51 | background-color: #2b3647;
52 | }
53 |
54 | .method-list a > b {
55 | color: #fff;
56 | }
57 |
58 | .interface-list li {
59 | text-overflow: ellipsis;
60 | overflow: hidden;
61 | white-space: nowrap;
62 | direction: rtl;
63 | }
64 |
65 | .header h1,
66 | .header h2,
67 | .header .separator {
68 | display: inline;
69 | font-size: 1.4rem;
70 | line-height: 38px;
71 | }
72 |
73 | .badge {
74 | transition: none;
75 | }
76 |
77 | a.badge {
78 | text-decoration: underline;
79 | }
80 |
81 | .no-select {
82 | user-select: none;
83 | }
84 |
85 | body {
86 | background-color: #161920;
87 | color: #acacac;
88 | }
89 |
90 | .header {
91 | background-color: #096295;
92 | border-bottom: 2px solid #67c1f5;
93 | color: #fff;
94 | }
95 |
96 | .search-input:focus,
97 | .search-input {
98 | border: 1px solid rgba(103, 193, 245, 0.7);
99 | background-color: #171b22;
100 | color: #fff;
101 | }
102 |
103 | .search-input:focus {
104 | box-shadow: 0 0 20px rgba(147, 211, 245, 0.5);
105 | }
106 |
107 | .search-input::placeholder {
108 | color: #888;
109 | }
110 |
111 | .header a {
112 | color: inherit;
113 | }
114 |
115 | .card {
116 | background: #22252b;
117 | color: #e5e5e5;
118 | border: 0;
119 | border-radius: 0;
120 | transition: box-shadow 0.2s;
121 | }
122 |
123 | .card:target {
124 | box-shadow: 0 0 0 5px #66c0f4;
125 | }
126 |
127 | .card-header {
128 | background: #4e5155;
129 | border: 0;
130 | top: 0;
131 | z-index: 10;
132 | }
133 |
134 | .card-header .badge {
135 | margin-right: 0.3rem;
136 | }
137 |
138 | .card-header .badge-version {
139 | margin-left: 0.3rem;
140 | }
141 |
142 | .card-inner-header {
143 | padding: 0.75rem 1.25rem;
144 | }
145 |
146 | .card-method-name {
147 | color: inherit;
148 | word-break: break-all;
149 | }
150 |
151 | .card hr {
152 | border-top: 1px solid #4e5155;
153 | }
154 |
155 | .card .table {
156 | color: #acacac;
157 | border-color: #161920;
158 | }
159 |
160 | .card .table > :not(caption) > * > * {
161 | /* bootstrap 5.3 fix */
162 | color: unset;
163 | background: unset;
164 | }
165 |
166 | .card .table .custom-control-label {
167 | color: #ddd;
168 | }
169 |
170 | .card .table th,
171 | .card .table td {
172 | border-color: #161920;
173 | }
174 |
175 | .card .table tr:nth-child(odd) td {
176 | background: #1d2027;
177 | }
178 |
179 | .attribute:hover {
180 | border-bottom-color: rgba(255, 255, 255, 0.4);
181 | }
182 |
183 | .card .btn-outline-primary,
184 | .card .form-select,
185 | .card .form-control {
186 | background-color: #232323;
187 | border: 1px solid #535354;
188 | color: #cda869;
189 | fill: currentColor;
190 | word-break: break-all;
191 | }
192 |
193 | .card .btn-outline-primary:hover {
194 | background-color: #535354;
195 | }
196 |
197 | .card .form-check-input:not(:checked) {
198 | background-color: #acacac;
199 | }
200 |
201 | .card .table .form-control {
202 | background-color: transparent;
203 | }
204 |
205 | .card .table .form-control:focus {
206 | background-color: rgba(13, 110, 253, 0.25);
207 | box-shadow: none;
208 | }
209 |
210 | .card .table .level-1 td:first-child {
211 | border-left: 10px solid #22252b;
212 | }
213 |
214 | .card .table .level-2 td:first-child {
215 | border-left: 20px solid #22252b;
216 | }
217 |
218 | .card .table .level-3 td:first-child {
219 | border-left: 30px solid #22252b;
220 | }
221 |
222 | .card .table .level-4 td:first-child {
223 | border-left: 40px solid #22252b;
224 | }
225 |
226 | .card .table .level-5 td:first-child {
227 | border-left: 50px solid #22252b;
228 | }
229 |
230 | .prefilled-key {
231 | color: #acacac;
232 | font-style: italic;
233 | }
234 |
235 | .interface-list-container + .interface-list-container {
236 | margin-top: 1rem;
237 | }
238 |
239 | .interface-group-name {
240 | margin-bottom: 0.1rem;
241 | color: #ddd;
242 | font-weight: bold;
243 | font-size: 1.2rem;
244 | }
245 |
246 | .interface-group-name::marker {
247 | color: #535354;
248 | }
249 |
250 | .interface-group-name:hover::marker,
251 | .interface-group-name:hover {
252 | color: #fff;
253 | }
254 |
255 | .interface-group-name img {
256 | border-radius: 3px;
257 | vertical-align: -5px;
258 | }
259 |
260 | .interface-list a {
261 | color: #98caff;
262 | }
263 |
264 | .interface-list a:hover {
265 | color: #fff;
266 | }
267 |
268 | .custom-control-label::before {
269 | background-color: transparent;
270 | }
271 |
272 | .no-email {
273 | color: hsl(45, 94%, 40%);
274 | background-color: hsl(46, 100%, 10%);
275 | border: 0;
276 | font-size: 13px;
277 | padding: 5px 10px;
278 | }
279 |
280 | .hidden-token,
281 | .hidden-key {
282 | font-size: 0;
283 | }
284 |
285 | .hidden-key:not(:empty):before {
286 | content: "key=is_here_but_hidden";
287 | font-size: 1rem;
288 | color: #997433;
289 | }
290 |
291 | .hidden-token:not(:empty):before {
292 | content: "access_token=is_here_but_hidden";
293 | font-size: 1rem;
294 | color: #997433;
295 | }
296 |
297 | .add-param-array {
298 | padding: 0;
299 | border-radius: 50%;
300 | height: 26px;
301 | width: 26px;
302 | line-height: 26px;
303 | float: right;
304 | border: 0;
305 | }
306 |
307 | .sidebar {
308 | scrollbar-gutter: stable;
309 | overflow-y: auto;
310 | scrollbar-width: thin;
311 | overscroll-behavior: contain;
312 | -webkit-overflow-scrolling: touch;
313 | }
314 |
315 | @media (max-width: 991px) {
316 | .sidebar {
317 | height: 200px;
318 | margin-top: 1.5rem;
319 | padding-top: 0 !important;
320 | padding-bottom: 0 !important;
321 | }
322 |
323 | .interface-list a {
324 | display: block;
325 | padding: 5px 0;
326 | }
327 |
328 | .header div[role="search"] {
329 | order: 2;
330 | margin-top: 0.5rem;
331 | }
332 |
333 | .card-method-name {
334 | display: block;
335 | }
336 |
337 | .value-column {
338 | min-width: 13em;
339 | }
340 | }
341 |
342 | @media (max-width: 575px) {
343 | .header .separator {
344 | display: none;
345 | }
346 |
347 | .header h1,
348 | .header h2 {
349 | display: block;
350 | font-size: 16px;
351 | line-height: 16px;
352 | }
353 | }
354 |
355 | @media (min-width: 992px) {
356 | html {
357 | scrollbar-gutter: stable;
358 | scroll-padding-top: 69px;
359 | }
360 |
361 | .header {
362 | height: 54px;
363 | position: sticky;
364 | top: 0;
365 | z-index: 100;
366 | }
367 |
368 | .sidebar {
369 | top: 54px;
370 | height: calc(100vh - 54px);
371 | position: sticky;
372 | }
373 |
374 | .card-header {
375 | position: sticky;
376 | top: 54px;
377 | }
378 |
379 | .value-column {
380 | width: 13em;
381 | }
382 | }
383 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@vue/tsconfig/tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "dist",
5 | "target": "esnext",
6 | "moduleResolution": "node",
7 | "forceConsistentCasingInFileNames": true,
8 | "strict": true,
9 | "skipLibCheck": true
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/update.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | cd "$(dirname "$0")"
4 |
5 | #disabled because it needs better check for valid apis (due to server errors?)
6 | #php generate_api_from_protos.php
7 |
8 | php generate_api.php
9 |
10 | git add -A
11 | git commit -a -m "Update Steam Web API reference" || exit 0
12 | git push
13 |
--------------------------------------------------------------------------------
/vue-shim.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | import { defineComponent } from 'vue';
3 |
4 | const component: ReturnType;
5 | export default component;
6 | }
7 |
--------------------------------------------------------------------------------
/wrangler.jsonc:
--------------------------------------------------------------------------------
1 | {
2 | "name": "steamapi",
3 | "compatibility_date": "2025-04-01",
4 | "assets": {
5 | "directory": "./public/",
6 | "not_found_handling": "404-page"
7 | }
8 | }
--------------------------------------------------------------------------------