refactor(pages): remove unused pages v1 dir

This commit is contained in:
daydreamer-json
2026-04-04 04:06:36 +09:00
parent 3d51461634
commit 0ab26d11d1
36 changed files with 0 additions and 3300 deletions

View File

@@ -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
View File

@@ -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

View File

@@ -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"
}

View File

@@ -1,6 +0,0 @@
{
"extends": "@parcel/config-default",
"transformers": {
"*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"]
}
}

View File

@@ -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"
}
}

View File

@@ -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
```

View File

@@ -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"
}
}
}
}

View File

@@ -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=="],
}
}

View File

@@ -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"
}
}

View File

@@ -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>

View File

@@ -1,8 +0,0 @@
#endmin-thumbsup {
width: 200px;
position: fixed;
bottom: 0;
left: 50%;
transform: translateX(-50%);
z-index: -1000;
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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());
});
});

View File

@@ -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')!),
]);
}

View File

@@ -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
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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);
}

View File

@@ -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
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;
}[];
}

View File

@@ -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,
};

View File

@@ -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);
},
};

View File

@@ -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;
}
}
},
};

View File

@@ -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(' / ');
}

View File

@@ -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:
&quot;calt&quot; 0,
&quot;palt&quot; 0;
"
>
<p style="font-size: 5rem; font-weight: 900">武陵<br />四号谷地</p>
<p style="font-size: 3rem; font-weight: 900">トリアンゲロス</p>
<p
style="
font-family:
&quot;Novecento Sans Wide (Number Only)&quot;,
&quot;Noto Sans JP&quot;;
"
>
//リソース整合性を確認中… (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>

View File

@@ -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>

View File

@@ -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"]
}