mirror of
https://github.com/daydreamer-json/ak-endfield-api-archive.git
synced 2026-04-04 14:42:25 +02:00
refactor(pages): remove unused pages v1 dir
This commit is contained in:
@@ -1,8 +0,0 @@
|
|||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
charset = utf-8
|
|
||||||
end_of_line = lf
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
insert_final_newline = true
|
|
||||||
34
pages/.gitignore
vendored
34
pages/.gitignore
vendored
@@ -1,34 +0,0 @@
|
|||||||
# dependencies (bun install)
|
|
||||||
node_modules
|
|
||||||
|
|
||||||
# output
|
|
||||||
out
|
|
||||||
dist
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# code coverage
|
|
||||||
coverage
|
|
||||||
*.lcov
|
|
||||||
|
|
||||||
# logs
|
|
||||||
logs
|
|
||||||
_.log
|
|
||||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
|
||||||
|
|
||||||
# dotenv environment variable files
|
|
||||||
.env
|
|
||||||
.env.development.local
|
|
||||||
.env.test.local
|
|
||||||
.env.production.local
|
|
||||||
.env.local
|
|
||||||
|
|
||||||
# caches
|
|
||||||
.eslintcache
|
|
||||||
.cache
|
|
||||||
*.tsbuildinfo
|
|
||||||
|
|
||||||
# IntelliJ based IDEs
|
|
||||||
.idea
|
|
||||||
|
|
||||||
# Finder (MacOS) folder config
|
|
||||||
.DS_Store
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "./node_modules/oxfmt/configuration_schema.json",
|
|
||||||
"ignorePatterns": [
|
|
||||||
"output/raw/**/index_main.json",
|
|
||||||
"output/raw/**/index_initial.json"
|
|
||||||
],
|
|
||||||
"useTabs": false,
|
|
||||||
"printWidth": 120,
|
|
||||||
"tabWidth": 2,
|
|
||||||
"endOfLine": "lf",
|
|
||||||
"arrowParens": "always",
|
|
||||||
"bracketSameLine": false,
|
|
||||||
"bracketSpacing": true,
|
|
||||||
"htmlWhitespaceSensitivity": "css",
|
|
||||||
"insertFinalNewline": true,
|
|
||||||
"jsxSingleQuote": true,
|
|
||||||
"objectWrap": "preserve",
|
|
||||||
"proseWrap": "preserve",
|
|
||||||
"quoteProps": "as-needed",
|
|
||||||
"semi": true,
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "all"
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "@parcel/config-default",
|
|
||||||
"transformers": {
|
|
||||||
"*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
23
pages/.vscode/settings.json
vendored
23
pages/.vscode/settings.json
vendored
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"editor.formatOnSave": true,
|
|
||||||
"editor.defaultFormatter": "biomejs.biome",
|
|
||||||
"[json]": {
|
|
||||||
"editor.defaultFormatter": "biomejs.biome"
|
|
||||||
},
|
|
||||||
"[javascript]": {
|
|
||||||
"editor.defaultFormatter": "biomejs.biome"
|
|
||||||
},
|
|
||||||
"[javascriptreact]": {
|
|
||||||
"editor.defaultFormatter": "biomejs.biome"
|
|
||||||
},
|
|
||||||
"[typescript]": {
|
|
||||||
"editor.defaultFormatter": "biomejs.biome"
|
|
||||||
},
|
|
||||||
"[typescriptreact]": {
|
|
||||||
"editor.defaultFormatter": "biomejs.biome"
|
|
||||||
},
|
|
||||||
"editor.codeActionsOnSave": {
|
|
||||||
"source.fixAll.biome": "explicit",
|
|
||||||
"source.organizeImports.biome": "explicit"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# pages
|
|
||||||
|
|
||||||
A very simple and very lame information display page written in HTML, CSS, and TypeScript
|
|
||||||
|
|
||||||
## For dev
|
|
||||||
|
|
||||||
Live server:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun x parcel
|
|
||||||
```
|
|
||||||
|
|
||||||
Build:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun x parcel build
|
|
||||||
```
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://biomejs.dev/schemas/2.2.4/schema.json",
|
|
||||||
"vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true },
|
|
||||||
"files": { "ignoreUnknown": false, "includes": ["**"] },
|
|
||||||
"formatter": {
|
|
||||||
"enabled": true,
|
|
||||||
"useEditorconfig": true,
|
|
||||||
"formatWithErrors": false,
|
|
||||||
"indentStyle": "space",
|
|
||||||
"indentWidth": 2,
|
|
||||||
"lineEnding": "lf",
|
|
||||||
"lineWidth": 120,
|
|
||||||
"attributePosition": "auto",
|
|
||||||
"bracketSpacing": true
|
|
||||||
},
|
|
||||||
"linter": { "enabled": false, "rules": { "recommended": true } },
|
|
||||||
"javascript": {
|
|
||||||
"formatter": {
|
|
||||||
"jsxQuoteStyle": "single",
|
|
||||||
"quoteProperties": "asNeeded",
|
|
||||||
"trailingCommas": "all",
|
|
||||||
"semicolons": "always",
|
|
||||||
"arrowParentheses": "always",
|
|
||||||
"bracketSameLine": false,
|
|
||||||
"quoteStyle": "single",
|
|
||||||
"attributePosition": "auto",
|
|
||||||
"bracketSpacing": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"assist": {
|
|
||||||
"enabled": true,
|
|
||||||
"actions": {
|
|
||||||
"source": {
|
|
||||||
"organizeImports": "on"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
517
pages/bun.lock
517
pages/bun.lock
@@ -1,517 +0,0 @@
|
|||||||
{
|
|
||||||
"lockfileVersion": 1,
|
|
||||||
"configVersion": 1,
|
|
||||||
"workspaces": {
|
|
||||||
"": {
|
|
||||||
"name": "pages",
|
|
||||||
"dependencies": {
|
|
||||||
"bootstrap": "^5.3.8",
|
|
||||||
"bootstrap-icons": "^1.13.1",
|
|
||||||
"ky": "^1.14.3",
|
|
||||||
"luxon": "^3.7.2",
|
|
||||||
"qs": "^6.15.0",
|
|
||||||
"semver": "^7.7.4",
|
|
||||||
"uuid": "^13.0.0",
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@biomejs/biome": "^2.4.4",
|
|
||||||
"@parcel/transformer-typescript-tsc": "^2.16.4",
|
|
||||||
"@tsconfig/bun": "^1.0.10",
|
|
||||||
"@tsconfig/node24": "^24.0.4",
|
|
||||||
"@tsconfig/recommended": "^1.0.13",
|
|
||||||
"@tsconfig/strictest": "^2.0.8",
|
|
||||||
"@types/bootstrap": "^5.2.10",
|
|
||||||
"@types/bun": "latest",
|
|
||||||
"@types/luxon": "^3.7.1",
|
|
||||||
"@types/qs": "^6.14.0",
|
|
||||||
"@types/semver": "^7.7.1",
|
|
||||||
"oxfmt": "^0.35.0",
|
|
||||||
"parcel": "^2.16.4",
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"typescript": "^5",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@biomejs/biome": ["@biomejs/biome@2.4.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.4", "@biomejs/cli-darwin-x64": "2.4.4", "@biomejs/cli-linux-arm64": "2.4.4", "@biomejs/cli-linux-arm64-musl": "2.4.4", "@biomejs/cli-linux-x64": "2.4.4", "@biomejs/cli-linux-x64-musl": "2.4.4", "@biomejs/cli-win32-arm64": "2.4.4", "@biomejs/cli-win32-x64": "2.4.4" }, "bin": { "biome": "bin/biome" } }, "sha512-tigwWS5KfJf0cABVd52NVaXyAVv4qpUXOWJ1rxFL8xF1RVoeS2q/LK+FHgYoKMclJCuRoCWAPy1IXaN9/mS61Q=="],
|
|
||||||
|
|
||||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-jZ+Xc6qvD6tTH5jM6eKX44dcbyNqJHssfl2nnwT6vma6B1sj7ZLTGIk6N5QwVBs5xGN52r3trk5fgd3sQ9We9A=="],
|
|
||||||
|
|
||||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-Dh1a/+W+SUCXhEdL7TiX3ArPTFCQKJTI1mGncZNWfO+6suk+gYA4lNyJcBB+pwvF49uw0pEbUS49BgYOY4hzUg=="],
|
|
||||||
|
|
||||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-V/NFfbWhsUU6w+m5WYbBenlEAz8eYnSqRMDMAW3K+3v0tYVkNyZn8VU0XPxk/lOqNXLSCCrV7FmV/u3SjCBShg=="],
|
|
||||||
|
|
||||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-+sPAXq3bxmFwhVFJnSwkSF5Rw2ZAJMH3MF6C9IveAEOdSpgajPhoQhbbAK12SehN9j2QrHpk4J/cHsa/HqWaYQ=="],
|
|
||||||
|
|
||||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.4", "", { "os": "linux", "cpu": "x64" }, "sha512-R4+ZCDtG9kHArasyBO+UBD6jr/FcFCTH8QkNTOCu0pRJzCWyWC4EtZa2AmUZB5h3e0jD7bRV2KvrENcf8rndBg=="],
|
|
||||||
|
|
||||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gGvFTGpOIQDb5CQ2VC0n9Z2UEqlP46c4aNgHmAMytYieTGEcfqhfCFnhs6xjt0S3igE6q5GLuIXtdQt3Izok+g=="],
|
|
||||||
|
|
||||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-trzCqM7x+Gn832zZHgr28JoYagQNX4CZkUZhMUac2YxvvyDRLJDrb5m9IA7CaZLlX6lTQmADVfLEKP1et1Ma4Q=="],
|
|
||||||
|
|
||||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.4", "", { "os": "win32", "cpu": "x64" }, "sha512-gnOHKVPFAAPrpoPt2t+Q6FZ7RPry/FDV3GcpU53P3PtLNnQjBmKyN2Vh/JtqXet+H4pme8CC76rScwdjDcT1/A=="],
|
|
||||||
|
|
||||||
"@lezer/common": ["@lezer/common@1.5.1", "", {}, "sha512-6YRVG9vBkaY7p1IVxL4s44n5nUnaNnGM2/AckNgYOnxTG2kWh1vR8BMxPseWPjRNpb5VtXnMpeYAEAADoRV1Iw=="],
|
|
||||||
|
|
||||||
"@lezer/lr": ["@lezer/lr@1.4.8", "", { "dependencies": { "@lezer/common": "^1.0.0" } }, "sha512-bPWa0Pgx69ylNlMlPvBPryqeLYQjyJjqPx+Aupm5zydLIF3NE+6MMLT8Yi23Bd9cif9VS00aUebn+6fDIGBcDA=="],
|
|
||||||
|
|
||||||
"@lmdb/lmdb-darwin-arm64": ["@lmdb/lmdb-darwin-arm64@2.8.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-KPDeVScZgA1oq0CiPBcOa3kHIqU+pTOwRFDIhxvmf8CTNvqdZQYp5cCKW0bUk69VygB2PuTiINFWbY78aR2pQw=="],
|
|
||||||
|
|
||||||
"@lmdb/lmdb-darwin-x64": ["@lmdb/lmdb-darwin-x64@2.8.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-w/sLhN4T7MW1nB3R/U8WK5BgQLz904wh+/SmA2jD8NnF7BLLoUgflCNxOeSPOWp8geP6nP/+VjWzZVip7rZ1ug=="],
|
|
||||||
|
|
||||||
"@lmdb/lmdb-linux-arm": ["@lmdb/lmdb-linux-arm@2.8.5", "", { "os": "linux", "cpu": "arm" }, "sha512-c0TGMbm2M55pwTDIfkDLB6BpIsgxV4PjYck2HiOX+cy/JWiBXz32lYbarPqejKs9Flm7YVAKSILUducU9g2RVg=="],
|
|
||||||
|
|
||||||
"@lmdb/lmdb-linux-arm64": ["@lmdb/lmdb-linux-arm64@2.8.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-vtbZRHH5UDlL01TT5jB576Zox3+hdyogvpcbvVJlmU5PdL3c5V7cj1EODdh1CHPksRl+cws/58ugEHi8bcj4Ww=="],
|
|
||||||
|
|
||||||
"@lmdb/lmdb-linux-x64": ["@lmdb/lmdb-linux-x64@2.8.5", "", { "os": "linux", "cpu": "x64" }, "sha512-Xkc8IUx9aEhP0zvgeKy7IQ3ReX2N8N1L0WPcQwnZweWmOuKfwpS3GRIYqLtK5za/w3E60zhFfNdS+3pBZPytqQ=="],
|
|
||||||
|
|
||||||
"@lmdb/lmdb-win32-x64": ["@lmdb/lmdb-win32-x64@2.8.5", "", { "os": "win32", "cpu": "x64" }, "sha512-4wvrf5BgnR8RpogHhtpCPJMKBmvyZPhhUtEwMJbXh0ni2BucpfF07jlmyM11zRqQ2XIq6PbC2j7W7UCCcm1rRQ=="],
|
|
||||||
|
|
||||||
"@mischnic/json-sourcemap": ["@mischnic/json-sourcemap@0.1.1", "", { "dependencies": { "@lezer/common": "^1.0.0", "@lezer/lr": "^1.0.0", "json5": "^2.2.1" } }, "sha512-iA7+tyVqfrATAIsIRWQG+a7ZLLD0VaOCKV2Wd/v4mqIU3J9c4jx9p7S0nw1XH3gJCKNBOOwACOPYYSUu9pgT+w=="],
|
|
||||||
|
|
||||||
"@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="],
|
|
||||||
|
|
||||||
"@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="],
|
|
||||||
|
|
||||||
"@msgpackr-extract/msgpackr-extract-linux-arm": ["@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3", "", { "os": "linux", "cpu": "arm" }, "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw=="],
|
|
||||||
|
|
||||||
"@msgpackr-extract/msgpackr-extract-linux-arm64": ["@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg=="],
|
|
||||||
|
|
||||||
"@msgpackr-extract/msgpackr-extract-linux-x64": ["@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg=="],
|
|
||||||
|
|
||||||
"@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ=="],
|
|
||||||
|
|
||||||
"@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.35.0", "", { "os": "android", "cpu": "arm" }, "sha512-BaRKlM3DyG81y/xWTsE6gZiv89F/3pHe2BqX2H4JbiB8HNVlWWtplzgATAE5IDSdwChdeuWLDTQzJ92Lglw3ZA=="],
|
|
||||||
|
|
||||||
"@oxfmt/binding-android-arm64": ["@oxfmt/binding-android-arm64@0.35.0", "", { "os": "android", "cpu": "arm64" }, "sha512-/O+EbuAJYs6nde/anv+aID6uHsGQApyE9JtYBo/79KyU8e6RBN3DMbT0ix97y1SOnCglurmL2iZ+hlohjP2PnQ=="],
|
|
||||||
|
|
||||||
"@oxfmt/binding-darwin-arm64": ["@oxfmt/binding-darwin-arm64@0.35.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-pGqRtqlNdn9d4VrmGUWVyQjkw79ryhI6je9y2jfqNUIZCfqceob+R97YYAoG7C5TFyt8ILdLVoN+L2vw/hSFyA=="],
|
|
||||||
|
|
||||||
"@oxfmt/binding-darwin-x64": ["@oxfmt/binding-darwin-x64@0.35.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-8GmsDcSozTPjrCJeGpp+sCmS9+9V5yRrdEZ1p/sTWxPG5nYeAfSLuS0nuEYjXSO+CtdSbStIW6dxa+4NM58yRw=="],
|
|
||||||
|
|
||||||
"@oxfmt/binding-freebsd-x64": ["@oxfmt/binding-freebsd-x64@0.35.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-QyfKfTe0ytHpFKHAcHCGQEzN45QSqq1AHJOYYxQMgLM3KY4xu8OsXHpCnINjDsV4XGnQzczJDU9e04Zmd8XqIQ=="],
|
|
||||||
|
|
||||||
"@oxfmt/binding-linux-arm-gnueabihf": ["@oxfmt/binding-linux-arm-gnueabihf@0.35.0", "", { "os": "linux", "cpu": "arm" }, "sha512-u+kv3JD6P3J38oOyUaiCqgY5TNESzBRZJ5lyZQ6c2czUW2v5SIN9E/KWWa9vxoc+P8AFXQFUVrdzGy1tK+nbPQ=="],
|
|
||||||
|
|
||||||
"@oxfmt/binding-linux-arm-musleabihf": ["@oxfmt/binding-linux-arm-musleabihf@0.35.0", "", { "os": "linux", "cpu": "arm" }, "sha512-1NiZroCiV57I7Pf8kOH4XGR366kW5zir3VfSMBU2D0V14GpYjiYmPYFAoJboZvp8ACnZKUReWyMkNKSa5ad58A=="],
|
|
||||||
|
|
||||||
"@oxfmt/binding-linux-arm64-gnu": ["@oxfmt/binding-linux-arm64-gnu@0.35.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-7Q0Xeg7ZnW2nxnZ4R7aF6DEbCFls4skgHZg+I63XitpNvJCbVIU8MFOTZlvZGRsY9+rPgWPQGeUpLHlyx0wvMA=="],
|
|
||||||
|
|
||||||
"@oxfmt/binding-linux-arm64-musl": ["@oxfmt/binding-linux-arm64-musl@0.35.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Okqi+uhYFxwKz8hcnUftNNwdm8BCkf6GSCbcz9xJxYMm87k1E4p7PEmAAbhLTk7cjSdDre6TDL0pDzNX+Y22Q=="],
|
|
||||||
|
|
||||||
"@oxfmt/binding-linux-ppc64-gnu": ["@oxfmt/binding-linux-ppc64-gnu@0.35.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9k66pbZQXM/lBJWys3Xbc5yhl4JexyfqkEf/tvtq8976VIJnLAAL3M127xHA3ifYSqxdVHfVGTg84eiBHCGcNw=="],
|
|
||||||
|
|
||||||
"@oxfmt/binding-linux-riscv64-gnu": ["@oxfmt/binding-linux-riscv64-gnu@0.35.0", "", { "os": "linux", "cpu": "none" }, "sha512-aUcY9ofKPtjO52idT6t0SAQvEF6ctjzUQa1lLp7GDsRpSBvuTrBQGeq0rYKz3gN8dMIQ7mtMdGD9tT4LhR8jAQ=="],
|
|
||||||
|
|
||||||
"@oxfmt/binding-linux-riscv64-musl": ["@oxfmt/binding-linux-riscv64-musl@0.35.0", "", { "os": "linux", "cpu": "none" }, "sha512-C6yhY5Hvc2sGM+mCPek9ZLe5xRUOC/BvhAt2qIWFAeXMn4il04EYIjl3DsWiJr0xDMTJhvMOmD55xTRPlNp39w=="],
|
|
||||||
|
|
||||||
"@oxfmt/binding-linux-s390x-gnu": ["@oxfmt/binding-linux-s390x-gnu@0.35.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-RG2hlvOMK4OMZpO3mt8MpxLQ0AAezlFqhn5mI/g5YrVbPFyoCv9a34AAvbSJS501ocOxlFIRcKEuw5hFvddf9g=="],
|
|
||||||
|
|
||||||
"@oxfmt/binding-linux-x64-gnu": ["@oxfmt/binding-linux-x64-gnu@0.35.0", "", { "os": "linux", "cpu": "x64" }, "sha512-wzmh90Pwvqj9xOKHJjkQYBpydRkaXG77ZvDz+iFDRRQpnqIEqGm5gmim2s6vnZIkDGsvKCuTdtxm0GFmBjM1+w=="],
|
|
||||||
|
|
||||||
"@oxfmt/binding-linux-x64-musl": ["@oxfmt/binding-linux-x64-musl@0.35.0", "", { "os": "linux", "cpu": "x64" }, "sha512-+HCqYCJPCUy5I+b2cf+gUVaApfgtoQT3HdnSg/l7NIcLHOhKstlYaGyrFZLmUpQt4WkFbpGKZZayG6zjRU0KFA=="],
|
|
||||||
|
|
||||||
"@oxfmt/binding-openharmony-arm64": ["@oxfmt/binding-openharmony-arm64@0.35.0", "", { "os": "none", "cpu": "arm64" }, "sha512-kFYmWfR9YL78XyO5ws+1dsxNvZoD973qfVMNFOS4e9bcHXGF7DvGC2tY5UDFwyMCeB33t3sDIuGONKggnVNSJA=="],
|
|
||||||
|
|
||||||
"@oxfmt/binding-win32-arm64-msvc": ["@oxfmt/binding-win32-arm64-msvc@0.35.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-uD/NGdM65eKNCDGyTGdO8e9n3IHX+wwuorBvEYrPJXhDXL9qz6gzddmXH8EN04ejUXUujlq4FsoSeCfbg0Y+Jg=="],
|
|
||||||
|
|
||||||
"@oxfmt/binding-win32-ia32-msvc": ["@oxfmt/binding-win32-ia32-msvc@0.35.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-oSRD2k8J2uxYDEKR2nAE/YTY9PobOEnhZgCmspHu0+yBQ665yH8lFErQVSTE7fcGJmJp/cC6322/gc8VFuQf7g=="],
|
|
||||||
|
|
||||||
"@oxfmt/binding-win32-x64-msvc": ["@oxfmt/binding-win32-x64-msvc@0.35.0", "", { "os": "win32", "cpu": "x64" }, "sha512-WCDJjlS95NboR0ugI2BEwzt1tYvRDorDRM9Lvctls1SLyKYuNRCyrPwp1urUPFBnwgBNn9p2/gnmo7gFMySRoQ=="],
|
|
||||||
|
|
||||||
"@parcel/bundler-default": ["@parcel/bundler-default@2.16.4", "", { "dependencies": { "@parcel/diagnostic": "2.16.4", "@parcel/graph": "3.6.4", "@parcel/plugin": "2.16.4", "@parcel/rust": "2.16.4", "@parcel/utils": "2.16.4", "nullthrows": "^1.1.1" } }, "sha512-Nb8peNvhfm1+660CLwssWh4weY+Mv6vEGS6GPKqzJmTMw50udi0eS1YuWFzvmhSiu1KsYcUD37mqQ1LuIDtWoA=="],
|
|
||||||
|
|
||||||
"@parcel/cache": ["@parcel/cache@2.16.4", "", { "dependencies": { "@parcel/fs": "2.16.4", "@parcel/logger": "2.16.4", "@parcel/utils": "2.16.4", "lmdb": "2.8.5" }, "peerDependencies": { "@parcel/core": "^2.16.4" } }, "sha512-+uCyeElSga2MBbmbXpIj/WVKH7TByCrKaxtHbelfKKIJpYMgEHVjO4cuc7GUfTrUAmRUS8ZGvnX7Etgq6/jQhw=="],
|
|
||||||
|
|
||||||
"@parcel/codeframe": ["@parcel/codeframe@2.16.4", "", { "dependencies": { "chalk": "^4.1.2" } }, "sha512-s64aMfOJoPrXhKH+Y98ahX0O8aXWvTR+uNlOaX4yFkpr4FFDnviLcGngDe/Yo4Qq2FJZ0P6dNswbJTUH9EGxkQ=="],
|
|
||||||
|
|
||||||
"@parcel/compressor-raw": ["@parcel/compressor-raw@2.16.4", "", { "dependencies": { "@parcel/plugin": "2.16.4" } }, "sha512-IK8IpNhw61B2HKgA1JhGhO9y+ZJFRZNTEmvhN1NdLdPqvgEXm2EunT+m6D9z7xeoeT6XnUKqM0eRckEdD0OXbA=="],
|
|
||||||
|
|
||||||
"@parcel/config-default": ["@parcel/config-default@2.16.4", "", { "dependencies": { "@parcel/bundler-default": "2.16.4", "@parcel/compressor-raw": "2.16.4", "@parcel/namer-default": "2.16.4", "@parcel/optimizer-css": "2.16.4", "@parcel/optimizer-html": "2.16.4", "@parcel/optimizer-image": "2.16.4", "@parcel/optimizer-svg": "2.16.4", "@parcel/optimizer-swc": "2.16.4", "@parcel/packager-css": "2.16.4", "@parcel/packager-html": "2.16.4", "@parcel/packager-js": "2.16.4", "@parcel/packager-raw": "2.16.4", "@parcel/packager-svg": "2.16.4", "@parcel/packager-wasm": "2.16.4", "@parcel/reporter-dev-server": "2.16.4", "@parcel/resolver-default": "2.16.4", "@parcel/runtime-browser-hmr": "2.16.4", "@parcel/runtime-js": "2.16.4", "@parcel/runtime-rsc": "2.16.4", "@parcel/runtime-service-worker": "2.16.4", "@parcel/transformer-babel": "2.16.4", "@parcel/transformer-css": "2.16.4", "@parcel/transformer-html": "2.16.4", "@parcel/transformer-image": "2.16.4", "@parcel/transformer-js": "2.16.4", "@parcel/transformer-json": "2.16.4", "@parcel/transformer-node": "2.16.4", "@parcel/transformer-postcss": "2.16.4", "@parcel/transformer-posthtml": "2.16.4", "@parcel/transformer-raw": "2.16.4", "@parcel/transformer-react-refresh-wrap": "2.16.4", "@parcel/transformer-svg": "2.16.4" }, "peerDependencies": { "@parcel/core": "^2.16.4" } }, "sha512-kBxuTY/5trEVnvXk92l7LVkYjNuz3SaqWymFhPjEnc8GY4ZVdcWrWdXWTB9hUhpmRYJctFCyGvM0nN05JTiM2g=="],
|
|
||||||
|
|
||||||
"@parcel/core": ["@parcel/core@2.16.4", "", { "dependencies": { "@mischnic/json-sourcemap": "^0.1.1", "@parcel/cache": "2.16.4", "@parcel/diagnostic": "2.16.4", "@parcel/events": "2.16.4", "@parcel/feature-flags": "2.16.4", "@parcel/fs": "2.16.4", "@parcel/graph": "3.6.4", "@parcel/logger": "2.16.4", "@parcel/package-manager": "2.16.4", "@parcel/plugin": "2.16.4", "@parcel/profiler": "2.16.4", "@parcel/rust": "2.16.4", "@parcel/source-map": "^2.1.1", "@parcel/types": "2.16.4", "@parcel/utils": "2.16.4", "@parcel/workers": "2.16.4", "base-x": "^3.0.11", "browserslist": "^4.24.5", "clone": "^2.1.2", "dotenv": "^16.5.0", "dotenv-expand": "^11.0.7", "json5": "^2.2.3", "msgpackr": "^1.11.2", "nullthrows": "^1.1.1", "semver": "^7.7.1" } }, "sha512-a0CgrW5A5kwuSu5J1RFRoMQaMs9yagvfH2jJMYVw56+/7NRI4KOtu612SG9Y1ERWfY55ZwzyFxtLWvD6LO+Anw=="],
|
|
||||||
|
|
||||||
"@parcel/diagnostic": ["@parcel/diagnostic@2.16.4", "", { "dependencies": { "@mischnic/json-sourcemap": "^0.1.1", "nullthrows": "^1.1.1" } }, "sha512-YN5CfX7lFd6yRLxyZT4Sj3sR6t7nnve4TdXSIqapXzQwL7Bw+sj79D95wTq2rCm3mzk5SofGxFAXul2/nG6gcQ=="],
|
|
||||||
|
|
||||||
"@parcel/error-overlay": ["@parcel/error-overlay@2.16.4", "", {}, "sha512-e8KYKnMsfmQnqIhsUWBUZAXlDK30wkxsAGle1tZ0gOdoplaIdVq/WjGPatHLf6igLM76c3tRn2vw8jZFput0jw=="],
|
|
||||||
|
|
||||||
"@parcel/events": ["@parcel/events@2.16.4", "", {}, "sha512-slWQkBRAA7o0cN0BLEd+yCckPmlVRVhBZn5Pn6ktm4EzEtrqoMzMeJOxxH8TXaRzrQDYnTcnYIHFgXWd4kkUfg=="],
|
|
||||||
|
|
||||||
"@parcel/feature-flags": ["@parcel/feature-flags@2.16.4", "", {}, "sha512-nYdx53siKPLYikHHxfzgjzzgxdrjquK6DMnuSgOTyIdRG4VHdEN0+NqKijRLuVgiUFo/dtxc2h+amwqFENMw8w=="],
|
|
||||||
|
|
||||||
"@parcel/fs": ["@parcel/fs@2.16.4", "", { "dependencies": { "@parcel/feature-flags": "2.16.4", "@parcel/rust": "2.16.4", "@parcel/types-internal": "2.16.4", "@parcel/utils": "2.16.4", "@parcel/watcher": "^2.0.7", "@parcel/workers": "2.16.4" }, "peerDependencies": { "@parcel/core": "^2.16.4" } }, "sha512-maCMOiVn7oJYZlqlfxgLne8n6tSktIT1k0AeyBp4UGWCXyeJUJ+nL7QYShFpKNLtMLeF0cEtgwRAknWzbcDS1g=="],
|
|
||||||
|
|
||||||
"@parcel/graph": ["@parcel/graph@3.6.4", "", { "dependencies": { "@parcel/feature-flags": "2.16.4", "nullthrows": "^1.1.1" } }, "sha512-Cj9yV+/k88kFhE+D+gz0YuNRpvNOCVDskO9pFqkcQhGbsGq6kg2XpZ9V7HlYraih31xf8Vb589bZOwjKIiHixQ=="],
|
|
||||||
|
|
||||||
"@parcel/logger": ["@parcel/logger@2.16.4", "", { "dependencies": { "@parcel/diagnostic": "2.16.4", "@parcel/events": "2.16.4" } }, "sha512-QR8QLlKo7xAy9JBpPDAh0RvluaixqPCeyY7Fvo2K7hrU3r85vBNNi06pHiPbWoDmB4x1+QoFwMaGnJOHR+/fMA=="],
|
|
||||||
|
|
||||||
"@parcel/markdown-ansi": ["@parcel/markdown-ansi@2.16.4", "", { "dependencies": { "chalk": "^4.1.2" } }, "sha512-0+oQApAVF3wMcQ6d1ZfZ0JsRzaMUYj9e4U+naj6YEsFsFGOPp+pQYKXBf1bobQeeB7cPKPT3SUHxFqced722Hw=="],
|
|
||||||
|
|
||||||
"@parcel/namer-default": ["@parcel/namer-default@2.16.4", "", { "dependencies": { "@parcel/diagnostic": "2.16.4", "@parcel/plugin": "2.16.4", "nullthrows": "^1.1.1" } }, "sha512-CE+0lFg881sJq575EXxj2lKUn81tsS5itpNUUErHxit195m3PExyAhoXM6ed/SXxwi+uv+T5FS/jjDLBNuUFDA=="],
|
|
||||||
|
|
||||||
"@parcel/node-resolver-core": ["@parcel/node-resolver-core@3.7.4", "", { "dependencies": { "@mischnic/json-sourcemap": "^0.1.1", "@parcel/diagnostic": "2.16.4", "@parcel/fs": "2.16.4", "@parcel/rust": "2.16.4", "@parcel/utils": "2.16.4", "nullthrows": "^1.1.1", "semver": "^7.7.1" } }, "sha512-b3VDG+um6IWW5CTod6M9hQsTX5mdIelKmam7mzxzgqg4j5hnycgTWqPMc9UxhYoUY/Q/PHfWepccNcKtvP5JiA=="],
|
|
||||||
|
|
||||||
"@parcel/optimizer-css": ["@parcel/optimizer-css@2.16.4", "", { "dependencies": { "@parcel/diagnostic": "2.16.4", "@parcel/plugin": "2.16.4", "@parcel/source-map": "^2.1.1", "@parcel/utils": "2.16.4", "browserslist": "^4.24.5", "lightningcss": "^1.30.1", "nullthrows": "^1.1.1" } }, "sha512-aqdXCtmvpcXYgJFGk2DtXF34wuM2TD1fZorKMrJdKB9sSkWVRs1tq6RAXQrbi0ZPDH9wfE/9An3YdkTex7RHuQ=="],
|
|
||||||
|
|
||||||
"@parcel/optimizer-html": ["@parcel/optimizer-html@2.16.4", "", { "dependencies": { "@parcel/plugin": "2.16.4", "@parcel/rust": "2.16.4", "@parcel/utils": "2.16.4" } }, "sha512-vg/R2uuSni+NYYUUV8m+5bz8p5zBv8wc/nNleoBnGuCDwn7uaUwTZ8Gt9CjZO8jjG0xCLILoc/TW+e2FF3pfgQ=="],
|
|
||||||
|
|
||||||
"@parcel/optimizer-image": ["@parcel/optimizer-image@2.16.4", "", { "dependencies": { "@parcel/diagnostic": "2.16.4", "@parcel/plugin": "2.16.4", "@parcel/rust": "2.16.4", "@parcel/utils": "2.16.4", "@parcel/workers": "2.16.4" }, "peerDependencies": { "@parcel/core": "^2.16.4" } }, "sha512-2RV54WnvMYr18lxSx7Zlx/DXpJwMzOiPxDnoFyvaUoYutvgHO6chtcgFgh1Bvw/PoI95vYzlTkZ8QfUOk5A0JA=="],
|
|
||||||
|
|
||||||
"@parcel/optimizer-svg": ["@parcel/optimizer-svg@2.16.4", "", { "dependencies": { "@parcel/plugin": "2.16.4", "@parcel/rust": "2.16.4", "@parcel/utils": "2.16.4" } }, "sha512-22+BqIffCrVErg8y2XwhasbTaFNn75OKXZ3KTDBIfOSAZKLUKs1iHfDXETzTRN7cVcS+Q36/6EHd7N/RA8i1fg=="],
|
|
||||||
|
|
||||||
"@parcel/optimizer-swc": ["@parcel/optimizer-swc@2.16.4", "", { "dependencies": { "@parcel/diagnostic": "2.16.4", "@parcel/plugin": "2.16.4", "@parcel/source-map": "^2.1.1", "@parcel/utils": "2.16.4", "@swc/core": "^1.11.24", "nullthrows": "^1.1.1" } }, "sha512-+URqwnB6u1gqaLbG1O1DDApH+UVj4WCbK9No1fdxLBxQ9a84jyli25o1kK1hYB9Nb/JMyYNnEBfvYUW6RphOxw=="],
|
|
||||||
|
|
||||||
"@parcel/package-manager": ["@parcel/package-manager@2.16.4", "", { "dependencies": { "@parcel/diagnostic": "2.16.4", "@parcel/fs": "2.16.4", "@parcel/logger": "2.16.4", "@parcel/node-resolver-core": "3.7.4", "@parcel/types": "2.16.4", "@parcel/utils": "2.16.4", "@parcel/workers": "2.16.4", "@swc/core": "^1.11.24", "semver": "^7.7.1" }, "peerDependencies": { "@parcel/core": "^2.16.4" } }, "sha512-obWv9gZgdnkT3Kd+fBkKjhdNEY7zfOP5gVaox5i4nQstVCaVnDlMv5FwLEXwehL+WbwEcGyEGGxOHHkAFKk7Cg=="],
|
|
||||||
|
|
||||||
"@parcel/packager-css": ["@parcel/packager-css@2.16.4", "", { "dependencies": { "@parcel/diagnostic": "2.16.4", "@parcel/plugin": "2.16.4", "@parcel/source-map": "^2.1.1", "@parcel/utils": "2.16.4", "lightningcss": "^1.30.1", "nullthrows": "^1.1.1" } }, "sha512-rWRtfiX+VVIOZvq64jpeNUKkvWAbnokfHQsk/js1s5jD4ViNQgPcNLiRaiIANjymqL6+dQqWvGUSW2a5FAZYfg=="],
|
|
||||||
|
|
||||||
"@parcel/packager-html": ["@parcel/packager-html@2.16.4", "", { "dependencies": { "@parcel/plugin": "2.16.4", "@parcel/rust": "2.16.4", "@parcel/types": "2.16.4", "@parcel/utils": "2.16.4" } }, "sha512-AWo5f6SSqBsg2uWOsX0gPX8hCx2iE6GYLg2Z4/cDy2mPlwDICN8/bxItEztSZFmObi+ti26eetBKRDxAUivyIQ=="],
|
|
||||||
|
|
||||||
"@parcel/packager-js": ["@parcel/packager-js@2.16.4", "", { "dependencies": { "@parcel/diagnostic": "2.16.4", "@parcel/plugin": "2.16.4", "@parcel/rust": "2.16.4", "@parcel/source-map": "^2.1.1", "@parcel/types": "2.16.4", "@parcel/utils": "2.16.4", "globals": "^13.24.0", "nullthrows": "^1.1.1" } }, "sha512-L2o39f/fhta+hxto7w8OTUKdstY+te5BmHZREckbQm0KTBg93BG7jB0bfoxLSZF0d8uuAYIVXjzeHNqha+du1g=="],
|
|
||||||
|
|
||||||
"@parcel/packager-raw": ["@parcel/packager-raw@2.16.4", "", { "dependencies": { "@parcel/plugin": "2.16.4" } }, "sha512-A9j60G9OmbTkEeE4WRMXCiErEprHLs9NkUlC4HXCxmSrPMOVaMaMva2LdejE3A9kujZqYtYfuc8+a+jN+Nro4w=="],
|
|
||||||
|
|
||||||
"@parcel/packager-svg": ["@parcel/packager-svg@2.16.4", "", { "dependencies": { "@parcel/plugin": "2.16.4", "@parcel/rust": "2.16.4", "@parcel/types": "2.16.4", "@parcel/utils": "2.16.4" } }, "sha512-LT9l7eInFrAZJ6w3mYzAUgDq3SIzYbbQyW46Dz26M9lJQbf6uCaATUTac3BEHegW0ikDuw4OOGHK41BVqeeusg=="],
|
|
||||||
|
|
||||||
"@parcel/packager-wasm": ["@parcel/packager-wasm@2.16.4", "", { "dependencies": { "@parcel/plugin": "2.16.4" } }, "sha512-AY96Aqu/RpmaSZK2RGkIrZWjAperDw8DAlxLAiaP1D/RPVnikZtl5BmcUt/Wz3PrzG7/q9ZVqqKkWsLmhkjXZQ=="],
|
|
||||||
|
|
||||||
"@parcel/plugin": ["@parcel/plugin@2.16.4", "", { "dependencies": { "@parcel/types": "2.16.4" } }, "sha512-aN2VQoRGC1eB41ZCDbPR/Sp0yKOxe31oemzPx1nJzOuebK2Q6FxSrJ9Bjj9j/YCaLzDtPwelsuLOazzVpXJ6qg=="],
|
|
||||||
|
|
||||||
"@parcel/profiler": ["@parcel/profiler@2.16.4", "", { "dependencies": { "@parcel/diagnostic": "2.16.4", "@parcel/events": "2.16.4", "@parcel/types-internal": "2.16.4", "chrome-trace-event": "^1.0.2" } }, "sha512-R3JhfcnoReTv2sVFHPR2xKZvs3d3IRrBl9sWmAftbIJFwT4rU70/W7IdwfaJVkD/6PzHq9mcgOh1WKL4KAxPdA=="],
|
|
||||||
|
|
||||||
"@parcel/reporter-cli": ["@parcel/reporter-cli@2.16.4", "", { "dependencies": { "@parcel/plugin": "2.16.4", "@parcel/types": "2.16.4", "@parcel/utils": "2.16.4", "chalk": "^4.1.2", "term-size": "^2.2.1" } }, "sha512-DQx9TwcTZrDv828+tcwEi//xyW7OHTGzGX1+UEVxPp0mSzuOmDn0zfER8qNIqGr1i4D/FXhb5UJQDhGHV8mOpQ=="],
|
|
||||||
|
|
||||||
"@parcel/reporter-dev-server": ["@parcel/reporter-dev-server@2.16.4", "", { "dependencies": { "@parcel/codeframe": "2.16.4", "@parcel/plugin": "2.16.4", "@parcel/source-map": "^2.1.1", "@parcel/utils": "2.16.4" } }, "sha512-YWvay25htQDifpDRJ0+yFh6xUxKnbfeJxYkPYyuXdxpEUhq4T0UWW0PbPCN/wFX7StgeUTXq5Poeo/+eys9m3w=="],
|
|
||||||
|
|
||||||
"@parcel/reporter-tracer": ["@parcel/reporter-tracer@2.16.4", "", { "dependencies": { "@parcel/plugin": "2.16.4", "@parcel/utils": "2.16.4", "chrome-trace-event": "^1.0.3", "nullthrows": "^1.1.1" } }, "sha512-JKnlXpPepak0/ZybmZn9JtyjJiDBWYrt7ZUlXQhQb0xzNcd/k+RqfwVkTKIwyFHsWtym0cwibkvsi2bWFzS7tw=="],
|
|
||||||
|
|
||||||
"@parcel/resolver-default": ["@parcel/resolver-default@2.16.4", "", { "dependencies": { "@parcel/node-resolver-core": "3.7.4", "@parcel/plugin": "2.16.4" } }, "sha512-wJe9XQS0hn/t32pntQpJbls3ZL8mGVVhK9L7s7BTmZT9ufnvP2nif1psJz/nbgnP9LF6mLSk43OdMJKpoStsjQ=="],
|
|
||||||
|
|
||||||
"@parcel/runtime-browser-hmr": ["@parcel/runtime-browser-hmr@2.16.4", "", { "dependencies": { "@parcel/plugin": "2.16.4", "@parcel/utils": "2.16.4" } }, "sha512-asx7p3NjUSfibI3bC7+8+jUIGHWVk2Zuq9SjJGCGDt+auT9A4uSGljnsk1BWWPqqZ0WILubq4czSAqm0+wt4cw=="],
|
|
||||||
|
|
||||||
"@parcel/runtime-js": ["@parcel/runtime-js@2.16.4", "", { "dependencies": { "@parcel/diagnostic": "2.16.4", "@parcel/plugin": "2.16.4", "@parcel/utils": "2.16.4", "nullthrows": "^1.1.1" } }, "sha512-gUKmsjg+PULQBu2QbX0QKll9tXSqHPO8NrfxHwWb2lz5xDKDos1oV0I7BoMWbHhUHkoToXZrm654oGViujtVUA=="],
|
|
||||||
|
|
||||||
"@parcel/runtime-rsc": ["@parcel/runtime-rsc@2.16.4", "", { "dependencies": { "@parcel/plugin": "2.16.4", "@parcel/rust": "2.16.4", "@parcel/utils": "2.16.4", "nullthrows": "^1.1.1" } }, "sha512-CHkotYE/cNiUjJmrc5FD9YhlFp1UF5wMNNJmoWaL40eBzsqcaV0sSn5V3bNapwewn3wrMYgdPgvOTHfaZaG73A=="],
|
|
||||||
|
|
||||||
"@parcel/runtime-service-worker": ["@parcel/runtime-service-worker@2.16.4", "", { "dependencies": { "@parcel/plugin": "2.16.4", "@parcel/utils": "2.16.4", "nullthrows": "^1.1.1" } }, "sha512-FT0Q58bf5Re+dq5cL2XHbxqHHFZco6qtRijeVpT3TSPMRPlniMArypSytTeZzVNL7h/hxjWsNu7fRuC0yLB5hA=="],
|
|
||||||
|
|
||||||
"@parcel/rust": ["@parcel/rust@2.16.4", "", { "optionalDependencies": { "@parcel/rust-darwin-arm64": "2.16.4", "@parcel/rust-darwin-x64": "2.16.4", "@parcel/rust-linux-arm-gnueabihf": "2.16.4", "@parcel/rust-linux-arm64-gnu": "2.16.4", "@parcel/rust-linux-arm64-musl": "2.16.4", "@parcel/rust-linux-x64-gnu": "2.16.4", "@parcel/rust-linux-x64-musl": "2.16.4", "@parcel/rust-win32-x64-msvc": "2.16.4" }, "peerDependencies": { "napi-wasm": "^1.1.2" }, "optionalPeers": ["napi-wasm"] }, "sha512-RBMKt9rCdv6jr4vXG6LmHtxzO5TuhQvXo1kSoSIF7fURRZ81D1jzBtLxwLmfxCPsofJNqWwdhy5vIvisX+TLlQ=="],
|
|
||||||
|
|
||||||
"@parcel/rust-darwin-arm64": ["@parcel/rust-darwin-arm64@2.16.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-P3Se36H9EO1fOlwXqQNQ+RsVKTGn5ztRSUGbLcT8ba6oOMmU1w7J4R810GgsCbwCuF10TJNUMkuD3Q2Sz15Q3Q=="],
|
|
||||||
|
|
||||||
"@parcel/rust-darwin-x64": ["@parcel/rust-darwin-x64@2.16.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-8aNKNyPIx3EthYpmVJevIdHmFsOApXAEYGi3HU69jTxLgSIfyEHDdGE9lEsMvhSrd/SSo4/euAtiV+pqK04wnA=="],
|
|
||||||
|
|
||||||
"@parcel/rust-linux-arm-gnueabihf": ["@parcel/rust-linux-arm-gnueabihf@2.16.4", "", { "os": "linux", "cpu": "arm" }, "sha512-QrvqiSHaWRLc0JBHgUHVvDthfWSkA6AFN+ikV1UGENv4j2r/QgvuwJiG0VHrsL6pH5dRqj0vvngHzEgguke9DA=="],
|
|
||||||
|
|
||||||
"@parcel/rust-linux-arm64-gnu": ["@parcel/rust-linux-arm64-gnu@2.16.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-f3gBWQHLHRUajNZi3SMmDQiEx54RoRbXtZYQNuBQy7+NolfFcgb1ik3QhkT7xovuTF/LBmaqP3UFy0PxvR/iwQ=="],
|
|
||||||
|
|
||||||
"@parcel/rust-linux-arm64-musl": ["@parcel/rust-linux-arm64-musl@2.16.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-cwml18RNKsBwHyZnrZg4jpecXkWjaY/mCArocWUxkFXjjB97L56QWQM9W86f2/Y3HcFcnIGJwx1SDDKJrV6OIA=="],
|
|
||||||
|
|
||||||
"@parcel/rust-linux-x64-gnu": ["@parcel/rust-linux-x64-gnu@2.16.4", "", { "os": "linux", "cpu": "x64" }, "sha512-0xIjQaN8hiG0F9R8coPYidHslDIrbfOS/qFy5GJNbGA3S49h61wZRBMQqa7JFW4+2T8R0J9j0SKHhLXpbLXrIg=="],
|
|
||||||
|
|
||||||
"@parcel/rust-linux-x64-musl": ["@parcel/rust-linux-x64-musl@2.16.4", "", { "os": "linux", "cpu": "x64" }, "sha512-fYn21GIecHK9RoZPKwT9NOwxwl3Gy3RYPR6zvsUi0+hpFo19Ph9EzFXN3lT8Pi5KiwQMCU4rsLb5HoWOBM1FeA=="],
|
|
||||||
|
|
||||||
"@parcel/rust-win32-x64-msvc": ["@parcel/rust-win32-x64-msvc@2.16.4", "", { "os": "win32", "cpu": "x64" }, "sha512-TcpWC3I1mJpfP2++018lgvM7UX0P8IrzNxceBTHUKEIDMwmAYrUKAQFiaU0j1Ldqk6yP8SPZD3cvphumsYpJOQ=="],
|
|
||||||
|
|
||||||
"@parcel/source-map": ["@parcel/source-map@2.1.1", "", { "dependencies": { "detect-libc": "^1.0.3" } }, "sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew=="],
|
|
||||||
|
|
||||||
"@parcel/transformer-babel": ["@parcel/transformer-babel@2.16.4", "", { "dependencies": { "@parcel/diagnostic": "2.16.4", "@parcel/plugin": "2.16.4", "@parcel/source-map": "^2.1.1", "@parcel/utils": "2.16.4", "browserslist": "^4.24.5", "json5": "^2.2.3", "nullthrows": "^1.1.1", "semver": "^7.7.1" } }, "sha512-CMDUOQYX7+cmeyHxHSFnoPcwvXNL7rRFE+Q06uVFzsYYiVhbwGF/1J5Bx4cW3Froumqla4YTytTsEteJEybkdA=="],
|
|
||||||
|
|
||||||
"@parcel/transformer-css": ["@parcel/transformer-css@2.16.4", "", { "dependencies": { "@parcel/diagnostic": "2.16.4", "@parcel/plugin": "2.16.4", "@parcel/source-map": "^2.1.1", "@parcel/utils": "2.16.4", "browserslist": "^4.24.5", "lightningcss": "^1.30.1", "nullthrows": "^1.1.1" } }, "sha512-VG/+DbDci2HKe20GFRDs65ZQf5GUFfnmZAa1BhVl/MO+ijT3XC3eoVUy5cExRkq4VLcPY4ytL0g/1T2D6x7lBQ=="],
|
|
||||||
|
|
||||||
"@parcel/transformer-html": ["@parcel/transformer-html@2.16.4", "", { "dependencies": { "@parcel/diagnostic": "2.16.4", "@parcel/plugin": "2.16.4", "@parcel/rust": "2.16.4" } }, "sha512-w6JErYTeNS+KAzUAER18NHFIFFvxiLGd4Fht1UYcb/FDjJdLAMB/FljyEs0Rto/WAhZ2D0MuSL25HQh837R62g=="],
|
|
||||||
|
|
||||||
"@parcel/transformer-image": ["@parcel/transformer-image@2.16.4", "", { "dependencies": { "@parcel/plugin": "2.16.4", "@parcel/utils": "2.16.4", "@parcel/workers": "2.16.4", "nullthrows": "^1.1.1" }, "peerDependencies": { "@parcel/core": "^2.16.4" } }, "sha512-ZzIn3KvvRqMfcect4Dy+57C9XoQXZhpVJKBdQWMp9wM1qJEgsVgGDcaSBYCs/UYSKMRMP6Wm20pKCt408RkQzg=="],
|
|
||||||
|
|
||||||
"@parcel/transformer-js": ["@parcel/transformer-js@2.16.4", "", { "dependencies": { "@parcel/diagnostic": "2.16.4", "@parcel/plugin": "2.16.4", "@parcel/rust": "2.16.4", "@parcel/source-map": "^2.1.1", "@parcel/utils": "2.16.4", "@parcel/workers": "2.16.4", "@swc/helpers": "^0.5.0", "browserslist": "^4.24.5", "nullthrows": "^1.1.1", "regenerator-runtime": "^0.14.1", "semver": "^7.7.1" }, "peerDependencies": { "@parcel/core": "^2.16.4" } }, "sha512-FD2fdO6URwAGBPidb3x1dDgLBt972mko0LelcSU05aC/pcKaV9mbCtINbPul1MlStzkxDelhuImcCYIyerheVQ=="],
|
|
||||||
|
|
||||||
"@parcel/transformer-json": ["@parcel/transformer-json@2.16.4", "", { "dependencies": { "@parcel/plugin": "2.16.4", "json5": "^2.2.3" } }, "sha512-pB3ZNqgokdkBCJ+4G0BrPYcIkyM9K1HVk0GvjzcLEFDKsoAp8BGEM68FzagFM/nVq9anYTshIaoh349GK0M/bg=="],
|
|
||||||
|
|
||||||
"@parcel/transformer-node": ["@parcel/transformer-node@2.16.4", "", { "dependencies": { "@parcel/plugin": "2.16.4" } }, "sha512-7t43CPGfMJk1LqFokwxHSsRi+kKC2QvDXaMtqiMShmk50LCwn81WgzuFvNhMwf6lSiBihWupGwF3Fqksg+aisg=="],
|
|
||||||
|
|
||||||
"@parcel/transformer-postcss": ["@parcel/transformer-postcss@2.16.4", "", { "dependencies": { "@parcel/diagnostic": "2.16.4", "@parcel/plugin": "2.16.4", "@parcel/rust": "2.16.4", "@parcel/utils": "2.16.4", "clone": "^2.1.2", "nullthrows": "^1.1.1", "postcss-value-parser": "^4.2.0", "semver": "^7.7.1" } }, "sha512-jfmh9ho03H+qwz9S1b/a/oaOmgfMovtHKYDweIGMjKULKIee3AFRqo8RZIOuUMjDuqHWK8SqQmjery4syFV3Xw=="],
|
|
||||||
|
|
||||||
"@parcel/transformer-posthtml": ["@parcel/transformer-posthtml@2.16.4", "", { "dependencies": { "@parcel/plugin": "2.16.4", "@parcel/utils": "2.16.4" } }, "sha512-+GXsmGx1L25KQGQnwclgEuQe1t4QU+IoDkgN+Ikj+EnQCOWG4/ts2VpMBeqP5F18ZT4cCSRafj6317o/2lSGJg=="],
|
|
||||||
|
|
||||||
"@parcel/transformer-raw": ["@parcel/transformer-raw@2.16.4", "", { "dependencies": { "@parcel/plugin": "2.16.4" } }, "sha512-7WDUPq+bW11G9jKxaQIVL+NPGolV99oq/GXhpjYip0SaGaLzRCW7gEk60cftuk0O7MsDaX5jcAJm3G/AX+LJKg=="],
|
|
||||||
|
|
||||||
"@parcel/transformer-react-refresh-wrap": ["@parcel/transformer-react-refresh-wrap@2.16.4", "", { "dependencies": { "@parcel/error-overlay": "2.16.4", "@parcel/plugin": "2.16.4", "@parcel/utils": "2.16.4", "react-refresh": "^0.16.0" } }, "sha512-MiLNZrsGQJTANKKa4lzZyUbGj/en0Hms474mMdQkCBFg6GmjfmXwaMMgtTfPA3ZwSp2+3LeObCyca/f9B2gBZQ=="],
|
|
||||||
|
|
||||||
"@parcel/transformer-svg": ["@parcel/transformer-svg@2.16.4", "", { "dependencies": { "@parcel/diagnostic": "2.16.4", "@parcel/plugin": "2.16.4", "@parcel/rust": "2.16.4" } }, "sha512-0dm4cQr/WpfQP6N0xjFtwdLTxcONDfoLgTOMk4eNUWydHipSgmLtvUk/nOc/FWkwztRScfAObtZXOiPOd3Oy9A=="],
|
|
||||||
|
|
||||||
"@parcel/transformer-typescript-tsc": ["@parcel/transformer-typescript-tsc@2.16.4", "", { "dependencies": { "@parcel/plugin": "2.16.4", "@parcel/source-map": "^2.1.1", "@parcel/ts-utils": "2.16.4" }, "peerDependencies": { "typescript": ">=3.0.0" } }, "sha512-zi/Z6jWrrcAm1JkR3660MgMyis0A4GOSdfWhzmO9Ljf3UpWnQ+JgDkkLgdeXXuCJPmwyHLnZIbg7avcJyPCRZg=="],
|
|
||||||
|
|
||||||
"@parcel/ts-utils": ["@parcel/ts-utils@2.16.4", "", { "dependencies": { "nullthrows": "^1.1.1" }, "peerDependencies": { "typescript": ">=3.0.0" } }, "sha512-hOYaQvtyq72jKqBjhtSBVn8vATZuaLnvauVRSDPH1DFOgQaPnM1r71Gw5djx6KMk+Tcpa5NyHNJmLVlVAIQ2tA=="],
|
|
||||||
|
|
||||||
"@parcel/types": ["@parcel/types@2.16.4", "", { "dependencies": { "@parcel/types-internal": "2.16.4", "@parcel/workers": "2.16.4" } }, "sha512-ctx4mBskZHXeDVHg4OjMwx18jfYH9BzI/7yqbDQVGvd5lyA+/oVVzYdpele2J2i2sSaJ87cA8nb57GDQ8kHAqA=="],
|
|
||||||
|
|
||||||
"@parcel/types-internal": ["@parcel/types-internal@2.16.4", "", { "dependencies": { "@parcel/diagnostic": "2.16.4", "@parcel/feature-flags": "2.16.4", "@parcel/source-map": "^2.1.1", "utility-types": "^3.11.0" } }, "sha512-PE6Qmt5cjzBxX+6MPLiF7r+twoC+V9Skt3zyuBQ+H1c0i9o07Bbz2NKX10nvlPukfmW6Fu/1RvTLkzBZR1bU6A=="],
|
|
||||||
|
|
||||||
"@parcel/utils": ["@parcel/utils@2.16.4", "", { "dependencies": { "@parcel/codeframe": "2.16.4", "@parcel/diagnostic": "2.16.4", "@parcel/logger": "2.16.4", "@parcel/markdown-ansi": "2.16.4", "@parcel/rust": "2.16.4", "@parcel/source-map": "^2.1.1", "chalk": "^4.1.2", "nullthrows": "^1.1.1" } }, "sha512-lkmxQHcHyOWZLbV8t+h2CGZIkPiBurLm/TS5wNT7+tq0qt9KbVwL7FP2K93TbXhLMGTmpI79Bf3qKniPM167Mw=="],
|
|
||||||
|
|
||||||
"@parcel/watcher": ["@parcel/watcher@2.5.6", "", { "dependencies": { "detect-libc": "^2.0.3", "is-glob": "^4.0.3", "node-addon-api": "^7.0.0", "picomatch": "^4.0.3" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.6", "@parcel/watcher-darwin-arm64": "2.5.6", "@parcel/watcher-darwin-x64": "2.5.6", "@parcel/watcher-freebsd-x64": "2.5.6", "@parcel/watcher-linux-arm-glibc": "2.5.6", "@parcel/watcher-linux-arm-musl": "2.5.6", "@parcel/watcher-linux-arm64-glibc": "2.5.6", "@parcel/watcher-linux-arm64-musl": "2.5.6", "@parcel/watcher-linux-x64-glibc": "2.5.6", "@parcel/watcher-linux-x64-musl": "2.5.6", "@parcel/watcher-win32-arm64": "2.5.6", "@parcel/watcher-win32-ia32": "2.5.6", "@parcel/watcher-win32-x64": "2.5.6" } }, "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.6", "", { "os": "android", "cpu": "arm64" }, "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.6", "", { "os": "freebsd", "cpu": "x64" }, "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.6", "", { "os": "linux", "cpu": "arm" }, "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.6", "", { "os": "linux", "cpu": "arm" }, "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.6", "", { "os": "win32", "cpu": "ia32" }, "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.6", "", { "os": "win32", "cpu": "x64" }, "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw=="],
|
|
||||||
|
|
||||||
"@parcel/workers": ["@parcel/workers@2.16.4", "", { "dependencies": { "@parcel/diagnostic": "2.16.4", "@parcel/logger": "2.16.4", "@parcel/profiler": "2.16.4", "@parcel/types-internal": "2.16.4", "@parcel/utils": "2.16.4", "nullthrows": "^1.1.1" }, "peerDependencies": { "@parcel/core": "^2.16.4" } }, "sha512-dkBEWqnHXDZnRbTZouNt4uEGIslJT+V0c8OH1MPOfjISp1ucD6/u9ET8k9d/PxS9h1hL53og0SpBuuSEPLDl6A=="],
|
|
||||||
|
|
||||||
"@popperjs/core": ["@popperjs/core@2.11.8", "", {}, "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="],
|
|
||||||
|
|
||||||
"@swc/core": ["@swc/core@1.15.13", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.25" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.15.13", "@swc/core-darwin-x64": "1.15.13", "@swc/core-linux-arm-gnueabihf": "1.15.13", "@swc/core-linux-arm64-gnu": "1.15.13", "@swc/core-linux-arm64-musl": "1.15.13", "@swc/core-linux-x64-gnu": "1.15.13", "@swc/core-linux-x64-musl": "1.15.13", "@swc/core-win32-arm64-msvc": "1.15.13", "@swc/core-win32-ia32-msvc": "1.15.13", "@swc/core-win32-x64-msvc": "1.15.13" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-0l1gl/72PErwUZuavcRpRAQN9uSst+Nk++niC5IX6lmMWpXoScYx3oq/narT64/sKv/eRiPTaAjBFGDEQiWJIw=="],
|
|
||||||
|
|
||||||
"@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.15.13", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ztXusRuC5NV2w+a6pDhX13CGioMLq8CjX5P4XgVJ21ocqz9t19288Do0y8LklplDtwcEhYGTNdMbkmUT7+lDTg=="],
|
|
||||||
|
|
||||||
"@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.15.13", "", { "os": "darwin", "cpu": "x64" }, "sha512-cVifxQUKhaE7qcO/y9Mq6PEhoyvN9tSLzCnnFZ4EIabFHBuLtDDO6a+vLveOy98hAs5Qu1+bb5Nv0oa1Pihe3Q=="],
|
|
||||||
|
|
||||||
"@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.15.13", "", { "os": "linux", "cpu": "arm" }, "sha512-t+xxEzZ48enl/wGGy7SRYd7kImWQ/+wvVFD7g5JZo234g6/QnIgZ+YdfIyjHB+ZJI3F7a2IQHS7RNjxF29UkWw=="],
|
|
||||||
|
|
||||||
"@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.15.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-VndeGvKmTXFn6AGwjy0Kg8i7HccOCE7Jt/vmZwRxGtOfNZM1RLYRQ7MfDLo6T0h1Bq6eYzps3L5Ma4zBmjOnOg=="],
|
|
||||||
|
|
||||||
"@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.15.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-SmZ9m+XqCB35NddHCctvHFLqPZDAs5j8IgD36GoutufDJmeq2VNfgk5rQoqNqKmAK3Y7iFdEmI76QoHIWiCLyw=="],
|
|
||||||
|
|
||||||
"@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.15.13", "", { "os": "linux", "cpu": "x64" }, "sha512-5rij+vB9a29aNkHq72EXI2ihDZPszJb4zlApJY4aCC/q6utgqFA6CkrfTfIb+O8hxtG3zP5KERETz8mfFK6A0A=="],
|
|
||||||
|
|
||||||
"@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.15.13", "", { "os": "linux", "cpu": "x64" }, "sha512-OlSlaOK9JplQ5qn07WiBLibkOw7iml2++ojEXhhR3rbWrNEKCD7sd8+6wSavsInyFdw4PhLA+Hy6YyDBIE23Yw=="],
|
|
||||||
|
|
||||||
"@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.15.13", "", { "os": "win32", "cpu": "arm64" }, "sha512-zwQii5YVdsfG8Ti9gIKgBKZg8qMkRZxl+OlYWUT5D93Jl4NuNBRausP20tfEkQdAPSRrMCSUZBM6FhW7izAZRg=="],
|
|
||||||
|
|
||||||
"@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.15.13", "", { "os": "win32", "cpu": "ia32" }, "sha512-hYXvyVVntqRlYoAIDwNzkS3tL2ijP3rxyWQMNKaxcCxxkCDto/w3meOK/OB6rbQSkNw0qTUcBfU9k+T0ptYdfQ=="],
|
|
||||||
|
|
||||||
"@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.15.13", "", { "os": "win32", "cpu": "x64" }, "sha512-XTzKs7c/vYCcjmcwawnQvlHHNS1naJEAzcBckMI5OJlnrcgW8UtcX9NHFYvNjGtXuKv0/9KvqL4fuahdvlNGKw=="],
|
|
||||||
|
|
||||||
"@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="],
|
|
||||||
|
|
||||||
"@swc/helpers": ["@swc/helpers@0.5.19", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA=="],
|
|
||||||
|
|
||||||
"@swc/types": ["@swc/types@0.1.25", "", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g=="],
|
|
||||||
|
|
||||||
"@tsconfig/bun": ["@tsconfig/bun@1.0.10", "", {}, "sha512-5AV5YknQjNyoYzZ/8NG0dawqew/wH+x7ANiCfCIn29qo0cdbd1EryvFD1k5NSZWLBMOI/fGqMIaxi58GPIP9Cg=="],
|
|
||||||
|
|
||||||
"@tsconfig/node24": ["@tsconfig/node24@24.0.4", "", {}, "sha512-2A933l5P5oCbv6qSxHs7ckKwobs8BDAe9SJ/Xr2Hy+nDlwmLE1GhFh/g/vXGRZWgxBg9nX/5piDtHR9Dkw/XuA=="],
|
|
||||||
|
|
||||||
"@tsconfig/recommended": ["@tsconfig/recommended@1.0.13", "", {}, "sha512-sySRuBfMKyKO/j2ZAhR8kSembhjuPEV4Ra3AHtmWLq51+iGaudr45crPSzNC5b7/Ctrh9dfUpBuTlYrH6rM58Q=="],
|
|
||||||
|
|
||||||
"@tsconfig/strictest": ["@tsconfig/strictest@2.0.8", "", {}, "sha512-XnQ7vNz5HRN0r88GYf1J9JJjqtZPiHt2woGJOo2dYqyHGGcd6OLGqSlBB6p1j9mpzja6Oe5BoPqWmeDx6X9rLw=="],
|
|
||||||
|
|
||||||
"@types/bootstrap": ["@types/bootstrap@5.2.10", "", { "dependencies": { "@popperjs/core": "^2.9.2" } }, "sha512-F2X+cd6551tep0MvVZ6nM8v7XgGN/twpdNDjqS1TUM7YFNEtQYWk+dKAnH+T1gr6QgCoGMPl487xw/9hXooa2g=="],
|
|
||||||
|
|
||||||
"@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="],
|
|
||||||
|
|
||||||
"@types/luxon": ["@types/luxon@3.7.1", "", {}, "sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg=="],
|
|
||||||
|
|
||||||
"@types/node": ["@types/node@25.3.1", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-hj9YIJimBCipHVfHKRMnvmHg+wfhKc0o4mTtXh9pKBjC8TLJzz0nzGmLi5UJsYAUgSvXFHgb0V2oY10DUFtImw=="],
|
|
||||||
|
|
||||||
"@types/qs": ["@types/qs@6.14.0", "", {}, "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ=="],
|
|
||||||
|
|
||||||
"@types/semver": ["@types/semver@7.7.1", "", {}, "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA=="],
|
|
||||||
|
|
||||||
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
|
||||||
|
|
||||||
"base-x": ["base-x@3.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA=="],
|
|
||||||
|
|
||||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.0", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA=="],
|
|
||||||
|
|
||||||
"bootstrap": ["bootstrap@5.3.8", "", { "peerDependencies": { "@popperjs/core": "^2.11.8" } }, "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg=="],
|
|
||||||
|
|
||||||
"bootstrap-icons": ["bootstrap-icons@1.13.1", "", {}, "sha512-ijombt4v6bv5CLeXvRWKy7CuM3TRTuPEuGaGKvTV5cz65rQSY8RQ2JcHt6b90cBBAC7s8fsf2EkQDldzCoXUjw=="],
|
|
||||||
|
|
||||||
"browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="],
|
|
||||||
|
|
||||||
"bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="],
|
|
||||||
|
|
||||||
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
|
||||||
|
|
||||||
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
|
|
||||||
|
|
||||||
"caniuse-lite": ["caniuse-lite@1.0.30001774", "", {}, "sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA=="],
|
|
||||||
|
|
||||||
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
|
||||||
|
|
||||||
"chrome-trace-event": ["chrome-trace-event@1.0.4", "", {}, "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ=="],
|
|
||||||
|
|
||||||
"clone": ["clone@2.1.2", "", {}, "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="],
|
|
||||||
|
|
||||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
|
||||||
|
|
||||||
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
|
||||||
|
|
||||||
"commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="],
|
|
||||||
|
|
||||||
"detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
|
|
||||||
|
|
||||||
"dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
|
|
||||||
|
|
||||||
"dotenv-expand": ["dotenv-expand@11.0.7", "", { "dependencies": { "dotenv": "^16.4.5" } }, "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA=="],
|
|
||||||
|
|
||||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
|
||||||
|
|
||||||
"electron-to-chromium": ["electron-to-chromium@1.5.302", "", {}, "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg=="],
|
|
||||||
|
|
||||||
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
|
||||||
|
|
||||||
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
|
||||||
|
|
||||||
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
|
|
||||||
|
|
||||||
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
|
||||||
|
|
||||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
|
||||||
|
|
||||||
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
|
||||||
|
|
||||||
"get-port": ["get-port@4.2.0", "", {}, "sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw=="],
|
|
||||||
|
|
||||||
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
|
||||||
|
|
||||||
"globals": ["globals@13.24.0", "", { "dependencies": { "type-fest": "^0.20.2" } }, "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ=="],
|
|
||||||
|
|
||||||
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
|
||||||
|
|
||||||
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
|
||||||
|
|
||||||
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
|
||||||
|
|
||||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
|
||||||
|
|
||||||
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
|
||||||
|
|
||||||
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
|
||||||
|
|
||||||
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
|
||||||
|
|
||||||
"ky": ["ky@1.14.3", "", {}, "sha512-9zy9lkjac+TR1c2tG+mkNSVlyOpInnWdSMiue4F+kq8TwJSgv6o8jhLRg8Ho6SnZ9wOYUq/yozts9qQCfk7bIw=="],
|
|
||||||
|
|
||||||
"lightningcss": ["lightningcss@1.31.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.31.1", "lightningcss-darwin-arm64": "1.31.1", "lightningcss-darwin-x64": "1.31.1", "lightningcss-freebsd-x64": "1.31.1", "lightningcss-linux-arm-gnueabihf": "1.31.1", "lightningcss-linux-arm64-gnu": "1.31.1", "lightningcss-linux-arm64-musl": "1.31.1", "lightningcss-linux-x64-gnu": "1.31.1", "lightningcss-linux-x64-musl": "1.31.1", "lightningcss-win32-arm64-msvc": "1.31.1", "lightningcss-win32-x64-msvc": "1.31.1" } }, "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ=="],
|
|
||||||
|
|
||||||
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.31.1", "", { "os": "android", "cpu": "arm64" }, "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg=="],
|
|
||||||
|
|
||||||
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.31.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg=="],
|
|
||||||
|
|
||||||
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.31.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA=="],
|
|
||||||
|
|
||||||
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.31.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A=="],
|
|
||||||
|
|
||||||
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.31.1", "", { "os": "linux", "cpu": "arm" }, "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g=="],
|
|
||||||
|
|
||||||
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.31.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg=="],
|
|
||||||
|
|
||||||
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.31.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg=="],
|
|
||||||
|
|
||||||
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.31.1", "", { "os": "linux", "cpu": "x64" }, "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA=="],
|
|
||||||
|
|
||||||
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.31.1", "", { "os": "linux", "cpu": "x64" }, "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA=="],
|
|
||||||
|
|
||||||
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.31.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w=="],
|
|
||||||
|
|
||||||
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.31.1", "", { "os": "win32", "cpu": "x64" }, "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw=="],
|
|
||||||
|
|
||||||
"lmdb": ["lmdb@2.8.5", "", { "dependencies": { "msgpackr": "^1.9.5", "node-addon-api": "^6.1.0", "node-gyp-build-optional-packages": "5.1.1", "ordered-binary": "^1.4.1", "weak-lru-cache": "^1.2.2" }, "optionalDependencies": { "@lmdb/lmdb-darwin-arm64": "2.8.5", "@lmdb/lmdb-darwin-x64": "2.8.5", "@lmdb/lmdb-linux-arm": "2.8.5", "@lmdb/lmdb-linux-arm64": "2.8.5", "@lmdb/lmdb-linux-x64": "2.8.5", "@lmdb/lmdb-win32-x64": "2.8.5" }, "bin": { "download-lmdb-prebuilds": "bin/download-prebuilds.js" } }, "sha512-9bMdFfc80S+vSldBmG3HOuLVHnxRdNTlpzR6QDnzqCQtCzGUEAGTzBKYMeIM+I/sU4oZfgbcbS7X7F65/z/oxQ=="],
|
|
||||||
|
|
||||||
"luxon": ["luxon@3.7.2", "", {}, "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew=="],
|
|
||||||
|
|
||||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
|
||||||
|
|
||||||
"msgpackr": ["msgpackr@1.11.8", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-bC4UGzHhVvgDNS7kn9tV8fAucIYUBuGojcaLiz7v+P63Lmtm0Xeji8B/8tYKddALXxJLpwIeBmUN3u64C4YkRA=="],
|
|
||||||
|
|
||||||
"msgpackr-extract": ["msgpackr-extract@3.0.3", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA=="],
|
|
||||||
|
|
||||||
"node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
|
|
||||||
|
|
||||||
"node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.1.1", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-test": "build-test.js", "node-gyp-build-optional-packages-optional": "optional.js" } }, "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw=="],
|
|
||||||
|
|
||||||
"node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="],
|
|
||||||
|
|
||||||
"nullthrows": ["nullthrows@1.1.1", "", {}, "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw=="],
|
|
||||||
|
|
||||||
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
|
|
||||||
|
|
||||||
"ordered-binary": ["ordered-binary@1.6.1", "", {}, "sha512-QkCdPooczexPLiXIrbVOPYkR3VO3T6v2OyKRkR1Xbhpy7/LAVXwahnRCgRp78Oe/Ehf0C/HATAxfSr6eA1oX+w=="],
|
|
||||||
|
|
||||||
"oxfmt": ["oxfmt@0.35.0", "", { "dependencies": { "tinypool": "2.1.0" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.35.0", "@oxfmt/binding-android-arm64": "0.35.0", "@oxfmt/binding-darwin-arm64": "0.35.0", "@oxfmt/binding-darwin-x64": "0.35.0", "@oxfmt/binding-freebsd-x64": "0.35.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.35.0", "@oxfmt/binding-linux-arm-musleabihf": "0.35.0", "@oxfmt/binding-linux-arm64-gnu": "0.35.0", "@oxfmt/binding-linux-arm64-musl": "0.35.0", "@oxfmt/binding-linux-ppc64-gnu": "0.35.0", "@oxfmt/binding-linux-riscv64-gnu": "0.35.0", "@oxfmt/binding-linux-riscv64-musl": "0.35.0", "@oxfmt/binding-linux-s390x-gnu": "0.35.0", "@oxfmt/binding-linux-x64-gnu": "0.35.0", "@oxfmt/binding-linux-x64-musl": "0.35.0", "@oxfmt/binding-openharmony-arm64": "0.35.0", "@oxfmt/binding-win32-arm64-msvc": "0.35.0", "@oxfmt/binding-win32-ia32-msvc": "0.35.0", "@oxfmt/binding-win32-x64-msvc": "0.35.0" }, "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-QYeXWkP+aLt7utt5SLivNIk09glWx9QE235ODjgcEZ3sd1VMaUBSpLymh6ZRCA76gD2rMP4bXanUz/fx+nLM9Q=="],
|
|
||||||
|
|
||||||
"parcel": ["parcel@2.16.4", "", { "dependencies": { "@parcel/config-default": "2.16.4", "@parcel/core": "2.16.4", "@parcel/diagnostic": "2.16.4", "@parcel/events": "2.16.4", "@parcel/feature-flags": "2.16.4", "@parcel/fs": "2.16.4", "@parcel/logger": "2.16.4", "@parcel/package-manager": "2.16.4", "@parcel/reporter-cli": "2.16.4", "@parcel/reporter-dev-server": "2.16.4", "@parcel/reporter-tracer": "2.16.4", "@parcel/utils": "2.16.4", "chalk": "^4.1.2", "commander": "^12.1.0", "get-port": "^4.2.0" }, "bin": { "parcel": "lib/bin.js" } }, "sha512-RQlrqs4ujYNJpTQi+dITqPKNhRWEqpjPd1YBcGp50Wy3FcJHpwu0/iRm7XWz2dKU/Bwp2qCcVYPIeEDYi2uOUw=="],
|
|
||||||
|
|
||||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
|
||||||
|
|
||||||
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
|
||||||
|
|
||||||
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
|
|
||||||
|
|
||||||
"qs": ["qs@6.15.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="],
|
|
||||||
|
|
||||||
"react-refresh": ["react-refresh@0.16.0", "", {}, "sha512-FPvF2XxTSikpJxcr+bHut2H4gJ17+18Uy20D5/F+SKzFap62R3cM5wH6b8WN3LyGSYeQilLEcJcR1fjBSI2S1A=="],
|
|
||||||
|
|
||||||
"regenerator-runtime": ["regenerator-runtime@0.14.1", "", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="],
|
|
||||||
|
|
||||||
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
|
||||||
|
|
||||||
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
|
||||||
|
|
||||||
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
|
|
||||||
|
|
||||||
"side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
|
|
||||||
|
|
||||||
"side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
|
|
||||||
|
|
||||||
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
|
||||||
|
|
||||||
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
|
||||||
|
|
||||||
"term-size": ["term-size@2.2.1", "", {}, "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg=="],
|
|
||||||
|
|
||||||
"tinypool": ["tinypool@2.1.0", "", {}, "sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw=="],
|
|
||||||
|
|
||||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
|
||||||
|
|
||||||
"type-fest": ["type-fest@0.20.2", "", {}, "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="],
|
|
||||||
|
|
||||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
||||||
|
|
||||||
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
|
||||||
|
|
||||||
"update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
|
|
||||||
|
|
||||||
"utility-types": ["utility-types@3.11.0", "", {}, "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw=="],
|
|
||||||
|
|
||||||
"uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="],
|
|
||||||
|
|
||||||
"weak-lru-cache": ["weak-lru-cache@1.2.2", "", {}, "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw=="],
|
|
||||||
|
|
||||||
"@parcel/watcher/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
|
||||||
|
|
||||||
"lightningcss/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
|
||||||
|
|
||||||
"lmdb/node-addon-api": ["node-addon-api@6.1.0", "", {}, "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="],
|
|
||||||
|
|
||||||
"msgpackr-extract/node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="],
|
|
||||||
|
|
||||||
"node-gyp-build-optional-packages/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
|
||||||
|
|
||||||
"msgpackr-extract/node-gyp-build-optional-packages/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "ak-endfield-api-archive-pages",
|
|
||||||
"source": "src/index.html",
|
|
||||||
"type": "module",
|
|
||||||
"private": true,
|
|
||||||
"targets": {
|
|
||||||
"default": {
|
|
||||||
"distDir": "dist",
|
|
||||||
"sourceMap": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@biomejs/biome": "^2.4.4",
|
|
||||||
"@parcel/transformer-typescript-tsc": "^2.16.4",
|
|
||||||
"@tsconfig/bun": "^1.0.10",
|
|
||||||
"@tsconfig/node24": "^24.0.4",
|
|
||||||
"@tsconfig/recommended": "^1.0.13",
|
|
||||||
"@tsconfig/strictest": "^2.0.8",
|
|
||||||
"@types/bootstrap": "^5.2.10",
|
|
||||||
"@types/bun": "latest",
|
|
||||||
"@types/luxon": "^3.7.1",
|
|
||||||
"@types/qs": "^6.14.0",
|
|
||||||
"@types/semver": "^7.7.1",
|
|
||||||
"oxfmt": "^0.35.0",
|
|
||||||
"parcel": "^2.16.4"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"typescript": "^5"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"bootstrap": "^5.3.8",
|
|
||||||
"bootstrap-icons": "^1.13.1",
|
|
||||||
"ky": "^1.14.3",
|
|
||||||
"luxon": "^3.7.2",
|
|
||||||
"qs": "^6.15.0",
|
|
||||||
"semver": "^7.7.4",
|
|
||||||
"uuid": "^13.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en" data-bs-theme="dark">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<title>Arknights: Endfield API Archive</title>
|
|
||||||
<meta
|
|
||||||
name="description"
|
|
||||||
content="Automated Arknights: Endfield game API response archive and download library"
|
|
||||||
/>
|
|
||||||
<meta property="og:title" content="Arknights: Endfield API Archive" />
|
|
||||||
<meta property="og:type" content="website" />
|
|
||||||
<meta property="og:url" content="" />
|
|
||||||
<meta property="og:image" content="" />
|
|
||||||
<meta property="og:image:alt" content="" />
|
|
||||||
<meta name="twitter:card" content="summary_large_image" />
|
|
||||||
<meta name="twitter:title" content="Arknights: Endfield API Archive" />
|
|
||||||
<meta name="twitter:image" content="" />
|
|
||||||
<!-- <link rel="icon" href="/favicon.ico" sizes="any" />
|
|
||||||
<link rel="icon" href="/icon.svg" type="image/svg+xml" />
|
|
||||||
<link rel="apple-touch-icon" href="icon.png" /> -->
|
|
||||||
<!-- <meta name="theme-color" content="#fafafa" /> -->
|
|
||||||
<style>
|
|
||||||
@import "./assets/css/essentials.css";
|
|
||||||
@import "./assets/css/about.css";
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container my-4 px-4" id="mainContainer">
|
|
||||||
<section>
|
|
||||||
<h1 class="text-center fw-bold">Arknights: Endfield API Archive</h1>
|
|
||||||
<ul class="nav nav-tabs justify-content-center">
|
|
||||||
<li class="nav-item">
|
|
||||||
<a href="./index.html" class="nav-link">Top</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link active">About</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
<section id="content" class="mt-4">
|
|
||||||
<h2>About this website</h2>
|
|
||||||
<p>
|
|
||||||
This site functions as a simplified display page for data from the
|
|
||||||
<strong>ak-endfield-api-archive</strong> repository.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The
|
|
||||||
<a href="https://github.com/daydreamer-json/ak-endfield-api-archive"
|
|
||||||
><strong>ak-endfield-api-archive</strong></a
|
|
||||||
>
|
|
||||||
repository automatically records various API data changes for
|
|
||||||
Arknights: Endfield. It also archives not only API changes but also
|
|
||||||
some packages and raw binary data. This is useful for certain users
|
|
||||||
interested in analyzing and researching game data.
|
|
||||||
</p>
|
|
||||||
<h3>Disclaimer</h3>
|
|
||||||
<p>
|
|
||||||
This project has no affiliation with Hypergryph (GRYPHLINE) and was
|
|
||||||
created solely for
|
|
||||||
<strong>private use, educational, and research purposes.</strong>
|
|
||||||
<br />
|
|
||||||
Copyright for the archived API data and binary data belongs to their
|
|
||||||
respective copyright holders.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
I assume no responsibility whatsoever.
|
|
||||||
<strong>PLEASE USE IT AT YOUR OWN RISK.</strong>
|
|
||||||
</p>
|
|
||||||
<h3>Thanks</h3>
|
|
||||||
<table
|
|
||||||
class="table table-sm table-borderless w-auto mb-2 bg-transparent"
|
|
||||||
>
|
|
||||||
<tr class="bg-transparent">
|
|
||||||
<td class="fw-bold bg-transparent">Vivi029</td>
|
|
||||||
<td class="bg-transparent">
|
|
||||||
Added Windows Google Play Games channel
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</section>
|
|
||||||
<hr />
|
|
||||||
<section>
|
|
||||||
<p class="text-center text-muted">
|
|
||||||
<small>(C) daydreamer-json and contributors</small>
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
<img id="endmin-thumbsup" src="./assets/img/endmin_thumbsup_640px.webp" />
|
|
||||||
<script type="module" src="./assets/ts/essentials.ts"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#endmin-thumbsup {
|
|
||||||
width: 200px;
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
z-index: -1000;
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
@import "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Noto+Sans:ital,wght@0,100..900;1,100..900&family=Roboto:ital,wght@0,100..900;1,100..900&family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&family=Noto+Sans+JP:wght@100..900&family=Noto+Sans+SC:wght@100..900&family=Noto+Sans+TC:wght@100..900&display=swap";
|
|
||||||
@import "https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200";
|
|
||||||
/* @import "../fonts/line_seed/line_seed.css"; */
|
|
||||||
@import "../fonts/iosevka/Iosevka.css";
|
|
||||||
/* @import "../fonts/novecento_sans/novecento_sans.css"; */
|
|
||||||
@import "../fonts/novecento_sans/novecento_sans_wide_number_only.css";
|
|
||||||
/* @import "../fonts/novecento_sans/novecento_sans_normal_number_only.css"; */
|
|
||||||
@import "../fonts/harmonyos_sans/harmonyos_sans.css";
|
|
||||||
/* @import "../fonts/dseg/dseg.css"; */
|
|
||||||
|
|
||||||
:root {
|
|
||||||
/* --ddjson-custom-font-main: 'Malgun Gothic'; */
|
|
||||||
/* --ddjson-custom-font-main:
|
|
||||||
"Inter", "LINE Seed JP (Web)", "Noto Sans JP", "Noto Sans SC", "SF Pro",
|
|
||||||
-apple-system, "BlinkMacSystemFont", "Segoe UI", "Roboto", "Oxygen",
|
|
||||||
"Ubuntu", "Helvetica Neue", "Helvetica", "Arial", sans-serif, system-ui; */
|
|
||||||
--ddjson-custom-font-main:
|
|
||||||
"Novecento Sans Wide (Number Only)", "HarmonyOS Sans", "Noto Sans JP",
|
|
||||||
"Noto Sans SC", "SF Pro", -apple-system, "BlinkMacSystemFont", "Segoe UI",
|
|
||||||
"Roboto", "Oxygen", "Ubuntu", "Helvetica Neue", "Helvetica", "Arial",
|
|
||||||
sans-serif, system-ui;
|
|
||||||
--ddjson-custom-font-mono:
|
|
||||||
"Iosevka Web", "JetBrains Mono", "SF Mono", "Noto Sans JP", "Noto Sans SC",
|
|
||||||
"SFMono-Regular", "Menlo", "Monaco", "Consolas", "Liberation Mono",
|
|
||||||
"Courier New", monospace, sans-serif, system-ui;
|
|
||||||
}
|
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
font-family: var(--ddjson-custom-font-main);
|
|
||||||
font-feature-settings:
|
|
||||||
"liga" 1,
|
|
||||||
"calt" 1,
|
|
||||||
"palt";
|
|
||||||
word-break: auto-phrase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.font-monospace {
|
|
||||||
font-family: var(--ddjson-custom-font-mono) !important;
|
|
||||||
font-feature-settings: "palt" 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre,
|
|
||||||
code,
|
|
||||||
kbd,
|
|
||||||
samp,
|
|
||||||
tt {
|
|
||||||
font-family: var(--ddjson-custom-font-mono);
|
|
||||||
font-feature-settings: "palt" 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.material-symbols-outlined {
|
|
||||||
font-variation-settings:
|
|
||||||
"FILL" 0,
|
|
||||||
"wght" 400,
|
|
||||||
"GRAD" 0,
|
|
||||||
"opsz" 24;
|
|
||||||
font-size: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-drag-none {
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#debug-log-inner {
|
|
||||||
display: block;
|
|
||||||
white-space: pre-wrap !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#debug-log-inner div {
|
|
||||||
padding-left: 15ch;
|
|
||||||
text-indent: -15ch;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.accordion-button {
|
|
||||||
padding: calc(var(--bs-accordion-btn-padding-y) / 1.5)
|
|
||||||
calc(var(--bs-accordion-btn-padding-x) / 1.5);
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
import ky from 'ky';
|
|
||||||
import { BASE_URL, gameTargets, launcherTargets, launcherWebApiLang } from './utils/constants.js';
|
|
||||||
|
|
||||||
const apiCache = new Map<string, Promise<any>>();
|
|
||||||
|
|
||||||
export function fetchJson<T>(url: string): Promise<T> {
|
|
||||||
if (!apiCache.has(url)) {
|
|
||||||
const promise = ky
|
|
||||||
.get(url)
|
|
||||||
.json<T>()
|
|
||||||
.catch((err) => {
|
|
||||||
apiCache.delete(url);
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
apiCache.set(url, promise);
|
|
||||||
}
|
|
||||||
return apiCache.get(url) as Promise<T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function preloadData() {
|
|
||||||
const promises: Promise<any>[] = [];
|
|
||||||
promises.push(fetchJson(`${BASE_URL}/mirror_file_list.json`));
|
|
||||||
const launcherWebApiFolderNames = ['announcement', 'banner', 'main_bg_image', 'sidebar', 'single_ent'];
|
|
||||||
for (const target of gameTargets) {
|
|
||||||
promises.push(fetchJson(`${BASE_URL}/akEndfield/launcher/game/${target.dirName}/all.json`));
|
|
||||||
promises.push(fetchJson(`${BASE_URL}/akEndfield/launcher/game/${target.dirName}/all_patch.json`));
|
|
||||||
for (const apiName of launcherWebApiFolderNames) {
|
|
||||||
for (const lang of launcherWebApiLang[target.region]) {
|
|
||||||
promises.push(fetchJson(`${BASE_URL}/akEndfield/launcher/web/${target.dirName}/${apiName}/${lang}/all.json`));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const resTargets = [
|
|
||||||
{ region: 'os', channel: 6 },
|
|
||||||
{ region: 'cn', channel: 1 },
|
|
||||||
];
|
|
||||||
const platforms = ['Windows', 'Android', 'iOS', 'PlayStation'];
|
|
||||||
for (const target of resTargets) {
|
|
||||||
for (const platform of platforms) {
|
|
||||||
promises.push(fetchJson(`${BASE_URL}/akEndfield/launcher/game_resources/${target.channel}/${platform}/all.json`));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const region of launcherTargets) {
|
|
||||||
for (const app of region.apps) {
|
|
||||||
promises.push(fetchJson(`${BASE_URL}/akEndfield/launcher/launcher/${app}/${region.channel}/all.json`));
|
|
||||||
promises.push(fetchJson(`${BASE_URL}/akEndfield/launcher/launcherExe/${app}/${region.channel}/all.json`));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all(promises);
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import 'bootstrap';
|
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
|
||||||
import 'bootstrap-icons/font/bootstrap-icons.min.css';
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
const setTheme = (theme: string) => {
|
|
||||||
document.documentElement.setAttribute('data-bs-theme', theme);
|
|
||||||
};
|
|
||||||
const getPreferredTheme = (): 'light' | 'dark' => {
|
|
||||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
||||||
};
|
|
||||||
setTheme(getPreferredTheme());
|
|
||||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
|
||||||
setTheme(getPreferredTheme());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
import { fetchJson, preloadData } from './api.js';
|
|
||||||
import { renderGamePackages } from './renderers/gamePackages.js';
|
|
||||||
import { renderLaunchers } from './renderers/launchers.js';
|
|
||||||
import { renderOverview } from './renderers/overview.js';
|
|
||||||
import { renderPatches } from './renderers/patches.js';
|
|
||||||
import { renderResources } from './renderers/resources.js';
|
|
||||||
import { renderWebPretty } from './renderers/webPretty.js';
|
|
||||||
import type { MirrorFileEntry } from './types.js';
|
|
||||||
import { BASE_URL } from './utils/constants.js';
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
main();
|
|
||||||
});
|
|
||||||
|
|
||||||
let mirrorFileDb: MirrorFileEntry[] = [];
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const contentDiv = document.getElementById('content');
|
|
||||||
if (!contentDiv) return;
|
|
||||||
|
|
||||||
await preloadData();
|
|
||||||
|
|
||||||
try {
|
|
||||||
mirrorFileDb = await fetchJson<MirrorFileEntry[]>(`${BASE_URL}/mirror_file_list.json`);
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('Failed to fetch mirror list', e);
|
|
||||||
}
|
|
||||||
|
|
||||||
const tabsHtml = `
|
|
||||||
<ul class="nav nav-tabs" id="mainTabs" role="tablist">
|
|
||||||
<li class="nav-item" role="presentation"><button class="nav-link active" data-bs-toggle="tab" data-bs-target="#tab-overview" type="button">Overview</button></li>
|
|
||||||
<li class="nav-item" role="presentation"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#tab-game" type="button">Game Packages</button></li>
|
|
||||||
<li class="nav-item" role="presentation"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#tab-patch" type="button">Patches</button></li>
|
|
||||||
<li class="nav-item" role="presentation"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#tab-resources" type="button">Resources</button></li>
|
|
||||||
<li class="nav-item" role="presentation"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#tab-launcher" type="button">Launcher</button></li>
|
|
||||||
<li class="nav-item" role="presentation"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#tab-web-pretty" type="button">Web</button></li>
|
|
||||||
</ul>
|
|
||||||
<div class="tab-content p-3 border border-top-0 rounded-bottom" id="mainTabsContent">
|
|
||||||
<div class="tab-pane fade show active" id="tab-overview" role="tabpanel"></div>
|
|
||||||
<div class="tab-pane fade" id="tab-game" role="tabpanel"></div>
|
|
||||||
<div class="tab-pane fade" id="tab-patch" role="tabpanel"></div>
|
|
||||||
<div class="tab-pane fade" id="tab-resources" role="tabpanel"></div>
|
|
||||||
<div class="tab-pane fade" id="tab-launcher" role="tabpanel"></div>
|
|
||||||
<div class="tab-pane fade" id="tab-web-pretty" role="tabpanel"></div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
contentDiv.innerHTML = tabsHtml;
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
renderOverview(document.getElementById('tab-overview')!, mirrorFileDb),
|
|
||||||
renderGamePackages(document.getElementById('tab-game')!, mirrorFileDb),
|
|
||||||
renderPatches(document.getElementById('tab-patch')!, mirrorFileDb),
|
|
||||||
renderResources(document.getElementById('tab-resources')!),
|
|
||||||
renderLaunchers(document.getElementById('tab-launcher')!, mirrorFileDb),
|
|
||||||
renderWebPretty(document.getElementById('tab-web-pretty')!),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
import { DateTime } from 'luxon';
|
|
||||||
import { fetchJson } from '../api.js';
|
|
||||||
import type { MirrorFileEntry, StoredData } from '../types.js';
|
|
||||||
import { BASE_URL, FILE_SIZE_OPTS, gameTargets } from '../utils/constants.js';
|
|
||||||
import math from '../utils/math.js';
|
|
||||||
import { generateDownloadLinks } from '../utils/ui.js';
|
|
||||||
|
|
||||||
export async function renderGamePackages(container: HTMLElement, mirrorFileDb: MirrorFileEntry[]) {
|
|
||||||
for (const target of gameTargets) {
|
|
||||||
const url = `${BASE_URL}/akEndfield/launcher/game/${target.dirName}/all.json`;
|
|
||||||
try {
|
|
||||||
const data = await fetchJson<StoredData<any>[]>(url);
|
|
||||||
const section = document.createElement('div');
|
|
||||||
section.className = 'mb-5';
|
|
||||||
section.innerHTML = `<h3 class="mb-3">${target.region === 'cn' ? 'China' : 'Global'}, ${target.name}</h3>`;
|
|
||||||
|
|
||||||
const accordion = document.createElement('div');
|
|
||||||
accordion.className = 'accordion';
|
|
||||||
accordion.id = `accordion-game-${target.dirName}`;
|
|
||||||
|
|
||||||
// Reverse order to show latest first
|
|
||||||
const list = [...data].reverse();
|
|
||||||
for (let i = 0; i < list.length; i++) {
|
|
||||||
const e = list[i];
|
|
||||||
if (!e) continue;
|
|
||||||
const version = e.rsp.version;
|
|
||||||
const dateStr = DateTime.fromISO(e.updatedAt).toFormat('yyyy/MM/dd HH:mm:ss');
|
|
||||||
const packedSize = math.arrayTotal(e.rsp.pkg.packs.map((f: any) => parseInt(f.package_size)));
|
|
||||||
const unpackedSize = parseInt(e.rsp.pkg.total_size) - packedSize;
|
|
||||||
|
|
||||||
let rows = '';
|
|
||||||
const fileName = (f: any) => new URL(f.url).pathname.split('/').pop() ?? '';
|
|
||||||
for (const f of e.rsp.pkg.packs) {
|
|
||||||
rows += `<tr>
|
|
||||||
<td>${fileName(f)}</td>
|
|
||||||
<td><code>${f.md5}</code></td>
|
|
||||||
<td class="text-end">${math.formatFileSize(parseInt(f.package_size), FILE_SIZE_OPTS)}</td>
|
|
||||||
<td class="text-center">${generateDownloadLinks(f.url, mirrorFileDb)}</td>
|
|
||||||
</tr>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const itemId = `game-${target.dirName}-${i}`;
|
|
||||||
const isExpanded = false;
|
|
||||||
const item = document.createElement('div');
|
|
||||||
item.className = 'accordion-item';
|
|
||||||
item.innerHTML = `
|
|
||||||
<h2 class="accordion-header" id="heading-${itemId}">
|
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-${itemId}" aria-expanded="${isExpanded}" aria-controls="collapse-${itemId}">
|
|
||||||
<div class="d-flex w-100 justify-content-between me-3">
|
|
||||||
<span class="fw-bold">${version}</span>
|
|
||||||
<span class="text-muted small align-bottom">${dateStr}</span>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</h2>
|
|
||||||
<div id="collapse-${itemId}" class="accordion-collapse collapse ${isExpanded ? 'show' : ''}" aria-labelledby="heading-${itemId}" data-bs-parent="#${accordion.id}">
|
|
||||||
<div class="accordion-body">
|
|
||||||
<table class="table table-sm table-borderless w-auto mb-2">
|
|
||||||
<tr><td>Unpacked Size</td><td class="text-end fw-bold">${math.formatFileSize(unpackedSize, FILE_SIZE_OPTS)}</td></tr>
|
|
||||||
<tr><td>Packed Size</td><td class="text-end fw-bold">${math.formatFileSize(packedSize, FILE_SIZE_OPTS)}</td></tr>
|
|
||||||
</table>
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-striped table-bordered table-sm align-middle text-nowrap">
|
|
||||||
<thead><tr><th>File</th><th>MD5 Checksum</th><th class="text-end">Size</th><th class="text-center">DL</th></tr></thead>
|
|
||||||
<tbody>${rows}</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
accordion.appendChild(item);
|
|
||||||
}
|
|
||||||
section.appendChild(accordion);
|
|
||||||
container.appendChild(section);
|
|
||||||
} catch (err) {
|
|
||||||
// Ignore 404 or errors
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
import { DateTime } from 'luxon';
|
|
||||||
import { fetchJson } from '../api.js';
|
|
||||||
import type { MirrorFileEntry, StoredData } from '../types.js';
|
|
||||||
import { BASE_URL, FILE_SIZE_OPTS, launcherTargets } from '../utils/constants.js';
|
|
||||||
import math from '../utils/math.js';
|
|
||||||
import { generateDownloadLinks } from '../utils/ui.js';
|
|
||||||
|
|
||||||
export async function renderLaunchers(container: HTMLElement, mirrorFileDb: MirrorFileEntry[]) {
|
|
||||||
for (const region of launcherTargets) {
|
|
||||||
for (const app of region.apps) {
|
|
||||||
const section = document.createElement('div');
|
|
||||||
section.className = 'mb-5';
|
|
||||||
section.innerHTML = `<h3 class="mb-3">${region.id.toUpperCase()} ${app}</h3>`;
|
|
||||||
|
|
||||||
const accordion = document.createElement('div');
|
|
||||||
accordion.className = 'accordion';
|
|
||||||
accordion.id = `accordion-launcher-${region.id}-${app}`;
|
|
||||||
let itemIndex = 0;
|
|
||||||
|
|
||||||
// Zip
|
|
||||||
try {
|
|
||||||
const urlZip = `${BASE_URL}/akEndfield/launcher/launcher/${app}/${region.channel}/all.json`;
|
|
||||||
const dataZip = await fetchJson<StoredData<any>[]>(urlZip);
|
|
||||||
|
|
||||||
let rows = '';
|
|
||||||
for (const e of [...dataZip].reverse()) {
|
|
||||||
const dateStr = DateTime.fromISO(e.updatedAt).toFormat('yyyy/MM/dd HH:mm:ss');
|
|
||||||
const fileName = new URL(e.rsp.zip_package_url).pathname.split('/').pop() ?? '';
|
|
||||||
const unpacked = parseInt(e.rsp.total_size) - parseInt(e.rsp.package_size);
|
|
||||||
|
|
||||||
rows += `<tr>
|
|
||||||
<td>${dateStr}</td>
|
|
||||||
<td>${e.rsp.version}</td>
|
|
||||||
<td>${fileName}</td>
|
|
||||||
<td><code>${e.rsp.md5}</code></td>
|
|
||||||
<td class="text-end">${math.formatFileSize(unpacked, FILE_SIZE_OPTS)}</td>
|
|
||||||
<td class="text-end">${math.formatFileSize(parseInt(e.rsp.package_size), FILE_SIZE_OPTS)}</td>
|
|
||||||
<td class="text-center">${generateDownloadLinks(e.rsp.zip_package_url, mirrorFileDb)}</td>
|
|
||||||
</tr>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const itemId = `launcher-zip-${region.id}-${app}`;
|
|
||||||
const isExpanded = false;
|
|
||||||
itemIndex++;
|
|
||||||
|
|
||||||
const item = document.createElement('div');
|
|
||||||
item.className = 'accordion-item';
|
|
||||||
item.innerHTML = `
|
|
||||||
<h2 class="accordion-header" id="heading-${itemId}">
|
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-${itemId}" aria-expanded="${isExpanded}" aria-controls="collapse-${itemId}">
|
|
||||||
Launcher Packages (zip)
|
|
||||||
</button>
|
|
||||||
</h2>
|
|
||||||
<div id="collapse-${itemId}" class="accordion-collapse collapse ${isExpanded ? 'show' : ''}" aria-labelledby="heading-${itemId}" data-bs-parent="#${accordion.id}">
|
|
||||||
<div class="accordion-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-striped table-bordered table-sm align-middle text-nowrap">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Date</th>
|
|
||||||
<th>Version</th>
|
|
||||||
<th>File</th>
|
|
||||||
<th>MD5 Checksum</th>
|
|
||||||
<th class="text-end">Unpacked</th>
|
|
||||||
<th class="text-end">Packed</th>
|
|
||||||
<th class="text-center">DL</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>${rows}</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
accordion.appendChild(item);
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
// Exe
|
|
||||||
try {
|
|
||||||
const urlExe = `${BASE_URL}/akEndfield/launcher/launcherExe/${app}/${region.channel}/all.json`;
|
|
||||||
const dataExe = await fetchJson<StoredData<any>[]>(urlExe);
|
|
||||||
|
|
||||||
let rows = '';
|
|
||||||
for (const e of [...dataExe].reverse()) {
|
|
||||||
const dateStr = DateTime.fromISO(e.updatedAt).toFormat('yyyy/MM/dd HH:mm:ss');
|
|
||||||
const fileName = new URL(e.rsp.exe_url).pathname.split('/').pop() ?? '';
|
|
||||||
|
|
||||||
rows += `<tr>
|
|
||||||
<td>${dateStr}</td>
|
|
||||||
<td>${e.rsp.version}</td>
|
|
||||||
<td>${fileName}</td>
|
|
||||||
<td class="text-end">${math.formatFileSize(parseInt(e.rsp.exe_size), FILE_SIZE_OPTS)}</td>
|
|
||||||
<td class="text-center">${generateDownloadLinks(e.rsp.exe_url, mirrorFileDb)}</td>
|
|
||||||
</tr>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const itemId = `launcher-exe-${region.id}-${app}`;
|
|
||||||
const isExpanded = false;
|
|
||||||
itemIndex++;
|
|
||||||
|
|
||||||
const item = document.createElement('div');
|
|
||||||
item.className = 'accordion-item';
|
|
||||||
item.innerHTML = `
|
|
||||||
<h2 class="accordion-header" id="heading-${itemId}">
|
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-${itemId}" aria-expanded="${isExpanded}" aria-controls="collapse-${itemId}">
|
|
||||||
Launcher Packages (Installer)
|
|
||||||
</button>
|
|
||||||
</h2>
|
|
||||||
<div id="collapse-${itemId}" class="accordion-collapse collapse ${isExpanded ? 'show' : ''}" aria-labelledby="heading-${itemId}" data-bs-parent="#${accordion.id}">
|
|
||||||
<div class="accordion-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-striped table-bordered table-sm align-middle text-nowrap">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Date</th>
|
|
||||||
<th>Version</th>
|
|
||||||
<th>File</th>
|
|
||||||
<th class="text-end">Size</th>
|
|
||||||
<th class="text-center">DL</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>${rows}</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
accordion.appendChild(item);
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
if (accordion.childElementCount > 0) {
|
|
||||||
section.appendChild(accordion);
|
|
||||||
container.appendChild(section);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,255 +0,0 @@
|
|||||||
import { DateTime } from 'luxon';
|
|
||||||
import { fetchJson } from '../api.js';
|
|
||||||
import type { MirrorFileEntry, StoredData } from '../types.js';
|
|
||||||
import { BASE_URL, FILE_SIZE_OPTS, gameTargets, launcherTargets } from '../utils/constants.js';
|
|
||||||
import math from '../utils/math.js';
|
|
||||||
|
|
||||||
export async function renderOverview(container: HTMLElement, mirrorFileDb: MirrorFileEntry[]) {
|
|
||||||
const mirrorOrigSet = new Set<string>();
|
|
||||||
for (const m of mirrorFileDb) {
|
|
||||||
try {
|
|
||||||
const u = new URL(m.orig);
|
|
||||||
u.search = '';
|
|
||||||
mirrorOrigSet.add(u.toString());
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
const countedUrls = new Set<string>();
|
|
||||||
let totalMirrorSize = 0;
|
|
||||||
|
|
||||||
const checkAndAddSize = (url: string, size: number) => {
|
|
||||||
if (!url || isNaN(size)) return;
|
|
||||||
try {
|
|
||||||
const u = new URL(url);
|
|
||||||
u.search = '';
|
|
||||||
const cleanUrl = u.toString();
|
|
||||||
if (countedUrls.has(cleanUrl)) return;
|
|
||||||
if (mirrorOrigSet.has(cleanUrl)) {
|
|
||||||
totalMirrorSize += size;
|
|
||||||
countedUrls.add(cleanUrl);
|
|
||||||
}
|
|
||||||
} catch {}
|
|
||||||
};
|
|
||||||
|
|
||||||
const section = document.createElement('div');
|
|
||||||
const sectionIn = document.createElement('div');
|
|
||||||
section.className = 'card mb-3';
|
|
||||||
sectionIn.className = 'card-body';
|
|
||||||
|
|
||||||
const [globalPkg, chinaPkg] = await Promise.all([
|
|
||||||
(async () => {
|
|
||||||
const url = `${BASE_URL}/akEndfield/launcher/game/6/all.json`;
|
|
||||||
const dat = await fetchJson<StoredData<any>[]>(url);
|
|
||||||
const latest = dat.at(-1);
|
|
||||||
if (!latest) return { version: '---', date: '---' };
|
|
||||||
return {
|
|
||||||
version: latest.rsp.version,
|
|
||||||
date: DateTime.fromISO(latest.updatedAt).toFormat('yyyy/MM/dd HH:mm:ss'),
|
|
||||||
};
|
|
||||||
})(),
|
|
||||||
(async () => {
|
|
||||||
const url = `${BASE_URL}/akEndfield/launcher/game/1/all.json`;
|
|
||||||
const dat = await fetchJson<StoredData<any>[]>(url);
|
|
||||||
const latest = dat.at(-1);
|
|
||||||
if (!latest) return { version: '---', date: '---' };
|
|
||||||
return {
|
|
||||||
version: latest.rsp.version,
|
|
||||||
date: DateTime.fromISO(latest.updatedAt).toFormat('yyyy/MM/dd HH:mm:ss'),
|
|
||||||
};
|
|
||||||
})(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
sectionIn.innerHTML = `
|
|
||||||
<h3 class="card-title mb-4">Latest Game Packages</h3>
|
|
||||||
<div class="row text-center mb-4">
|
|
||||||
<div class="col-md-6 mb-md-0 mb-3">
|
|
||||||
<p class="lh-1 mb-0">
|
|
||||||
<span class="fw-bold fs-1">${globalPkg.version}</span><br />
|
|
||||||
<span class="small opacity-75" style="line-height: 1.5;">${globalPkg.date}</span><br />
|
|
||||||
Latest Version (Global)
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<p class="lh-1 mb-0">
|
|
||||||
<span class="fw-bold fs-1">${chinaPkg.version}</span><br />
|
|
||||||
<span class="small opacity-75" style="line-height: 1.5;">${chinaPkg.date}</span><br />
|
|
||||||
Latest Version (China)
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
const tableWrapper = document.createElement('div');
|
|
||||||
tableWrapper.className = 'table-responsive';
|
|
||||||
|
|
||||||
const table = document.createElement('table');
|
|
||||||
table.className = 'table table-striped table-bordered table-sm align-middle text-nowrap';
|
|
||||||
table.innerHTML = `
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Region</th>
|
|
||||||
<th>Channel</th>
|
|
||||||
<th>Version</th>
|
|
||||||
<th class="text-end">Packed</th>
|
|
||||||
<th class="text-end">Unpacked</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody></tbody>
|
|
||||||
`;
|
|
||||||
const tbody = table.querySelector('tbody')!;
|
|
||||||
tableWrapper.appendChild(table);
|
|
||||||
sectionIn.appendChild(tableWrapper);
|
|
||||||
section.appendChild(sectionIn);
|
|
||||||
container.appendChild(section);
|
|
||||||
|
|
||||||
// 1. Game Packages
|
|
||||||
for (const target of gameTargets) {
|
|
||||||
const url = `${BASE_URL}/akEndfield/launcher/game/${target.dirName}/all.json`;
|
|
||||||
try {
|
|
||||||
const data = await fetchJson<StoredData<any>[]>(url);
|
|
||||||
if (!data || data.length === 0) continue;
|
|
||||||
|
|
||||||
const latest = data[data.length - 1];
|
|
||||||
if (!latest) continue;
|
|
||||||
const version = latest.rsp.version;
|
|
||||||
const packedSize = math.arrayTotal(latest.rsp.pkg.packs.map((f: any) => parseInt(f.package_size)));
|
|
||||||
const totalSize = parseInt(latest.rsp.pkg.total_size);
|
|
||||||
const unpackedSize = totalSize - packedSize;
|
|
||||||
|
|
||||||
const row = document.createElement('tr');
|
|
||||||
row.innerHTML = `
|
|
||||||
<td>${target.region === 'cn' ? 'China' : 'Global'}</td>
|
|
||||||
<td>${target.name}</td>
|
|
||||||
<td>${version}</td>
|
|
||||||
<td class="text-end">${math.formatFileSize(packedSize, FILE_SIZE_OPTS)}</td>
|
|
||||||
<td class="text-end">${math.formatFileSize(unpackedSize, FILE_SIZE_OPTS)}</td>
|
|
||||||
`;
|
|
||||||
tbody.appendChild(row);
|
|
||||||
|
|
||||||
for (const entry of data) {
|
|
||||||
if (entry.rsp.pkg && entry.rsp.pkg.packs) {
|
|
||||||
for (const pack of entry.rsp.pkg.packs) {
|
|
||||||
checkAndAddSize(pack.url, parseInt(pack.package_size));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('Overview: Failed to fetch game data', target.name, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Patches
|
|
||||||
for (const target of gameTargets) {
|
|
||||||
const url = `${BASE_URL}/akEndfield/launcher/game/${target.dirName}/all_patch.json`;
|
|
||||||
try {
|
|
||||||
const data = await fetchJson<StoredData<any>[]>(url);
|
|
||||||
for (const entry of data) {
|
|
||||||
if (!entry.rsp.patch) continue;
|
|
||||||
if (entry.rsp.patch.url) {
|
|
||||||
checkAndAddSize(entry.rsp.patch.url, parseInt(entry.rsp.patch.package_size));
|
|
||||||
}
|
|
||||||
if (entry.rsp.patch.patches) {
|
|
||||||
for (const p of entry.rsp.patch.patches) {
|
|
||||||
checkAndAddSize(p.url, parseInt(p.package_size));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Launchers
|
|
||||||
for (const region of launcherTargets) {
|
|
||||||
for (const app of region.apps) {
|
|
||||||
try {
|
|
||||||
const urlZip = `${BASE_URL}/akEndfield/launcher/launcher/${app}/${region.channel}/all.json`;
|
|
||||||
const dataZip = await fetchJson<StoredData<any>[]>(urlZip);
|
|
||||||
for (const e of dataZip) {
|
|
||||||
checkAndAddSize(e.rsp.zip_package_url, parseInt(e.rsp.package_size));
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
try {
|
|
||||||
const urlExe = `${BASE_URL}/akEndfield/launcher/launcherExe/${app}/${region.channel}/all.json`;
|
|
||||||
const dataExe = await fetchJson<StoredData<any>[]>(urlExe);
|
|
||||||
for (const e of dataExe) {
|
|
||||||
checkAndAddSize(e.rsp.exe_url, parseInt(e.rsp.exe_size));
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Latest Game Resources (Global)
|
|
||||||
{
|
|
||||||
const resPlatforms = ['Windows', 'Android', 'iOS', 'PlayStation'];
|
|
||||||
const resData = await Promise.all(
|
|
||||||
resPlatforms.map(async (p) => {
|
|
||||||
try {
|
|
||||||
const url = `${BASE_URL}/akEndfield/launcher/game_resources/6/${p}/all.json`;
|
|
||||||
const dat = await fetchJson<StoredData<any>[]>(url);
|
|
||||||
return dat.at(-1);
|
|
||||||
} catch {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const resSection = document.createElement('div');
|
|
||||||
resSection.className = 'card mb-3';
|
|
||||||
const resSectionIn = document.createElement('div');
|
|
||||||
resSectionIn.className = 'card-body';
|
|
||||||
resSectionIn.innerHTML = `
|
|
||||||
<h3 class="card-title mb-4">Latest Game Resources</h3>
|
|
||||||
<div class="row text-center">
|
|
||||||
${resPlatforms
|
|
||||||
.map((p, i) => {
|
|
||||||
const item = resData[i];
|
|
||||||
if (!item) {
|
|
||||||
return `
|
|
||||||
<div class="col-md-3 mb-3 mb-md-0">
|
|
||||||
<p class="lh-1 mb-0">
|
|
||||||
<span class="fw-bold fs-1">---</span><br />
|
|
||||||
${p}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const version = (() => {
|
|
||||||
const initialRes = item.rsp.resources.find((e: any) => e.name === 'initial');
|
|
||||||
const mainRes = item.rsp.resources.find((e: any) => e.name === 'main');
|
|
||||||
if (!initialRes || !mainRes) return '---';
|
|
||||||
if (initialRes.version === mainRes.version) return mainRes.version;
|
|
||||||
return item.rsp.res_version;
|
|
||||||
})();
|
|
||||||
|
|
||||||
const dateStr = DateTime.fromISO(item.updatedAt).toFormat('yyyy/MM/dd HH:mm:ss');
|
|
||||||
|
|
||||||
return `
|
|
||||||
<div class="col-md-3 mb-3 mb-md-0">
|
|
||||||
<p class="lh-1 mb-0">
|
|
||||||
<span class="fw-bold fs-1">${version}</span><br />
|
|
||||||
<span class="small opacity-75" style="line-height: 1.5;">${dateStr}</span><br />
|
|
||||||
${p}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
})
|
|
||||||
.join('')}
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
resSection.appendChild(resSectionIn);
|
|
||||||
container.appendChild(resSection);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mirrorSection = document.createElement('div');
|
|
||||||
mirrorSection.className = 'card';
|
|
||||||
mirrorSection.innerHTML = `
|
|
||||||
<div class="card-body">
|
|
||||||
<h3 class="card-title">Mirror Statistics</h3>
|
|
||||||
<p class="card-text text-center lh-1">
|
|
||||||
<span class="fw-bold fs-1">${math.formatFileSize(totalMirrorSize, { ...FILE_SIZE_OPTS, unit: 'G' })}</span><br />
|
|
||||||
uploaded to mirror
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
container.appendChild(mirrorSection);
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
import { DateTime } from 'luxon';
|
|
||||||
import { fetchJson } from '../api.js';
|
|
||||||
import type { MirrorFileEntry, StoredData } from '../types.js';
|
|
||||||
import { BASE_URL, FILE_SIZE_OPTS, gameTargets } from '../utils/constants.js';
|
|
||||||
import math from '../utils/math.js';
|
|
||||||
import { generateDownloadLinks } from '../utils/ui.js';
|
|
||||||
|
|
||||||
export async function renderPatches(container: HTMLElement, mirrorFileDb: MirrorFileEntry[]) {
|
|
||||||
for (const target of gameTargets) {
|
|
||||||
const url = `${BASE_URL}/akEndfield/launcher/game/${target.dirName}/all_patch.json`;
|
|
||||||
try {
|
|
||||||
const data = await fetchJson<StoredData<any>[]>(url);
|
|
||||||
if (data.length === 0) continue;
|
|
||||||
|
|
||||||
const section = document.createElement('div');
|
|
||||||
section.className = 'mb-5';
|
|
||||||
section.innerHTML = `<h3 class="mb-3">${target.region === 'cn' ? 'China' : 'Global'}, ${target.name}</h3>`;
|
|
||||||
|
|
||||||
const accordion = document.createElement('div');
|
|
||||||
accordion.className = 'accordion';
|
|
||||||
accordion.id = `accordion-patch-${target.dirName}`;
|
|
||||||
|
|
||||||
let itemIndex = 0;
|
|
||||||
for (const e of [...data].reverse()) {
|
|
||||||
if (!e.rsp.patch) continue;
|
|
||||||
const version = e.rsp.version;
|
|
||||||
const reqVersion = e.rsp.request_version;
|
|
||||||
const dateStr = DateTime.fromISO(e.updatedAt).toFormat('yyyy/MM/dd HH:mm:ss');
|
|
||||||
const packedSize = math.arrayTotal(e.rsp.patch.patches.map((f: any) => parseInt(f.package_size)));
|
|
||||||
const unpackedSize = parseInt(e.rsp.patch.total_size) - packedSize;
|
|
||||||
|
|
||||||
let rows = '';
|
|
||||||
const fileName = (url: string) => new URL(url).pathname.split('/').pop() ?? '';
|
|
||||||
if (e.rsp.patch.url) {
|
|
||||||
rows += `<tr>
|
|
||||||
<td>${fileName(e.rsp.patch.url)}</td>
|
|
||||||
<td><code>${e.rsp.patch.md5}</code></td>
|
|
||||||
<td class="text-end">${math.formatFileSize(parseInt(e.rsp.patch.package_size), FILE_SIZE_OPTS)}</td>
|
|
||||||
<td class="text-center">${generateDownloadLinks(e.rsp.patch.url, mirrorFileDb)}</td>
|
|
||||||
</tr>`;
|
|
||||||
}
|
|
||||||
for (const f of e.rsp.patch.patches) {
|
|
||||||
rows += `<tr>
|
|
||||||
<td>${fileName(f.url)}</td>
|
|
||||||
<td><code>${f.md5}</code></td>
|
|
||||||
<td class="text-end">${math.formatFileSize(parseInt(f.package_size), FILE_SIZE_OPTS)}</td>
|
|
||||||
<td class="text-center">${generateDownloadLinks(f.url, mirrorFileDb)}</td>
|
|
||||||
</tr>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const itemId = `patch-${target.dirName}-${itemIndex}`;
|
|
||||||
const isExpanded = false;
|
|
||||||
itemIndex++;
|
|
||||||
|
|
||||||
const item = document.createElement('div');
|
|
||||||
item.className = 'accordion-item';
|
|
||||||
item.innerHTML = `
|
|
||||||
<h2 class="accordion-header" id="heading-${itemId}">
|
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-${itemId}" aria-expanded="${isExpanded}" aria-controls="collapse-${itemId}">
|
|
||||||
<div class="d-flex w-100 justify-content-between me-3">
|
|
||||||
<span class="fw-bold">${reqVersion} → ${version}</span>
|
|
||||||
<span class="text-muted small">${dateStr}</span>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</h2>
|
|
||||||
<div id="collapse-${itemId}" class="accordion-collapse collapse ${isExpanded ? 'show' : ''}" aria-labelledby="heading-${itemId}" data-bs-parent="#${accordion.id}">
|
|
||||||
<div class="accordion-body">
|
|
||||||
<table class="table table-sm table-borderless w-auto mb-2">
|
|
||||||
<tr><td>Unpacked Size</td><td class="text-end fw-bold">${math.formatFileSize(unpackedSize, FILE_SIZE_OPTS)}</td></tr>
|
|
||||||
<tr><td>Packed Size</td><td class="text-end fw-bold">${math.formatFileSize(packedSize, FILE_SIZE_OPTS)}</td></tr>
|
|
||||||
</table>
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-striped table-bordered table-sm align-middle text-nowrap">
|
|
||||||
<thead><tr><th>File</th><th>MD5 Checksum</th><th class="text-end">Size</th><th class="text-center">DL</th></tr></thead>
|
|
||||||
<tbody>${rows}</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
accordion.appendChild(item);
|
|
||||||
}
|
|
||||||
section.appendChild(accordion);
|
|
||||||
container.appendChild(section);
|
|
||||||
} catch (err) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
import { DateTime } from 'luxon';
|
|
||||||
import * as semver from 'semver';
|
|
||||||
import { fetchJson } from '../api.js';
|
|
||||||
import type { StoredData } from '../types.js';
|
|
||||||
import { BASE_URL } from '../utils/constants.js';
|
|
||||||
|
|
||||||
export async function renderResources(container: HTMLElement) {
|
|
||||||
const platforms = ['Windows', 'Android', 'iOS', 'PlayStation'];
|
|
||||||
const targets = [
|
|
||||||
{ region: 'os', channel: 6 },
|
|
||||||
{ region: 'cn', channel: 1 },
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const target of targets) {
|
|
||||||
const section = document.createElement('div');
|
|
||||||
section.className = 'mb-5';
|
|
||||||
section.innerHTML = `<h3 class="mb-3">${target.region === 'cn' ? 'China' : 'Global'}</h3>`;
|
|
||||||
|
|
||||||
const accordion = document.createElement('div');
|
|
||||||
accordion.className = 'accordion';
|
|
||||||
accordion.id = `accordion-res-${target.region}-${target.channel}`;
|
|
||||||
let itemIndex = 0;
|
|
||||||
|
|
||||||
for (const platform of platforms) {
|
|
||||||
const url = `${BASE_URL}/akEndfield/launcher/game_resources/${target.channel}/${platform}/all.json`;
|
|
||||||
try {
|
|
||||||
const data = await fetchJson<StoredData<any>[]>(url);
|
|
||||||
|
|
||||||
// Group by res_version
|
|
||||||
const resVersionMap = new Map<string, { rsp: StoredData<any>; versions: Set<string> }>();
|
|
||||||
for (const e of data) {
|
|
||||||
const resVer = e.rsp.res_version;
|
|
||||||
if (!resVersionMap.has(resVer)) {
|
|
||||||
resVersionMap.set(resVer, { rsp: e, versions: new Set() });
|
|
||||||
}
|
|
||||||
resVersionMap.get(resVer)!.versions.add(e.req.version);
|
|
||||||
}
|
|
||||||
|
|
||||||
const resVersionSet = Array.from(resVersionMap.values()).map((d) => ({
|
|
||||||
resVersion: d.rsp.rsp.res_version,
|
|
||||||
rsp: d.rsp,
|
|
||||||
versions: Array.from(d.versions).sort(semver.rcompare),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const sortedSet = resVersionSet.reverse();
|
|
||||||
let rows = '';
|
|
||||||
for (let i = 0; i < sortedSet.length; i++) {
|
|
||||||
const item = sortedSet[i]!;
|
|
||||||
const nextItem = sortedSet[i + 1];
|
|
||||||
// Newest first
|
|
||||||
const currentDate = DateTime.fromISO(item.rsp.updatedAt);
|
|
||||||
const dateStr = currentDate.toFormat('yyyy/MM/dd HH:mm:ss');
|
|
||||||
|
|
||||||
const intervalStr = (() => {
|
|
||||||
if (nextItem) {
|
|
||||||
const nextDate = DateTime.fromISO(nextItem.rsp.updatedAt);
|
|
||||||
const diff = currentDate.diff(nextDate);
|
|
||||||
return diff.toFormat('dd:hh:mm:ss');
|
|
||||||
}
|
|
||||||
return '-';
|
|
||||||
})();
|
|
||||||
|
|
||||||
const initialRes = item.rsp.rsp.resources.find((e: any) => e.name === 'initial');
|
|
||||||
const mainRes = item.rsp.rsp.resources.find((e: any) => e.name === 'main');
|
|
||||||
const isKick = JSON.parse(item.rsp.rsp.configs).kick_flag === true;
|
|
||||||
|
|
||||||
rows += `<tr>
|
|
||||||
<td style="font-feature-settings: 'tnum'">${dateStr}</td>
|
|
||||||
<td style="font-feature-settings: 'tnum'">${intervalStr}</td>
|
|
||||||
<td><a href="${initialRes.path}" target="_blank">${initialRes.version}</a></td>
|
|
||||||
<td><a href="${mainRes.path}" target="_blank">${mainRes.version}</a></td>
|
|
||||||
<td class="text-center">${isKick ? '✅' : ''}</td>
|
|
||||||
<td>${item.versions.join(', ')}</td>
|
|
||||||
</tr>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const itemId = `res-${target.region}-${target.channel}-${platform}`;
|
|
||||||
const isExpanded = false;
|
|
||||||
itemIndex++;
|
|
||||||
|
|
||||||
const item = document.createElement('div');
|
|
||||||
item.className = 'accordion-item';
|
|
||||||
item.innerHTML = `
|
|
||||||
<h2 class="accordion-header" id="heading-${itemId}">
|
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-${itemId}" aria-expanded="${isExpanded}" aria-controls="collapse-${itemId}">
|
|
||||||
${platform}
|
|
||||||
</button>
|
|
||||||
</h2>
|
|
||||||
<div id="collapse-${itemId}" class="accordion-collapse collapse ${isExpanded ? 'show' : ''}" aria-labelledby="heading-${itemId}" data-bs-parent="#${accordion.id}">
|
|
||||||
<div class="accordion-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-striped table-bordered table-sm align-middle text-nowrap">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Date</th>
|
|
||||||
<th>Interval</th>
|
|
||||||
<th>Initial</th>
|
|
||||||
<th>Main</th>
|
|
||||||
<th>Kick</th>
|
|
||||||
<th>Game version</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>${rows}</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
accordion.appendChild(item);
|
|
||||||
} catch (err) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (accordion.childElementCount > 0) {
|
|
||||||
section.appendChild(accordion);
|
|
||||||
container.appendChild(section);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
import { DateTime } from 'luxon';
|
|
||||||
import { fetchJson } from '../api.js';
|
|
||||||
import type { StoredData } from '../types.js';
|
|
||||||
import { BASE_URL, gameTargets, launcherWebApiLang } from '../utils/constants.js';
|
|
||||||
|
|
||||||
const apiTypes = ['announcement', 'banner', 'main_bg_image', 'sidebar', 'single_ent'];
|
|
||||||
|
|
||||||
export async function renderWeb(container: HTMLElement) {
|
|
||||||
for (const target of gameTargets) {
|
|
||||||
const section = document.createElement('div');
|
|
||||||
section.className = 'mb-5';
|
|
||||||
section.innerHTML = `<h3 class="mb-3">${target.region === 'cn' ? 'China' : 'Global'}, ${target.name}</h3>`;
|
|
||||||
|
|
||||||
const langs = launcherWebApiLang[target.region] || [];
|
|
||||||
const defaultLang = target.region === 'os' ? 'en-us' : 'zh-cn';
|
|
||||||
|
|
||||||
// Language Selector
|
|
||||||
const langSelectGroup = document.createElement('div');
|
|
||||||
langSelectGroup.className = 'input-group mb-3';
|
|
||||||
langSelectGroup.innerHTML = '<span class="input-group-text">Language</span>';
|
|
||||||
|
|
||||||
const langSelect = document.createElement('select');
|
|
||||||
langSelect.className = 'form-select';
|
|
||||||
|
|
||||||
langs.forEach((lang) => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = lang;
|
|
||||||
option.textContent = lang;
|
|
||||||
if (lang === defaultLang) {
|
|
||||||
option.selected = true;
|
|
||||||
}
|
|
||||||
langSelect.appendChild(option);
|
|
||||||
});
|
|
||||||
langSelectGroup.appendChild(langSelect);
|
|
||||||
|
|
||||||
if (langs.length <= 1) {
|
|
||||||
langSelectGroup.style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
section.appendChild(langSelectGroup);
|
|
||||||
|
|
||||||
const accordion = document.createElement('div');
|
|
||||||
accordion.className = 'accordion';
|
|
||||||
accordion.id = `accordion-web-${target.dirName}`;
|
|
||||||
|
|
||||||
const renderApiList = async (lang: string) => {
|
|
||||||
accordion.innerHTML = '<div class="text-muted p-2">Loading...</div>';
|
|
||||||
|
|
||||||
const results = await Promise.all(
|
|
||||||
apiTypes.map(async (apiType) => {
|
|
||||||
const url = `${BASE_URL}/akEndfield/launcher/web/${target.dirName}/${apiType}/${lang}/all.json`;
|
|
||||||
try {
|
|
||||||
const data = await fetchJson<StoredData<any>[]>(url);
|
|
||||||
if (!data || data.length === 0) return null;
|
|
||||||
return { apiType, list: [...data].reverse() };
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(`Failed to load ${url}`, e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
accordion.innerHTML = '';
|
|
||||||
const validResults = results.filter((r): r is NonNullable<typeof r> => r !== null);
|
|
||||||
|
|
||||||
if (validResults.length === 0) {
|
|
||||||
accordion.innerHTML = '<div class="text-muted p-2">No data found.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
validResults.forEach(({ apiType, list }, idx) => {
|
|
||||||
const itemId = `web-${target.dirName}-${lang}-${apiType}`;
|
|
||||||
|
|
||||||
const item = document.createElement('div');
|
|
||||||
item.className = 'accordion-item';
|
|
||||||
|
|
||||||
// Header
|
|
||||||
const header = document.createElement('h2');
|
|
||||||
header.className = 'accordion-header';
|
|
||||||
header.id = `heading-${itemId}`;
|
|
||||||
header.innerHTML = `
|
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-${itemId}" aria-expanded="false" aria-controls="collapse-${itemId}">
|
|
||||||
${apiType}
|
|
||||||
</button>
|
|
||||||
`;
|
|
||||||
item.appendChild(header);
|
|
||||||
|
|
||||||
// Body
|
|
||||||
const collapse = document.createElement('div');
|
|
||||||
collapse.id = `collapse-${itemId}`;
|
|
||||||
collapse.className = 'accordion-collapse collapse';
|
|
||||||
collapse.setAttribute('aria-labelledby', `heading-${itemId}`);
|
|
||||||
collapse.setAttribute('data-bs-parent', `#${accordion.id}`);
|
|
||||||
|
|
||||||
const body = document.createElement('div');
|
|
||||||
body.className = 'accordion-body';
|
|
||||||
|
|
||||||
// Select for UpdatedAt
|
|
||||||
const selectGroup = document.createElement('div');
|
|
||||||
selectGroup.className = 'input-group mb-3';
|
|
||||||
selectGroup.innerHTML = `<span class="input-group-text">History</span>`;
|
|
||||||
|
|
||||||
const select = document.createElement('select');
|
|
||||||
select.className = 'form-select';
|
|
||||||
select.ariaLabel = 'Select version';
|
|
||||||
|
|
||||||
list.forEach((entry, idx) => {
|
|
||||||
const dateStr = DateTime.fromISO(entry.updatedAt).toFormat('yyyy/MM/dd HH:mm:ss');
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = idx.toString();
|
|
||||||
option.textContent = `${dateStr}`;
|
|
||||||
select.appendChild(option);
|
|
||||||
});
|
|
||||||
selectGroup.appendChild(select);
|
|
||||||
body.appendChild(selectGroup);
|
|
||||||
|
|
||||||
// Content Area
|
|
||||||
const contentArea = document.createElement('pre');
|
|
||||||
contentArea.className = 'p-3 border rounded overflow-auto';
|
|
||||||
contentArea.style.maxHeight = '500px';
|
|
||||||
contentArea.style.fontSize = '0.875rem';
|
|
||||||
|
|
||||||
const updateContent = (index: number) => {
|
|
||||||
const entry = list[index];
|
|
||||||
if (entry) {
|
|
||||||
contentArea.textContent = JSON.stringify(entry.rsp, null, 2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initial render for this item
|
|
||||||
updateContent(0);
|
|
||||||
|
|
||||||
select.addEventListener('change', (e) => {
|
|
||||||
const val = parseInt((e.target as HTMLSelectElement).value, 10);
|
|
||||||
updateContent(val);
|
|
||||||
});
|
|
||||||
|
|
||||||
body.appendChild(contentArea);
|
|
||||||
collapse.appendChild(body);
|
|
||||||
item.appendChild(collapse);
|
|
||||||
accordion.appendChild(item);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
langSelect.addEventListener('change', (e) => {
|
|
||||||
renderApiList((e.target as HTMLSelectElement).value);
|
|
||||||
});
|
|
||||||
|
|
||||||
section.appendChild(accordion);
|
|
||||||
container.appendChild(section);
|
|
||||||
|
|
||||||
// Initial load
|
|
||||||
if (defaultLang) {
|
|
||||||
renderApiList(defaultLang);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { renderAnnouncement } from './webPretty/announcement.js';
|
|
||||||
import { renderBanner } from './webPretty/banner.js';
|
|
||||||
import { renderMainBgImage } from './webPretty/mainBgImage.js';
|
|
||||||
import { renderSidebar } from './webPretty/sidebar.js';
|
|
||||||
import { renderSingleEnt } from './webPretty/singleEnt.js';
|
|
||||||
|
|
||||||
export async function renderWebPretty(container: HTMLElement) {
|
|
||||||
container.innerHTML = '';
|
|
||||||
await renderAnnouncement(container);
|
|
||||||
await renderBanner(container);
|
|
||||||
await renderMainBgImage(container);
|
|
||||||
await renderSingleEnt(container);
|
|
||||||
await renderSidebar(container);
|
|
||||||
}
|
|
||||||
@@ -1,179 +0,0 @@
|
|||||||
import { DateTime } from 'luxon';
|
|
||||||
import { fetchJson } from '../../api.js';
|
|
||||||
import type { LauncherWebAnnouncement, StoredData } from '../../types.js';
|
|
||||||
import { BASE_URL, gameTargets, launcherWebApiLang } from '../../utils/constants.js';
|
|
||||||
|
|
||||||
export async function renderAnnouncement(container: HTMLElement) {
|
|
||||||
const outerCard = document.createElement('div');
|
|
||||||
outerCard.className = 'card mb-3';
|
|
||||||
|
|
||||||
const header = document.createElement('div');
|
|
||||||
header.className = 'card-header d-flex justify-content-between align-items-center';
|
|
||||||
header.style.cursor = 'pointer';
|
|
||||||
header.setAttribute('data-bs-toggle', 'collapse');
|
|
||||||
header.setAttribute('data-bs-target', '#collapseAnnouncement');
|
|
||||||
header.setAttribute('role', 'button');
|
|
||||||
header.innerHTML = '<h3 class="h4 mb-0">Announcement</h3><i class="bi bi-chevron-down"></i>';
|
|
||||||
outerCard.appendChild(header);
|
|
||||||
|
|
||||||
const collapseDiv = document.createElement('div');
|
|
||||||
collapseDiv.id = 'collapseAnnouncement';
|
|
||||||
collapseDiv.className = 'collapse';
|
|
||||||
outerCard.appendChild(collapseDiv);
|
|
||||||
|
|
||||||
const outerCardBody = document.createElement('div');
|
|
||||||
outerCardBody.className = 'card-body';
|
|
||||||
collapseDiv.appendChild(outerCardBody);
|
|
||||||
|
|
||||||
// --- UI Controls ---
|
|
||||||
const controls = document.createElement('div');
|
|
||||||
controls.className = 'row g-3 mb-4';
|
|
||||||
|
|
||||||
const targetCol = document.createElement('div');
|
|
||||||
targetCol.className = 'col-md-6';
|
|
||||||
targetCol.innerHTML = '<label class="form-label fw-bold">Target</label>';
|
|
||||||
const targetSelect = document.createElement('select');
|
|
||||||
targetSelect.className = 'form-select';
|
|
||||||
gameTargets.forEach((target, idx) => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = idx.toString();
|
|
||||||
option.textContent = `${target.region === 'cn' ? 'China' : 'Global'} - ${target.name}`;
|
|
||||||
targetSelect.appendChild(option);
|
|
||||||
});
|
|
||||||
targetCol.appendChild(targetSelect);
|
|
||||||
|
|
||||||
const langCol = document.createElement('div');
|
|
||||||
langCol.className = 'col-md-6';
|
|
||||||
langCol.innerHTML = '<label class="form-label fw-bold">Language</label>';
|
|
||||||
const langSelect = document.createElement('select');
|
|
||||||
langSelect.className = 'form-select';
|
|
||||||
langCol.appendChild(langSelect);
|
|
||||||
|
|
||||||
controls.appendChild(targetCol);
|
|
||||||
controls.appendChild(langCol);
|
|
||||||
outerCardBody.appendChild(controls);
|
|
||||||
|
|
||||||
const contentDiv = document.createElement('div');
|
|
||||||
outerCardBody.appendChild(contentDiv);
|
|
||||||
|
|
||||||
// --- Logic ---
|
|
||||||
const updateLanguages = () => {
|
|
||||||
const targetIdx = parseInt(targetSelect.value, 10);
|
|
||||||
const target = gameTargets[targetIdx]!;
|
|
||||||
const langs = launcherWebApiLang[target.region] || [];
|
|
||||||
const defaultLang = target.region === 'os' ? 'en-us' : 'zh-cn';
|
|
||||||
|
|
||||||
langSelect.innerHTML = '';
|
|
||||||
langs.forEach((lang) => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = lang;
|
|
||||||
option.textContent = lang;
|
|
||||||
if (lang === defaultLang) option.selected = true;
|
|
||||||
langSelect.appendChild(option);
|
|
||||||
});
|
|
||||||
|
|
||||||
langCol.style.display = langs.length <= 1 ? 'none' : 'block';
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderContent = async () => {
|
|
||||||
const targetIdx = parseInt(targetSelect.value, 10);
|
|
||||||
const target = gameTargets[targetIdx]!;
|
|
||||||
const lang = langSelect.value;
|
|
||||||
|
|
||||||
if (!lang) {
|
|
||||||
contentDiv.innerHTML = '<div class="text-muted p-2">No language selected.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
contentDiv.innerHTML = '<div class="text-muted p-2">Loading announcements...</div>';
|
|
||||||
|
|
||||||
const url = `${BASE_URL}/akEndfield/launcher/web/${target.dirName}/announcement/${lang}/all.json`;
|
|
||||||
try {
|
|
||||||
const data = await fetchJson<StoredData<LauncherWebAnnouncement>[]>(url);
|
|
||||||
if (!data || data.length === 0) {
|
|
||||||
contentDiv.innerHTML = '<div class="text-muted p-2">No data found.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tabsMap = new Map<
|
|
||||||
string,
|
|
||||||
{ tabName: string; announcements: Map<string, LauncherWebAnnouncement['tabs'][0]['announcements'][0]> }
|
|
||||||
>();
|
|
||||||
const sortedData = [...data].sort(
|
|
||||||
(a, b) => DateTime.fromISO(b.updatedAt).toMillis() - DateTime.fromISO(a.updatedAt).toMillis(),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const entry of sortedData) {
|
|
||||||
if (!entry.rsp || !entry.rsp.tabs) continue;
|
|
||||||
for (const tab of entry.rsp.tabs) {
|
|
||||||
if (!tabsMap.has(tab.tab_id)) {
|
|
||||||
tabsMap.set(tab.tab_id, { tabName: tab.tabName, announcements: new Map() });
|
|
||||||
}
|
|
||||||
const targetTab = tabsMap.get(tab.tab_id)!;
|
|
||||||
for (const ann of tab.announcements) {
|
|
||||||
if (!targetTab.announcements.has(ann.id)) {
|
|
||||||
targetTab.announcements.set(ann.id, ann);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contentDiv.innerHTML = '';
|
|
||||||
if (tabsMap.size === 0) {
|
|
||||||
contentDiv.innerHTML = '<div class="text-muted p-2">No announcements found.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [_tabId, tabData] of tabsMap) {
|
|
||||||
const card = document.createElement('div');
|
|
||||||
card.className = 'card mb-4 shadow-sm';
|
|
||||||
|
|
||||||
const cardHeader = document.createElement('div');
|
|
||||||
cardHeader.className = 'card-header bg-secondary text-white fw-bold py-1';
|
|
||||||
cardHeader.textContent = tabData.tabName;
|
|
||||||
card.appendChild(cardHeader);
|
|
||||||
|
|
||||||
const listGroup = document.createElement('ul');
|
|
||||||
listGroup.className = 'list-group list-group-flush';
|
|
||||||
|
|
||||||
const sortedAnnouncements = Array.from(tabData.announcements.values()).sort(
|
|
||||||
(a, b) => parseInt(b.start_ts, 10) - parseInt(a.start_ts, 10),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const ann of sortedAnnouncements) {
|
|
||||||
const item = document.createElement('li');
|
|
||||||
item.className = 'list-group-item py-2';
|
|
||||||
const date = DateTime.fromMillis(parseInt(ann.start_ts, 10)).toFormat('yyyy/MM/dd HH:mm');
|
|
||||||
|
|
||||||
item.innerHTML = `
|
|
||||||
<div class="d-flex flex-wrap align-items-center gap-2">
|
|
||||||
<span class="text-muted small" style="min-width: 120px;">${date}</span>
|
|
||||||
<span class="flex-grow-1 fw-bold">${ann.content}</span>
|
|
||||||
<div class="d-flex align-items-center gap-2">
|
|
||||||
${ann.need_token ? '<span class="badge bg-warning text-dark px-1 py-0" style="font-size: 0.7rem;">Auth</span>' : ''}
|
|
||||||
${ann.jump_url ? `<a href="${ann.jump_url}" target="_blank" class="btn btn-sm btn-outline-secondary py-0 px-2" style="font-size: 0.75rem;">Link</a>` : ''}
|
|
||||||
<span class="text-muted border-start ps-2" style="font-size: 0.7rem;">ID:${ann.id}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
listGroup.appendChild(item);
|
|
||||||
}
|
|
||||||
card.appendChild(listGroup);
|
|
||||||
contentDiv.appendChild(card);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(`Failed to load ${url}`, e);
|
|
||||||
contentDiv.innerHTML = '<div class="text-danger p-2">Failed to load data.</div>';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
targetSelect.addEventListener('change', () => {
|
|
||||||
updateLanguages();
|
|
||||||
renderContent();
|
|
||||||
});
|
|
||||||
langSelect.addEventListener('change', renderContent);
|
|
||||||
|
|
||||||
updateLanguages();
|
|
||||||
renderContent();
|
|
||||||
container.appendChild(outerCard);
|
|
||||||
}
|
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
import { DateTime } from 'luxon';
|
|
||||||
import { fetchJson } from '../../api.js';
|
|
||||||
import type { LauncherWebBanner, StoredData } from '../../types.js';
|
|
||||||
import { BASE_URL, gameTargets, launcherWebApiLang } from '../../utils/constants.js';
|
|
||||||
|
|
||||||
export async function renderBanner(container: HTMLElement) {
|
|
||||||
const outerCard = document.createElement('div');
|
|
||||||
outerCard.className = 'card mb-3';
|
|
||||||
|
|
||||||
const header = document.createElement('div');
|
|
||||||
header.className = 'card-header d-flex justify-content-between align-items-center';
|
|
||||||
header.style.cursor = 'pointer';
|
|
||||||
header.setAttribute('data-bs-toggle', 'collapse');
|
|
||||||
header.setAttribute('data-bs-target', '#collapseBanner');
|
|
||||||
header.setAttribute('role', 'button');
|
|
||||||
header.innerHTML = '<h3 class="h4 mb-0">Banner</h3><i class="bi bi-chevron-down"></i>';
|
|
||||||
outerCard.appendChild(header);
|
|
||||||
|
|
||||||
const collapseDiv = document.createElement('div');
|
|
||||||
collapseDiv.id = 'collapseBanner';
|
|
||||||
collapseDiv.className = 'collapse';
|
|
||||||
outerCard.appendChild(collapseDiv);
|
|
||||||
|
|
||||||
const outerCardBody = document.createElement('div');
|
|
||||||
outerCardBody.className = 'card-body';
|
|
||||||
collapseDiv.appendChild(outerCardBody);
|
|
||||||
|
|
||||||
// --- UI Controls ---
|
|
||||||
const controls = document.createElement('div');
|
|
||||||
controls.className = 'row g-3 mb-4';
|
|
||||||
|
|
||||||
const targetCol = document.createElement('div');
|
|
||||||
targetCol.className = 'col-md-6';
|
|
||||||
targetCol.innerHTML = '<label class="form-label fw-bold">Target</label>';
|
|
||||||
const targetSelect = document.createElement('select');
|
|
||||||
targetSelect.className = 'form-select';
|
|
||||||
gameTargets.forEach((target, idx) => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = idx.toString();
|
|
||||||
option.textContent = `${target.region === 'cn' ? 'China' : 'Global'} - ${target.name}`;
|
|
||||||
targetSelect.appendChild(option);
|
|
||||||
});
|
|
||||||
targetCol.appendChild(targetSelect);
|
|
||||||
|
|
||||||
const langCol = document.createElement('div');
|
|
||||||
langCol.className = 'col-md-6';
|
|
||||||
langCol.innerHTML = '<label class="form-label fw-bold">Language</label>';
|
|
||||||
const langSelect = document.createElement('select');
|
|
||||||
langSelect.className = 'form-select';
|
|
||||||
langCol.appendChild(langSelect);
|
|
||||||
|
|
||||||
controls.appendChild(targetCol);
|
|
||||||
controls.appendChild(langCol);
|
|
||||||
outerCardBody.appendChild(controls);
|
|
||||||
|
|
||||||
const contentDiv = document.createElement('div');
|
|
||||||
outerCardBody.appendChild(contentDiv);
|
|
||||||
|
|
||||||
// --- Logic ---
|
|
||||||
const updateLanguages = () => {
|
|
||||||
const targetIdx = parseInt(targetSelect.value, 10);
|
|
||||||
const target = gameTargets[targetIdx]!;
|
|
||||||
const langs = launcherWebApiLang[target.region] || [];
|
|
||||||
const defaultLang = target.region === 'os' ? 'en-us' : 'zh-cn';
|
|
||||||
|
|
||||||
langSelect.innerHTML = '';
|
|
||||||
langs.forEach((lang) => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = lang;
|
|
||||||
option.textContent = lang;
|
|
||||||
if (lang === defaultLang) option.selected = true;
|
|
||||||
langSelect.appendChild(option);
|
|
||||||
});
|
|
||||||
|
|
||||||
langCol.style.display = langs.length <= 1 ? 'none' : 'block';
|
|
||||||
};
|
|
||||||
|
|
||||||
const getMirrorUrl = (url: string) => {
|
|
||||||
try {
|
|
||||||
const u = new URL(url);
|
|
||||||
return `https://raw.githubusercontent.com/daydreamer-json/ak-endfield-api-archive/refs/heads/main/output/raw/${u.hostname}${u.pathname}`;
|
|
||||||
} catch {
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderContent = async () => {
|
|
||||||
const targetIdx = parseInt(targetSelect.value, 10);
|
|
||||||
const target = gameTargets[targetIdx]!;
|
|
||||||
const lang = langSelect.value;
|
|
||||||
|
|
||||||
if (!lang) {
|
|
||||||
contentDiv.innerHTML = '<div class="text-muted p-2">No language selected.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
contentDiv.innerHTML = '<div class="text-muted p-2">Loading banners...</div>';
|
|
||||||
|
|
||||||
const url = `${BASE_URL}/akEndfield/launcher/web/${target.dirName}/banner/${lang}/all.json`;
|
|
||||||
try {
|
|
||||||
const data = await fetchJson<StoredData<LauncherWebBanner>[]>(url);
|
|
||||||
if (!data || data.length === 0) {
|
|
||||||
contentDiv.innerHTML = '<div class="text-muted p-2">No data found.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect unique banners by ID from the entire history
|
|
||||||
const bannerMap = new Map<string, { banner: LauncherWebBanner['banners'][0]; firstSeen: string }>();
|
|
||||||
const sortedData = [...data].sort(
|
|
||||||
(a, b) => DateTime.fromISO(b.updatedAt).toMillis() - DateTime.fromISO(a.updatedAt).toMillis(),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const entry of sortedData) {
|
|
||||||
if (!entry.rsp || !entry.rsp.banners) continue;
|
|
||||||
for (const banner of entry.rsp.banners) {
|
|
||||||
if (!bannerMap.has(banner.id)) {
|
|
||||||
bannerMap.set(banner.id, { banner, firstSeen: entry.updatedAt });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contentDiv.innerHTML = '';
|
|
||||||
if (bannerMap.size === 0) {
|
|
||||||
contentDiv.innerHTML = '<div class="text-muted p-2">No banners found.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const row = document.createElement('div');
|
|
||||||
row.className = 'row row-cols-1 row-cols-md-3 row-cols-lg-4 g-3';
|
|
||||||
contentDiv.appendChild(row);
|
|
||||||
|
|
||||||
for (const [id, { banner, firstSeen }] of bannerMap) {
|
|
||||||
const col = document.createElement('div');
|
|
||||||
col.className = 'col';
|
|
||||||
|
|
||||||
const dateStr = DateTime.fromISO(firstSeen).toFormat('yyyy/MM/dd HH:mm');
|
|
||||||
const mirrorUrl = getMirrorUrl(banner.url);
|
|
||||||
const linkUrl = banner.jump_url || mirrorUrl;
|
|
||||||
|
|
||||||
col.innerHTML = `
|
|
||||||
<a href="${linkUrl}" target="_blank" class="text-decoration-none text-reset">
|
|
||||||
<div class="card h-100 shadow-sm border-0">
|
|
||||||
<div class="position-relative">
|
|
||||||
<img src="${mirrorUrl}" class="card-img-top rounded" alt="Banner Image" style="object-fit: cover; aspect-ratio: 16 / 9;">
|
|
||||||
<div class="position-absolute top-0 end-0 p-1">
|
|
||||||
${banner.need_token ? '<span class="badge bg-warning text-dark" style="font-size: 0.6rem;">Auth</span>' : ''}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-body py-1 px-1">
|
|
||||||
<div class="d-flex justify-content-between align-items-center" style="font-size: 0.7rem;">
|
|
||||||
<span class="text-muted text-truncate me-1">ID: ${id}</span>
|
|
||||||
<span class="text-muted flex-shrink-0">${dateStr}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
`;
|
|
||||||
row.appendChild(col);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(`Failed to load ${url}`, e);
|
|
||||||
contentDiv.innerHTML = '<div class="text-danger p-2">Failed to load data.</div>';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
targetSelect.addEventListener('change', () => {
|
|
||||||
updateLanguages();
|
|
||||||
renderContent();
|
|
||||||
});
|
|
||||||
langSelect.addEventListener('change', renderContent);
|
|
||||||
|
|
||||||
updateLanguages();
|
|
||||||
renderContent();
|
|
||||||
container.appendChild(outerCard);
|
|
||||||
}
|
|
||||||
@@ -1,174 +0,0 @@
|
|||||||
import { DateTime } from 'luxon';
|
|
||||||
import { fetchJson } from '../../api.js';
|
|
||||||
import type { LauncherWebMainBgImage, StoredData } from '../../types.js';
|
|
||||||
import { BASE_URL, gameTargets, launcherWebApiLang } from '../../utils/constants.js';
|
|
||||||
|
|
||||||
export async function renderMainBgImage(container: HTMLElement) {
|
|
||||||
const outerCard = document.createElement('div');
|
|
||||||
outerCard.className = 'card mb-3';
|
|
||||||
|
|
||||||
const header = document.createElement('div');
|
|
||||||
header.className = 'card-header d-flex justify-content-between align-items-center';
|
|
||||||
header.style.cursor = 'pointer';
|
|
||||||
header.setAttribute('data-bs-toggle', 'collapse');
|
|
||||||
header.setAttribute('data-bs-target', '#collapseMainBgImage');
|
|
||||||
header.setAttribute('role', 'button');
|
|
||||||
header.innerHTML = '<h3 class="h4 mb-0">Main Background Image</h3><i class="bi bi-chevron-down"></i>';
|
|
||||||
outerCard.appendChild(header);
|
|
||||||
|
|
||||||
const collapseDiv = document.createElement('div');
|
|
||||||
collapseDiv.id = 'collapseMainBgImage';
|
|
||||||
collapseDiv.className = 'collapse';
|
|
||||||
outerCard.appendChild(collapseDiv);
|
|
||||||
|
|
||||||
const outerCardBody = document.createElement('div');
|
|
||||||
outerCardBody.className = 'card-body';
|
|
||||||
collapseDiv.appendChild(outerCardBody);
|
|
||||||
|
|
||||||
// --- UI Controls ---
|
|
||||||
const controls = document.createElement('div');
|
|
||||||
controls.className = 'row g-3 mb-4';
|
|
||||||
|
|
||||||
const targetCol = document.createElement('div');
|
|
||||||
targetCol.className = 'col-md-6';
|
|
||||||
targetCol.innerHTML = '<label class="form-label fw-bold">Target</label>';
|
|
||||||
const targetSelect = document.createElement('select');
|
|
||||||
targetSelect.className = 'form-select';
|
|
||||||
gameTargets.forEach((target, idx) => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = idx.toString();
|
|
||||||
option.textContent = `${target.region === 'cn' ? 'China' : 'Global'} - ${target.name}`;
|
|
||||||
targetSelect.appendChild(option);
|
|
||||||
});
|
|
||||||
targetCol.appendChild(targetSelect);
|
|
||||||
|
|
||||||
const langCol = document.createElement('div');
|
|
||||||
langCol.className = 'col-md-6';
|
|
||||||
langCol.innerHTML = '<label class="form-label fw-bold">Language</label>';
|
|
||||||
const langSelect = document.createElement('select');
|
|
||||||
langSelect.className = 'form-select';
|
|
||||||
langCol.appendChild(langSelect);
|
|
||||||
|
|
||||||
controls.appendChild(targetCol);
|
|
||||||
controls.appendChild(langCol);
|
|
||||||
outerCardBody.appendChild(controls);
|
|
||||||
|
|
||||||
const contentDiv = document.createElement('div');
|
|
||||||
outerCardBody.appendChild(contentDiv);
|
|
||||||
|
|
||||||
// --- Logic ---
|
|
||||||
const updateLanguages = () => {
|
|
||||||
const targetIdx = parseInt(targetSelect.value, 10);
|
|
||||||
const target = gameTargets[targetIdx]!;
|
|
||||||
const langs = launcherWebApiLang[target.region] || [];
|
|
||||||
const defaultLang = target.region === 'os' ? 'en-us' : 'zh-cn';
|
|
||||||
|
|
||||||
langSelect.innerHTML = '';
|
|
||||||
langs.forEach((lang) => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = lang;
|
|
||||||
option.textContent = lang;
|
|
||||||
if (lang === defaultLang) option.selected = true;
|
|
||||||
langSelect.appendChild(option);
|
|
||||||
});
|
|
||||||
|
|
||||||
langCol.style.display = langs.length <= 1 ? 'none' : 'block';
|
|
||||||
};
|
|
||||||
|
|
||||||
const getMirrorUrl = (url: string) => {
|
|
||||||
try {
|
|
||||||
const u = new URL(url);
|
|
||||||
return `https://raw.githubusercontent.com/daydreamer-json/ak-endfield-api-archive/refs/heads/main/output/raw/${u.hostname}${u.pathname}`;
|
|
||||||
} catch {
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderContent = async () => {
|
|
||||||
const targetIdx = parseInt(targetSelect.value, 10);
|
|
||||||
const target = gameTargets[targetIdx]!;
|
|
||||||
const lang = langSelect.value;
|
|
||||||
|
|
||||||
if (!lang) {
|
|
||||||
contentDiv.innerHTML = '<div class="text-muted p-2">No language selected.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
contentDiv.innerHTML = '<div class="text-muted p-2">Loading background images...</div>';
|
|
||||||
|
|
||||||
const url = `${BASE_URL}/akEndfield/launcher/web/${target.dirName}/main_bg_image/${lang}/all.json`;
|
|
||||||
try {
|
|
||||||
const data = await fetchJson<StoredData<LauncherWebMainBgImage>[]>(url);
|
|
||||||
if (!data || data.length === 0) {
|
|
||||||
contentDiv.innerHTML = '<div class="text-muted p-2">No data found.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect unique images by MD5 from the entire history
|
|
||||||
const imageMap = new Map<string, { image: LauncherWebMainBgImage['main_bg_image']; firstSeen: string }>();
|
|
||||||
const sortedData = [...data].sort(
|
|
||||||
(a, b) => DateTime.fromISO(b.updatedAt).toMillis() - DateTime.fromISO(a.updatedAt).toMillis(),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const entry of sortedData) {
|
|
||||||
if (!entry.rsp || !entry.rsp.main_bg_image) continue;
|
|
||||||
const img = entry.rsp.main_bg_image;
|
|
||||||
if (!imageMap.has(img.md5)) {
|
|
||||||
imageMap.set(img.md5, { image: img, firstSeen: entry.updatedAt });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contentDiv.innerHTML = '';
|
|
||||||
if (imageMap.size === 0) {
|
|
||||||
contentDiv.innerHTML = '<div class="text-muted p-2">No images found.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const row = document.createElement('div');
|
|
||||||
row.className = 'row row-cols-1 row-cols-md-2 row-cols-lg-3 g-3';
|
|
||||||
contentDiv.appendChild(row);
|
|
||||||
|
|
||||||
for (const [md5, { image, firstSeen }] of imageMap) {
|
|
||||||
const col = document.createElement('div');
|
|
||||||
col.className = 'col';
|
|
||||||
|
|
||||||
const dateStr = DateTime.fromISO(firstSeen).toFormat('yyyy/MM/dd HH:mm');
|
|
||||||
const mirrorUrl = getMirrorUrl(image.url);
|
|
||||||
const linkUrl = image.video_url ? getMirrorUrl(image.video_url) : mirrorUrl;
|
|
||||||
|
|
||||||
col.innerHTML = `
|
|
||||||
<a href="${linkUrl}" target="_blank" class="text-decoration-none text-reset">
|
|
||||||
<div class="card h-100 shadow-sm border-0">
|
|
||||||
<div class="position-relative">
|
|
||||||
<img src="${mirrorUrl}" class="card-img-top rounded" alt="Background Image" style="object-fit: cover; aspect-ratio: 16 / 9;">
|
|
||||||
<div class="position-absolute top-0 end-0 p-1">
|
|
||||||
${image.video_url ? '<span class="badge bg-primary" style="font-size: 0.6rem;">Video</span>' : ''}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-body py-1 px-1">
|
|
||||||
<div class="d-flex justify-content-between align-items-center" style="font-size: 0.7rem;">
|
|
||||||
<span class="text-muted text-truncate font-monospace me-1">${md5}</span>
|
|
||||||
<span class="text-muted flex-shrink-0">${dateStr}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
`;
|
|
||||||
row.appendChild(col);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(`Failed to load ${url}`, e);
|
|
||||||
contentDiv.innerHTML = '<div class="text-danger p-2">Failed to load data.</div>';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
targetSelect.addEventListener('change', () => {
|
|
||||||
updateLanguages();
|
|
||||||
renderContent();
|
|
||||||
});
|
|
||||||
langSelect.addEventListener('change', renderContent);
|
|
||||||
|
|
||||||
updateLanguages();
|
|
||||||
renderContent();
|
|
||||||
container.appendChild(outerCard);
|
|
||||||
}
|
|
||||||
@@ -1,195 +0,0 @@
|
|||||||
import { DateTime } from 'luxon';
|
|
||||||
import { fetchJson } from '../../api.js';
|
|
||||||
import type { LauncherWebSidebar, StoredData } from '../../types.js';
|
|
||||||
import { BASE_URL, gameTargets, launcherWebApiLang } from '../../utils/constants.js';
|
|
||||||
|
|
||||||
export async function renderSidebar(container: HTMLElement) {
|
|
||||||
const outerCard = document.createElement('div');
|
|
||||||
outerCard.className = 'card mb-3';
|
|
||||||
|
|
||||||
const header = document.createElement('div');
|
|
||||||
header.className = 'card-header d-flex justify-content-between align-items-center';
|
|
||||||
header.style.cursor = 'pointer';
|
|
||||||
header.setAttribute('data-bs-toggle', 'collapse');
|
|
||||||
header.setAttribute('data-bs-target', '#collapseSidebar');
|
|
||||||
header.setAttribute('role', 'button');
|
|
||||||
header.innerHTML = '<h3 class="h4 mb-0">Sidebar</h3><i class="bi bi-chevron-down"></i>';
|
|
||||||
outerCard.appendChild(header);
|
|
||||||
|
|
||||||
const collapseDiv = document.createElement('div');
|
|
||||||
collapseDiv.id = 'collapseSidebar';
|
|
||||||
collapseDiv.className = 'collapse';
|
|
||||||
outerCard.appendChild(collapseDiv);
|
|
||||||
|
|
||||||
const outerCardBody = document.createElement('div');
|
|
||||||
outerCardBody.className = 'card-body';
|
|
||||||
collapseDiv.appendChild(outerCardBody);
|
|
||||||
|
|
||||||
// --- UI Controls ---
|
|
||||||
const controls = document.createElement('div');
|
|
||||||
controls.className = 'row g-3 mb-4';
|
|
||||||
|
|
||||||
const targetCol = document.createElement('div');
|
|
||||||
targetCol.className = 'col-md-6';
|
|
||||||
targetCol.innerHTML = '<label class="form-label fw-bold">Target</label>';
|
|
||||||
const targetSelect = document.createElement('select');
|
|
||||||
targetSelect.className = 'form-select';
|
|
||||||
gameTargets.forEach((target, idx) => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = idx.toString();
|
|
||||||
option.textContent = `${target.region === 'cn' ? 'China' : 'Global'} - ${target.name}`;
|
|
||||||
targetSelect.appendChild(option);
|
|
||||||
});
|
|
||||||
targetCol.appendChild(targetSelect);
|
|
||||||
|
|
||||||
const langCol = document.createElement('div');
|
|
||||||
langCol.className = 'col-md-6';
|
|
||||||
langCol.innerHTML = '<label class="form-label fw-bold">Language</label>';
|
|
||||||
const langSelect = document.createElement('select');
|
|
||||||
langSelect.className = 'form-select';
|
|
||||||
langCol.appendChild(langSelect);
|
|
||||||
|
|
||||||
controls.appendChild(targetCol);
|
|
||||||
controls.appendChild(langCol);
|
|
||||||
outerCardBody.appendChild(controls);
|
|
||||||
|
|
||||||
const contentDiv = document.createElement('div');
|
|
||||||
outerCardBody.appendChild(contentDiv);
|
|
||||||
|
|
||||||
// --- Logic ---
|
|
||||||
const updateLanguages = () => {
|
|
||||||
const targetIdx = parseInt(targetSelect.value, 10);
|
|
||||||
const target = gameTargets[targetIdx]!;
|
|
||||||
const langs = launcherWebApiLang[target.region] || [];
|
|
||||||
const defaultLang = target.region === 'os' ? 'en-us' : 'zh-cn';
|
|
||||||
|
|
||||||
langSelect.innerHTML = '';
|
|
||||||
langs.forEach((lang) => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = lang;
|
|
||||||
option.textContent = lang;
|
|
||||||
if (lang === defaultLang) option.selected = true;
|
|
||||||
langSelect.appendChild(option);
|
|
||||||
});
|
|
||||||
|
|
||||||
langCol.style.display = langs.length <= 1 ? 'none' : 'block';
|
|
||||||
};
|
|
||||||
|
|
||||||
const getMirrorUrl = (url: string) => {
|
|
||||||
try {
|
|
||||||
const u = new URL(url);
|
|
||||||
return `https://raw.githubusercontent.com/daydreamer-json/ak-endfield-api-archive/refs/heads/main/output/raw/${u.hostname}${u.pathname}`;
|
|
||||||
} catch {
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderContent = async () => {
|
|
||||||
const targetIdx = parseInt(targetSelect.value, 10);
|
|
||||||
const target = gameTargets[targetIdx]!;
|
|
||||||
const lang = langSelect.value;
|
|
||||||
|
|
||||||
if (!lang) {
|
|
||||||
contentDiv.innerHTML = '<div class="text-muted p-2">No language selected.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
contentDiv.innerHTML = '<div class="text-muted p-2">Loading sidebar data...</div>';
|
|
||||||
|
|
||||||
const url = `${BASE_URL}/akEndfield/launcher/web/${target.dirName}/sidebar/${lang}/all.json`;
|
|
||||||
try {
|
|
||||||
const data = await fetchJson<StoredData<LauncherWebSidebar>[]>(url);
|
|
||||||
if (!data || data.length === 0) {
|
|
||||||
contentDiv.innerHTML = '<div class="text-muted p-2">No data found.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect the latest sidebar configuration
|
|
||||||
const sortedData = [...data].sort(
|
|
||||||
(a, b) => DateTime.fromISO(b.updatedAt).toMillis() - DateTime.fromISO(a.updatedAt).toMillis(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// We only show the latest version as sidebars are usually state-dependent
|
|
||||||
const latest = sortedData[0];
|
|
||||||
if (!latest || !latest.rsp || !latest.rsp.sidebars) {
|
|
||||||
contentDiv.innerHTML = '<div class="text-muted p-2">No active sidebars.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
contentDiv.innerHTML = '';
|
|
||||||
const row = document.createElement('div');
|
|
||||||
row.className = 'row row-cols-1 row-cols-md-2 row-cols-lg-3 g-3';
|
|
||||||
contentDiv.appendChild(row);
|
|
||||||
|
|
||||||
for (const item of latest.rsp.sidebars) {
|
|
||||||
const col = document.createElement('div');
|
|
||||||
col.className = 'col';
|
|
||||||
|
|
||||||
const card = document.createElement('div');
|
|
||||||
card.className = 'card h-100 shadow-sm';
|
|
||||||
|
|
||||||
let innerHtml = `
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
||||||
<h5 class="card-title mb-0">${item.media}</h5>
|
|
||||||
${item.need_token ? '<span class="badge bg-warning text-dark lh-1 py-1">Auth</span>' : ''}
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
if (item.pic) {
|
|
||||||
innerHtml += `
|
|
||||||
<div class="mb-3">
|
|
||||||
<img src="${getMirrorUrl(item.pic.url)}" class="img-fluid rounded" alt="${item.pic.description}">
|
|
||||||
<p class="text-muted small mt-1 mb-0">${item.pic.description}</p>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.jump_url) {
|
|
||||||
innerHtml += `
|
|
||||||
<a href="${item.jump_url}" target="_blank" class="btn btn-sm btn-outline-primary mb-2 w-100">Open Link</a>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.sidebar_labels && item.sidebar_labels.length > 0) {
|
|
||||||
innerHtml += '<div class="list-group list-group-flush border-top mt-2">';
|
|
||||||
for (const label of item.sidebar_labels) {
|
|
||||||
innerHtml += `
|
|
||||||
<a href="${label.jump_url}" target="_blank" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center py-2">
|
|
||||||
<span style="font-size: 0.9rem;">${label.content}</span>
|
|
||||||
<div class="d-flex gap-1">
|
|
||||||
${label.need_token ? '<span class="badge bg-warning text-dark lh-1 py-1">Auth</span>' : ''}
|
|
||||||
<i class="bi bi-box-arrow-up-right small text-muted"></i>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
innerHtml += '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
innerHtml += '</div>';
|
|
||||||
card.innerHTML = innerHtml;
|
|
||||||
col.appendChild(card);
|
|
||||||
row.appendChild(col);
|
|
||||||
}
|
|
||||||
|
|
||||||
const infoDiv = document.createElement('div');
|
|
||||||
infoDiv.className = 'text-muted small mt-3 text-end';
|
|
||||||
infoDiv.textContent = `Last updated: ${DateTime.fromISO(latest.updatedAt).toFormat('yyyy/MM/dd HH:mm')}`;
|
|
||||||
contentDiv.appendChild(infoDiv);
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(`Failed to load ${url}`, e);
|
|
||||||
contentDiv.innerHTML = '<div class="text-danger p-2">Failed to load data.</div>';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
targetSelect.addEventListener('change', () => {
|
|
||||||
updateLanguages();
|
|
||||||
renderContent();
|
|
||||||
});
|
|
||||||
langSelect.addEventListener('change', renderContent);
|
|
||||||
|
|
||||||
updateLanguages();
|
|
||||||
renderContent();
|
|
||||||
container.appendChild(outerCard);
|
|
||||||
}
|
|
||||||
@@ -1,205 +0,0 @@
|
|||||||
import { DateTime } from 'luxon';
|
|
||||||
import { fetchJson } from '../../api.js';
|
|
||||||
import type { LauncherWebSingleEnt, StoredData } from '../../types.js';
|
|
||||||
import { BASE_URL, gameTargets, launcherWebApiLang } from '../../utils/constants.js';
|
|
||||||
|
|
||||||
export async function renderSingleEnt(container: HTMLElement) {
|
|
||||||
const outerCard = document.createElement('div');
|
|
||||||
outerCard.className = 'card mb-3';
|
|
||||||
|
|
||||||
const header = document.createElement('div');
|
|
||||||
header.className = 'card-header d-flex justify-content-between align-items-center';
|
|
||||||
header.style.cursor = 'pointer';
|
|
||||||
header.setAttribute('data-bs-toggle', 'collapse');
|
|
||||||
header.setAttribute('data-bs-target', '#collapseSingleEnt');
|
|
||||||
header.setAttribute('role', 'button');
|
|
||||||
header.innerHTML = '<h3 class="h4 mb-0">Single Ent.</h3><i class="bi bi-chevron-down"></i>';
|
|
||||||
outerCard.appendChild(header);
|
|
||||||
|
|
||||||
const collapseDiv = document.createElement('div');
|
|
||||||
collapseDiv.id = 'collapseSingleEnt';
|
|
||||||
collapseDiv.className = 'collapse';
|
|
||||||
outerCard.appendChild(collapseDiv);
|
|
||||||
|
|
||||||
const outerCardBody = document.createElement('div');
|
|
||||||
outerCardBody.className = 'card-body';
|
|
||||||
collapseDiv.appendChild(outerCardBody);
|
|
||||||
|
|
||||||
// --- UI Controls ---
|
|
||||||
const controls = document.createElement('div');
|
|
||||||
controls.className = 'row g-3 mb-4';
|
|
||||||
|
|
||||||
const targetCol = document.createElement('div');
|
|
||||||
targetCol.className = 'col-md-6';
|
|
||||||
targetCol.innerHTML = '<label class="form-label fw-bold">Target</label>';
|
|
||||||
const targetSelect = document.createElement('select');
|
|
||||||
targetSelect.className = 'form-select';
|
|
||||||
gameTargets.forEach((target, idx) => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = idx.toString();
|
|
||||||
option.textContent = `${target.region === 'cn' ? 'China' : 'Global'} - ${target.name}`;
|
|
||||||
targetSelect.appendChild(option);
|
|
||||||
});
|
|
||||||
targetCol.appendChild(targetSelect);
|
|
||||||
|
|
||||||
const langCol = document.createElement('div');
|
|
||||||
langCol.className = 'col-md-6';
|
|
||||||
langCol.innerHTML = '<label class="form-label fw-bold">Language</label>';
|
|
||||||
const langSelect = document.createElement('select');
|
|
||||||
langSelect.className = 'form-select';
|
|
||||||
langCol.appendChild(langSelect);
|
|
||||||
|
|
||||||
controls.appendChild(targetCol);
|
|
||||||
controls.appendChild(langCol);
|
|
||||||
outerCardBody.appendChild(controls);
|
|
||||||
|
|
||||||
const contentDiv = document.createElement('div');
|
|
||||||
outerCardBody.appendChild(contentDiv);
|
|
||||||
|
|
||||||
// --- Logic ---
|
|
||||||
const updateLanguages = () => {
|
|
||||||
const targetIdx = parseInt(targetSelect.value, 10);
|
|
||||||
const target = gameTargets[targetIdx]!;
|
|
||||||
const langs = launcherWebApiLang[target.region] || [];
|
|
||||||
const defaultLang = target.region === 'os' ? 'en-us' : 'zh-cn';
|
|
||||||
|
|
||||||
langSelect.innerHTML = '';
|
|
||||||
langs.forEach((lang) => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = lang;
|
|
||||||
option.textContent = lang;
|
|
||||||
if (lang === defaultLang) option.selected = true;
|
|
||||||
langSelect.appendChild(option);
|
|
||||||
});
|
|
||||||
|
|
||||||
langCol.style.display = langs.length <= 1 ? 'none' : 'block';
|
|
||||||
};
|
|
||||||
|
|
||||||
const getMirrorUrl = (url: string) => {
|
|
||||||
if (!url) return '';
|
|
||||||
try {
|
|
||||||
const u = new URL(url);
|
|
||||||
return `https://raw.githubusercontent.com/daydreamer-json/ak-endfield-api-archive/refs/heads/main/output/raw/${u.hostname}${u.pathname}`;
|
|
||||||
} catch {
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderContent = async () => {
|
|
||||||
const targetIdx = parseInt(targetSelect.value, 10);
|
|
||||||
const target = gameTargets[targetIdx]!;
|
|
||||||
const lang = langSelect.value;
|
|
||||||
|
|
||||||
if (!lang) {
|
|
||||||
contentDiv.innerHTML = '<div class="text-muted p-2">No language selected.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
contentDiv.innerHTML = '<div class="text-muted p-2">Loading single entry data...</div>';
|
|
||||||
|
|
||||||
const url = `${BASE_URL}/akEndfield/launcher/web/${target.dirName}/single_ent/${lang}/all.json`;
|
|
||||||
try {
|
|
||||||
const data = await fetchJson<StoredData<LauncherWebSingleEnt>[]>(url);
|
|
||||||
if (!data || data.length === 0) {
|
|
||||||
contentDiv.innerHTML = '<div class="text-muted p-2">No data found.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect unique visuals by MD5 from the entire history
|
|
||||||
const entMap = new Map<string, { ent: LauncherWebSingleEnt['single_ent']; firstSeen: string }>();
|
|
||||||
const sortedData = [...data].sort(
|
|
||||||
(a, b) => DateTime.fromISO(b.updatedAt).toMillis() - DateTime.fromISO(a.updatedAt).toMillis(),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const entry of sortedData) {
|
|
||||||
if (!entry.rsp || !entry.rsp.single_ent) continue;
|
|
||||||
const ent = entry.rsp.single_ent;
|
|
||||||
const key = ent.version_md5 || ent.version_url;
|
|
||||||
if (!entMap.has(key)) {
|
|
||||||
entMap.set(key, { ent, firstSeen: entry.updatedAt });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contentDiv.innerHTML = '';
|
|
||||||
if (entMap.size === 0) {
|
|
||||||
contentDiv.innerHTML = '<div class="text-muted p-2">No data found.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const row = document.createElement('div');
|
|
||||||
row.className = 'row row-cols-1 row-cols-md-2 g-4';
|
|
||||||
contentDiv.appendChild(row);
|
|
||||||
|
|
||||||
for (const [_key, { ent, firstSeen }] of entMap) {
|
|
||||||
const col = document.createElement('div');
|
|
||||||
col.className = 'col';
|
|
||||||
|
|
||||||
const card = document.createElement('div');
|
|
||||||
card.className = 'card h-100 shadow-sm';
|
|
||||||
|
|
||||||
let innerHtml = `
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
||||||
<span class="text-muted small">First seen: ${DateTime.fromISO(firstSeen).toFormat('yyyy/MM/dd HH:mm')}</span>
|
|
||||||
${ent.need_token ? '<span class="badge bg-warning text-dark">Auth</span>' : ''}
|
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label small fw-bold">Version Image</label>
|
|
||||||
<a href="${getMirrorUrl(ent.version_url)}" target="_blank">
|
|
||||||
<img src="${getMirrorUrl(ent.version_url)}" class="img-fluid rounded border" alt="Version Image">
|
|
||||||
</a>
|
|
||||||
<p class="text-muted font-monospace mt-1" style="font-size: 0.7rem; word-break: break-all;">MD5: ${ent.version_md5}</p>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
if (ent.button_url) {
|
|
||||||
innerHtml += `
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label small fw-bold">Action Button</label>
|
|
||||||
<div class="d-flex gap-2">
|
|
||||||
<div class="flex-grow-1">
|
|
||||||
<img src="${getMirrorUrl(ent.button_url)}" class="img-fluid rounded border bg-light" alt="Button" style="max-height: 60px;">
|
|
||||||
<p class="text-muted small mt-1 mb-0">Normal</p>
|
|
||||||
</div>
|
|
||||||
${
|
|
||||||
ent.button_hover_url
|
|
||||||
? `
|
|
||||||
<div class="flex-grow-1">
|
|
||||||
<img src="${getMirrorUrl(ent.button_hover_url)}" class="img-fluid rounded border bg-light" alt="Button Hover" style="max-height: 60px;">
|
|
||||||
<p class="text-muted small mt-1 mb-0">Hover</p>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ent.jump_url) {
|
|
||||||
innerHtml += `
|
|
||||||
<a href="${ent.jump_url}" target="_blank" class="btn btn-sm btn-outline-primary w-100">Jump URL</a>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
innerHtml += '</div>';
|
|
||||||
card.innerHTML = innerHtml;
|
|
||||||
col.appendChild(card);
|
|
||||||
row.appendChild(col);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(`Failed to load ${url}`, e);
|
|
||||||
contentDiv.innerHTML = '<div class="text-danger p-2">Failed to load data.</div>';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
targetSelect.addEventListener('change', () => {
|
|
||||||
updateLanguages();
|
|
||||||
renderContent();
|
|
||||||
});
|
|
||||||
langSelect.addEventListener('change', renderContent);
|
|
||||||
|
|
||||||
updateLanguages();
|
|
||||||
renderContent();
|
|
||||||
container.appendChild(outerCard);
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
export interface MirrorFileEntry {
|
|
||||||
orig: string;
|
|
||||||
mirror: string;
|
|
||||||
origStatus: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StoredData<T> {
|
|
||||||
req: any;
|
|
||||||
rsp: T;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LauncherWebAnnouncement {
|
|
||||||
data_version: string;
|
|
||||||
tabs: {
|
|
||||||
tabName: string;
|
|
||||||
announcements: {
|
|
||||||
content: string;
|
|
||||||
jump_url: string;
|
|
||||||
start_ts: string;
|
|
||||||
id: string;
|
|
||||||
need_token: boolean;
|
|
||||||
}[];
|
|
||||||
tab_id: string;
|
|
||||||
}[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LauncherWebBanner {
|
|
||||||
data_version: string;
|
|
||||||
banners: {
|
|
||||||
url: string;
|
|
||||||
md5: string;
|
|
||||||
jump_url: string;
|
|
||||||
id: string;
|
|
||||||
need_token: boolean;
|
|
||||||
}[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LauncherWebMainBgImage {
|
|
||||||
data_version: string;
|
|
||||||
main_bg_image: {
|
|
||||||
url: string;
|
|
||||||
md5: string;
|
|
||||||
video_url: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LauncherWebSingleEnt {
|
|
||||||
single_ent: {
|
|
||||||
version_url: string;
|
|
||||||
version_md5: string;
|
|
||||||
jump_url: string;
|
|
||||||
button_url: string;
|
|
||||||
button_md5: string;
|
|
||||||
button_hover_url: string;
|
|
||||||
button_hover_md5: string;
|
|
||||||
need_token: boolean;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LauncherWebSidebar {
|
|
||||||
data_version: string;
|
|
||||||
sidebars: {
|
|
||||||
display_type: 'DisplayType_RESERVE';
|
|
||||||
media: string;
|
|
||||||
pic: { url: string; md5: string; description: string } | null;
|
|
||||||
sidebar_labels: { content: string; jump_url: string; need_token: boolean }[];
|
|
||||||
grid_info: null;
|
|
||||||
jump_url: string;
|
|
||||||
need_token: boolean;
|
|
||||||
}[];
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
export const BASE_URL =
|
|
||||||
'https://raw.githubusercontent.com/daydreamer-json/ak-endfield-api-archive/refs/heads/main/output';
|
|
||||||
|
|
||||||
export const FILE_SIZE_OPTS = {
|
|
||||||
decimals: 2,
|
|
||||||
decimalPadding: true,
|
|
||||||
useBinaryUnit: true,
|
|
||||||
useBitUnit: false,
|
|
||||||
unitVisible: true,
|
|
||||||
unit: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const gameTargets = [
|
|
||||||
{ name: 'Official', region: 'os' as const, dirName: '6', channel: 6 },
|
|
||||||
{ name: 'Epic', region: 'os' as const, dirName: '801', channel: 6 },
|
|
||||||
{ name: 'Google Play', region: 'os' as const, dirName: '802', channel: 6 },
|
|
||||||
{ name: 'Official', region: 'cn' as const, dirName: '1', channel: 1 },
|
|
||||||
{ name: 'Bilibili', region: 'cn' as const, dirName: '2', channel: 2 },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const launcherTargets = [
|
|
||||||
{ id: 'os', apps: ['EndField', 'Official'], channel: 6 },
|
|
||||||
{ id: 'cn', apps: ['EndField', 'Arknights', 'Official'], channel: 1 },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const launcherWebApiLang = {
|
|
||||||
os: [
|
|
||||||
'de-de',
|
|
||||||
'en-us',
|
|
||||||
'es-mx',
|
|
||||||
'fr-fr',
|
|
||||||
'id-id',
|
|
||||||
'it-it',
|
|
||||||
'ja-jp',
|
|
||||||
'ko-kr',
|
|
||||||
'pt-br',
|
|
||||||
'ru-ru',
|
|
||||||
'th-th',
|
|
||||||
'vi-vn',
|
|
||||||
'zh-cn',
|
|
||||||
'zh-tw',
|
|
||||||
] as const,
|
|
||||||
cn: ['zh-cn'] as const,
|
|
||||||
};
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import { DateTime } from 'luxon';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
write(message: string) {
|
|
||||||
const debugLogElement = document.querySelector('#debug-log code');
|
|
||||||
if (!debugLogElement) return;
|
|
||||||
const prettyMessage = `${DateTime.now().toFormat('HH:mm:ss.SSS')} > ${message}`;
|
|
||||||
const divEl = document.createElement('div');
|
|
||||||
divEl.textContent = prettyMessage;
|
|
||||||
debugLogElement.appendChild(divEl);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
import logger from './logger.js';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
arrayMax(array: Array<number>) {
|
|
||||||
return array.reduce((a, b) => Math.max(a, b));
|
|
||||||
},
|
|
||||||
|
|
||||||
arrayMin(array: Array<number>) {
|
|
||||||
return array.reduce((a, b) => Math.min(a, b));
|
|
||||||
},
|
|
||||||
|
|
||||||
arrayTotal(array: Array<number>) {
|
|
||||||
return array.reduce((acc, f) => acc + f, 0);
|
|
||||||
},
|
|
||||||
|
|
||||||
arrayAvg(array: Array<number>) {
|
|
||||||
return this.arrayTotal(array) / array.length;
|
|
||||||
},
|
|
||||||
|
|
||||||
rounder(method: 'floor' | 'ceil' | 'round', num: number, n: number) {
|
|
||||||
const pow = Math.pow(10, n);
|
|
||||||
let result: number;
|
|
||||||
switch (method) {
|
|
||||||
case 'floor':
|
|
||||||
result = Math.floor(num * pow) / pow;
|
|
||||||
break;
|
|
||||||
case 'ceil':
|
|
||||||
result = Math.ceil(num * pow) / pow;
|
|
||||||
break;
|
|
||||||
case 'round':
|
|
||||||
result = Math.round(num * pow) / pow;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
orig: result,
|
|
||||||
padded: result.toFixed(n),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
formatFileSize(
|
|
||||||
bytes: number,
|
|
||||||
options: {
|
|
||||||
decimals: number;
|
|
||||||
decimalPadding: boolean;
|
|
||||||
useBinaryUnit: boolean;
|
|
||||||
useBitUnit: boolean;
|
|
||||||
unitVisible: boolean;
|
|
||||||
unit: 'B' | 'K' | 'M' | 'G' | 'T' | 'P' | 'E' | 'Z' | 'Y' | null;
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
const k = options.useBinaryUnit ? 1024 : 1000;
|
|
||||||
const dm = options.decimals < 0 ? 0 : options.decimals;
|
|
||||||
|
|
||||||
const baseUnits = ['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
|
|
||||||
const binaryUnitSuffix = options.useBitUnit ? 'ib' : 'iB';
|
|
||||||
const siUnitSuffix = options.useBitUnit ? 'b' : 'B';
|
|
||||||
|
|
||||||
const getUnitString = (i: number) => {
|
|
||||||
if (i === 0) return options.useBitUnit ? 'b' : 'B';
|
|
||||||
return baseUnits[i] + (options.useBinaryUnit ? binaryUnitSuffix : siUnitSuffix);
|
|
||||||
};
|
|
||||||
|
|
||||||
let value = bytes < 0 ? 0 : Math.floor(bytes);
|
|
||||||
if (options.useBitUnit) {
|
|
||||||
value *= 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
let i: number;
|
|
||||||
if (options.unit !== null) {
|
|
||||||
i = baseUnits.indexOf(options.unit);
|
|
||||||
if (i === -1) throw new Error(`Invalid unit: ${options.unit}`);
|
|
||||||
} else {
|
|
||||||
if (value === 0) {
|
|
||||||
i = 0;
|
|
||||||
} else {
|
|
||||||
i = Math.floor(Math.log(value) / Math.log(k));
|
|
||||||
i = Math.max(0, Math.min(baseUnits.length - 1, i)); // clamp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const resultValue = value / Math.pow(k, i);
|
|
||||||
|
|
||||||
let formattedValue: string;
|
|
||||||
if (options.decimalPadding) {
|
|
||||||
formattedValue = resultValue.toFixed(dm);
|
|
||||||
} else {
|
|
||||||
formattedValue = resultValue.toFixed(dm).replace(/\.?0+$/, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
return formattedValue + (options.unitVisible ? ' ' + getUnitString(i) : '');
|
|
||||||
},
|
|
||||||
|
|
||||||
secureRandomFloatInRange(min: number, max: number): number {
|
|
||||||
if (min > max) [min, max] = [max, min];
|
|
||||||
const crypto = globalThis.crypto;
|
|
||||||
if (!crypto) {
|
|
||||||
throw new Error('Cryptographically secure random float number gen is not available');
|
|
||||||
}
|
|
||||||
const randomValues = new Uint32Array(2);
|
|
||||||
crypto.getRandomValues(randomValues);
|
|
||||||
const highBits = randomValues[1]! & 0x1fffff; // 0x1FFFFF = 2^21 - 1
|
|
||||||
const lowBits = randomValues[0];
|
|
||||||
const combined = highBits * 0x100000000 + lowBits!; // 0x100000000 = 2^32
|
|
||||||
const randomFraction = combined / 0x20000000000000; // 0x20000000000000 = 2^53
|
|
||||||
return randomFraction * (max - min) + min;
|
|
||||||
},
|
|
||||||
|
|
||||||
secureRandomIntInRange(min: number, max: number, writeLog: boolean = false): number {
|
|
||||||
if (min === max) {
|
|
||||||
writeLog ? logger.write(`randomInt: Range=${min}-${max}, Output=${min}`) : undefined;
|
|
||||||
return min;
|
|
||||||
}
|
|
||||||
if (min > max) [min, max] = [max, min];
|
|
||||||
const crypto = globalThis.crypto;
|
|
||||||
if (!crypto) {
|
|
||||||
throw new Error('Cryptographically secure random int number gen is not available');
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert to integer anyway
|
|
||||||
const minInt = Math.ceil(min);
|
|
||||||
const maxInt = Math.floor(max);
|
|
||||||
|
|
||||||
// safe integer check
|
|
||||||
if (!Number.isSafeInteger(minInt) || !Number.isSafeInteger(maxInt)) {
|
|
||||||
throw new Error('Range boundaries must be within safe integer limits');
|
|
||||||
}
|
|
||||||
|
|
||||||
// valid range check
|
|
||||||
if (minInt > maxInt) {
|
|
||||||
throw new Error('Invalid range after integer conversion: min > max');
|
|
||||||
}
|
|
||||||
|
|
||||||
const range = maxInt - minInt + 1;
|
|
||||||
|
|
||||||
if (range <= 0 || range > Number.MAX_SAFE_INTEGER) {
|
|
||||||
throw new Error(`Range size must be between 1 and ${Number.MAX_SAFE_INTEGER} inclusive`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 53-bit random num gen
|
|
||||||
const MAX_53 = BigInt(1) << BigInt(53); // 2^53
|
|
||||||
const rangeBigInt = BigInt(range);
|
|
||||||
const maxAcceptable = MAX_53 - (MAX_53 % rangeBigInt);
|
|
||||||
|
|
||||||
// generate
|
|
||||||
const randomBuffer = new Uint32Array(2);
|
|
||||||
while (true) {
|
|
||||||
crypto.getRandomValues(randomBuffer);
|
|
||||||
const highBits = randomBuffer[1]! & 0x1fffff; // use lower 21-bit only
|
|
||||||
const lowBits = randomBuffer[0];
|
|
||||||
const combined = BigInt(highBits) * BigInt(0x100000000) + BigInt(lowBits!); // 0x100000000 = 2^32
|
|
||||||
// accept condition: combined < maxAcceptable
|
|
||||||
if (combined < maxAcceptable) {
|
|
||||||
const offset = Number(combined % rangeBigInt); // 0 to range-1
|
|
||||||
writeLog
|
|
||||||
? logger.write(
|
|
||||||
`randomInt: Range=${min}-${max}, Raw=0x${new Uint8Array(randomBuffer).toHex()}, Output=${minInt + offset}`,
|
|
||||||
)
|
|
||||||
: undefined;
|
|
||||||
return minInt + offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import type { MirrorFileEntry } from '../types.js';
|
|
||||||
|
|
||||||
export function generateDownloadLinks(url: string, mirrorFileDb: MirrorFileEntry[]) {
|
|
||||||
const cleanUrl = new URL(url);
|
|
||||||
cleanUrl.search = '';
|
|
||||||
const mirrorEntry = mirrorFileDb.find((g) => g.orig.includes(cleanUrl.toString()));
|
|
||||||
|
|
||||||
const links: string[] = [];
|
|
||||||
if (!mirrorEntry || mirrorEntry.origStatus === true) {
|
|
||||||
links.push(`<a href="${url}" target="_blank">Orig</a>`);
|
|
||||||
}
|
|
||||||
if (mirrorEntry) {
|
|
||||||
links.push(`<a href="${mirrorEntry.mirror}" target="_blank">Mirror</a>`);
|
|
||||||
}
|
|
||||||
return links.join(' / ');
|
|
||||||
}
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en" data-bs-theme="dark">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<title>Arknights: Endfield API Archive</title>
|
|
||||||
<meta name="description" content="" />
|
|
||||||
<meta property="og:title" content="" />
|
|
||||||
<meta property="og:type" content="website" />
|
|
||||||
<meta property="og:url" content="" />
|
|
||||||
<meta property="og:image" content="" />
|
|
||||||
<meta property="og:image:alt" content="" />
|
|
||||||
<meta name="twitter:card" content="summary_large_image" />
|
|
||||||
<meta name="twitter:title" content="" />
|
|
||||||
<meta name="twitter:image" content="" />
|
|
||||||
<!-- <link rel="icon" href="/favicon.ico" sizes="any" />
|
|
||||||
<link rel="icon" href="/icon.svg" type="image/svg+xml" />
|
|
||||||
<link rel="apple-touch-icon" href="icon.png" /> -->
|
|
||||||
<!-- <meta name="theme-color" content="#fafafa" /> -->
|
|
||||||
<style>
|
|
||||||
@import "./assets/css/essentials.css";
|
|
||||||
@import "./assets/css/index.css";
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container my-4 px-4" id="mainContainer">
|
|
||||||
<section>
|
|
||||||
<h1 class="text-center">Arknights: Endfield Font Design Test</h1>
|
|
||||||
</section>
|
|
||||||
<section
|
|
||||||
class="text-center"
|
|
||||||
style="
|
|
||||||
font-feature-settings:
|
|
||||||
"calt" 0,
|
|
||||||
"palt" 0;
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<p style="font-size: 5rem; font-weight: 900">武陵<br />四号谷地</p>
|
|
||||||
<p style="font-size: 3rem; font-weight: 900">トリアンゲロス</p>
|
|
||||||
<p
|
|
||||||
style="
|
|
||||||
font-family:
|
|
||||||
"Novecento Sans Wide (Number Only)",
|
|
||||||
"Noto Sans JP";
|
|
||||||
"
|
|
||||||
>
|
|
||||||
//リソース整合性を確認中… (77.42%)
|
|
||||||
</p>
|
|
||||||
<p>2560×1440 Fullscreen</p>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<p>The quick brown fox jumps over the lazy dog. 1234567890</p>
|
|
||||||
<p>
|
|
||||||
English alphabet: HarmonyOS Sans by Huawei Technologies<br />
|
|
||||||
Number (120% size): Novecento Sans Wide by Synthview Type Design<br />
|
|
||||||
Japanese glyph: Noto Sans JP by Adobe and Google
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<p>
|
|
||||||
CSS Priority:<br />
|
|
||||||
EN: "Novecento Sans Wide (Number Only)", "HarmonyOS Sans", "Noto Sans
|
|
||||||
SC"<br />JP: "Novecento Sans Wide (Number Only)", "Noto Sans JP"
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
<hr />
|
|
||||||
<section>
|
|
||||||
<button
|
|
||||||
id="debug-log-openBtn"
|
|
||||||
class="btn btn-secondary btn-sm"
|
|
||||||
data-bs-toggle="modal"
|
|
||||||
data-bs-target="#debug-log-modal"
|
|
||||||
>
|
|
||||||
Open Debug Log
|
|
||||||
</button>
|
|
||||||
<p><small>(C) daydreamer-json</small></p>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
<div class="modal fade" id="debug-log-modal" tabindex="-1">
|
|
||||||
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h1 class="modal-title fs-5" id="debug-log-modal-label">
|
|
||||||
Debug Panel
|
|
||||||
</h1>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn-close"
|
|
||||||
data-bs-dismiss="modal"
|
|
||||||
></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<section>
|
|
||||||
<p>Debug Log</p>
|
|
||||||
<pre id="debug-log"><code id="debug-log-inner"></code></pre>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script type="module" src="./assets/ts/essentials.ts"></script>
|
|
||||||
<script type="module" src="./assets/ts/index.ts"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en" data-bs-theme="dark">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<title>Arknights: Endfield API Archive</title>
|
|
||||||
<meta
|
|
||||||
name="description"
|
|
||||||
content="Automated Arknights: Endfield game API response archive and download library"
|
|
||||||
/>
|
|
||||||
<meta property="og:title" content="Arknights: Endfield API Archive" />
|
|
||||||
<meta property="og:type" content="website" />
|
|
||||||
<meta property="og:url" content="" />
|
|
||||||
<meta property="og:image" content="" />
|
|
||||||
<meta property="og:image:alt" content="" />
|
|
||||||
<meta name="twitter:card" content="summary_large_image" />
|
|
||||||
<meta name="twitter:title" content="Arknights: Endfield API Archive" />
|
|
||||||
<meta name="twitter:image" content="" />
|
|
||||||
<!-- <link rel="icon" href="/favicon.ico" sizes="any" />
|
|
||||||
<link rel="icon" href="/icon.svg" type="image/svg+xml" />
|
|
||||||
<link rel="apple-touch-icon" href="icon.png" /> -->
|
|
||||||
<!-- <meta name="theme-color" content="#fafafa" /> -->
|
|
||||||
<style>
|
|
||||||
@import "./assets/css/essentials.css";
|
|
||||||
@import "./assets/css/index.css";
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container my-4 px-4" id="mainContainer">
|
|
||||||
<section>
|
|
||||||
<h1 class="text-center fw-bold">Arknights: Endfield API Archive</h1>
|
|
||||||
<ul class="nav nav-tabs justify-content-center">
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link active">Top</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a href="./about.html" class="nav-link">About</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
<section id="content" class="mt-4">
|
|
||||||
<div class="text-center">
|
|
||||||
<div class="spinner-border" role="status"></div>
|
|
||||||
<p>Loading data...</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<hr />
|
|
||||||
<section>
|
|
||||||
<button
|
|
||||||
id="debug-log-openBtn"
|
|
||||||
class="d-none btn btn-secondary btn-sm"
|
|
||||||
data-bs-toggle="modal"
|
|
||||||
data-bs-target="#debug-log-modal"
|
|
||||||
>
|
|
||||||
Open Debug Log
|
|
||||||
</button>
|
|
||||||
<p class="text-center text-muted">
|
|
||||||
<small>(C) daydreamer-json and contributors</small>
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
<div class="modal fade" id="debug-log-modal" tabindex="-1">
|
|
||||||
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h1 class="modal-title fs-5" id="debug-log-modal-label">
|
|
||||||
Debug Panel
|
|
||||||
</h1>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn-close"
|
|
||||||
data-bs-dismiss="modal"
|
|
||||||
></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<section>
|
|
||||||
<p>Debug Log</p>
|
|
||||||
<pre id="debug-log"><code id="debug-log-inner"></code></pre>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script type="module" src="./assets/ts/essentials.ts"></script>
|
|
||||||
<script type="module" src="./assets/ts/index.ts"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": [
|
|
||||||
// "@tsconfig/bun",
|
|
||||||
"@tsconfig/recommended",
|
|
||||||
"@tsconfig/node24",
|
|
||||||
"@tsconfig/strictest"
|
|
||||||
],
|
|
||||||
"compilerOptions": {
|
|
||||||
// Environment setup & latest features
|
|
||||||
"lib": ["ESNext", "DOM"]
|
|
||||||
// "target": "ESNext"
|
|
||||||
},
|
|
||||||
"include": ["src/**/*"],
|
|
||||||
"exclude": ["node_modules"]
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user