diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index ee3d1d8..4dccdc6 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -20,7 +20,6 @@ jobs: # Copilot will be given its own token for its operations. permissions: contents: read - workflows: write steps: - name: Checkout code diff --git a/.github/workflows/editors-picks.yml b/.github/workflows/editors-picks.yml index 0ce8f3f..680ce1a 100644 --- a/.github/workflows/editors-picks.yml +++ b/.github/workflows/editors-picks.yml @@ -37,10 +37,6 @@ jobs: with: python-version: '3.x' - - name: Install webp - if: steps.backoff.outputs.skip == 'false' - run: sudo apt-get update && sudo apt-get install -y webp - - name: Archive current editors picks if: steps.backoff.outputs.skip == 'false' run: | @@ -68,14 +64,19 @@ jobs: with open("public/editors-picks.json") as f: current = json.load(f) - # Replace cover URLs with original UUIDs - url_pattern = re.compile(r'^https://monochrome\.tf/editors-picks-images/([a-f0-9-]+)\.webp$') + # Replace cover URLs with original UUIDs (handles both legacy and intermediate formats) + url_pattern1 = re.compile(r'^https://monochrome\.tf/editors-picks-images/([a-f0-9-]+)\.webp$') + url_pattern2 = re.compile(r'^https://wsrv\.nl/\?url=https://resources\.tidal\.com/images/([a-f0-9/]+)/320x320\.jpg&w=250&h=250&output=webp$') for item in current: for field in ['cover', 'picture', 'image']: if field in item and item[field]: - match = url_pattern.match(item[field]) - if match: - item[field] = match.group(1) + m1 = url_pattern1.match(item[field]) + if m1: + item[field] = m1.group(1) + continue + m2 = url_pattern2.match(item[field]) + if m2: + item[field] = m2.group(1).replace("/", "-") with open(archive_path, "w") as f: json.dump(current, f, indent=4) @@ -103,7 +104,8 @@ jobs: run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git add public/editors-picks.json public/editors-picks-old/ public/editors-picks-images/ + git pull --rebase origin main + git add public/editors-picks.json public/editors-picks-old/ git diff --staged --quiet && echo "No changes to commit." && exit 0 git commit -m "chore: update editors picks" git push diff --git a/.github/workflows/lighthouse.yml b/.github/workflows/lighthouse.yml new file mode 100644 index 0000000..47d6941 --- /dev/null +++ b/.github/workflows/lighthouse.yml @@ -0,0 +1,43 @@ +name: Lighthouse + +on: + workflow_dispatch: + push: + branches: [main] + +jobs: + lighthouse: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm install + + - name: Build + run: npm run build + + - name: Preview build + run: npm run preview & + continue-on-error: true + + - name: Wait for preview server + run: sleep 10 + + - name: Run Lighthouse + run: | + npx lhci autorun --config=.lhci.yml || true + + - name: Upload results + if: always() + uses: actions/upload-artifact@v4 + with: + name: lighthouse-results + path: .lighthouseci/ diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index ba2b7bc..0028f5b 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -20,6 +20,11 @@ + + + + + diff --git a/android/app/src/main/java/tf/monochrome/music/AudioPlaybackService.java b/android/app/src/main/java/tf/monochrome/music/AudioPlaybackService.java new file mode 100644 index 0000000..11998e0 --- /dev/null +++ b/android/app/src/main/java/tf/monochrome/music/AudioPlaybackService.java @@ -0,0 +1,129 @@ +package tf.monochrome.music; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Intent; +import android.content.pm.ServiceInfo; +import android.os.Build; +import android.os.IBinder; +import android.os.PowerManager; + +import androidx.core.app.NotificationCompat; + +/** + * Foreground service that keeps the app process alive while audio is playing + * in the background. Without this, Android will throttle the WebView and + * suspend Web Audio API processing, causing audible skips and dropouts. + */ +public class AudioPlaybackService extends Service { + + private static final String CHANNEL_ID = "audio_playback"; + private static final int NOTIFICATION_ID = 1; + + private PowerManager.WakeLock wakeLock; + + @Override + public void onCreate() { + super.onCreate(); + createNotificationChannel(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (intent != null && "STOP".equals(intent.getAction())) { + stopSelf(); + return START_NOT_STICKY; + } + + Notification notification = buildNotification(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + startForeground(NOTIFICATION_ID, notification, + ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK); + } else { + startForeground(NOTIFICATION_ID, notification); + } + + acquireWakeLock(); + + // If the system kills this service, don't restart it automatically — + // MainActivity will re-start it when audio resumes. + return START_NOT_STICKY; + } + + @Override + public void onDestroy() { + releaseWakeLock(); + super.onDestroy(); + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + private void createNotificationChannel() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + return; + } + NotificationChannel channel = new NotificationChannel( + CHANNEL_ID, + "Audio Playback", + NotificationManager.IMPORTANCE_LOW + ); + channel.setDescription("Keeps audio playing in the background"); + channel.setSound(null, null); + channel.setShowBadge(false); + + NotificationManager manager = getSystemService(NotificationManager.class); + if (manager != null) { + manager.createNotificationChannel(channel); + } + } + + private Notification buildNotification() { + Intent launchIntent = new Intent(this, MainActivity.class); + launchIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + PendingIntent pendingIntent = PendingIntent.getActivity( + this, 0, launchIntent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE + ); + + return new NotificationCompat.Builder(this, CHANNEL_ID) + .setContentTitle("Monochrome") + .setContentText("Playing audio") + .setSmallIcon(android.R.drawable.ic_media_play) + .setContentIntent(pendingIntent) + .setOngoing(true) + .setSilent(true) + .build(); + } + + private void acquireWakeLock() { + if (wakeLock != null && !wakeLock.isHeld()) { + wakeLock = null; + } + if (wakeLock == null) { + PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE); + if (pm != null) { + wakeLock = pm.newWakeLock( + PowerManager.PARTIAL_WAKE_LOCK, + "monochrome:audio_playback" + ); + // 4-hour timeout as a safety net to prevent battery drain + // if the service is accidentally left running + wakeLock.acquire(4 * 60 * 60 * 1000L); + } + } + } + + private void releaseWakeLock() { + if (wakeLock != null && wakeLock.isHeld()) { + wakeLock.release(); + wakeLock = null; + } + } +} diff --git a/android/app/src/main/java/tf/monochrome/music/BackgroundAudioPlugin.java b/android/app/src/main/java/tf/monochrome/music/BackgroundAudioPlugin.java new file mode 100644 index 0000000..42a6b43 --- /dev/null +++ b/android/app/src/main/java/tf/monochrome/music/BackgroundAudioPlugin.java @@ -0,0 +1,47 @@ +package tf.monochrome.music; + +import android.content.Intent; +import android.os.Build; + +import com.getcapacitor.Plugin; +import com.getcapacitor.PluginCall; +import com.getcapacitor.PluginMethod; +import com.getcapacitor.annotation.CapacitorPlugin; + +/** + * Capacitor plugin that exposes start/stop controls for the foreground + * AudioPlaybackService. Called from JS when audio playback begins or ends + * so Android keeps the process alive in the background. + */ +@CapacitorPlugin(name = "BackgroundAudio") +public class BackgroundAudioPlugin extends Plugin { + + @PluginMethod + public void start(PluginCall call) { + try { + Intent intent = new Intent(getContext(), AudioPlaybackService.class); + intent.setAction("START"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + getContext().startForegroundService(intent); + } else { + getContext().startService(intent); + } + call.resolve(); + } catch (Exception e) { + call.reject("Failed to start audio service: " + e.getMessage(), e); + } + } + + `@PluginMethod` + public void stop(PluginCall call) { + try { + Intent intent = new Intent(getContext(), AudioPlaybackService.class); + intent.setAction("STOP"); + // Use startService so onStartCommand receives the STOP action + getContext().startService(intent); + call.resolve(); + } catch (Exception e) { + call.reject("Failed to stop audio service: " + e.getMessage(), e); + } + } +} diff --git a/android/app/src/main/java/tf/monochrome/music/MainActivity.java b/android/app/src/main/java/tf/monochrome/music/MainActivity.java index d150f82..11149a9 100644 --- a/android/app/src/main/java/tf/monochrome/music/MainActivity.java +++ b/android/app/src/main/java/tf/monochrome/music/MainActivity.java @@ -1,5 +1,14 @@ package tf.monochrome.music; +import android.os.Bundle; + import com.getcapacitor.BridgeActivity; -public class MainActivity extends BridgeActivity {} +public class MainActivity extends BridgeActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + registerPlugin(BackgroundAudioPlugin.class); + super.onCreate(savedInstanceState); + } +} diff --git a/bun.lock b/bun.lock index 13aca0b..c4873ee 100644 --- a/bun.lock +++ b/bun.lock @@ -45,6 +45,7 @@ "devDependencies": { "@capacitor/assets": "^3.0.5", "@capacitor/cli": "^8.2.0", + "@lhci/cli": "^0.14.0", "@testing-library/dom": "^10.4.1", "@types/node": "^25.3.5", "@vitest/browser-playwright": "^4.1.2", @@ -59,9 +60,11 @@ "stylelint": "^17.6.0", "stylelint-config-standard": "^39.0.1", "stylelint-config-standard-scss": "^16.0.0", + "terser": "^5.46.1", "typescript": "^5.9.3", "vite": "^7.3.1", "vite-bundle-visualizer": "^1.2.1", + "vite-plugin-purgecss": "^0.2.13", "vite-plugin-pwa": "^1.2.0", }, }, @@ -382,6 +385,16 @@ "@ffmpeg/util": ["@ffmpeg/util@0.12.2", "", {}, "sha512-ouyoW+4JB7WxjeZ2y6KpRvB+dLp7Cp4ro8z0HIVpZVCM7AwFlHa0c4R8Y/a4M3wMqATpYKhC7lSFHQ0T11MEDw=="], + "@formatjs/ecma402-abstract": ["@formatjs/ecma402-abstract@2.3.6", "", { "dependencies": { "@formatjs/fast-memoize": "2.2.7", "@formatjs/intl-localematcher": "0.6.2", "decimal.js": "^10.4.3", "tslib": "^2.8.0" } }, "sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw=="], + + "@formatjs/fast-memoize": ["@formatjs/fast-memoize@2.2.7", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ=="], + + "@formatjs/icu-messageformat-parser": ["@formatjs/icu-messageformat-parser@2.11.4", "", { "dependencies": { "@formatjs/ecma402-abstract": "2.3.6", "@formatjs/icu-skeleton-parser": "1.8.16", "tslib": "^2.8.0" } }, "sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw=="], + + "@formatjs/icu-skeleton-parser": ["@formatjs/icu-skeleton-parser@1.8.16", "", { "dependencies": { "@formatjs/ecma402-abstract": "2.3.6", "tslib": "^2.8.0" } }, "sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ=="], + + "@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.6.2", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA=="], + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="], @@ -480,6 +493,10 @@ "@keyv/serialize": ["@keyv/serialize@1.1.1", "", {}, "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA=="], + "@lhci/cli": ["@lhci/cli@0.14.0", "", { "dependencies": { "@lhci/utils": "0.14.0", "chrome-launcher": "^0.13.4", "compression": "^1.7.4", "debug": "^4.3.1", "express": "^4.17.1", "inquirer": "^6.3.1", "isomorphic-fetch": "^3.0.0", "lighthouse": "12.1.0", "lighthouse-logger": "1.2.0", "open": "^7.1.0", "proxy-agent": "^6.4.0", "tmp": "^0.1.0", "uuid": "^8.3.1", "yargs": "^15.4.1", "yargs-parser": "^13.1.2" }, "bin": { "lhci": "./src/cli.js" } }, "sha512-TxOH9pFBnmmN7Jmo2Aimxx5UhE8veqXpHfFJDMWsCVxkwh7mGxcAWchGl84mK139SZbbRmerqZ72c+h2nG9/QQ=="], + + "@lhci/utils": ["@lhci/utils@0.14.0", "", { "dependencies": { "debug": "^4.3.1", "isomorphic-fetch": "^3.0.0", "js-yaml": "^3.13.1", "lighthouse": "12.1.0", "tree-kill": "^1.2.1" } }, "sha512-LyP1RbvYQ9xNl7uLnl5AO8fDRata9MG/KYfVFKFkYenlsVS6QJsNjLzWNEoMIaE4jOPdQQlSp4tO7dtnyDxzbQ=="], + "@lit-labs/ssr-dom-shim": ["@lit-labs/ssr-dom-shim@1.5.1", "", {}, "sha512-Aou5UdlSpr5whQe8AA/bZG0jMj96CoJIWbGfZ91qieWu5AWUMKw8VR/pAkQkJYvBNhmCcWnZlyyk5oze8JIqYA=="], "@lit/reactive-element": ["@lit/reactive-element@2.1.2", "", { "dependencies": { "@lit-labs/ssr-dom-shim": "^1.5.0" } }, "sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A=="], @@ -494,6 +511,8 @@ "@paralleldrive/cuid2": ["@paralleldrive/cuid2@2.3.1", "", { "dependencies": { "@noble/hashes": "^1.1.5" } }, "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw=="], + "@paulirish/trace_engine": ["@paulirish/trace_engine@0.0.23", "", {}, "sha512-2ym/q7HhC5K+akXkNV6Gip3oaHpbI6TsGjmcAsl7bcJ528MVbacPQeoauLFEeLXH4ulJvsxQwNDIg/kAEhFZxw=="], + "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], "@poppinss/colors": ["@poppinss/colors@4.1.6", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg=="], @@ -504,6 +523,8 @@ "@prettier/plugin-xml": ["@prettier/plugin-xml@2.2.0", "", { "dependencies": { "@xml-tools/parser": "^1.0.11", "prettier": ">=2.4.0" } }, "sha512-UWRmygBsyj4bVXvDiqSccwT1kmsorcwQwaIy30yVh8T+Gspx4OlC0shX1y+ZuwXZvgnafmpRYKks0bAu9urJew=="], + "@puppeteer/browsers": ["@puppeteer/browsers@2.3.0", "", { "dependencies": { "debug": "^4.3.5", "extract-zip": "^2.0.1", "progress": "^2.0.3", "proxy-agent": "^6.4.0", "semver": "^7.6.3", "tar-fs": "^3.0.6", "unbzip2-stream": "^1.4.3", "yargs": "^17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" } }, "sha512-ioXoq9gPxkss4MYhD+SFaU9p1IHFUX0ILAWFPyjGaBdjLsYAlZw6j1iLA0N/m12uVHLFDfSYNF7EQccjinIMDA=="], + "@rollup/plugin-babel": ["@rollup/plugin-babel@5.3.1", "", { "dependencies": { "@babel/helper-module-imports": "^7.10.4", "@rollup/pluginutils": "^3.1.0" }, "peerDependencies": { "@babel/core": "^7.0.0", "@types/babel__core": "^7.1.9", "rollup": "^1.20.0||^2.0.0" }, "optionalPeers": ["@types/babel__core"] }, "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q=="], "@rollup/plugin-node-resolve": ["@rollup/plugin-node-resolve@15.3.1", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "@types/resolve": "1.20.2", "deepmerge": "^4.2.2", "is-module": "^1.0.0", "resolve": "^1.22.1" }, "peerDependencies": { "rollup": "^2.78.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA=="], @@ -564,6 +585,18 @@ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.60.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ=="], + "@sentry/core": ["@sentry/core@6.19.7", "", { "dependencies": { "@sentry/hub": "6.19.7", "@sentry/minimal": "6.19.7", "@sentry/types": "6.19.7", "@sentry/utils": "6.19.7", "tslib": "^1.9.3" } }, "sha512-tOfZ/umqB2AcHPGbIrsFLcvApdTm9ggpi/kQZFkej7kMphjT+SGBiQfYtjyg9jcRW+ilAR4JXC9BGKsdEQ+8Vw=="], + + "@sentry/hub": ["@sentry/hub@6.19.7", "", { "dependencies": { "@sentry/types": "6.19.7", "@sentry/utils": "6.19.7", "tslib": "^1.9.3" } }, "sha512-y3OtbYFAqKHCWezF0EGGr5lcyI2KbaXW2Ik7Xp8Mu9TxbSTuwTe4rTntwg8ngPjUQU3SUHzgjqVB8qjiGqFXCA=="], + + "@sentry/minimal": ["@sentry/minimal@6.19.7", "", { "dependencies": { "@sentry/hub": "6.19.7", "@sentry/types": "6.19.7", "tslib": "^1.9.3" } }, "sha512-wcYmSJOdvk6VAPx8IcmZgN08XTXRwRtB1aOLZm+MVHjIZIhHoBGZJYTVQS/BWjldsamj2cX3YGbGXNunaCfYJQ=="], + + "@sentry/node": ["@sentry/node@6.19.7", "", { "dependencies": { "@sentry/core": "6.19.7", "@sentry/hub": "6.19.7", "@sentry/types": "6.19.7", "@sentry/utils": "6.19.7", "cookie": "^0.4.1", "https-proxy-agent": "^5.0.0", "lru_map": "^0.3.3", "tslib": "^1.9.3" } }, "sha512-gtmRC4dAXKODMpHXKfrkfvyBL3cI8y64vEi3fDD046uqYcrWdgoQsffuBbxMAizc6Ez1ia+f0Flue6p15Qaltg=="], + + "@sentry/types": ["@sentry/types@6.19.7", "", {}, "sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg=="], + + "@sentry/utils": ["@sentry/utils@6.19.7", "", { "dependencies": { "@sentry/types": "6.19.7", "tslib": "^1.9.3" } }, "sha512-z95ECmE3i9pbWoXQrD/7PgkBAzJYR+iXtPuTkpBjDKs86O3mT+PXOT3BAn79w2wkn7/i3vOGD2xVr1uiMl26dA=="], + "@sindresorhus/is": ["@sindresorhus/is@7.2.0", "", {}, "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw=="], "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="], @@ -578,6 +611,8 @@ "@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], + "@tootallnate/quickjs-emscripten": ["@tootallnate/quickjs-emscripten@0.23.0", "", {}, "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA=="], + "@trapezedev/gradle-parse": ["@trapezedev/gradle-parse@7.1.3", "", {}, "sha512-WQVF5pEJ5o/mUyvfGTG9nBKx9Te/ilKM3r2IT69GlbaooItT5ao7RyF1MUTBNjHLPk/xpGUY3c6PyVnjDlz0Vw=="], "@trapezedev/project": ["@trapezedev/project@7.1.3", "", { "dependencies": { "@ionic/utils-fs": "^3.1.5", "@ionic/utils-subprocess": "^2.1.8", "@prettier/plugin-xml": "^2.2.0", "@trapezedev/gradle-parse": "7.1.3", "@xmldom/xmldom": "^0.7.5", "conventional-changelog": "^3.1.4", "cross-spawn": "^7.0.3", "diff": "^5.1.0", "env-paths": "^3.0.0", "gradle-to-js": "^2.0.0", "ini": "^2.0.0", "kleur": "^4.1.5", "lodash": "^4.17.21", "mergexml": "^1.2.3", "plist": "^3.0.4", "prettier": "^2.7.1", "prompts": "^2.4.2", "replace": "^1.1.0", "tempy": "^1.0.1", "tmp": "^0.2.1", "ts-node": "^10.2.1", "xcode": "^3.0.1", "xml-js": "^1.6.11", "xpath": "^0.0.32", "yargs": "^17.2.1" } }, "sha512-GANh8Ey73MechZrryfJoILY9hBnWqzS6AdB53zuWBCBbaiImyblXT41fWdN6pB2f5+cNI2FAUxGfVhl+LeEVbQ=="], @@ -618,6 +653,8 @@ "@types/wicg-file-system-access": ["@types/wicg-file-system-access@2023.10.7", "", {}, "sha512-g49ijasEJvCd7ifmAY2D0wdEtt1xRjBbA33PJTiv8mKBr7DoMsPeISoJ8oQOTopSRi+FBWPpPW5ouDj2QPKtGA=="], + "@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="], + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.58.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.58.0", "@typescript-eslint/type-utils": "8.58.0", "@typescript-eslint/utils": "8.58.0", "@typescript-eslint/visitor-keys": "8.58.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.58.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg=="], "@typescript-eslint/parser": ["@typescript-eslint/parser@8.58.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.58.0", "@typescript-eslint/types": "8.58.0", "@typescript-eslint/typescript-estree": "8.58.0", "@typescript-eslint/visitor-keys": "8.58.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA=="], @@ -666,6 +703,8 @@ "JSONStream": ["JSONStream@1.3.5", "", { "dependencies": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" }, "bin": { "JSONStream": "./bin.js" } }, "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ=="], + "accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], + "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], @@ -674,24 +713,34 @@ "add-stream": ["add-stream@1.0.0", "", {}, "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ=="], + "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], + "aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="], "ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="], + "ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="], + + "ansi-escapes": ["ansi-escapes@3.2.0", "", {}, "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ=="], + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="], + "appwrite": ["appwrite@23.0.0", "", { "dependencies": { "json-bigint": "1.0.0" } }, "sha512-K11a597npl3jsnxWKzjw163n4GguH4+/zBCOiU15yc1u+7QF0nP9mxsY4JxKrBU6bmQRtgtMTPv/6YOLSwp/QQ=="], "arg": ["arg@4.1.3", "", {}, "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="], - "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], "aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], "array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="], + "array-flatten": ["array-flatten@1.1.1", "", {}, "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="], + "array-ify": ["array-ify@1.0.0", "", {}, "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng=="], "array-union": ["array-union@2.1.0", "", {}, "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="], @@ -704,6 +753,8 @@ "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + "ast-types": ["ast-types@0.13.4", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w=="], + "astral-regex": ["astral-regex@2.0.0", "", {}, "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ=="], "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="], @@ -714,6 +765,8 @@ "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], + "axe-core": ["axe-core@4.11.2", "", {}, "sha512-byD6KPdvo72y/wj2T/4zGEvvlis+PsZsn/yPS3pEO+sFpcrqRpX/TJCxvVaEsNeMrfQbCr7w163YqoD9IYwHXw=="], + "b4a": ["b4a@1.8.0", "", { "peerDependencies": { "react-native-b4a": "*" }, "optionalPeers": ["react-native-b4a"] }, "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg=="], "babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.17", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-define-polyfill-provider": "^0.6.8", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w=="], @@ -742,12 +795,16 @@ "baseline-browser-mapping": ["baseline-browser-mapping@2.10.14", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-fOVLPAsFTsQfuCkvahZkzq6nf8KvGWanlYoTh0SVA0A/PIUxQGU2AOZAoD95n2gFLVDW/jP6sbGLny95nmEuHA=="], + "basic-ftp": ["basic-ftp@5.2.0", "", {}, "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw=="], + "big-integer": ["big-integer@1.6.52", "", {}, "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg=="], "bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="], "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], + "body-parser": ["body-parser@1.20.4", "", { "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "~1.2.0", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", "qs": "~6.14.0", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" } }, "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA=="], + "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], "bplist-creator": ["bplist-creator@0.1.0", "", { "dependencies": { "stream-buffers": "2.2.x" } }, "sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg=="], @@ -766,10 +823,14 @@ "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], + "bundle-require": ["bundle-require@5.1.0", "", { "dependencies": { "load-tsconfig": "^0.2.3" }, "peerDependencies": { "esbuild": ">=0.18" } }, "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA=="], + "butterchurn": ["butterchurn@2.6.7", "", { "dependencies": { "@babel/runtime": "^7.0.0", "ecma-proposal-math-extensions": "0.0.2" } }, "sha512-BJiRA8L0L2+84uoG2SSfkp0kclBuN+vQKf217pK7pMlwEO2ZEg3MtO2/o+l8Qpr8Nbejg8tmL1ZHD1jmhiaaqg=="], "butterchurn-presets": ["butterchurn-presets@2.4.7", "", { "dependencies": { "babel-runtime": "^6.26.0", "ecma-proposal-math-extensions": "0.0.2", "lodash": "^4.17.4" } }, "sha512-4MdM8ripz/VfH1BCldrIKdAc/1ryJFBDvqlyow6Ivo1frwj0H3duzvSMFC7/wIjAjxb1QpwVHVqGqS9uAFKhpg=="], + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], "cacheable": ["cacheable@2.3.4", "", { "dependencies": { "@cacheable/memory": "^2.0.8", "@cacheable/utils": "^2.4.0", "hookified": "^1.15.0", "keyv": "^5.6.0", "qified": "^0.9.0" } }, "sha512-djgxybDbw9fL/ZWMI3+CE8ZilNxcwFkVtDc1gJ+IlOSSWkSMPQabhV/XCHTQ6pwwN6aivXPZ43omTooZiX06Ew=="], @@ -792,12 +853,24 @@ "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "chardet": ["chardet@0.7.0", "", {}, "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="], + "chevrotain": ["chevrotain@7.1.1", "", { "dependencies": { "regexp-to-ast": "0.5.0" } }, "sha512-wy3mC1x4ye+O+QkEinVJkPf5u2vsrDIYW9G7ZuwFl6v/Yu0LwUuT2POsb+NUWApebyxfkQq6+yDfRExbnI5rcw=="], + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], + "chrome-launcher": ["chrome-launcher@0.13.4", "", { "dependencies": { "@types/node": "*", "escape-string-regexp": "^1.0.5", "is-wsl": "^2.2.0", "lighthouse-logger": "^1.0.0", "mkdirp": "^0.5.3", "rimraf": "^3.0.2" } }, "sha512-nnzXiDbGKjDSK6t2I+35OAPBy5Pw/39bgkb/ZAFwMhwJbdYBp6aH+vW28ZgtjdU890Q7D+3wN/tB8N66q5Gi2A=="], + + "chromium-bidi": ["chromium-bidi@0.6.3", "", { "dependencies": { "mitt": "3.0.1", "urlpattern-polyfill": "10.0.0", "zod": "3.23.8" }, "peerDependencies": { "devtools-protocol": "*" } }, "sha512-qXlsCmpCZJAnoTYI83Iu6EdYQpMYdVkCfq08KDh2pmlVqK5t5IA9mGs4/LwCwp4fqisSOMXZxP3HIh8w8aRn0A=="], + "clean-stack": ["clean-stack@2.2.0", "", {}, "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="], + "cli-cursor": ["cli-cursor@2.1.0", "", { "dependencies": { "restore-cursor": "^2.0.0" } }, "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw=="], + + "cli-width": ["cli-width@2.2.1", "", {}, "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw=="], + "client-zip": ["client-zip@2.5.0", "", {}, "sha512-ydG4nDZesbFurnNq0VVCp/yyomIBh+X/1fZPI/P24zbnG4dtC4tQAfI5uQsomigsUMeiRO2wiTPizLWQh+IAyQ=="], "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], @@ -818,8 +891,22 @@ "compare-func": ["compare-func@2.0.0", "", { "dependencies": { "array-ify": "^1.0.0", "dot-prop": "^5.1.0" } }, "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA=="], + "compressible": ["compressible@2.0.18", "", { "dependencies": { "mime-db": ">= 1.43.0 < 2" } }, "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg=="], + + "compression": ["compression@1.8.1", "", { "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", "debug": "2.6.9", "negotiator": "~0.6.4", "on-headers": "~1.1.0", "safe-buffer": "5.2.1", "vary": "~1.1.2" } }, "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w=="], + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], + + "configstore": ["configstore@5.0.1", "", { "dependencies": { "dot-prop": "^5.2.0", "graceful-fs": "^4.1.2", "make-dir": "^3.0.0", "unique-string": "^2.0.0", "write-file-atomic": "^3.0.0", "xdg-basedir": "^4.0.0" } }, "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA=="], + + "consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], + + "content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + "conventional-changelog": ["conventional-changelog@3.1.25", "", { "dependencies": { "conventional-changelog-angular": "^5.0.12", "conventional-changelog-atom": "^2.0.8", "conventional-changelog-codemirror": "^2.0.8", "conventional-changelog-conventionalcommits": "^4.5.0", "conventional-changelog-core": "^4.2.1", "conventional-changelog-ember": "^2.0.9", "conventional-changelog-eslint": "^3.0.9", "conventional-changelog-express": "^2.0.6", "conventional-changelog-jquery": "^3.0.11", "conventional-changelog-jshint": "^2.0.9", "conventional-changelog-preset-loader": "^2.3.4" } }, "sha512-ryhi3fd1mKf3fSjbLXOfK2D06YwKNic1nC9mWqybBHdObPd8KJ2vjaXZfYj1U23t+V8T8n0d7gwnc9XbIdFbyQ=="], "conventional-changelog-angular": ["conventional-changelog-angular@5.0.13", "", { "dependencies": { "compare-func": "^2.0.0", "q": "^1.5.1" } }, "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA=="], @@ -852,10 +939,12 @@ "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], - "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], + "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], "cookie-session": ["cookie-session@2.1.1", "", { "dependencies": { "cookies": "0.9.1", "debug": "3.2.7", "on-headers": "~1.1.0", "safe-buffer": "5.2.1" } }, "sha512-ji3kym/XZaFVew1+tIZk5ZLp9Z/fLv9rK1aZmpug0FsgE7Cu3ZDrUdRo7FT9vFjMYfNimrrUHJzywDwT7XEFlg=="], + "cookie-signature": ["cookie-signature@1.0.7", "", {}, "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA=="], + "cookies": ["cookies@0.9.1", "", { "dependencies": { "depd": "~2.0.0", "keygrip": "~1.1.0" } }, "sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw=="], "core-js": ["core-js@2.6.12", "", {}, "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="], @@ -872,6 +961,8 @@ "crypto-random-string": ["crypto-random-string@2.0.0", "", {}, "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA=="], + "csp_evaluator": ["csp_evaluator@1.1.1", "", {}, "sha512-N3ASg0C4kNPUaNxt1XAvzHIVuzdtr8KLgfk1O8WDyimp1GisPAHESupArO2ieHk9QWbrJ/WkQODyh21Ps/xhxw=="], + "css-functions-list": ["css-functions-list@3.3.3", "", {}, "sha512-8HFEBPKhOpJPEPu70wJJetjKta86Gw9+CCyCnB3sui2qQfOvRyqBy4IKLKKAwdMpWb2lHXWk9Wb4Z6AmaUT1Pg=="], "css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="], @@ -886,6 +977,8 @@ "dargs": ["dargs@7.0.0", "", {}, "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg=="], + "data-uri-to-buffer": ["data-uri-to-buffer@6.0.2", "", {}, "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw=="], + "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="], "data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="], @@ -900,6 +993,8 @@ "decamelize-keys": ["decamelize-keys@1.1.1", "", { "dependencies": { "decamelize": "^1.1.0", "map-obj": "^1.0.0" } }, "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg=="], + "decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="], + "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="], "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], @@ -914,14 +1009,20 @@ "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], + "degenerator": ["degenerator@5.0.1", "", { "dependencies": { "ast-types": "^0.13.4", "escodegen": "^2.1.0", "esprima": "^4.0.1" } }, "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ=="], + "del": ["del@6.1.1", "", { "dependencies": { "globby": "^11.0.1", "graceful-fs": "^4.2.4", "is-glob": "^4.0.1", "is-path-cwd": "^2.2.0", "is-path-inside": "^3.0.2", "p-map": "^4.0.0", "rimraf": "^3.0.2", "slash": "^3.0.0" } }, "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg=="], "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + "destroy": ["destroy@1.2.0", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="], + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "devtools-protocol": ["devtools-protocol@0.0.1312386", "", {}, "sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA=="], + "dezalgo": ["dezalgo@1.0.4", "", { "dependencies": { "asap": "^2.0.0", "wrappy": "1" } }, "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig=="], "diff": ["diff@5.2.2", "", {}, "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A=="], @@ -944,6 +1045,8 @@ "ecma-proposal-math-extensions": ["ecma-proposal-math-extensions@0.0.2", "", {}, "sha512-80BnDp2Fn7RxXlEr5HHZblniY4aQ97MOAicdWWpSo0vkQiISSE9wLR4SqxKsu4gCtXFBIPPzy8JMhay4NWRg/Q=="], + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + "ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="], "electron-to-chromium": ["electron-to-chromium@1.5.331", "", {}, "sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q=="], @@ -952,8 +1055,12 @@ "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], + "enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="], + "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], "env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], @@ -980,8 +1087,12 @@ "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + "escodegen": ["escodegen@2.1.0", "", { "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", "esutils": "^2.0.2" }, "optionalDependencies": { "source-map": "~0.6.1" }, "bin": { "esgenerate": "bin/esgenerate.js", "escodegen": "bin/escodegen.js" } }, "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w=="], + "eslint": ["eslint@9.39.4", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.5", "@eslint/js": "9.39.4", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ=="], "eslint-config-prettier": ["eslint-config-prettier@10.1.8", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w=="], @@ -992,6 +1103,8 @@ "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + "esquery": ["esquery@1.7.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g=="], "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], @@ -1002,6 +1115,8 @@ "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + "eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="], "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], @@ -1012,6 +1127,12 @@ "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], + "express": ["express@4.22.1", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.3", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "~1.3.1", "fresh": "~0.5.2", "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", "qs": "~6.14.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", "serve-static": "~1.16.2", "setprototypeof": "1.2.0", "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g=="], + + "external-editor": ["external-editor@3.1.0", "", { "dependencies": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", "tmp": "^0.0.33" } }, "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew=="], + + "extract-zip": ["extract-zip@2.0.1", "", { "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" }, "optionalDependencies": { "@types/yauzl": "^2.9.1" }, "bin": { "extract-zip": "cli.js" } }, "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg=="], + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], "fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="], @@ -1032,14 +1153,20 @@ "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + "figures": ["figures@2.0.0", "", { "dependencies": { "escape-string-regexp": "^1.0.5" } }, "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA=="], + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], "filelist": ["filelist@1.0.6", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA=="], "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + "finalhandler": ["finalhandler@1.3.2", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "statuses": "~2.0.2", "unpipe": "~1.0.0" } }, "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg=="], + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + "fix-dts-default-cjs-exports": ["fix-dts-default-cjs-exports@1.0.1", "", { "dependencies": { "magic-string": "^0.30.17", "mlly": "^1.7.4", "rollup": "^4.34.8" } }, "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg=="], + "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], "flatted": ["flatted@3.4.2", "", {}, "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA=="], @@ -1050,6 +1177,10 @@ "formidable": ["formidable@3.5.4", "", { "dependencies": { "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", "once": "^1.4.0" } }, "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug=="], + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + + "fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], + "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], "fs-extra": ["fs-extra@10.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="], @@ -1084,8 +1215,12 @@ "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + "get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="], + "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="], + "get-uri": ["get-uri@6.0.5", "", { "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", "debug": "^4.3.4" } }, "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg=="], + "git-raw-commits": ["git-raw-commits@2.0.11", "", { "dependencies": { "dargs": "^7.0.0", "lodash": "^4.17.15", "meow": "^8.0.0", "split2": "^3.0.0", "through2": "^4.0.0" }, "bin": { "git-raw-commits": "cli.js" } }, "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A=="], "git-remote-origin-url": ["git-remote-origin-url@2.0.0", "", { "dependencies": { "gitconfiglocal": "^1.0.0", "pify": "^2.3.0" } }, "sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw=="], @@ -1150,12 +1285,24 @@ "htmlhint": ["htmlhint@1.9.2", "", { "dependencies": { "async": "3.2.6", "chalk": "4.1.2", "commander": "11.1.0", "glob": "^13.0.6", "is-glob": "^4.0.3", "node-sarif-builder": "3.2.0", "strip-json-comments": "3.1.1", "xml": "1.0.1" }, "bin": { "htmlhint": "bin/htmlhint" } }, "sha512-PweWSPA1Pb+AVFIOSpIGu5KhLdmtk/uf/0CpjvrDf6XUWmdTyqUljlylwSxQ0AWLvPGcBxK2n8uISsI4lCOkBQ=="], + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + + "http-link-header": ["http-link-header@1.1.3", "", {}, "sha512-3cZ0SRL8fb9MUlU3mKM61FcQvPfXx2dBrZW3Vbg5CXa8jFlK8OaEpePenLe1oEXQduhz8b0QjsqfS59QP4AJDQ=="], + + "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], + + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + + "iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], + "idb": ["idb@7.1.1", "", {}, "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ=="], "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + "image-ssim": ["image-ssim@0.2.0", "", {}, "sha512-W7+sO6/yhxy83L0G7xR8YAc5Z5QFtYEXXRV6EaE8tuYBZJnA3gVgp3q7X7muhLZVodeb9UfvjSbwt9VJwjIYAg=="], + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], "import-from-esm": ["import-from-esm@1.3.4", "", { "dependencies": { "debug": "^4.3.4", "import-meta-resolve": "^4.0.0" } }, "sha512-7EyUlPFC0HOlBDpUFGfYstsU7XHxZJKAAMzCT8wZ0hMW7b+hG51LIKTDcsgtz8Pu6YC0HqRVbX+rVUtsGMUKvg=="], @@ -1172,8 +1319,16 @@ "ini": ["ini@2.0.0", "", {}, "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA=="], + "inquirer": ["inquirer@6.5.2", "", { "dependencies": { "ansi-escapes": "^3.2.0", "chalk": "^2.4.2", "cli-cursor": "^2.1.0", "cli-width": "^2.0.0", "external-editor": "^3.0.3", "figures": "^2.0.0", "lodash": "^4.17.12", "mute-stream": "0.0.7", "run-async": "^2.2.0", "rxjs": "^6.4.0", "string-width": "^2.1.0", "strip-ansi": "^5.1.0", "through": "^2.3.6" } }, "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ=="], + "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], + "intl-messageformat": ["intl-messageformat@10.7.18", "", { "dependencies": { "@formatjs/ecma402-abstract": "2.3.6", "@formatjs/fast-memoize": "2.2.7", "@formatjs/icu-messageformat-parser": "2.11.4", "tslib": "^2.8.0" } }, "sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g=="], + + "ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="], + + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="], "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], @@ -1242,6 +1397,8 @@ "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], + "is-typedarray": ["is-typedarray@1.0.0", "", {}, "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="], + "is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="], "is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="], @@ -1254,15 +1411,23 @@ "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + "isomorphic-fetch": ["isomorphic-fetch@3.0.0", "", { "dependencies": { "node-fetch": "^2.6.1", "whatwg-fetch": "^3.4.1" } }, "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA=="], + "jackspeak": ["jackspeak@4.2.3", "", { "dependencies": { "@isaacs/cliui": "^9.0.0" } }, "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg=="], "jake": ["jake@10.9.4", "", { "dependencies": { "async": "^3.2.6", "filelist": "^1.0.4", "picocolors": "^1.1.1" }, "bin": { "jake": "bin/cli.js" } }, "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA=="], "jose": ["jose@6.2.2", "", {}, "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ=="], + "joycon": ["joycon@3.1.1", "", {}, "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw=="], + + "jpeg-js": ["jpeg-js@0.4.4", "", {}, "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg=="], + + "js-library-detector": ["js-library-detector@6.7.0", "", {}, "sha512-c80Qupofp43y4cJ7+8TTDN/AsDwLi5oOm/plBrWI+iQt485vKXCco+yVmOwEgdo9VOdsYTuV0UlTeetVPTriXA=="], + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + "js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], @@ -1302,6 +1467,14 @@ "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + "lighthouse": ["lighthouse@12.1.0", "", { "dependencies": { "@paulirish/trace_engine": "^0.0.23", "@sentry/node": "^6.17.4", "axe-core": "^4.9.1", "chrome-launcher": "^1.1.2", "configstore": "^5.0.1", "csp_evaluator": "1.1.1", "devtools-protocol": "0.0.1312386", "enquirer": "^2.3.6", "http-link-header": "^1.1.1", "intl-messageformat": "^10.5.3", "jpeg-js": "^0.4.4", "js-library-detector": "^6.7.0", "lighthouse-logger": "^2.0.1", "lighthouse-stack-packs": "1.12.1", "lodash": "^4.17.21", "lookup-closest-locale": "6.2.0", "metaviewport-parser": "0.3.0", "open": "^8.4.0", "parse-cache-control": "1.0.1", "puppeteer-core": "^22.11.1", "robots-parser": "^3.0.1", "semver": "^5.3.0", "speedline-core": "^1.4.3", "third-party-web": "^0.24.3", "tldts-icann": "^6.1.16", "ws": "^7.0.0", "yargs": "^17.3.1", "yargs-parser": "^21.0.0" }, "bin": { "lighthouse": "cli/index.js", "smokehouse": "cli/test/smokehouse/frontends/smokehouse-bin.js", "chrome-debug": "core/scripts/manual-chrome-launcher.js" } }, "sha512-PQLaNcv3tQcybnYux6T8uoS6+RNrNYvVJBbGo0kkbD4XTjesGslOXWeMkUQDK7c28nLfVZi7gYWDUsicTLglKQ=="], + + "lighthouse-logger": ["lighthouse-logger@1.2.0", "", { "dependencies": { "debug": "^2.6.8", "marky": "^1.2.0" } }, "sha512-wzUvdIeJZhRsG6gpZfmSCfysaxNEr43i+QT+Hie94wvHDKFLi4n7C2GqZ4sTC+PH5b5iktmXJvU87rWvhP3lHw=="], + + "lighthouse-stack-packs": ["lighthouse-stack-packs@1.12.1", "", {}, "sha512-i4jTmg7tvZQFwNFiwB+nCK6a7ICR68Xcwo+VIVd6Spi71vBNFUlds5HiDrSbClZdkQDON2Bhqv+KKJIo5zkPeA=="], + + "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], + "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], "lit": ["lit@3.3.2", "", { "dependencies": { "@lit/reactive-element": "^2.1.0", "lit-element": "^4.2.0", "lit-html": "^3.3.0" } }, "sha512-NF9zbsP79l4ao2SNrH3NkfmFgN/hBYSQo90saIVI1o5GpjAdCPVstVzO1MrLOakHoEhYkrtRjPK6Ob521aoYWQ=="], @@ -1312,6 +1485,8 @@ "load-json-file": ["load-json-file@4.0.0", "", { "dependencies": { "graceful-fs": "^4.1.2", "parse-json": "^4.0.0", "pify": "^3.0.0", "strip-bom": "^3.0.0" } }, "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw=="], + "load-tsconfig": ["load-tsconfig@0.2.5", "", {}, "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg=="], + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], "lodash": ["lodash@4.18.1", "", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="], @@ -1324,7 +1499,11 @@ "lodash.truncate": ["lodash.truncate@4.4.2", "", {}, "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw=="], - "lru-cache": ["lru-cache@11.2.7", "", {}, "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA=="], + "lookup-closest-locale": ["lookup-closest-locale@6.2.0", "", {}, "sha512-/c2kL+Vnp1jnV6K6RpDTHK3dgg0Tu2VVp+elEiJpjfS1UyY7AjOYHohRug6wT0OpoX2qFgNORndE9RqesfVxWQ=="], + + "lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="], + + "lru_map": ["lru_map@0.3.3", "", {}, "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ=="], "lucide-static": ["lucide-static@0.577.0", "", {}, "sha512-hx39J5Tq4JWF2ALY+5YRg+SxQLpeAmLJDXNcqiBJH/UuVwp43it9fyki/onZO7AVFgG5ZbB+fWwZR9mwGHE2XQ=="], @@ -1332,26 +1511,44 @@ "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + "make-dir": ["make-dir@3.1.0", "", { "dependencies": { "semver": "^6.0.0" } }, "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw=="], + "make-error": ["make-error@1.3.6", "", {}, "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="], "map-obj": ["map-obj@4.3.0", "", {}, "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ=="], + "marky": ["marky@1.3.0", "", {}, "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ=="], + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], "mathml-tag-names": ["mathml-tag-names@4.0.0", "", {}, "sha512-aa6AU2Pcx0VP/XWnh8IGL0SYSgQHDT6Ucror2j2mXeFAlN3ahaNs8EZtG1YiticMkSLj3Gt6VPFfZogt7G5iFQ=="], "mdn-data": ["mdn-data@2.27.1", "", {}, "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ=="], + "media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="], + "meow": ["meow@14.1.0", "", {}, "sha512-EDYo6VlmtnumlcBCbh1gLJ//9jvM/ndXHfVXIFrZVr6fGcwTUyCTFNTLCKuY3ffbK8L/+3Mzqnd58RojiZqHVw=="], + "merge-descriptors": ["merge-descriptors@1.0.3", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="], + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], "mergexml": ["mergexml@1.2.4", "", { "dependencies": { "@xmldom/xmldom": "^0.7.0", "formidable": "^3.5.1", "xpath": "0.0.27" } }, "sha512-yiOlDqcVCz7AG1eSboonc18FTlfqDEKYfGoAV3Lul98u6YRV/s0kjtf4bjk47t0hLTFJR0BSYMd6BpmX3xDjNQ=="], + "metaviewport-parser": ["metaviewport-parser@0.3.0", "", {}, "sha512-EoYJ8xfjQ6kpe9VbVHvZTZHiOl4HL1Z18CrZ+qahvLXT7ZO4YTC2JMyt5FaUp9JJp6J4Ybb/z7IsCXZt86/QkQ=="], + + "methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="], + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], "mime": ["mime@4.1.0", "", { "bin": { "mime": "bin/cli.js" } }, "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw=="], + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "mimic-fn": ["mimic-fn@1.2.0", "", {}, "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="], + "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], "min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="], @@ -1368,16 +1565,24 @@ "minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="], - "mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], + "mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="], + + "mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="], "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], + "mlly": ["mlly@1.8.2", "", { "dependencies": { "acorn": "^8.16.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.3" } }, "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA=="], + "modify-values": ["modify-values@1.0.1", "", {}, "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw=="], "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], "ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="], + "mute-stream": ["mute-stream@0.0.7", "", {}, "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ=="], + + "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], "napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="], @@ -1386,8 +1591,12 @@ "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + "negotiator": ["negotiator@0.6.4", "", {}, "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w=="], + "neo-async": ["neo-async@2.6.2", "", {}, "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="], + "netmask": ["netmask@2.0.2", "", {}, "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg=="], + "node-abi": ["node-abi@3.89.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA=="], "node-addon-api": ["node-addon-api@6.1.0", "", {}, "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="], @@ -1408,6 +1617,8 @@ "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], @@ -1416,14 +1627,20 @@ "obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="], + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + "on-headers": ["on-headers@1.1.0", "", {}, "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A=="], "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + "onetime": ["onetime@2.0.1", "", { "dependencies": { "mimic-fn": "^1.0.0" } }, "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ=="], + "open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="], "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + "os-tmpdir": ["os-tmpdir@1.0.2", "", {}, "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g=="], + "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], @@ -1434,12 +1651,20 @@ "p-try": ["p-try@2.2.0", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="], + "pac-proxy-agent": ["pac-proxy-agent@7.2.0", "", { "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", "agent-base": "^7.1.2", "debug": "^4.3.4", "get-uri": "^6.0.1", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.6", "pac-resolver": "^7.0.1", "socks-proxy-agent": "^8.0.5" } }, "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA=="], + + "pac-resolver": ["pac-resolver@7.0.1", "", { "dependencies": { "degenerator": "^5.0.0", "netmask": "^2.0.2" } }, "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg=="], + "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + "parse-cache-control": ["parse-cache-control@1.0.1", "", {}, "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg=="], + "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], @@ -1450,6 +1675,8 @@ "path-scurry": ["path-scurry@2.0.2", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg=="], + "path-to-regexp": ["path-to-regexp@0.1.13", "", {}, "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA=="], + "path-type": ["path-type@3.0.0", "", { "dependencies": { "pify": "^3.0.0" } }, "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg=="], "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], @@ -1462,6 +1689,10 @@ "pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="], + "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="], + + "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], + "playwright": ["playwright@1.59.1", "", { "dependencies": { "playwright-core": "1.59.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw=="], "playwright-core": ["playwright-core@1.59.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg=="], @@ -1476,6 +1707,8 @@ "postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="], + "postcss-load-config": ["postcss-load-config@6.0.1", "", { "dependencies": { "lilconfig": "^3.1.1" }, "peerDependencies": { "jiti": ">=1.21.0", "postcss": ">=8.0.9", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["jiti", "postcss", "tsx", "yaml"] }, "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g=="], + "postcss-media-query-parser": ["postcss-media-query-parser@0.2.3", "", {}, "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig=="], "postcss-resolve-nested-selector": ["postcss-resolve-nested-selector@0.1.6", "", {}, "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw=="], @@ -1500,20 +1733,38 @@ "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], + "progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="], + "prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="], + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + + "proxy-agent": ["proxy-agent@6.5.0", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "http-proxy-agent": "^7.0.1", "https-proxy-agent": "^7.0.6", "lru-cache": "^7.14.1", "pac-proxy-agent": "^7.1.0", "proxy-from-env": "^1.1.0", "socks-proxy-agent": "^8.0.5" } }, "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A=="], + + "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], + "pump": ["pump@3.0.4", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA=="], "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + "puppeteer-core": ["puppeteer-core@22.15.0", "", { "dependencies": { "@puppeteer/browsers": "2.3.0", "chromium-bidi": "0.6.3", "debug": "^4.3.6", "devtools-protocol": "0.0.1312386", "ws": "^8.18.0" } }, "sha512-cHArnywCiAAVXa3t4GGL2vttNxh7GqXtIYGym99egkNJ3oG//wL9LkvO4WE8W1TJe95t1F1ocu9X4xWaGsOKOA=="], + + "purgecss": ["purgecss@4.1.3", "", { "dependencies": { "commander": "^8.0.0", "glob": "^7.1.7", "postcss": "^8.3.5", "postcss-selector-parser": "^6.0.6" }, "bin": { "purgecss": "bin/purgecss.js" } }, "sha512-99cKy4s+VZoXnPxaoM23e5ABcP851nC2y2GROkkjS8eJaJtlciGavd7iYAw2V84WeBqggZ12l8ef44G99HmTaw=="], + "q": ["q@1.5.1", "", {}, "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw=="], "qified": ["qified@0.9.1", "", { "dependencies": { "hookified": "^2.1.1" } }, "sha512-n7mar4T0xQ+39dE2vGTAlbxUEpndwPANH0kDef1/MYsB8Bba9wshkybIRx74qgcvKQPEWErf9AqAdYjhzY2Ilg=="], + "qs": ["qs@6.14.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q=="], + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], "quick-lru": ["quick-lru@4.0.1", "", {}, "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g=="], + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + + "raw-body": ["raw-body@2.5.3", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "unpipe": "~1.0.0" } }, "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA=="], + "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], @@ -1524,6 +1775,8 @@ "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + "redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="], "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="], @@ -1554,18 +1807,26 @@ "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], - "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], + + "restore-cursor": ["restore-cursor@2.0.0", "", { "dependencies": { "onetime": "^2.0.0", "signal-exit": "^3.0.2" } }, "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q=="], "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], "rimraf": ["rimraf@6.1.3", "", { "dependencies": { "glob": "^13.0.3", "package-json-from-dist": "^1.0.1" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA=="], + "robots-parser": ["robots-parser@3.0.1", "", {}, "sha512-s+pyvQeIKIZ0dx5iJiQk1tPLJAWln39+MI5jtM8wnyws+G5azk+dMnMX0qfbqNetKKNgcWWOdi0sfm+FbQbgdQ=="], + "rollup": ["rollup@4.60.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.1", "@rollup/rollup-android-arm64": "4.60.1", "@rollup/rollup-darwin-arm64": "4.60.1", "@rollup/rollup-darwin-x64": "4.60.1", "@rollup/rollup-freebsd-arm64": "4.60.1", "@rollup/rollup-freebsd-x64": "4.60.1", "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", "@rollup/rollup-linux-arm-musleabihf": "4.60.1", "@rollup/rollup-linux-arm64-gnu": "4.60.1", "@rollup/rollup-linux-arm64-musl": "4.60.1", "@rollup/rollup-linux-loong64-gnu": "4.60.1", "@rollup/rollup-linux-loong64-musl": "4.60.1", "@rollup/rollup-linux-ppc64-gnu": "4.60.1", "@rollup/rollup-linux-ppc64-musl": "4.60.1", "@rollup/rollup-linux-riscv64-gnu": "4.60.1", "@rollup/rollup-linux-riscv64-musl": "4.60.1", "@rollup/rollup-linux-s390x-gnu": "4.60.1", "@rollup/rollup-linux-x64-gnu": "4.60.1", "@rollup/rollup-linux-x64-musl": "4.60.1", "@rollup/rollup-openbsd-x64": "4.60.1", "@rollup/rollup-openharmony-arm64": "4.60.1", "@rollup/rollup-win32-arm64-msvc": "4.60.1", "@rollup/rollup-win32-ia32-msvc": "4.60.1", "@rollup/rollup-win32-x64-gnu": "4.60.1", "@rollup/rollup-win32-x64-msvc": "4.60.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w=="], "rollup-plugin-visualizer": ["rollup-plugin-visualizer@5.14.0", "", { "dependencies": { "open": "^8.4.0", "picomatch": "^4.0.2", "source-map": "^0.7.4", "yargs": "^17.5.1" }, "peerDependencies": { "rolldown": "1.x", "rollup": "2.x || 3.x || 4.x" }, "optionalPeers": ["rolldown", "rollup"], "bin": { "rollup-plugin-visualizer": "dist/bin/cli.js" } }, "sha512-VlDXneTDaKsHIw8yzJAFWtrzguoJ/LnQ+lMpoVfYJ3jJF4Ihe5oYLAqLklIK/35lgUY+1yEzCkHyZ1j4A5w5fA=="], + "run-async": ["run-async@2.4.1", "", {}, "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ=="], + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + "rxjs": ["rxjs@6.6.7", "", { "dependencies": { "tslib": "^1.9.0" } }, "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ=="], + "safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="], "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], @@ -1574,12 +1835,18 @@ "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + "sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="], "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "send": ["send@0.19.2", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "~0.5.2", "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "~2.4.1", "range-parser": "~1.2.1", "statuses": "~2.0.2" } }, "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg=="], + "serialize-javascript": ["serialize-javascript@7.0.5", "", {}, "sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw=="], + "serve-static": ["serve-static@1.16.3", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "~0.19.1" } }, "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA=="], + "set-blocking": ["set-blocking@2.0.0", "", {}, "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="], "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], @@ -1588,6 +1855,8 @@ "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="], + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + "shaka-player": ["shaka-player@5.0.9", "", {}, "sha512-Y391+8gZHpLYGkiYmY/jHOfHQKQXHrR1op+awAEOxn3BALskjDuyoteNwfFyFScuQOqKIiq0O0PjPe1NfHi+WQ=="], "sharp": ["sharp@0.32.6", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.2", "node-addon-api": "^6.1.0", "prebuild-install": "^7.1.1", "semver": "^7.5.4", "simple-get": "^4.0.1", "tar-fs": "^3.0.4", "tunnel-agent": "^0.6.0" } }, "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w=="], @@ -1626,8 +1895,14 @@ "slice-ansi": ["slice-ansi@4.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" } }, "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ=="], + "smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="], + "smob": ["smob@1.6.1", "", {}, "sha512-KAkBqZl3c2GvNgNhcoyJae1aKldDW0LO279wF9bk1PnluRTETKBq0WyzRXxEhoQLk56yHaOY4JCBEKDuJIET5g=="], + "socks": ["socks@2.8.7", "", { "dependencies": { "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" } }, "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A=="], + + "socks-proxy-agent": ["socks-proxy-agent@8.0.5", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" } }, "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw=="], + "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], @@ -1644,12 +1919,18 @@ "spdx-license-ids": ["spdx-license-ids@3.0.23", "", {}, "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw=="], + "speedline-core": ["speedline-core@1.4.3", "", { "dependencies": { "@types/node": "*", "image-ssim": "^0.2.0", "jpeg-js": "^0.4.1" } }, "sha512-DI7/OuAUD+GMpR6dmu8lliO2Wg5zfeh+/xsdyJZCzd8o5JgFUjCeLsBDuZjIQJdwXS3J0L/uZYrELKYqx+PXog=="], + "split": ["split@1.0.1", "", { "dependencies": { "through": "2" } }, "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg=="], "split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="], + "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], + "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + "std-env": ["std-env@4.0.0", "", {}, "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ=="], "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], @@ -1694,6 +1975,8 @@ "stylelint-scss": ["stylelint-scss@6.14.0", "", { "dependencies": { "css-tree": "^3.0.1", "is-plain-object": "^5.0.0", "known-css-properties": "^0.37.0", "mdn-data": "^2.25.0", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.6", "postcss-selector-parser": "^7.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "stylelint": "^16.8.2" } }, "sha512-ZKmHMZolxeuYsnB+PCYrTpFce0/QWX9i9gh0hPXzp73WjuIMqUpzdQaBCrKoLWh6XtCFSaNDErkMPqdjy1/8aA=="], + "sucrase": ["sucrase@3.35.1", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="], + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], "supports-hyperlinks": ["supports-hyperlinks@4.4.0", "", { "dependencies": { "has-flag": "^5.0.1", "supports-color": "^10.2.2" } }, "sha512-UKbpT93hN5Nr9go5UY7bopIB9YQlMz9nm/ct4IXt/irb5YRkn9WaqrOBJGZ5Pwvsd5FQzSVeYlGdXoCAPQZrPg=="], @@ -1724,6 +2007,12 @@ "text-extensions": ["text-extensions@1.9.0", "", {}, "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ=="], + "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], + + "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], + + "third-party-web": ["third-party-web@0.24.5", "", {}, "sha512-1rUOdMYpNTRajgk1F7CmHD26oA6rTKekBjHay854J6OkPXeNyPcR54rhWDaamlWyi9t2wAVPQESdedBhucmOLA=="], + "through": ["through@2.3.8", "", {}, "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="], "through2": ["through2@4.0.2", "", { "dependencies": { "readable-stream": "3" } }, "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw=="], @@ -1736,10 +2025,16 @@ "tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="], - "tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="], + "tldts-core": ["tldts-core@6.1.86", "", {}, "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA=="], + + "tldts-icann": ["tldts-icann@6.1.86", "", { "dependencies": { "tldts-core": "^6.1.86" } }, "sha512-NFxmRT2lAEMcCOBgeZ0NuM0zsK/xgmNajnY6n4S1mwAKocft2s2ise1O3nQxrH3c+uY6hgHUV9GGNVp7tUE4Sg=="], + + "tmp": ["tmp@0.1.0", "", { "dependencies": { "rimraf": "^2.6.3" } }, "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw=="], "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], @@ -1750,18 +2045,24 @@ "ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="], + "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="], + "ts-node": ["ts-node@10.9.2", "", { "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", "@tsconfig/node16": "^1.0.2", "acorn": "^8.4.1", "acorn-walk": "^8.1.1", "arg": "^4.1.0", "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "peerDependencies": { "@swc/core": ">=1.2.50", "@swc/wasm": ">=1.2.50", "@types/node": "*", "typescript": ">=2.7" }, "optionalPeers": ["@swc/core", "@swc/wasm"], "bin": { "ts-node": "dist/bin.js", "ts-script": "dist/bin-script-deprecated.js", "ts-node-cwd": "dist/bin-cwd.js", "ts-node-esm": "dist/bin-esm.js", "ts-node-script": "dist/bin-script.js", "ts-node-transpile-only": "dist/bin-transpile.js" } }, "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ=="], "tslib": ["tslib@2.6.2", "", {}, "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="], "tsscmp": ["tsscmp@1.0.6", "", {}, "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA=="], + "tsup": ["tsup@8.5.1", "", { "dependencies": { "bundle-require": "^5.1.0", "cac": "^6.7.14", "chokidar": "^4.0.3", "consola": "^3.4.0", "debug": "^4.4.0", "esbuild": "^0.27.0", "fix-dts-default-cjs-exports": "^1.0.0", "joycon": "^3.1.1", "picocolors": "^1.1.1", "postcss-load-config": "^6.0.1", "resolve-from": "^5.0.0", "rollup": "^4.34.8", "source-map": "^0.7.6", "sucrase": "^3.35.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.11", "tree-kill": "^1.2.2" }, "peerDependencies": { "@microsoft/api-extractor": "^7.36.0", "@swc/core": "^1", "postcss": "^8.4.12", "typescript": ">=4.5.0" }, "optionalPeers": ["@microsoft/api-extractor", "@swc/core", "postcss", "typescript"], "bin": { "tsup": "dist/cli-default.js", "tsup-node": "dist/cli-node.js" } }, "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing=="], + "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], "type-fest": ["type-fest@0.16.0", "", {}, "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg=="], + "type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="], + "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="], @@ -1770,14 +2071,20 @@ "typed-array-length": ["typed-array-length@1.0.7", "", { "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", "is-typed-array": "^1.1.13", "possible-typed-array-names": "^1.0.0", "reflect.getprototypeof": "^1.0.6" } }, "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg=="], + "typedarray-to-buffer": ["typedarray-to-buffer@3.1.5", "", { "dependencies": { "is-typedarray": "^1.0.0" } }, "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q=="], + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "typescript-eslint": ["typescript-eslint@8.58.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.58.0", "@typescript-eslint/parser": "8.58.0", "@typescript-eslint/typescript-estree": "8.58.0", "@typescript-eslint/utils": "8.58.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA=="], + "ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="], + "uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="], "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], + "unbzip2-stream": ["unbzip2-stream@1.4.3", "", { "dependencies": { "buffer": "^5.2.1", "through": "^2.3.8" } }, "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg=="], + "undici": ["undici@7.24.4", "", {}, "sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w=="], "undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="], @@ -1796,6 +2103,8 @@ "universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="], + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + "untildify": ["untildify@4.0.0", "", {}, "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw=="], "upath": ["upath@1.2.0", "", {}, "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg=="], @@ -1806,24 +2115,34 @@ "url-toolkit": ["url-toolkit@2.2.5", "", {}, "sha512-mtN6xk+Nac+oyJ/PrI7tzfmomRVNFIWKUbG8jdYFt52hxbiReFAXIjYskvu64/dvuW71IcB7lV8l0HvZMac6Jg=="], + "urlpattern-polyfill": ["urlpattern-polyfill@10.0.0", "", {}, "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg=="], + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + "utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="], + "uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="], "v8-compile-cache-lib": ["v8-compile-cache-lib@3.0.1", "", {}, "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="], "validate-npm-package-license": ["validate-npm-package-license@3.0.4", "", { "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew=="], + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + "vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="], "vite-bundle-visualizer": ["vite-bundle-visualizer@1.2.1", "", { "dependencies": { "cac": "^6.7.14", "import-from-esm": "^1.3.3", "rollup-plugin-visualizer": "^5.11.0", "tmp": "^0.2.1" }, "bin": { "vite-bundle-visualizer": "bin.js" } }, "sha512-cwz/Pg6+95YbgIDp+RPwEToc4TKxfsFWSG/tsl2DSZd9YZicUag1tQXjJ5xcL7ydvEoaC2FOZeaXOU60t9BRXw=="], + "vite-plugin-purgecss": ["vite-plugin-purgecss@0.2.13", "", { "dependencies": { "purgecss": "^4.1.3", "tsup": "^8.4.0", "vite": "^6.2.1" } }, "sha512-b+ZHnZPm2tsnlPVkACUErTrVjnGN8c/ZOkPe0j0hha03S37RB+pPd+uIUKLsZs9l/HYelafgaudI+W4MTxhawA=="], + "vite-plugin-pwa": ["vite-plugin-pwa@1.2.0", "", { "dependencies": { "debug": "^4.3.6", "pretty-bytes": "^6.1.1", "tinyglobby": "^0.2.10", "workbox-build": "^7.4.0", "workbox-window": "^7.4.0" }, "peerDependencies": { "@vite-pwa/assets-generator": "^1.0.0", "vite": "^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" }, "optionalPeers": ["@vite-pwa/assets-generator"] }, "sha512-a2xld+SJshT9Lgcv8Ji4+srFJL4k/1bVbd1x06JIkvecpQkwkvCncD1+gSzcdm3s+owWLpMJerG3aN5jupJEVw=="], "vitest": ["vitest@4.1.2", "", { "dependencies": { "@vitest/expect": "4.1.2", "@vitest/mocker": "4.1.2", "@vitest/pretty-format": "4.1.2", "@vitest/runner": "4.1.2", "@vitest/snapshot": "4.1.2", "@vitest/spy": "4.1.2", "@vitest/utils": "4.1.2", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.2", "@vitest/browser-preview": "4.1.2", "@vitest/browser-webdriverio": "4.1.2", "@vitest/ui": "4.1.2", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg=="], "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], + "whatwg-fetch": ["whatwg-fetch@3.6.20", "", {}, "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg=="], + "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], @@ -1888,6 +2207,8 @@ "xcode": ["xcode@3.0.1", "", { "dependencies": { "simple-plist": "^1.1.0", "uuid": "^7.0.3" } }, "sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA=="], + "xdg-basedir": ["xdg-basedir@4.0.0", "", {}, "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q=="], + "xml": ["xml@1.0.1", "", {}, "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw=="], "xml-js": ["xml-js@1.6.11", "", { "dependencies": { "sax": "^1.2.4" }, "bin": { "xml-js": "./bin/cli.js" } }, "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g=="], @@ -1906,7 +2227,7 @@ "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], - "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + "yargs-parser": ["yargs-parser@13.1.2", "", { "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" } }, "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg=="], "yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="], @@ -1918,6 +2239,8 @@ "youch-core": ["youch-core@0.3.3", "", { "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } }, "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA=="], + "zod": ["zod@3.23.8", "", {}, "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g=="], + "@apideck/better-ajv-errors/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], "@babel/core/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], @@ -1962,6 +2285,18 @@ "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], + "@eslint/eslintrc/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + + "@formatjs/ecma402-abstract/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "@formatjs/fast-memoize/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "@formatjs/icu-messageformat-parser/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "@formatjs/icu-skeleton-parser/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "@formatjs/intl-localematcher/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "@ionic/cli-framework-output/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], "@ionic/cli-framework-output/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], @@ -2000,8 +2335,20 @@ "@keyv/bigmap/keyv": ["keyv@5.6.0", "", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw=="], + "@lhci/cli/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "@lhci/cli/open": ["open@7.4.2", "", { "dependencies": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" } }, "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q=="], + + "@lhci/cli/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], + + "@lhci/cli/yargs": ["yargs@15.4.1", "", { "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A=="], + + "@lhci/utils/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "@poppinss/dumper/supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], + "@puppeteer/browsers/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "@rollup/plugin-babel/rollup": ["rollup@2.80.0", "", { "optionalDependencies": { "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ=="], "@rollup/plugin-node-resolve/@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="], @@ -2018,6 +2365,20 @@ "@rollup/pluginutils/rollup": ["rollup@2.80.0", "", { "optionalDependencies": { "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ=="], + "@sentry/core/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], + + "@sentry/hub/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], + + "@sentry/minimal/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], + + "@sentry/node/cookie": ["cookie@0.4.2", "", {}, "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA=="], + + "@sentry/node/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], + + "@sentry/node/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], + + "@sentry/utils/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], + "@surma/rollup-plugin-off-main-thread/magic-string": ["magic-string@0.25.9", "", { "dependencies": { "sourcemap-codec": "^1.4.8" } }, "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ=="], "@trapezedev/project/@ionic/utils-subprocess": ["@ionic/utils-subprocess@2.1.14", "", { "dependencies": { "@ionic/utils-array": "2.1.6", "@ionic/utils-fs": "3.1.7", "@ionic/utils-process": "2.1.11", "@ionic/utils-stream": "3.1.6", "@ionic/utils-terminal": "2.3.4", "cross-spawn": "^7.0.3", "debug": "^4.0.0", "tslib": "^2.0.1" } }, "sha512-nGYvyGVjU0kjPUcSRFr4ROTraT3w/7r502f5QJEsMRKTqa4eEzCshtwRk+/mpASm0kgBN5rrjYA5A/OZg8ahqg=="], @@ -2026,6 +2387,8 @@ "@trapezedev/project/prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], + "@trapezedev/project/tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="], + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], "@typescript-eslint/parser/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], @@ -2042,14 +2405,28 @@ "@vitest/browser/ws": ["ws@8.20.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA=="], + "accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], + + "ast-types/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "babel-plugin-polyfill-corejs2/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + "cacheable/keyv": ["keyv@5.6.0", "", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw=="], "chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "chrome-launcher/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "chrome-launcher/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], + "cliui/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "compression/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + + "configstore/write-file-atomic": ["write-file-atomic@3.0.3", "", { "dependencies": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", "signal-exit": "^3.0.2", "typedarray-to-buffer": "^3.1.5" } }, "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q=="], + "conventional-changelog-writer/meow": ["meow@8.1.2", "", { "dependencies": { "@types/minimist": "^1.2.0", "camelcase-keys": "^6.2.2", "decamelize-keys": "^1.1.0", "hard-rejection": "^2.1.0", "minimist-options": "4.1.0", "normalize-package-data": "^3.0.0", "read-pkg-up": "^7.0.1", "redent": "^3.0.0", "trim-newlines": "^3.0.0", "type-fest": "^0.18.0", "yargs-parser": "^20.2.3" } }, "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q=="], "conventional-changelog-writer/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], @@ -2060,6 +2437,8 @@ "cookie-session/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + "cosmiconfig/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + "csso/css-tree": ["css-tree@2.2.1", "", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="], "decamelize-keys/map-obj": ["map-obj@1.0.1", "", {}, "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg=="], @@ -2080,10 +2459,20 @@ "eslint/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "express/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + + "external-editor/tmp": ["tmp@0.0.33", "", { "dependencies": { "os-tmpdir": "~1.0.2" } }, "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw=="], + + "extract-zip/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "figures/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + "filelist/minimatch": ["minimatch@5.1.9", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw=="], + "finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + "foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], "fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], @@ -2092,6 +2481,8 @@ "get-pkg-repo/yargs": ["yargs@16.2.0", "", { "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } }, "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="], + "get-uri/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "git-raw-commits/meow": ["meow@8.1.2", "", { "dependencies": { "@types/minimist": "^1.2.0", "camelcase-keys": "^6.2.2", "decamelize-keys": "^1.1.0", "hard-rejection": "^2.1.0", "minimist-options": "4.1.0", "normalize-package-data": "^3.0.0", "read-pkg-up": "^7.0.1", "redent": "^3.0.0", "trim-newlines": "^3.0.0", "type-fest": "^0.18.0", "yargs-parser": "^20.2.3" } }, "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q=="], "git-raw-commits/split2": ["split2@3.2.2", "", { "dependencies": { "readable-stream": "^3.0.0" } }, "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg=="], @@ -2114,16 +2505,46 @@ "htmlhint/commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="], + "http-proxy-agent/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "https-proxy-agent/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + "import-from-esm/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "inquirer/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + + "inquirer/string-width": ["string-width@2.1.1", "", { "dependencies": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" } }, "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw=="], + + "inquirer/strip-ansi": ["strip-ansi@5.2.0", "", { "dependencies": { "ansi-regex": "^4.1.0" } }, "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA=="], + + "intl-messageformat/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "lighthouse/chrome-launcher": ["chrome-launcher@1.2.1", "", { "dependencies": { "@types/node": "*", "escape-string-regexp": "^4.0.0", "is-wsl": "^2.2.0", "lighthouse-logger": "^2.0.1" }, "bin": { "print-chrome-path": "bin/print-chrome-path.cjs" } }, "sha512-qmFR5PLMzHyuNJHwOloHPAHhbaNglkfeV/xDtt5b7xiFFyU1I+AZZX0PYseMuhenJSSirgxELYIbswcoc+5H4A=="], + + "lighthouse/lighthouse-logger": ["lighthouse-logger@2.0.2", "", { "dependencies": { "debug": "^4.4.1", "marky": "^1.2.2" } }, "sha512-vWl2+u5jgOQuZR55Z1WM0XDdrJT6mzMP8zHUct7xTlWhuQs+eV0g+QL0RQdFjT54zVmbhLCP8vIVpy1wGn/gCg=="], + + "lighthouse/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], + + "lighthouse/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "lighthouse/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + + "lighthouse-logger/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + "load-json-file/parse-json": ["parse-json@4.0.0", "", { "dependencies": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" } }, "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw=="], "load-json-file/pify": ["pify@3.0.0", "", {}, "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="], + "make-dir/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "mergexml/xpath": ["xpath@0.0.27", "", {}, "sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ=="], "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + "mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "miniflare/sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], "native-run/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], @@ -2416,6 +2837,10 @@ "npm/yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], + "pac-proxy-agent/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "path-scurry/lru-cache": ["lru-cache@11.2.7", "", {}, "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA=="], + "path-type/pify": ["pify@3.0.0", "", {}, "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="], "plist/@xmldom/xmldom": ["@xmldom/xmldom@0.8.12", "", {}, "sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg=="], @@ -2424,6 +2849,16 @@ "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], + "proxy-agent/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "puppeteer-core/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "puppeteer-core/ws": ["ws@8.20.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA=="], + + "purgecss/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + + "purgecss/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], + "qified/hookified": ["hookified@2.1.1", "", {}, "sha512-AHb76R16GB5EsPBE2J7Ko5kiEyXwviB9P5SMrAKcuAu4vJPZttViAbj9+tZeaQE5zjDme+1vcHP78Yj/WoAveA=="], "rc/ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], @@ -2442,12 +2877,22 @@ "rollup/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "rxjs/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], + + "send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + + "send/mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], + + "send/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "simple-plist/bplist-parser": ["bplist-parser@0.3.1", "", { "dependencies": { "big-integer": "1.6.x" } }, "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA=="], "simple-swizzle/is-arrayish": ["is-arrayish@0.3.4", "", {}, "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA=="], "slice-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "socks-proxy-agent/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "string-width/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], "stylelint/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], @@ -2456,6 +2901,8 @@ "stylelint/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + "sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], + "supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], "supports-hyperlinks/supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], @@ -2468,10 +2915,20 @@ "terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + "tmp/rimraf": ["rimraf@2.7.1", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w=="], + "ts-node/diff": ["diff@4.0.4", "", {}, "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ=="], + "tsup/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "tsup/tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], + "vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "vite-bundle-visualizer/tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="], + + "vite-plugin-purgecss/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="], + "vite-plugin-pwa/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], "workbox-build/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], @@ -2498,6 +2955,10 @@ "yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + + "youch/cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], + "@apideck/better-ajv-errors/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], "@babel/core/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], @@ -2528,6 +2989,8 @@ "@eslint/eslintrc/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "@eslint/eslintrc/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "@ionic/cli-framework-output/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "@ionic/utils-array/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], @@ -2544,6 +3007,22 @@ "@ionic/utils-terminal/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "@lhci/cli/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "@lhci/cli/yargs/cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="], + + "@lhci/cli/yargs/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], + + "@lhci/cli/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "@lhci/cli/yargs/y18n": ["y18n@4.0.3", "", {}, "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="], + + "@lhci/cli/yargs/yargs-parser": ["yargs-parser@18.1.3", "", { "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" } }, "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ=="], + + "@lhci/utils/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "@puppeteer/browsers/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "@rollup/plugin-babel/rollup/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "@rollup/plugin-node-resolve/@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], @@ -2552,6 +3031,10 @@ "@rollup/pluginutils/rollup/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "@sentry/node/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], + + "@sentry/node/https-proxy-agent/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "@trapezedev/project/@ionic/utils-subprocess/@ionic/utils-process": ["@ionic/utils-process@2.1.11", "", { "dependencies": { "@ionic/utils-object": "2.1.6", "@ionic/utils-terminal": "2.3.4", "debug": "^4.0.0", "signal-exit": "^3.0.3", "tree-kill": "^1.2.2", "tslib": "^2.0.1" } }, "sha512-Uavxn+x8j3rDlZEk1X7YnaN6wCgbCwYQOeIjv/m94i1dzslqWhqIHEqxEyeE8HsT5Negboagg7GtQiABy+BLbA=="], "@trapezedev/project/@ionic/utils-subprocess/@ionic/utils-stream": ["@ionic/utils-stream@3.1.6", "", { "dependencies": { "debug": "^4.0.0", "tslib": "^2.0.1" } }, "sha512-4+Kitey1lTA1yGtnigeYNhV/0tggI3lWBMjC7tBs1K9GXa/q7q4CtOISppdh8QgtOhrhAXS2Igp8rbko/Cj+lA=="], @@ -2570,6 +3053,12 @@ "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.5", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="], + "body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + + "chrome-launcher/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + + "compression/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + "conventional-changelog-writer/meow/read-pkg-up": ["read-pkg-up@7.0.1", "", { "dependencies": { "find-up": "^4.1.0", "read-pkg": "^5.2.0", "type-fest": "^0.8.1" } }, "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg=="], "conventional-changelog-writer/meow/type-fest": ["type-fest@0.18.1", "", {}, "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw=="], @@ -2584,14 +3073,22 @@ "cookie-session/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "cosmiconfig/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], "del/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], "eslint/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + + "extract-zip/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "filelist/minimatch/brace-expansion": ["brace-expansion@2.0.3", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA=="], + "finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + "fs-minipass/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], "get-pkg-repo/through2/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], @@ -2602,6 +3099,8 @@ "get-pkg-repo/yargs/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], + "get-uri/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "git-raw-commits/meow/read-pkg-up": ["read-pkg-up@7.0.1", "", { "dependencies": { "find-up": "^4.1.0", "read-pkg": "^5.2.0", "type-fest": "^0.8.1" } }, "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg=="], "git-raw-commits/meow/type-fest": ["type-fest@0.18.1", "", {}, "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw=="], @@ -2618,8 +3117,28 @@ "hosted-git-info/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], + "http-proxy-agent/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "https-proxy-agent/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "import-from-esm/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "inquirer/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "inquirer/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "inquirer/chalk/supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="], + + "inquirer/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@2.0.0", "", {}, "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w=="], + + "inquirer/string-width/strip-ansi": ["strip-ansi@4.0.0", "", { "dependencies": { "ansi-regex": "^3.0.0" } }, "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow=="], + + "inquirer/strip-ansi/ansi-regex": ["ansi-regex@4.1.1", "", {}, "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="], + + "lighthouse-logger/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + + "lighthouse/lighthouse-logger/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "native-run/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "node-html-parser/css-select/domhandler": ["domhandler@4.3.1", "", { "dependencies": { "domelementtype": "^2.2.0" } }, "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ=="], @@ -2630,10 +3149,16 @@ "npm/minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + "pac-proxy-agent/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "prebuild-install/tar-fs/chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], "prebuild-install/tar-fs/tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], + "proxy-agent/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "puppeteer-core/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "read-pkg-up/find-up/locate-path": ["locate-path@2.0.0", "", { "dependencies": { "p-locate": "^2.0.0", "path-exists": "^3.0.0" } }, "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA=="], "read-pkg/normalize-package-data/hosted-git-info": ["hosted-git-info@2.8.9", "", {}, "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="], @@ -2656,6 +3181,10 @@ "replace/yargs/yargs-parser": ["yargs-parser@18.1.3", "", { "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" } }, "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ=="], + "send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + + "socks-proxy-agent/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], "stylelint/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], @@ -2664,6 +3193,14 @@ "table/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "tmp/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + + "tsup/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "vite-plugin-purgecss/vite/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + + "vite-plugin-purgecss/vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "vite-plugin-pwa/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "workbox-build/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], @@ -2690,10 +3227,18 @@ "@capacitor/assets/@capacitor/cli/tar/minizlib": ["minizlib@2.1.2", "", { "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" } }, "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg=="], + "@capacitor/assets/@capacitor/cli/tar/mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], + "@capacitor/assets/@capacitor/cli/tar/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], "@capacitor/assets/@capacitor/cli/xml2js/xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="], + "@lhci/cli/yargs/cliui/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="], + + "@lhci/cli/yargs/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], + + "@sentry/node/https-proxy-agent/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "@trapezedev/project/@ionic/utils-subprocess/@ionic/utils-terminal/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "@trapezedev/project/@ionic/utils-subprocess/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], @@ -2732,6 +3277,14 @@ "glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + "inquirer/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "inquirer/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], + + "inquirer/string-width/strip-ansi/ansi-regex": ["ansi-regex@3.0.1", "", {}, "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw=="], + + "lighthouse/lighthouse-logger/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "node-html-parser/css-select/domutils/dom-serializer": ["dom-serializer@1.4.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", "entities": "^2.0.0" } }, "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag=="], "npm/minipass-flush/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], @@ -2750,6 +3303,58 @@ "replace/yargs/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], + "vite-plugin-purgecss/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + + "vite-plugin-purgecss/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + "workbox-build/glob/minimatch/brace-expansion": ["brace-expansion@5.0.5", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="], "@capacitor/assets/@capacitor/cli/@ionic/utils-subprocess/@ionic/utils-terminal/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -2762,6 +3367,10 @@ "@capacitor/assets/@capacitor/cli/tar/minizlib/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + "@lhci/cli/yargs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "@lhci/cli/yargs/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], + "conventional-changelog-writer/meow/read-pkg-up/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], "conventional-changelog-writer/meow/read-pkg-up/read-pkg/normalize-package-data": ["normalize-package-data@2.5.0", "", { "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" } }, "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA=="], @@ -2786,6 +3395,8 @@ "git-semver-tags/meow/read-pkg-up/read-pkg/type-fest": ["type-fest@0.6.0", "", {}, "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg=="], + "inquirer/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + "node-html-parser/css-select/domutils/dom-serializer/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="], "read-pkg-up/find-up/locate-path/p-locate/p-limit": ["p-limit@1.3.0", "", { "dependencies": { "p-try": "^1.0.0" } }, "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q=="], @@ -2804,6 +3415,8 @@ "@capacitor/assets/@capacitor/cli/rimraf/glob/path-scurry/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], + "@lhci/cli/yargs/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], + "conventional-changelog-writer/meow/read-pkg-up/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], "conventional-changelog-writer/meow/read-pkg-up/read-pkg/normalize-package-data/hosted-git-info": ["hosted-git-info@2.8.9", "", {}, "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="], diff --git a/editors-picks-input.txt b/editors-picks-input.txt index b365994..6418dc8 100644 --- a/editors-picks-input.txt +++ b/editors-picks-input.txt @@ -21,4 +21,7 @@ album:250986538 album:509761344 album:15621057 album:103897783 -album:151728406 \ No newline at end of file +album:151728406 +album:199412873 +album:3280432 +album:37927851 \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js index 33502b8..f3a30e1 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -14,9 +14,9 @@ export default defineConfig( tseslint.configs.recommendedTypeChecked, { languageOptions: { - parser: tsParser, // 👈 REQUIRED + parser: tsParser, parserOptions: { - project: './tsconfig-eslint.json', // 👈 REQUIRED + project: './tsconfig-eslint.json', }, ecmaVersion: 2022, sourceType: 'module', diff --git a/fix-gen.py b/fix-gen.py new file mode 100644 index 0000000..938291a --- /dev/null +++ b/fix-gen.py @@ -0,0 +1,21 @@ +import re +with open("gen-editors-picks.py", "r") as f: content = f.read() + +content = re.sub(r"IMAGES_DIR = \"public/editors-picks-images\"\n+", "", content) + +# Remove clear_images_dir definition +content = re.sub(r"def clear_images_dir\(\):[\s\S]*?os\.makedirs[^\n]*\n+", "", content) +content = re.sub(r"clear_images_dir\(\)\n+", "", content) + +# Remove the import line for subprocess, shutil if present +content = re.sub(r"import subprocess\n", "", content) +content = re.sub(r"import shutil\n", "", content) + +# Replace download_and_process_cover +new_func = """def download_and_process_cover(cover_uuid): + url = f"https://resources.tidal.com/images/{uuid_to_path_segments(cover_uuid)}/320x320.jpg" + return f"https://wsrv.nl/?url={url}&w=250&h=250&output=webp" +""" +content = re.sub(r"def download_and_process_cover\(cover_uuid\):[\s\S]*?(?=def process_cover)", new_func + "\n\n", content) + +with open("gen-editors-picks.py", "w") as f: f.write(content) diff --git a/gen-editors-picks.py b/gen-editors-picks.py index e6404e0..cfb6b2e 100644 --- a/gen-editors-picks.py +++ b/gen-editors-picks.py @@ -8,36 +8,59 @@ import sys import hashlib import time import os -import subprocess -import shutil import tempfile +import base64 INPUT_FILE = "editors-picks-input.txt" -IMAGES_DIR = "public/editors-picks-images" COUNTRY = "US" -# Tidal internal token replace when expired -TIDAL_TOKEN = "eyJraWQiOiJ2OU1GbFhqWSIsImFsZyI6IkVTMjU2In0.eyJ0eXBlIjoibzJfYWNjZXNzIiwic2NvcGUiOiIiLCJnVmVyIjowLCJzVmVyIjowLCJjaWQiOjEzNTU3LCJhdCI6IklOVEVSTkFMIiwiZXhwIjoxNzc1MzY0MTQwLCJpc3MiOiJodHRwczovL2F1dGgudGlkYWwuY29tL3YxIn0.6ui6itHVQ-OXPF0F9mbf5KcKz1fKYJNsa1vBAj60upXpcN-DQG8JPKBlqJN6RuBEH8yhwYj2wh4YJ-TOOuO8DA" +TIDAL_CLIENT_ID = "txNoH4kkV41MfH25" +TIDAL_CLIENT_SECRET = "dQjy0MinCEvxi1O4UmxvxWnDjt4cgHBPw8ll6nYBk98=" -TIDAL_HEADERS = { - "accept": "*/*", - "authorization": f"Bearer {TIDAL_TOKEN}", -} - -# PodcastIndex credentials -PODCAST_API_KEY = "YU5HMSDYBQQVYDF6QN4P" -PODCAST_API_SECRET = "8hCvpjSL7T$S7^5ftnf5MhqQwYUYVjM^fmUL3Ld$" -PODCASTINDEX_BASE = "https://api.podcastindex.org/api/1.0" +_tidal_token = None -# ── Tidal helpers ───────────────────────────────────────────────────────────── +def get_tidal_token(): + global _tidal_token + if _tidal_token: + return _tidal_token + + credentials = base64.b64encode(f"{TIDAL_CLIENT_ID}:{TIDAL_CLIENT_SECRET}".encode()).decode() + params = urllib.parse.urlencode({ + "client_id": TIDAL_CLIENT_ID, + "client_secret": TIDAL_CLIENT_SECRET, + "grant_type": "client_credentials", + }) + req = urllib.request.Request( + "https://auth.tidal.com/v1/oauth2/token", + data=params.encode(), + headers={ + "Content-Type": "application/x-www-form-urlencoded", + "Authorization": f"Basic {credentials}", + }, + method="POST" + ) + try: + with urllib.request.urlopen(req) as resp: + data = json.loads(resp.read().decode()) + _tidal_token = data["access_token"] + return _tidal_token + except Exception as e: + print(f"Error getting Tidal token: {e}", file=sys.stderr) + return None + def tidal_get(path, params=None): if params is None: params = {} params.setdefault("countryCode", COUNTRY) + + token = get_tidal_token() + if not token: + return None + url = f"https://api.tidal.com/v1/{path}?{urllib.parse.urlencode(params)}" - req = urllib.request.Request(url, headers=TIDAL_HEADERS) + req = urllib.request.Request(url, headers={"Authorization": f"Bearer {token}"}) try: with urllib.request.urlopen(req) as resp: return json.loads(resp.read().decode()) @@ -90,59 +113,6 @@ def fetch_podcast(feed_id): # ── Image processing ─────────────────────────────────────────────────────────── -def clear_images_dir(): - if os.path.exists(IMAGES_DIR): - shutil.rmtree(IMAGES_DIR) - os.makedirs(IMAGES_DIR, exist_ok=True) - - -def is_uuid_cover(cover_value): - if not cover_value: - return False - return bool(re.match(r'^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$', cover_value)) - - -def uuid_to_path_segments(uuid): - return uuid.replace('-', '/') - - -def download_and_process_cover(cover_uuid): - url = f"https://resources.tidal.com/images/{uuid_to_path_segments(cover_uuid)}/320x320.jpg" - - with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as tmp: - tmp_path = tmp.name - - try: - req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) - with urllib.request.urlopen(req) as resp: - with open(tmp_path, 'wb') as f: - shutil.copyfileobj(resp, f) - - output_path = os.path.join(IMAGES_DIR, f"{cover_uuid}.webp") - - subprocess.run( - ['cwebp', '-q', '50', tmp_path, '-o', output_path], - check=True, - capture_output=True - ) - - return f"https://monochrome.tf/editors-picks-images/{cover_uuid}.webp" - except Exception as e: - print(f"Error processing cover {cover_uuid}: {e}", file=sys.stderr) - return None - finally: - if os.path.exists(tmp_path): - os.remove(tmp_path) - - -def process_cover(cover_value): - if not cover_value: - return cover_value - if is_uuid_cover(cover_value): - return download_and_process_cover(cover_value) - return cover_value - - # ── Transformers ────────────────────────────────────────────────────────────── def transform_album(d): @@ -155,7 +125,7 @@ def transform_album(d): "name": d.get("artist", {}).get("name"), }, "releaseDate": d.get("releaseDate"), - "cover": process_cover(d.get("cover")), + "cover": d.get("cover"), "explicit": d.get("explicit"), "audioQuality": d.get("audioQuality"), "mediaMetadata": d.get("mediaMetadata"), @@ -167,7 +137,7 @@ def transform_artist(d): "type": "artist", "id": d.get("id"), "name": d.get("name"), - "picture": process_cover(d.get("picture")), + "picture": d.get("picture"), } @@ -184,7 +154,7 @@ def transform_track(d): "album": { "id": album.get("id"), "title": album.get("title"), - "cover": process_cover(album.get("cover")), + "cover": album.get("cover"), }, "duration": d.get("duration"), "explicit": d.get("explicit"), @@ -200,7 +170,7 @@ def transform_playlist(d): "type": "playlist", "id": d.get("uuid"), "title": d.get("title"), - "cover": process_cover(cover), + "cover": cover, "numberOfTracks": d.get("numberOfTracks", 0), } @@ -213,7 +183,7 @@ def transform_userplaylist(d): "type": "user-playlist", "id": d.get("uuid"), "name": d.get("title"), - "cover": process_cover(cover), + "cover": cover, "numberOfTracks": d.get("numberOfTracks", 0), "username": creator.get("name"), } @@ -258,8 +228,6 @@ def read_items(path): # ── Main ────────────────────────────────────────────────────────────────────── -clear_images_dir() - FETCHERS = { "album": (fetch_album, transform_album), "artist": (fetch_artist, transform_artist), diff --git a/index.html b/index.html index eb274e0..4125d3c 100644 --- a/index.html +++ b/index.html @@ -3,32 +3,99 @@ - Monochrome Music + Monochrome + - + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + @@ -214,6 +281,7 @@ + + + +
@@ -4249,29 +4385,42 @@
-
+

Database

AutoEq Repo
- -
- -
-
- +
+ +
-
- +
+
+ +
+
+ +
+
+ +
@@ -5379,6 +5528,7 @@

Contributors List:


+
- - diff --git a/ios/App/App/AppDelegate.swift b/ios/App/App/AppDelegate.swift index c3cd83b..88e35b1 100644 --- a/ios/App/App/AppDelegate.swift +++ b/ios/App/App/AppDelegate.swift @@ -1,4 +1,5 @@ import UIKit +import AVFoundation import Capacitor @UIApplicationMain @@ -7,10 +8,82 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - // Override point for customization after application launch. + configureAudioSession() return true } + private func configureAudioSession() { + let session = AVAudioSession.sharedInstance() + do { + // .playback keeps audio alive when the app is backgrounded or the screen locks + try session.setCategory(.playback, mode: .default, options: []) + try session.setActive(true) + } catch { + print("[AudioSession] Failed to configure: \(error.localizedDescription)") + } + + // Handle audio interruptions (phone calls, Siri, alarms, etc.) + NotificationCenter.default.addObserver( + self, + selector: #selector(handleAudioInterruption), + name: AVAudioSession.interruptionNotification, + object: session + ) + + // Handle route changes (headphones unplugged, Bluetooth disconnect, etc.) + NotificationCenter.default.addObserver( + self, + selector: #selector(handleRouteChange), + name: AVAudioSession.routeChangeNotification, + object: session + ) + } + + @objc private func handleAudioInterruption(notification: Notification) { + guard let userInfo = notification.userInfo, + let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt, + let type = AVAudioSession.InterruptionType(rawValue: typeValue) else { + return + } + + switch type { + case .began: + // Interruption began — system pauses audio automatically + break + case .ended: + // Interruption ended — reactivate session so playback can resume + if let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt { + let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue) + if options.contains(.shouldResume) { + do { + try AVAudioSession.sharedInstance().setActive(true) + } catch { + print("[AudioSession] Failed to reactivate after interruption: \(error.localizedDescription)") + } + } + } + @unknown default: + break + } + } + + @objc private func handleRouteChange(notification: Notification) { + guard let userInfo = notification.userInfo, + let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt, + let reason = AVAudioSession.RouteChangeReason(rawValue: reasonValue) else { + return + } + + if reason == .oldDeviceUnavailable { + // Headphones/Bluetooth disconnected — reactivate session to keep background alive + do { + try AVAudioSession.sharedInstance().setActive(true) + } catch { + print("[AudioSession] Failed to reactivate after route change: \(error.localizedDescription)") + } + } + } + func applicationWillResignActive(_ application: UIApplication) { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. diff --git a/ios/App/App/Info.plist b/ios/App/App/Info.plist index ef1bec3..17255e7 100644 --- a/ios/App/App/Info.plist +++ b/ios/App/App/Info.plist @@ -47,5 +47,9 @@ UIViewControllerBasedStatusBarAppearance + UIBackgroundModes + + audio + diff --git a/js/HiFi.ts b/js/HiFi.ts index 0ea4e8d..df33f49 100644 --- a/js/HiFi.ts +++ b/js/HiFi.ts @@ -1,11 +1,5 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ - import { EventEmitter } from 'events'; +import type { PlaybackInfo } from './container-classes'; type Params = Record; @@ -17,7 +11,31 @@ class ResponseError extends Error { } } -export class TidalResponse extends Response { +/** + * A generic response interface that types the return value of `.json()` as `T`. + * + * Extends the standard `Response` interface with a typed `.json()` method so callers + * can receive properly-typed data without manual casting. + * + * @typeParam T - The expected shape of the parsed JSON body. + */ +export interface TypedResponse extends Response { + /** Returns a promise that resolves with the response body parsed as JSON, typed as `T`. */ + json(): Promise; +} + +/** + * A typed extension of the standard `Response` class returned by all TIDAL API methods. + * + * The generic type parameter `T` controls the type returned by `.json()`, enabling + * full type safety on API responses without manual casts. + * + * @typeParam T - The expected JSON body type. Defaults to `unknown`. + */ +export class TidalResponse extends Response implements TypedResponse { + /** Returns a promise that resolves with the response body parsed as JSON, typed as `T`. */ + declare json: () => Promise; + constructor(response: Response); constructor(body: BodyInit, init?: ResponseInit); constructor(body: BodyInit | Response, init?: ResponseInit) { @@ -33,6 +51,1033 @@ export class TidalResponse extends Response { } } +// ─── Route response interfaces ─────────────────────────────────────────────── + +/** + * Base interface shared by all versioned TIDAL API responses. + * Every response envelope includes a `version` string matching {@link HiFiClient.API_VERSION}. + */ +export interface VersionedResponse { + /** The API version string, e.g. `"2.7"`. */ + version: string; +} + +// ─── Shared sub-types (derived from live API samples) ──────────────────────── + +/** + * Minimal artist reference as it appears inside track and album objects. + * For the full artist profile returned by the `/artist` route, see {@link TidalArtistProfile}. + */ +export interface TidalArtistRef { + /** Numeric TIDAL artist ID. */ + id: number; + /** Artist display name. */ + name: string; + /** TIDAL handle, or `null` if not set. */ + handle: string | null; + /** Artist role in this context, e.g. `"MAIN"`. */ + type: string; + /** Picture UUID, or `null` if no image is available. */ + picture: string | null; +} + +/** + * A single artist-role entry as returned inside {@link TidalArtistProfile}. + */ +export interface TidalArtistRole { + /** Internal category identifier (`-1` for the primary artist role). */ + categoryId: number; + /** Human-readable role label, e.g. `"Artist"`, `"Producer"`. */ + category: string; +} + +/** + * Full artist profile as returned by the `/artist/?id=` route. + * Contains fields not present on the minimal {@link TidalArtistRef} seen inside tracks/albums. + */ +export interface TidalArtistProfile { + /** Numeric TIDAL artist ID. */ + id: number; + /** Artist display name. */ + name: string; + /** Roles this artist holds on TIDAL, e.g. `["ARTIST", "CONTRIBUTOR"]`. */ + artistTypes: string[]; + /** Canonical TIDAL artist URL. */ + url: string; + /** Picture UUID, or `null` if no image is available. */ + picture: string | null; + /** Fallback album cover UUID used when no artist picture exists, or `null`. */ + selectedAlbumCoverFallback: string | null; + /** Popularity score (0–100). */ + popularity: number; + /** List of credited roles for this artist. */ + artistRoles: TidalArtistRole[]; + /** Map of mix type → mix ID, e.g. `{ "ARTIST_MIX": "000ff..." }`. */ + mixes: Record; + /** TIDAL handle, or `null` if not set. */ + handle: string | null; + /** Associated TIDAL user ID, or `null`. */ + userId: number | null; + /** Whether the artist is currently spotlighted. */ + spotlighted: boolean; +} + +/** + * Media metadata object attached to tracks and albums. + */ +export interface TidalMediaMetadata { + /** Quality tags, e.g. `["LOSSLESS"]`, `["HIRES_LOSSLESS"]`. */ + tags: string[]; +} + +/** + * Slim album reference embedded inside a track object. + */ +export interface TidalTrackAlbumRef { + /** Numeric TIDAL album ID. */ + id: number; + /** Album title. */ + title: string; + /** Cover image UUID. */ + cover: string; + /** Vibrant accent colour hex string derived from the cover art. */ + vibrantColor: string; + /** Video cover UUID, or `null`. */ + videoCover: string | null; +} + +/** + * Full track object as returned by the `/info` route and embedded in albums, playlists, and mixes. + * + * @remarks + * Fields `bpm`, `key`, and `keyScale` are nullable — they are absent for some tracks. + * `version` is present in the payload but may be `null`. + */ +export interface TidalTrack { + /** Numeric TIDAL track ID. */ + id: number; + /** Track title. */ + title: string; + /** Duration in seconds. */ + duration: number; + /** Track replay-gain value in dB. */ + replayGain: number; + /** Track peak amplitude (0–1). */ + peak: number; + /** Whether the track is available for streaming. */ + allowStreaming: boolean; + /** Whether the stream is ready. */ + streamReady: boolean; + /** Whether the track requires payment to stream. */ + payToStream: boolean; + /** Whether the track is available for ad-supported streaming. */ + adSupportedStreamReady: boolean; + /** Whether the track is available for DJ use. */ + djReady: boolean; + /** Whether stem files are available. */ + stemReady: boolean; + /** ISO-8601 timestamp from which the stream became available. */ + streamStartDate: string; + /** Whether a premium subscription is required. */ + premiumStreamingOnly: boolean; + /** Track number within its volume. */ + trackNumber: number; + /** Disc/volume number. */ + volumeNumber: number; + /** Version suffix (e.g. `"Remastered"`), or `null`. */ + version: string | null; + /** Popularity score (0–100). */ + popularity: number; + /** Copyright notice. */ + copyright: string; + /** Beats per minute, or `null` if unavailable. */ + bpm: number | null; + /** Musical key (e.g. `"Bb"`), or `null` if unavailable. */ + key: string | null; + /** Key scale (`"MAJOR"` / `"MINOR"`), or `null` if unavailable. */ + keyScale: string | null; + /** Canonical TIDAL track URL. */ + url: string; + /** International Standard Recording Code. */ + isrc: string; + /** Whether the track metadata can be edited. */ + editable: boolean; + /** Whether the track contains explicit content. */ + explicit: boolean; + /** Highest available audio quality, e.g. `"LOSSLESS"`, `"HI_RES_LOSSLESS"`. */ + audioQuality: string; + /** Available audio modes, e.g. `["STEREO"]`. */ + audioModes: string[]; + /** Media metadata including quality tags. */ + mediaMetadata: TidalMediaMetadata; + /** Whether this is a user-uploaded track. */ + upload: boolean; + /** Access type, e.g. `"PUBLIC"`. */ + accessType: string; + /** Whether the track is currently spotlighted. */ + spotlighted: boolean; + /** Primary artist. */ + artist: TidalArtistRef; + /** All credited artists. */ + artists: TidalArtistRef[]; + /** Album this track belongs to. */ + album: TidalTrackAlbumRef; + /** Map of mix type → mix ID, e.g. `{ "TRACK_MIX": "001e91..." }`. */ + mixes: Record; +} + +/** + * A track as it appears inside a playlist, extending {@link TidalTrack} with + * playlist-specific fields added by the TIDAL API. + */ +export interface TidalPlaylistTrack extends TidalTrack { + /** Track description text, or `null`. */ + description: string | null; + /** ISO-8601 timestamp when this track was added to the playlist. */ + dateAdded: string; + /** Position index within the playlist. */ + index: number; + /** Unique item UUID within the playlist. */ + itemUuid: string; +} + +/** + * Full album object as returned by the `/artist/?f=` discography route and + * embedded in search results. + * + * @remarks + * `artist` is present in discography responses but absent in some search results; + * it is therefore typed as optional. + * `version` and `videoCover` are present in the payload but may be `null`. + */ +export interface TidalAlbum { + /** Numeric TIDAL album ID. */ + id: number; + /** Album title. */ + title: string; + /** Total duration in seconds. */ + duration: number; + /** Whether the stream is ready. */ + streamReady: boolean; + /** Whether the album requires payment to stream. */ + payToStream: boolean; + /** Whether the album is available for ad-supported streaming. */ + adSupportedStreamReady: boolean; + /** Whether the album is available for DJ use. */ + djReady: boolean; + /** Whether stem files are available. */ + stemReady: boolean; + /** ISO-8601 timestamp from which the stream became available. */ + streamStartDate: string; + /** Whether streaming is allowed. */ + allowStreaming: boolean; + /** Whether a premium subscription is required. */ + premiumStreamingOnly: boolean; + /** Number of tracks on the album. */ + numberOfTracks: number; + /** Number of videos on the album. */ + numberOfVideos: number; + /** Number of discs/volumes. */ + numberOfVolumes: number; + /** Release date string, e.g. `"2025-02-14"`. */ + releaseDate: string; + /** Copyright notice. */ + copyright: string; + /** Release type, e.g. `"ALBUM"`, `"EP"`, `"SINGLE"`. */ + type: string; + /** Version suffix, or `null`. */ + version: string | null; + /** Canonical TIDAL album URL. */ + url: string; + /** Cover image UUID. */ + cover: string; + /** Vibrant accent colour hex string. */ + vibrantColor: string; + /** Video cover UUID, or `null`. */ + videoCover: string | null; + /** Whether the album contains explicit content. */ + explicit: boolean; + /** UPC barcode. */ + upc: string; + /** Popularity score (0–100). */ + popularity: number; + /** Highest available audio quality. */ + audioQuality: string; + /** Available audio modes. */ + audioModes: string[]; + /** Media metadata including quality tags. */ + mediaMetadata: TidalMediaMetadata; + /** Whether this is a user-uploaded album. */ + upload: boolean; + /** Primary artist (present in discography responses; absent in some search results). */ + artist?: TidalArtistRef; + /** All credited artists. */ + artists: TidalArtistRef[]; +} + +/** + * A video item as returned inside search results and the topvideos page modules. + */ +export interface TidalVideoItem { + /** Numeric TIDAL video ID. */ + id: number; + /** Video title. */ + title: string; + /** Duration in seconds. */ + duration: number; + /** Version suffix, or `null`. */ + version: string | null; + /** Canonical TIDAL video URL. */ + url: string; + /** All credited artists. */ + artists: TidalArtistRef[]; + /** Associated album, or `null`. */ + album: TidalTrackAlbumRef | null; + /** Whether the video contains explicit content. */ + explicit: boolean; + /** Disc/volume number. */ + volumeNumber: number; + /** Track number on the disc. */ + trackNumber: number; + /** Popularity score (0–100). */ + popularity: number; + /** Double-precision popularity score (present in topvideos). */ + doublePopularity?: number; + /** Whether streaming is allowed. */ + allowStreaming: boolean; + /** Whether the stream is ready. */ + streamReady: boolean; + /** ISO-8601 timestamp from which streaming became available. */ + streamStartDate: string; + /** Whether the video is available for ad-supported streaming. */ + adSupportedStreamReady: boolean; + /** Whether the video is available for DJ use. */ + djReady: boolean; + /** Whether stem files are available. */ + stemReady: boolean; + /** Thumbnail image UUID. */ + imageId: string; + /** Image path (present in some search results), or `null`. */ + imagePath?: string | null; + /** Vibrant accent colour hex string. */ + vibrantColor: string; + /** Release date string. */ + releaseDate: string; + /** Content type, e.g. `"Music Video"`. */ + type: string; + /** Ad tag URL, or `null`. */ + adsUrl: string | null; + /** Whether ads are pre-paywall only. */ + adsPrePaywallOnly: boolean; + /** Playback quality label (present in some search results), e.g. `"MP4_1080P"`. */ + quality?: string; +} + +/** + * A page module object as returned inside {@link TopVideosResponse.videos}. + * + * @remarks + * The `/topvideos` route processes a TIDAL pages API response. When the page + * contains `VIDEO_LIST`-type modules (which do not match the `VIDEO_PLAYLIST` / + * `VIDEO_ROW` / `PAGED_LIST` extraction path) the entire module object is pushed + * into the output array. The actual video items are nested inside `pagedList.items`. + */ +export interface TidalVideoPageModule { + /** Base-64 encoded module identifier. */ + id: string; + /** Module type, e.g. `"VIDEO_LIST"`. */ + type: string; + /** Column width percentage. */ + width: number; + /** Scroll direction, e.g. `"VERTICAL"`. */ + scroll: string; + /** Module title. */ + title: string; + /** Module description. */ + description: string; + /** "Show more" link data, or `null`. */ + showMore: string | null; + /** Paged list of video items. */ + pagedList: { + /** Internal API path for paged data. */ + dataApiPath: string; + /** Page size. */ + limit: number; + /** Current offset. */ + offset: number; + /** Total number of items available. */ + totalNumberOfItems: number; + /** Video items on this page. */ + items: TidalVideoItem[]; + }; + /** Whether the module supports paging. */ + supportsPaging: boolean; + /** Whether to show table headers. */ + showTableHeaders: boolean; + /** List display format. */ + listFormat: string; + /** Layout hint, or `null`. */ + layout: string | null; + /** Whether quick-play is enabled. */ + quickPlay: boolean; + /** Pre-title label, or `null`. */ + preTitle: string | null; +} + +/** + * A similar album entry as returned by the TIDAL OpenAPI `/album/similar` endpoint. + * + * @remarks + * This shape differs substantially from a standard {@link TidalAlbum}: `duration` is an + * ISO 8601 duration string (e.g. `"PT1H14M30S"`), `copyright` is an object, and several + * fields (`barcodeId`, `mediaTags`, `availability`, `albumType`) have no equivalent in the + * standard album object. + */ +export interface TidalSimilarAlbum { + /** Numeric TIDAL album ID. */ + id: number; + /** Album title. */ + title: string; + /** UPC/EAN barcode identifier. */ + barcodeId: string; + /** Number of discs/volumes. */ + numberOfVolumes: number; + /** Total number of tracks (called `numberOfItems` in this endpoint). */ + numberOfItems: number; + /** ISO 8601 duration string, e.g. `"PT1H14M30S"`. */ + duration: string; + /** Whether the album contains explicit content. */ + explicit: boolean; + /** Release date string, e.g. `"2015-10-09"`. */ + releaseDate: string; + /** Copyright information. */ + copyright: { text: string }; + /** Popularity score (0–1 float). */ + popularity: number; + /** Access type, e.g. `"PUBLIC"`. */ + accessType: string; + /** Availability modes, e.g. `["STREAM", "DJ"]`. */ + availability: string[]; + /** Quality tags, e.g. `["LOSSLESS", "HIRES_LOSSLESS"]`. */ + mediaTags: string[]; + /** External link entries (e.g. TIDAL sharing URL). */ + externalLinks: Array<{ href: string; meta: { type: string } }>; + /** Release type, e.g. `"ALBUM"`. */ + type: string; + /** Album type classification, e.g. `"ALBUM"`. */ + albumType: string; + /** ISO-8601 creation timestamp (present for some albums). */ + createdAt?: string; + /** Cover image UUID. */ + cover: string; + /** Abbreviated artist list for this album. */ + artists: Array<{ id: number; name: string }>; + /** Canonical TIDAL album URL. */ + url: string; +} + +// ─── Response interfaces ────────────────────────────────────────────────────── + +/** + * Response returned by the root `/` route. + * Contains a link to the upstream HiFi API repository. + */ +export interface RootResponse extends VersionedResponse { + /** URL of the upstream HiFi API repository. */ + Repo: string; +} + +/** + * Response returned by the `/info` route. + * Contains full TIDAL track metadata. + */ +export interface InfoResponse extends VersionedResponse { + /** Full metadata for the requested track. */ + data: TidalTrack; +} + +/** + * Response returned by the `/track` route. + * Contains playback/stream information for a track. + * + * @remarks `data` is typed as {@link PlaybackInfo} from `container-classes`, whose + * fields match the live API sample exactly. + */ +export interface TrackResponse extends VersionedResponse { + /** Playback info including manifest, quality, and replay-gain data. */ + data: PlaybackInfo; +} + +/** + * Response returned by the `/recommendations` route. + * Contains a paginated list of recommended tracks for a given track ID. + * + * @remarks No live API sample is available for this route. + */ +export interface RecommendationsResponse extends VersionedResponse { + /** Raw TIDAL v1 recommendations payload. */ + data: unknown; +} + +/** + * A similar-artist entry as returned by the TIDAL OpenAPI `/artist/similar` endpoint. + */ +export interface SimilarArtist { + /** Numeric TIDAL artist ID. */ + id: number; + /** Artist display name. */ + name: string; + /** Picture UUID, or `null` if no image is available. */ + picture: string | null; + /** Canonical TIDAL artist URL. */ + url: string; + /** Relation type, e.g. `"SIMILAR_ARTIST"`. */ + relationType: string; + /** Popularity score (0–1 float). */ + popularity: number; + /** External link entries (e.g. TIDAL sharing URL). */ + externalLinks: Array<{ href: string; meta: { type: string } }>; + /** Whether the artist is spotlighted. */ + spotlighted: boolean; + /** Whether artist contributions are enabled. */ + contributionsEnabled: boolean; +} + +/** + * Response returned by the `/artist/similar` route. + * Contains a list of artists similar to the requested artist. + */ +export interface SimilarArtistsResponse extends VersionedResponse { + /** List of similar artists. */ + artists: SimilarArtist[]; +} + +/** + * Response returned by the `/album/similar` route. + * Contains a list of albums similar to the requested album. + */ +export interface SimilarAlbumsResponse extends VersionedResponse { + /** List of similar albums. */ + albums: TidalSimilarAlbum[]; +} + +/** + * Artist cover image URL at 750 px resolution. + * Returned inside {@link ArtistByIdResponse}. + */ +export interface ArtistCover { + /** The TIDAL artist ID. */ + id: number; + /** The artist display name. */ + name: string; + /** 750×750 JPEG cover URL. */ + '750': string; +} + +/** + * Response returned by the `/artist` route when an `id` query parameter is supplied. + * Contains the artist's full profile and optional cover image URL. + */ +export interface ArtistByIdResponse extends VersionedResponse { + /** Full TIDAL artist profile data. */ + artist: TidalArtistProfile; + /** Cover image URL at 750 px, or `null` if no picture is available. */ + cover: ArtistCover | null; +} + +/** + * Response returned by the `/artist` route when an `f` query parameter is supplied. + * Contains the artist's discography and, when `skip_tracks` is false, their top tracks. + */ +export interface ArtistDiscographyResponse extends VersionedResponse { + /** Paginated album list for the artist. */ + albums: { items: TidalAlbum[] }; + /** + * Top tracks for the artist across all albums. + * Absent when the request includes `skip_tracks=true`. + */ + tracks?: TidalTrack[]; +} + +/** + * Union of the two possible response shapes from the `/artist` route. + * Use {@link ArtistByIdResponse} (has `artist`) when querying by `id`, + * or {@link ArtistDiscographyResponse} (has `albums`) when querying by `f`. + */ +export type ArtistResponse = ArtistByIdResponse | ArtistDiscographyResponse; + +/** + * Artist biography as returned by the TIDAL API. + */ +export interface ArtistBiography { + /** Provider or publication source of the biography text, e.g. `"TiVo"`. */ + source: string; + /** ISO-8601 timestamp of the last biography update. */ + lastUpdated: string; + /** Full biography text. */ + text: string; + /** Short biography summary (may be an empty string). */ + summary: string; +} + +/** + * Response returned by the `/artist/bio` route. + * Contains the biography text for the requested artist. + */ +export interface ArtistBioResponse extends VersionedResponse { + /** Biography data for the requested artist. */ + data: ArtistBiography; +} + +/** + * A single cover-image entry with pre-built URLs at multiple resolutions. + * Returned inside {@link CoverResponse}. + */ +export interface CoverEntry { + /** The TIDAL track or album ID associated with this cover. */ + id: number; + /** The track or album title associated with this cover. */ + name: string; + /** 1280×1280 JPEG cover URL. */ + '1280': string; + /** 640×640 JPEG cover URL. */ + '640': string; + /** 80×80 JPEG cover URL. */ + '80': string; +} + +/** + * Response returned by the `/cover` route. + * Contains one or more cover-image entries matching the query. + */ +export interface CoverResponse extends VersionedResponse { + /** Resolved cover image entries. */ + covers: CoverEntry[]; +} + +/** + * A paginated result bucket as returned inside {@link SearchResponse.data}. + */ +export interface TidalSearchBucket { + /** Maximum number of items per page. */ + limit: number; + /** Current page offset. */ + offset: number; + /** Total number of matching items. */ + totalNumberOfItems: number; + /** Items on this page. */ + items: T[]; +} + +/** + * Response returned by the `/search` route. + * + * @remarks + * Two distinct query formats exist: + * - `?q=` (general search): returns `topHit` and combined buckets for artists, albums, tracks, videos, playlists. + * - `?v=` (video-focused search): returns `topHits` (plural), `genres`, and the same buckets. + */ +export interface SearchResponse extends VersionedResponse { + /** Combined search result buckets. */ + data: { + /** Matching artist results. */ + artists?: TidalSearchBucket; + /** Matching album results. */ + albums?: TidalSearchBucket; + /** Matching track results. */ + tracks?: TidalSearchBucket; + /** Matching video results. */ + videos?: TidalSearchBucket; + /** Matching playlist results. */ + playlists?: TidalSearchBucket; + /** Genre results (present for `?v=` video searches). */ + genres?: TidalSearchBucket; + /** Single top-hit result (present for `?q=` general searches). */ + topHit?: { value: TidalTrack | TidalArtistProfile | TidalAlbum | TidalVideoItem; type: string }; + /** Multiple top-hit results (present for `?v=` video searches). */ + topHits?: Array; + }; +} + +/** + * An album with its full track listing, as returned by the `/album` route. + * + * @remarks + * Each element of `items` wraps the track in an envelope `{ item, type }`. + */ +export interface TidalAlbumWithTracks extends TidalAlbum { + /** Ordered list of track envelopes. */ + items: Array<{ item: TidalTrack; type: string }>; +} + +/** + * Response returned by the `/album` route. + * Contains album metadata together with its full track listing. + */ +export interface AlbumResponse extends VersionedResponse { + /** Album data including all tracks as `items`. */ + data: TidalAlbumWithTracks; +} + +/** + * A promoted artist entry as it appears inside {@link TidalPlaylist}. + */ +export interface TidalPromotedArtist { + /** Numeric TIDAL artist ID. */ + id: number; + /** Artist display name. */ + name: string; + /** TIDAL handle, or `null`. */ + handle: string | null; + /** Artist role, e.g. `"MAIN"`. */ + type: string; + /** Picture UUID, or `null`. */ + picture: string | null; +} + +/** + * A TIDAL playlist as returned by the `/playlist` route. + */ +export interface TidalPlaylist { + /** Unique playlist UUID. */ + uuid: string; + /** Playlist display title. */ + title: string; + /** Total number of tracks in the playlist. */ + numberOfTracks: number; + /** Total number of videos in the playlist. */ + numberOfVideos: number; + /** Playlist creator. */ + creator: { id: number }; + /** Playlist description text. */ + description: string; + /** Total playlist duration in seconds. */ + duration: number; + /** ISO-8601 timestamp of the last update. */ + lastUpdated: string; + /** ISO-8601 creation timestamp. */ + created: string; + /** Playlist type, e.g. `"EDITORIAL"`. */ + type: string; + /** Whether the playlist is publicly accessible. */ + publicPlaylist: boolean; + /** Canonical TIDAL URL for this playlist. */ + url: string; + /** Rectangular cover image UUID. */ + image: string; + /** Playlist popularity score. */ + popularity: number; + /** Square cover image UUID, or `undefined` if not set. */ + squareImage?: string; + /** Custom image URL override, or `null`. */ + customImageUrl: string | null; + /** Artists featured/promoted in the playlist header. */ + promotedArtists: TidalPromotedArtist[]; + /** ISO-8601 timestamp when the most recent item was added. */ + lastItemAddedAt: string; +} + +/** + * A single item in a TIDAL playlist. + */ +export interface PlaylistItem { + /** The track object, augmented with playlist-specific fields. */ + item: TidalPlaylistTrack; + /** Item type string, e.g. `"track"`. */ + type: string; + /** Cut data associated with the item, or `null`. */ + cut: string | null; +} + +/** + * Response returned by the `/playlist` route. + * Contains the playlist metadata and its item list. + */ +export interface PlaylistResponse extends VersionedResponse { + /** Playlist metadata. */ + playlist: TidalPlaylist; + /** Ordered list of playlist items. */ + items: PlaylistItem[]; +} + +/** + * A TIDAL mix header as parsed from a `/pages/mix` response. + * + * @remarks No live API sample is available for this route. + */ +export interface Mix { + /** Mix identifier. */ + id: string; + /** Mix display title. */ + title: string; + /** Optional mix subtitle. */ + subTitle?: string; +} + +/** + * Response returned by the `/mix` route. + * Contains the mix header and its constituent tracks. + * + * @remarks No live API sample is available for this route; the shape is inferred + * from the TIDAL pages API structure. + */ +export interface MixResponse extends VersionedResponse { + /** Mix metadata. */ + mix: unknown; + /** Ordered list of tracks in this mix. */ + items: TidalTrack[]; +} + +/** + * Lyrics data as returned by the TIDAL API. + * + * @remarks No live API sample is available for this route. + */ +export interface Lyrics { + /** The TIDAL track ID these lyrics belong to. */ + trackId: number; + /** Name of the lyrics provider. */ + lyricsProvider: string; + /** Provider's common-track identifier. */ + providerCommontrackId: string; + /** Provider's lyrics-specific identifier. */ + providerLyricsId: string; + /** Full unsynced lyrics text. */ + lyrics: string; + /** Time-synced subtitle text (LRC format). */ + subtitles: string; + /** Whether the lyrics text reads right-to-left. */ + isRightToLeft: boolean; +} + +/** + * Response returned by the `/lyrics` route. + * Contains the lyrics data for the requested track. + * + * @remarks No live API sample is available for this route. + */ +export interface LyricsResponse extends VersionedResponse { + /** Lyrics data for the requested track. */ + lyrics: unknown; +} + +/** + * Video playback info as returned by the TIDAL API for the `/video` route. + */ +export interface VideoPlaybackInfo { + /** The TIDAL video ID. */ + videoId: number; + /** Stream type, e.g. `"ON_DEMAND"`. */ + streamType: string; + /** Asset presentation type, e.g. `"FULL"`. */ + assetPresentation: string; + /** Requested video quality, e.g. `"HIGH"`. */ + videoQuality: string; + /** MIME type of the manifest, e.g. `"application/vnd.tidal.emu"`. */ + manifestMimeType: string; + /** Hash of the manifest content. */ + manifestHash: string; + /** Base-64 encoded manifest. */ + manifest: string; +} + +/** + * Response returned by the `/video` route. + * Contains playback information for the requested video. + */ +export interface VideoResponse extends VersionedResponse { + /** Video playback info. */ + video: VideoPlaybackInfo; +} + +/** + * Response returned by the `/topvideos` route. + * + * @remarks + * The `videos` array contains the video items extracted from the page modules' + * `pagedList.items` arrays. Individual video objects matching {@link TidalVideoItem} + * are pushed into this array at runtime. + */ +export interface TopVideosResponse extends VersionedResponse { + /** Video items extracted from the page modules. */ + videos: TidalVideoItem[]; + /** Total number of video items before pagination. */ + total: number; +} + +/** + * Audio normalisation data embedded in a track manifest attributes object. + */ +export interface TidalAudioNormData { + /** Replay gain value in dB. */ + replayGain: number; + /** Peak amplitude (0–1). */ + peakAmplitude: number; +} + +/** + * DRM licence data embedded in a track manifest attributes object. + */ +export interface DrmData { + /** DRM system identifier, e.g. `"WIDEVINE"`. */ + drmSystem: string; + /** Licence acquisition URL. */ + licenseUrl: string; + /** Certificate URL. */ + certificateUrl: string; + /** DRM initialisation data, or `null`. */ + initData: string | null; +} + +/** + * Attributes of a single track-manifest resource from the TIDAL OpenAPI. + * + * @remarks + * The `uri` field contains the signed manifest URL (rather than an inline Base-64 + * `manifest` string). `previewReason` is only present for preview-only tracks. + */ +export interface TrackManifestAttributes { + /** Presentation tier, e.g. `"PREVIEW"` or `"FULL"`. */ + trackPresentation: string; + /** Reason the track is restricted to a preview (only present when `trackPresentation` is `"PREVIEW"`). */ + previewReason?: string; + /** Signed manifest URI. */ + uri: string; + /** Hash of the manifest content. */ + hash: string; + /** Playback formats included in this manifest, e.g. `["HEAACV1", "AACLC", "FLAC"]`. */ + formats: string[]; + /** Album-level audio normalisation data. */ + albumAudioNormalizationData: TidalAudioNormData; + /** Track-level audio normalisation data. */ + trackAudioNormalizationData: TidalAudioNormData; + /** DRM data (only present when the track is DRM-protected). */ + drmData?: DrmData; +} + +/** + * A single `trackManifests` resource object from the TIDAL OpenAPI (JSON:API format). + */ +export interface TrackManifestResource { + /** Resource identifier (track ID as a string). */ + id: string; + /** JSON:API resource type — always `"trackManifests"`. */ + type: string; + /** Manifest attributes. */ + attributes: TrackManifestAttributes; +} + +/** + * The raw JSON:API response envelope returned by the TIDAL OpenAPI track-manifest endpoint. + */ +export interface TrackManifestApiResponse { + /** The primary track-manifest resource. */ + data: TrackManifestResource; + /** JSON:API links object. */ + links: { self: string }; +} + +/** + * Response returned by the `/trackManifests` route. + * Wraps the raw JSON:API track-manifest response from the TIDAL OpenAPI. + */ +export interface TrackManifestResponse extends VersionedResponse { + /** The full JSON:API response object from the TIDAL OpenAPI. */ + data: TrackManifestApiResponse; +} + +// ───────────────────────────────────────────────────────────────────────────── + +/** + * A genre entry as returned inside `?v=` video search results. + */ +export interface TidalGenre { + /** Genre identifier. */ + id: string; + /** Genre display name. */ + name: string; +} + +// ─── Private implementation types ──────────────────────────────────────────── + +/** A JSON:API reference item (id + type only), used in similar-artist/album payloads. */ +type JsonApiRef = { id: string; type: string }; + +/** Attribute fields present in TIDAL OpenAPI included resources for similar-artist/album endpoints. */ +interface JsonApiIncludeAttributes { + name?: string; + popularity?: number; + externalLinks?: Array<{ href: string; meta: { type: string } }>; + spotlighted?: boolean; + contributionsEnabled?: boolean; + selectedAlbumCoverFallback?: string | null; + files?: Array<{ href: string }>; + title?: string; + barcodeId?: string; + numberOfVolumes?: number; + numberOfItems?: number; + duration?: string; + explicit?: boolean; + releaseDate?: string; + copyright?: { text: string }; + accessType?: string; + availability?: string[]; + mediaTags?: string[]; + albumType?: string; + createdAt?: string; + type?: string; +} + +/** An included resource node from a TIDAL OpenAPI JSON:API response. */ +interface JsonApiInclude { + id: string; + type: string; + attributes: JsonApiIncludeAttributes; + relationships?: Record; +} + +/** A TIDAL OpenAPI JSON:API list response (similar-artists/albums). */ +interface JsonApiListResponse { + data?: JsonApiRef[]; + included?: JsonApiInclude[]; +} + +/** A generic paginated list response from TIDAL v1 endpoints. */ +interface TidalListResponse { + items?: T[]; + totalNumberOfItems?: number; +} + +/** A module within a TIDAL pages API row. */ +interface TidalPageModule { + type: string; + mix?: Mix; + item?: TidalVideoItem; + pagedList?: { items: Array<{ item?: TidalTrack | TidalVideoItem }> }; +} + +/** A row within a TIDAL pages API response. */ +interface TidalPageRow { + modules?: TidalPageModule[]; +} + +/** Response shape from TIDAL v1 pages endpoints (mix, top videos, album pages). */ +interface TidalPagesApiResponse { + rows?: TidalPageRow[]; +} + +/** Type guard: returns true if the given page-module item is a {@link TidalTrack}. */ +function isTidalTrack(v: TidalTrack | TidalVideoItem | { item?: TidalTrack | TidalVideoItem }): v is TidalTrack { + return 'trackNumber' in v; +} + +/** Type guard: returns true if the given page-module item is a {@link TidalVideoItem}. */ +function isTidalVideoItem( + v: TidalTrack | TidalVideoItem | { item?: TidalTrack | TidalVideoItem } +): v is TidalVideoItem { + return 'imageId' in v; +} + export enum HiFiClientEvents { TokenUpdate, TokenExpiryUpdate, @@ -59,7 +1104,7 @@ class HiFiClient { #token: string | null = null; #refreshToken: string | null = null; #appTokenExpiry = 0; - #tokenPromise: Promise | null = null; + #tokenPromise: Promise | null = null; #albumTracksActive = 0; readonly #albumTracksMax = 20; readonly #albumTracksQueue: Array<() => void> = []; @@ -72,18 +1117,19 @@ class HiFiClient { on(event: HiFiClientEvents.TokenUpdate, listener: (token: string | null) => void): void; on(event: HiFiClientEvents.TokenExpiryUpdate, listener: (expiry: number) => void): void; on(event: HiFiClientEvents.RefreshTokenUpdate, listener: (refreshToken: string | null) => void): void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any on(event: HiFiClientEvents, listener: (...args: any[]) => void) { this.#emitter.addListener(HiFiClientEvents[event], listener); } - off(event: HiFiClientEvents, listener: (...args: any[]) => void) { + off(event: HiFiClientEvents, listener: (...args: (string | number | null)[]) => void) { this.#emitter.removeListener(HiFiClientEvents[event], listener); } #emit(event: HiFiClientEvents.TokenUpdate, token: string | null): void; #emit(event: HiFiClientEvents.TokenExpiryUpdate, expiry: number): void; #emit(event: HiFiClientEvents.RefreshTokenUpdate, refreshToken: string | null): void; - #emit(event: HiFiClientEvents, data: any) { + #emit(event: HiFiClientEvents, data: string | number | null) { this.#emitter.emit(HiFiClientEvents[event], data); } @@ -132,8 +1178,8 @@ class HiFiClient { }); } - static #jsonResponse(data: any) { - return new Response(JSON.stringify(data), { + static #jsonResponse(data: T): TidalResponse { + return new TidalResponse(JSON.stringify(data), { headers: { 'Content-Type': 'application/json', }, @@ -155,10 +1201,18 @@ class HiFiClient { return u.toString(); } + /** + * Manually sets the access token, expiry, and optional refresh token on this client. + * + * Useful when tokens have been obtained externally (e.g. from a server-side OAuth flow) + * and need to be injected into the client. + * + * @param options - Token values to apply. + */ setToken({ token, tokenExpiry, refreshToken }: HiFiClient.TokenOptions & HiFiClient.RefreshTokenOptions) { - this.token = token; - this.appTokenExpiry = tokenExpiry; - this.refreshToken = refreshToken; + this.token = token || null; + this.appTokenExpiry = tokenExpiry || 0; + this.refreshToken = refreshToken || null; } static #basicAuth(username: string, password: string) { @@ -180,7 +1234,7 @@ class HiFiClient { }): Promise { if (!force && this.token && (this.appTokenExpiry < 0 || Date.now() < this.appTokenExpiry)) return this.token; - return await (this.#tokenPromise ??= (async () => { + return await (this.#tokenPromise ??= (async (): Promise => { try { const params = new URLSearchParams({ client_id: clientId, @@ -210,13 +1264,13 @@ class HiFiClient { throw new Error(`Failed to obtain app token: ${res.status} ${txt}`); } - const json = await res.json(); + const json = (await res.json()) as { access_token?: string; expires_in?: number }; const token = json.access_token; const expires_in = json.expires_in ?? 3600; - this.token = token; + this.token = token || null; this.appTokenExpiry = Date.now() + (expires_in - 60) * 1000; - return token; + return token || null; } finally { this.#tokenPromise = null; } @@ -226,12 +1280,12 @@ class HiFiClient { static #getOptions({ locale = 'en_US', countryCode = 'US', - baseUrl = null, + baseUrl = '', clientId = HiFiClient.BROWSER_CLIENT_ID, clientSecret = HiFiClient.BROWSER_CLIENT_SECRET, - token, - tokenExpiry, - refreshToken, + token = '', + tokenExpiry = 0, + refreshToken = '', storage = [], }: HiFiClient.ConstructorOptions = {}): WithRequiredKeys { return { @@ -247,6 +1301,16 @@ class HiFiClient { }; } + /** + * Obtains (or refreshes) the TIDAL application access token. + * + * If a non-expired token is already held and `force` is `false`, the cached + * token is returned immediately without a network request. + * + * @param force - When `true`, forces a token refresh even if the current token is still valid. + * @param signal - Optional {@link AbortSignal} to cancel the token request. + * @returns The access token string, or `null` if one could not be obtained. + */ async fetchToken(force: boolean = false, signal: AbortSignal | undefined = undefined) { return await this.#fetchAppToken({ clientId: this.#clientId, @@ -297,14 +1361,14 @@ class HiFiClient { return res; } - async #fetchJson( + async #fetchJson( url: string, params?: Params | URLSearchParams, signal: AbortSignal = new AbortController().signal ): Promise { const res = await this.#fetchAuthenticated(url, params, signal); - return res.json(); + return res.json() as Promise; } constructor(options: HiFiClient.ConstructorOptions = {}) { @@ -312,18 +1376,28 @@ class HiFiClient { HiFiClient.#getOptions(options); this.#locale = locale; this.#countryCode = countryCode; - this.#baseUrl = baseUrl; + this.#baseUrl = baseUrl || null; this.#clientId = clientId; this.#clientSecret = clientSecret; - this.token = token; - this.appTokenExpiry = tokenExpiry; - this.refreshToken = refreshToken; + this.token = token || null; + this.appTokenExpiry = tokenExpiry || 0; + this.refreshToken = refreshToken || null; for (const store of !Array.isArray(storage) ? [storage] : storage) { this.#useStorage(store); } } + /** + * Creates and initialises the singleton {@link HiFiClient} instance. + * + * Throws if {@link HiFiClient.initialize} has already been called. After + * initialisation the instance can be retrieved via {@link HiFiClient.instance}. + * + * @param options - Constructor options including optional credentials and locale settings. + * @returns The newly created {@link HiFiClient} instance. + * @throws If a singleton instance already exists. + */ static async initialize(options: HiFiClient.ConstructorOptions & { signal?: AbortSignal } = {}) { if (HiFiClient.#instance) { throw new Error('HiFiClient is already initialized'); @@ -361,13 +1435,34 @@ class HiFiClient { } } - async getInfo(id: number, signal?: AbortSignal) { + /** + * Fetches full track metadata for the given track ID. + * + * @param id - TIDAL track ID. + * @param signal - Optional {@link AbortSignal} to cancel the request. + * @returns A {@link TidalResponse} whose `.json()` resolves to an {@link InfoResponse}. + */ + async getInfo(id: number, signal?: AbortSignal): Promise> { const url = `https://api.tidal.com/v1/tracks/${id}/`; - const data = await this.#fetchJson(url, { countryCode: this.#countryCode }, signal); + const data = await this.#fetchJson(url, { countryCode: this.#countryCode }, signal); return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, data }); } - async getTrack(id: number, quality = 'HI_RES_LOSSLESS', immersiveAudio: boolean = false, signal?: AbortSignal) { + /** + * Fetches playback/stream info for the given track ID. + * + * @param id - TIDAL track ID. + * @param quality - Audio quality string, e.g. `"HI_RES_LOSSLESS"` (default). + * @param immersiveAudio - Whether to request immersive audio (Dolby Atmos / 360). Defaults to `false`. + * @param signal - Optional {@link AbortSignal} to cancel the request. + * @returns A {@link TidalResponse} whose `.json()` resolves to a {@link TrackResponse}. + */ + async getTrack( + id: number, + quality = 'HI_RES_LOSSLESS', + immersiveAudio: boolean = false, + signal?: AbortSignal + ): Promise> { const url = `https://api.tidal.com/v1/tracks/${id}/playbackinfo`; const params = { audioquality: quality, @@ -376,10 +1471,18 @@ class HiFiClient { countryCode: this.#countryCode, immersiveAudio: String(immersiveAudio), }; - const data = await this.#fetchJson(url, params, signal); + const data = await this.#fetchJson(url, params, signal); return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, data }); } + /** + * Fetches the MPEG-DASH (or alternative) track manifest from the TIDAL OpenAPI. + * + * @param id - TIDAL track ID. + * @param options - Optional manifest request options (formats, adaptive, manifestType, etc.). + * @param signal - Optional {@link AbortSignal} to cancel the request. + * @returns A {@link TidalResponse} whose `.json()` resolves to a {@link TrackManifestResponse}. + */ async getTrackManifest( id: number, { @@ -390,7 +1493,7 @@ class HiFiClient { usage = 'PLAYBACK', }: HiFiClient.GetTrackManifestOptions = {}, signal?: AbortSignal - ) { + ): Promise> { const url = `https://openapi.tidal.com/v2/trackManifests/${id}`; const params = new URLSearchParams({ adaptive: String(adaptive), @@ -403,7 +1506,7 @@ class HiFiClient { params.append('formats', format); } - const res = await this.#fetchJson(url, params, signal); + const res = await this.#fetchJson(url, params, signal); const drmData = res.data.attributes.drmData; if (drmData && this.#baseUrl) { @@ -415,17 +1518,45 @@ class HiFiClient { return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, data: res }); } - async getWidevine() { + /** + * Fetches a raw Widevine licence response from the TIDAL API. + * + * @returns The raw {@link Response} from the Widevine endpoint. + */ + async getWidevine(): Promise { return await this.#fetchAuthenticated('https://api.tidal.com/v2/widevine'); } - async getRecommendations(id: number, signal?: AbortSignal) { + /** + * Fetches track recommendations for the given track ID. + * + * @param id - TIDAL track ID. + * @param signal - Optional {@link AbortSignal} to cancel the request. + * @returns A {@link TidalResponse} whose `.json()` resolves to a {@link RecommendationsResponse}. + */ + async getRecommendations(id: number, signal?: AbortSignal): Promise> { const url = `https://api.tidal.com/v1/tracks/${id}/recommendations`; - const data = await this.#fetchJson(url, { limit: '20', countryCode: this.#countryCode }, signal); + const data = await this.#fetchJson<{ items: TidalTrack[]; totalNumberOfItems: number }>( + url, + { limit: '20', countryCode: this.#countryCode }, + signal + ); return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, data }); } - async getSimilarArtists(id: number, cursor?: string | number | null, signal?: AbortSignal) { + /** + * Fetches artists similar to the given artist ID. + * + * @param id - TIDAL artist ID. + * @param cursor - Optional pagination cursor from a previous response. + * @param signal - Optional {@link AbortSignal} to cancel the request. + * @returns A {@link TidalResponse} whose `.json()` resolves to a {@link SimilarArtistsResponse}. + */ + async getSimilarArtists( + id: number, + cursor?: string | number | null, + signal?: AbortSignal + ): Promise> { const url = `https://openapi.tidal.com/v2/artists/${id}/relationships/similarArtists`; const params: Params = { 'page[cursor]': cursor ?? undefined, @@ -433,19 +1564,19 @@ class HiFiClient { include: 'similarArtists,similarArtists.profileArt', }; - const payload = await this.#fetchJson(url, params, signal); - const included = Array.isArray(payload?.included) ? payload.included : []; - const artists_map: Record = {}; - const artworks_map: Record = {}; + const payload = await this.#fetchJson(url, params, signal); + const included: JsonApiInclude[] = Array.isArray(payload?.included) ? payload.included : []; + const artists_map: Record = {}; + const artworks_map: Record = {}; for (const i of included) { if (i.type === 'artists') artists_map[i.id] = i; if (i.type === 'artworks') artworks_map[i.id] = i; } - const resolveArtist = (entry: any) => { + const resolveArtist = (entry: JsonApiRef): SimilarArtist => { const aid = entry.id; - const inc = artists_map[aid] || {}; - const attr = inc.attributes || {}; + const inc = artists_map[aid] ?? ({} as JsonApiInclude); + const attr = inc.attributes ?? ({} as JsonApiIncludeAttributes); let pic_id: string | null = null; const art_data = inc.relationships?.profileArt?.data; @@ -459,20 +1590,37 @@ class HiFiClient { return { ...attr, - id: String(aid).match(/^\d+$/) ? Number(aid) : aid, - picture: pic_id || attr.selectedAlbumCoverFallback, + id: Number(aid), + name: attr.name ?? '', + picture: pic_id ?? attr.selectedAlbumCoverFallback ?? null, url: `http://www.tidal.com/artist/${aid}`, relationType: 'SIMILAR_ARTIST', + popularity: attr.popularity ?? 0, + externalLinks: attr.externalLinks ?? [], + spotlighted: attr.spotlighted ?? false, + contributionsEnabled: attr.contributionsEnabled ?? false, }; }; return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, - artists: (payload?.data || []).map(resolveArtist), + artists: (payload?.data ?? []).map(resolveArtist), }); } - async getSimilarAlbums(id: number, cursor?: string | number | null, signal?: AbortSignal) { + /** + * Fetches albums similar to the given album ID. + * + * @param id - TIDAL album ID. + * @param cursor - Optional pagination cursor from a previous response. + * @param signal - Optional {@link AbortSignal} to cancel the request. + * @returns A {@link TidalResponse} whose `.json()` resolves to a {@link SimilarAlbumsResponse}. + */ + async getSimilarAlbums( + id: number, + cursor?: string | number | null, + signal?: AbortSignal + ): Promise> { const url = `https://openapi.tidal.com/v2/albums/${id}/relationships/similarAlbums`; const params: Params = { 'page[cursor]': cursor ?? undefined, @@ -480,21 +1628,21 @@ class HiFiClient { include: 'similarAlbums,similarAlbums.coverArt,similarAlbums.artists', }; - const payload = await this.#fetchJson(url, params, signal); - const included = Array.isArray(payload?.included) ? payload.included : []; - const albums_map: Record = {}; - const artworks_map: Record = {}; - const artists_map: Record = {}; + const payload = await this.#fetchJson(url, params, signal); + const included: JsonApiInclude[] = Array.isArray(payload?.included) ? payload.included : []; + const albums_map: Record = {}; + const artworks_map: Record = {}; + const artists_map: Record = {}; for (const i of included) { if (i.type === 'albums') albums_map[i.id] = i; if (i.type === 'artworks') artworks_map[i.id] = i; if (i.type === 'artists') artists_map[i.id] = i; } - const resolveAlbum = (entry: any) => { + const resolveAlbum = (entry: JsonApiRef): TidalSimilarAlbum => { const aid = entry.id; - const inc = albums_map[aid] || {}; - const attr = inc.attributes || {}; + const inc = albums_map[aid] ?? ({} as JsonApiInclude); + const attr = inc.attributes ?? ({} as JsonApiIncludeAttributes); let cover_id: string | null = null; const art_data = inc.relationships?.coverArt?.data; @@ -506,16 +1654,15 @@ class HiFiClient { } } - const artist_list: any[] = []; + const artist_list: Array<{ id: number; name: string }> = []; const artists_data = inc.relationships?.artists?.data; if (Array.isArray(artists_data)) { for (const a_entry of artists_data) { const a_obj = artists_map[a_entry.id]; if (a_obj) { - const a_id = a_obj.id; artist_list.push({ - id: String(a_id).match(/^\d+$/) ? Number(a_id) : a_id, - name: a_obj.attributes?.name, + id: Number(a_obj.id), + name: a_obj.attributes?.name ?? '', }); } } @@ -523,31 +1670,52 @@ class HiFiClient { return { ...attr, - id: String(aid).match(/^\d+$/) ? Number(aid) : aid, - cover: cover_id, + id: Number(aid), + title: attr.title ?? '', + cover: cover_id ?? '', artists: artist_list, url: `http://www.tidal.com/album/${aid}`, - }; + } as TidalSimilarAlbum; }; return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, - albums: (payload?.data || []).map(resolveAlbum), + albums: (payload?.data ?? []).map(resolveAlbum), }); } + /** + * Fetches artist data including profile information, discography, and/or top tracks. + * + * When `id` is supplied, returns the artist's profile and a cover-image entry + * ({@link ArtistByIdResponse}). + * + * When `f` is supplied, returns the artist's full discography and, if `skip_tracks` + * is `false`, the tracks from all albums ({@link ArtistDiscographyResponse}). + * + * @param id - TIDAL artist ID for profile lookup. + * @param f - TIDAL artist ID for discography/tracks lookup. + * @param skip_tracks - When `true`, fetches only top tracks instead of all album tracks. + * @param signal - Optional {@link AbortSignal} to cancel the request. + * @param options - Optional pagination options (`offset`, `limit`) for track fetching. + * @returns A {@link TidalResponse} whose `.json()` resolves to an {@link ArtistResponse}. + */ async getArtist( id?: number | null, f?: number | null, skip_tracks = false, signal?: AbortSignal, options?: { offset?: number; limit?: number } - ) { + ): Promise> { if (!id && !f) throw new ResponseError(400, 'Provide id or f query param'); if (id) { const artist_url = `https://api.tidal.com/v1/artists/${id}`; - const artist_data = await this.#fetchJson(artist_url, { countryCode: this.#countryCode }, signal); + const artist_data = await this.#fetchJson( + artist_url, + { countryCode: this.#countryCode }, + signal + ); let picture = artist_data.picture; const fallback = artist_data.selectedAlbumCoverFallback; @@ -556,7 +1724,7 @@ class HiFiClient { picture = fallback; } - let cover = null; + let cover: ArtistCover | null = null; if (picture) { const slug = picture.replace(/-/g, '/'); cover = { @@ -573,9 +1741,13 @@ class HiFiClient { const albums_url = `https://api.tidal.com/v1/artists/${f}/albums`; const common_params: Params = { countryCode: this.#countryCode, limit: 50 }; - const tasks: Promise[] = [ - this.#fetchJson(albums_url, common_params, signal), - this.#fetchJson(albums_url, { ...common_params, filter: 'EPSANDSINGLES' }, signal), + const tasks: Array | TidalListResponse>> = [ + this.#fetchJson>(albums_url, common_params, signal), + this.#fetchJson>( + albums_url, + { ...common_params, filter: 'EPSANDSINGLES' }, + signal + ), ]; if (skip_tracks) { @@ -585,24 +1757,28 @@ class HiFiClient { if (offset !== undefined) { toptracks_params.offset = offset; } - tasks.push(this.#fetchJson(`https://api.tidal.com/v1/artists/${f}/toptracks`, toptracks_params, signal)); + tasks.push( + this.#fetchJson>( + `https://api.tidal.com/v1/artists/${f}/toptracks`, + toptracks_params, + signal + ) + ); } - const results = await Promise.all(tasks.map((p) => p.catch((e) => e))); + const results = await Promise.all(tasks.map((p) => p.catch((e: Error) => e))); - const unique_releases: any[] = []; - const seen_ids = new Set(); + const unique_releases: TidalAlbum[] = []; + const seen_ids = new Set(); for (const res of results.slice(0, 2)) { if (res && !(res instanceof Error)) { - const data = res; - const items = Array.isArray(data?.items) ? data.items : data || []; - if (Array.isArray(items)) { - for (const item of items) { - if (item && item.id && !seen_ids.has(item.id)) { - unique_releases.push(item); - seen_ids.add(item.id); - } + const data = res as TidalListResponse; + const items = data?.items ?? []; + for (const item of items) { + if (item && item.id && !seen_ids.has(item.id)) { + unique_releases.push(item); + seen_ids.add(item.id); } } } @@ -612,11 +1788,12 @@ class HiFiClient { const page_data = { items: unique_releases }; if (skip_tracks) { - let top_tracks: any[] = []; + let top_tracks: TidalTrack[] = []; if (results.length > 2) { const res = results[2]; if (res && !(res instanceof Error)) { - top_tracks = Array.isArray(res.items) ? res.items : res || []; + const data = res as TidalListResponse; + top_tracks = data?.items ?? []; } } @@ -626,26 +1803,27 @@ class HiFiClient { if (!album_ids.length) return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, albums: page_data, tracks: [] }); - const fetchAlbumTracks = async (album_id: number) => { + const fetchAlbumTracks = async (album_id: number): Promise => { return await this.#withAlbumTrackSlot(async () => { - const album_data = await this.#fetchJson( + const album_data = await this.#fetchJson( 'https://api.tidal.com/v1/pages/album', { albumId: album_id, countryCode: this.#countryCode, deviceType: 'BROWSER' }, signal ); const rows = Array.isArray(album_data?.rows) ? album_data.rows : []; if (rows.length < 2) return []; - const modules = rows[1].modules || []; + const modules = rows[1].modules ?? []; if (!modules || modules.length === 0) return []; - const paged_list = modules[0].pagedList || {}; - const items = paged_list.items || []; - const tracks = items.map((t: any) => (t.item ? t.item : t)); - return tracks; + const paged_list = modules[0].pagedList ?? { items: [] }; + const items = paged_list.items ?? []; + return items.map((t) => t.item ?? t).filter((t): t is TidalTrack => isTidalTrack(t)); }); }; - const trackResults = await Promise.all(album_ids.map((aid) => fetchAlbumTracks(aid).catch(() => []))); - const tracks: any[] = []; + const trackResults = await Promise.all( + album_ids.map((aid) => fetchAlbumTracks(aid).catch((): TidalTrack[] => [])) + ); + const tracks: TidalTrack[] = []; for (const t of trackResults) { if (Array.isArray(t)) tracks.push(...t); } @@ -653,54 +1831,71 @@ class HiFiClient { return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, albums: page_data, tracks }); } - async getArtistBiography(artistId: number, signal?: AbortSignal) { + /** + * Fetches the biography text for the given artist ID. + * + * @param artistId - TIDAL artist ID. + * @param signal - Optional {@link AbortSignal} to cancel the request. + * @returns A {@link TidalResponse} whose `.json()` resolves to an {@link ArtistBioResponse}. + */ + async getArtistBiography(artistId: number, signal?: AbortSignal): Promise> { const url = `https://api.tidal.com/v1/artists/${artistId}/bio`; const params = { locale: this.#locale, countryCode: this.#countryCode, }; - const data = await this.#fetchJson(url, params, signal); + const data = await this.#fetchJson(url, params, signal); return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, data: data }); } - #buildCoverEntry(cover_slug: string, name?: string | null, track_id?: number | null) { + #buildCoverEntry(cover_slug: string, name?: string | null, track_id?: number | null): CoverEntry { const slug = cover_slug.replace(/-/g, '/'); return { - id: track_id, - name, + id: track_id ?? 0, + name: name ?? '', '1280': `https://resources.tidal.com/images/${slug}/1280x1280.jpg`, '640': `https://resources.tidal.com/images/${slug}/640x640.jpg`, '80': `https://resources.tidal.com/images/${slug}/80x80.jpg`, }; } - async getCover(id?: number | null, q?: string | null, signal?: AbortSignal) { + /** + * Fetches cover-image URLs for a track (by `id`) or a search query (`q`). + * + * @param id - TIDAL track ID; if provided, returns the cover for that track's album. + * @param q - Free-text search query; returns covers for matching tracks. + * @param signal - Optional {@link AbortSignal} to cancel the request. + * @returns A {@link TidalResponse} whose `.json()` resolves to a {@link CoverResponse}. + * @throws {@link ResponseError} with status 400 if neither `id` nor `q` is provided. + * @throws {@link ResponseError} with status 404 if no cover could be found. + */ + async getCover(id?: number | null, q?: string | null, signal?: AbortSignal): Promise> { if (!id && !q) throw new ResponseError(400, 'Provide id or q query param'); if (id) { - const track_data = await this.#fetchJson( + const track_data = await this.#fetchJson( `https://api.tidal.com/v1/tracks/${id}/`, { countryCode: this.#countryCode }, signal ); - const album = track_data.album || {}; + const album = track_data.album ?? ({} as TidalTrackAlbumRef); const cover_slug = album.cover; if (!cover_slug) throw new ResponseError(404, 'Cover not found'); const entry = this.#buildCoverEntry(cover_slug, album.title || track_data.title, album.id || id); return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, covers: [entry] }); } - const search_data = await this.#fetchJson( + const search_data = await this.#fetchJson<{ items: TidalTrack[] }>( 'https://api.tidal.com/v1/search/tracks', { countryCode: this.#countryCode, query: q, limit: 10 }, signal ); const items = Array.isArray(search_data?.items) ? search_data.items.slice(0, 10) : []; if (!items.length) throw new ResponseError(404, 'Cover not found'); - const covers: any[] = []; + const covers: CoverEntry[] = []; for (const track of items) { - const album = track.album || {}; + const album = track.album ?? ({} as TidalTrackAlbumRef); const cover_slug = album.cover; if (!cover_slug) continue; covers.push(this.#buildCoverEntry(cover_slug, track.title, track.id)); @@ -709,26 +1904,52 @@ class HiFiClient { return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, covers }); } + /** + * Performs a TIDAL search. Exactly one search option must be provided. + * + * | Option | Description | + * |--------|-------------| + * | `q` | General search across artists, albums, tracks, videos, and playlists. | + * | `s` | Track-specific text search. | + * | `a` | Top-hits search scoped to artists and tracks. | + * | `al` | Top-hits search scoped to albums. | + * | `v` | Top-hits search scoped to videos. | + * | `p` | Top-hits search scoped to playlists. | + * | `i` | ISRC-based track lookup (falls back to text search). | + * + * @param options - Search parameters; at least one of `q`, `s`, `a`, `al`, `v`, `p`, or `i` is required. + * @param signal - Optional {@link AbortSignal} to cancel the request. + * @returns A {@link TidalResponse} whose `.json()` resolves to a {@link SearchResponse}. + */ async search( options: { + /** General search query (artists, albums, tracks, videos, playlists). */ q?: string; + /** Track text search query. */ s?: string; + /** Artist/track top-hits query. */ a?: string; + /** Album top-hits query. */ al?: string; + /** Video top-hits query. */ v?: string; + /** Playlist top-hits query. */ p?: string; + /** ISRC code for exact track lookup. */ i?: string; + /** Result offset for pagination. */ offset?: number; + /** Maximum number of results to return. */ limit?: number; }, signal?: AbortSignal - ) { + ): Promise> { const { q, s, a, al, v, p, i, offset = 0, limit = 25 } = options; if (i) { // try filtered track search first try { - const res = await this.#fetchJson( + const res = await this.#fetchJson( 'https://api.tidal.com/v1/tracks', { 'filter[isrc]': i, @@ -739,11 +1960,11 @@ class HiFiClient { signal ); return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, data: res }); - } catch (err: any) { - if (err.status && ![400, 404].includes(err.status)) throw err; + } catch (err: unknown) { + if (err instanceof ResponseError && ![400, 404].includes(err.status)) throw err; // fallback to text search } - const fallback = await this.#fetchJson( + const fallback = await this.#fetchJson( 'https://api.tidal.com/v1/search/tracks', { query: i, @@ -793,7 +2014,7 @@ class HiFiClient { for (const [val, url, params] of mapping) { if (val) { - const data = await this.#fetchJson(url, params, signal); + const data = await this.#fetchJson(url, params, signal); return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, data }); } } @@ -801,18 +2022,29 @@ class HiFiClient { throw new Error('Provide one of s, a, al, v, p, or i'); } - async getAlbum(id: number, limit = 100, offset = 0, signal?: AbortSignal) { + /** + * Fetches album metadata together with its full track listing. + * + * @param id - TIDAL album ID. + * @param limit - Maximum number of tracks to fetch. Defaults to `100`. + * @param offset - Track list offset for pagination. Defaults to `0`. + * @param signal - Optional {@link AbortSignal} to cancel the request. + * @returns A {@link TidalResponse} whose `.json()` resolves to an {@link AlbumResponse}. + */ + async getAlbum(id: number, limit = 100, offset = 0, signal?: AbortSignal): Promise> { const albumUrl = `https://api.tidal.com/v1/albums/${id}`; const itemsUrl = `https://api.tidal.com/v1/albums/${id}/items`; - const tasks: Promise[] = [this.#fetchJson(albumUrl, { countryCode: this.#countryCode }, signal)]; + type ItemsPage = { items?: Array<{ item: TidalTrack; type: string }> }; + const albumTask = this.#fetchJson(albumUrl, { countryCode: this.#countryCode }, signal); + const itemsTasks: Array> = []; let remaining = limit; let currentOffset = offset; const maxChunk = 100; while (remaining > 0) { const chunk = Math.min(remaining, maxChunk); - tasks.push( - this.#fetchJson( + itemsTasks.push( + this.#fetchJson( itemsUrl, { countryCode: this.#countryCode, limit: chunk, offset: currentOffset }, signal @@ -822,71 +2054,118 @@ class HiFiClient { remaining -= chunk; } - const results = await Promise.all(tasks); - const albumData = results[0]; - const pages = results.slice(1); - const allItems: any[] = []; + const [albumRaw, ...pages] = await Promise.all([albumTask, ...itemsTasks]); + const allItems: Array<{ item: TidalTrack; type: string }> = []; for (const p of pages) { - const pageItems = (p && p.items) || p; + const pageItems = p?.items ?? []; if (Array.isArray(pageItems)) allItems.push(...pageItems); } - albumData.items = allItems; + const albumData: TidalAlbumWithTracks = { ...albumRaw, items: allItems }; return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, data: albumData }); } - async getMix(id: string, signal?: AbortSignal) { + /** + * Fetches the header and track list for a TIDAL mix. + * + * @param id - TIDAL mix ID string. + * @param signal - Optional {@link AbortSignal} to cancel the request. + * @returns A {@link TidalResponse} whose `.json()` resolves to a {@link MixResponse}. + */ + async getMix(id: string, signal?: AbortSignal): Promise> { const url = 'https://api.tidal.com/v1/pages/mix'; - const data = await this.#fetchJson( + const data = await this.#fetchJson( url, { mixId: id, countryCode: this.#countryCode, deviceType: 'BROWSER' }, signal ); - let header = {}, - items: any[] = []; - const rows = data.rows || []; + let header: unknown = {}; + let items: TidalTrack[] = []; + const rows = data.rows ?? []; + for (const row of rows) { - for (const module of row.modules || []) { - if (module.type === 'MIX_HEADER') header = module.mix || {}; - if (module.type === 'TRACK_LIST') items = (module.pagedList || {}).items || []; + for (const module of row.modules ?? []) { + if (module.type === 'MIX_HEADER') header = module.mix ?? {}; + if (module.type === 'TRACK_LIST') items = ((module.pagedList || {}).items as TidalTrack[]) ?? []; } } return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, mix: header, - items: items.map((it: any) => (it.item ? it.item : it)), + items, }); } - async getPlaylist(id: string, limit = 100, offset = 0, signal?: AbortSignal) { + /** + * Fetches playlist metadata together with its item list. + * + * @param id - TIDAL playlist UUID string. + * @param limit - Maximum number of items to fetch. Defaults to `100`. + * @param offset - Item list offset for pagination. Defaults to `0`. + * @param signal - Optional {@link AbortSignal} to cancel the request. + * @returns A {@link TidalResponse} whose `.json()` resolves to a {@link PlaylistResponse}. + */ + async getPlaylist( + id: string, + limit = 100, + offset = 0, + signal?: AbortSignal + ): Promise> { const playlistUrl = `https://api.tidal.com/v1/playlists/${id}`; const itemsUrl = `https://api.tidal.com/v1/playlists/${id}/items`; const [playlistData, itemsData] = await Promise.all([ - this.#fetchJson(playlistUrl, { countryCode: this.#countryCode }, signal), - this.#fetchJson(itemsUrl, { countryCode: this.#countryCode, limit, offset }, signal), + this.#fetchJson(playlistUrl, { countryCode: this.#countryCode }, signal), + this.#fetchJson<{ items: PlaylistItem[] }>( + itemsUrl, + { countryCode: this.#countryCode, limit, offset }, + signal + ), ]); - const items = (itemsData && itemsData.items) || itemsData; + const items = itemsData?.items ?? []; return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, playlist: playlistData, items }); } // simplified artist/cover/lyrics/video/topvideos/similar methods (same pattern) - async getLyrics(id: number, signal?: AbortSignal) { + /** + * Fetches the lyrics for the given track ID. + * + * @param id - TIDAL track ID. + * @param signal - Optional {@link AbortSignal} to cancel the request. + * @returns A {@link TidalResponse} whose `.json()` resolves to a {@link LyricsResponse}. + * @throws An error with `status = 404` if lyrics are unavailable for the track. + */ + async getLyrics(id: number, signal?: AbortSignal): Promise> { const url = `https://api.tidal.com/v1/tracks/${id}/lyrics`; - const data = await this.#fetchJson( + const data = await this.#fetchJson( url, { countryCode: this.#countryCode, locale: 'en_US', deviceType: 'BROWSER' }, signal ); if (!data) { - const err: any = new Error('Lyrics not found'); - err.status = 404; + const err = Object.assign(new Error('Lyrics not found'), { status: 404 }); throw err; } return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, lyrics: data }); } - async getVideo(id: number, quality = 'HIGH', mode = 'STREAM', presentation = 'FULL', signal?: AbortSignal) { + /** + * Fetches video playback info for the given video ID. + * + * @param id - TIDAL video ID. + * @param quality - Video quality string, e.g. `"HIGH"` (default). + * @param mode - Playback mode, e.g. `"STREAM"` (default). + * @param presentation - Asset presentation, e.g. `"FULL"` (default). + * @param signal - Optional {@link AbortSignal} to cancel the request. + * @returns A {@link TidalResponse} whose `.json()` resolves to a {@link VideoResponse}. + */ + async getVideo( + id: number, + quality = 'HIGH', + mode = 'STREAM', + presentation = 'FULL', + signal?: AbortSignal + ): Promise> { const url = `https://api.tidal.com/v1/videos/${id}/playbackinfo`; - const data = await this.#fetchJson( + const data = await this.#fetchJson( url, { videoquality: quality, playbackmode: mode, assetpresentation: presentation }, signal @@ -894,23 +2173,34 @@ class HiFiClient { return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, video: data }); } + /** + * Fetches a paginated list of recommended videos from TIDAL. + * + * @param options - Optional locale, device type, and pagination parameters. + * @param signal - Optional {@link AbortSignal} to cancel the request. + * @returns A {@link TidalResponse} whose `.json()` resolves to a {@link TopVideosResponse}. + */ async getTopVideos( { countryCode = 'US', locale = 'en_US', deviceType = 'BROWSER', limit = 25, offset = 0 } = {}, signal?: AbortSignal - ) { + ): Promise> { const url = 'https://api.tidal.com/v1/pages/mymusic_recommended_videos'; - const data = await this.#fetchJson(url, { countryCode, locale, deviceType }, signal); - const rows = data.rows || []; - const videos: any[] = []; + const data = await this.#fetchJson(url, { countryCode, locale, deviceType }, signal); + const rows = data.rows ?? []; + const videos: TidalVideoItem[] = []; for (const row of rows) { - for (const module of row.modules || []) { + for (const module of row.modules ?? []) { const mt = module.type; if (['VIDEO_PLAYLIST', 'VIDEO_ROW', 'PAGED_LIST'].includes(mt)) { - const items = (module.pagedList || {}).items || []; - for (const item of items) videos.push(item.item || item); + const items = module.pagedList?.items ?? []; + for (const item of items) { + const v = item.item ?? item; + videos.push(v as TidalVideoItem); + } } else if (mt === 'VIDEO' || (mt && mt.toLowerCase().includes('video'))) { - const it = module.item || module; - if (typeof it === 'object') videos.push(it); + const it = module.item; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + videos.push(it as TidalVideoItem); } } } @@ -921,8 +2211,21 @@ class HiFiClient { }); } - // generic helper that accepts local route strings like "/info/?id=123" or full URLs - async query(pathOrUrl: string, signal?: AbortSignal) { + /** + * Dispatches a local route string (e.g. `"/info/?id=123"`) to the appropriate + * {@link HiFiClient} method and returns a {@link TidalResponse}. + * + * This is a convenience method that mirrors an HTTP-router interface so external + * code can call the API with path-style strings. Because the route is resolved + * at runtime the response is untyped (`TidalResponse`); prefer calling + * the individual typed methods directly when the route is known at compile time. + * + * @param pathOrUrl - A local route path such as `"/track/?id=123"`, or a full URL. + * @param signal - Optional {@link AbortSignal} to cancel the request. + * @returns A {@link TidalResponse} wrapping the route handler's response. + * @throws An error if the pathname does not match any known route. + */ + async query(pathOrUrl: string, signal?: AbortSignal): Promise { // normalize: if starts with http use as-is, else treat as local route try { const u = new URL(pathOrUrl, 'http://localhost'); @@ -1039,7 +2342,9 @@ class HiFiClient { throw new Error(`Unknown route: ${pathname}`); } } catch (err) { - console.error(err?.message || err, err?.message ? err : undefined); + const message = (err as { message?: string }).message ?? String(err); + console.error(message, err); + throw err; } } diff --git a/js/api.js b/js/api.js index d5ae23d..199fc1f 100644 --- a/js/api.js +++ b/js/api.js @@ -1073,18 +1073,20 @@ export class LosslessAPI { entries.forEach((entry) => scan(entry, visited)); scan(primaryData, visited); + const matchesArtistId = (item) => { + const candidateIds = [ + item.artist?.id, + ...(Array.isArray(item.artists) ? item.artists.map((a) => a.id) : []), + ].filter((id) => id != null); + return candidateIds.some((id) => Number(id) === Number(artistId)); + }; + if (!options.lightweight) { try { const videoSearch = await this.searchVideos(artist.name); if (videoSearch && videoSearch.items) { - const numericArtistId = Number(artistId); for (const item of videoSearch.items) { - const itemArtistId = item.artist?.id; - const matchesArtist = - itemArtistId === numericArtistId || - (Array.isArray(item.artists) && item.artists.some((a) => a.id === numericArtistId)); - - if (matchesArtist && !videoMap.has(item.id)) { + if (matchesArtistId(item) && !videoMap.has(item.id)) { videoMap.set(item.id, item); } } @@ -1094,7 +1096,7 @@ export class LosslessAPI { } } - const rawReleases = Array.from(albumMap.values()); + const rawReleases = Array.from(albumMap.values()).filter(matchesArtistId); const allReleases = this.deduplicateAlbums(rawReleases).sort( (a, b) => new Date(b.releaseDate || 0) - new Date(a.releaseDate || 0) ); @@ -1103,6 +1105,7 @@ export class LosslessAPI { const albums = allReleases.filter((a) => !eps.includes(a)); const topTracks = Array.from(trackMap.values()) + .filter(matchesArtistId) .sort((a, b) => (b.popularity || 0) - (a.popularity || 0)) .slice(0, 15); @@ -1952,6 +1955,19 @@ export class LosslessAPI { return `https://resources.tidal.com/images/${formattedId}/${size}x${size}.jpg`; } + getCoverSrcset(id) { + if ( + !id || + (typeof id === 'string' && (id.startsWith('http') || id.startsWith('blob:') || id.startsWith('assets/'))) + ) { + return ''; + } + + const formattedId = String(id).replace(/-/g, '/'); + const baseUrl = `https://resources.tidal.com/images/${formattedId}`; + return `${baseUrl}/160x160.jpg 160w, ${baseUrl}/320x320.jpg 320w, ${baseUrl}/640x640.jpg 640w`; + } + getArtistPictureUrl(id, size = '320') { if (!id) { return `https://picsum.photos/seed/${Math.random()}/${size}`; @@ -1965,6 +1981,16 @@ export class LosslessAPI { return `https://resources.tidal.com/images/${formattedId}/${size}x${size}.jpg`; } + getArtistPictureSrcset(id) { + if (!id || (typeof id === 'string' && (id.startsWith('blob:') || id.startsWith('assets/')))) { + return ''; + } + + const formattedId = String(id).replace(/-/g, '/'); + const baseUrl = `https://resources.tidal.com/images/${formattedId}`; + return `${baseUrl}/160x160.jpg 160w, ${baseUrl}/320x320.jpg 320w, ${baseUrl}/640x640.jpg 640w`; + } + getVideoCoverUrl(imageId, size = '1280') { if (!imageId) { return null; diff --git a/js/app.js b/js/app.js index 74db58a..a61aff2 100644 --- a/js/app.js +++ b/js/app.js @@ -131,8 +131,9 @@ async function fetchcontributors() { const response = await fetch('https://api.samidy.com/api/contributors'); if (!response.ok) return; const data1 = await response.json(); + if (!Array.isArray(data1)) return; - const data = data1.filter( + let data = data1.filter( (user) => user.type !== 'Bot' && user.login !== 'edidealt' && user.login !== 'satanyahoo' ); @@ -142,6 +143,8 @@ async function fetchcontributors() { edideaur.contributions += data1.find((u) => u.login === 'satanyahoo')?.contributions || 0; } + data.sort((a, b) => b.contributions - a.contributions); + const con = document.querySelector('.about-contributors'); if (!con) return; @@ -149,14 +152,22 @@ async function fetchcontributors() { const userDIV = document.createElement('div'); userDIV.innerHTML = ` - ${user.login} + ${user.login} ${user.login} Contributions: ${user.contributions} `; con.appendChild(userDIV); }); - } catch (e) {} + } catch (e) { + const con = document.querySelector('.about-contributors-failed'); + if (!con) return; + const userDIV = document.createElement('div'); + userDIV.innerHTML = ` +

Failed to Fetch Contributor List

+ `; + con.appendChild(userDIV); + } } async function loadMetadataModule() { @@ -2894,7 +2905,7 @@ document.addEventListener('DOMContentLoaded', async () => { if (user) { const data = await syncManager.getUserData(); if (data && data.profile && data.profile.avatar_url) { - headerAccountImg.src = data.profile.avatar_url; + headerAccountImg.src = data.profile.avatar_url + '&s=100'; headerAccountImg.style.display = 'block'; headerAccountIcon.style.display = 'none'; return; diff --git a/js/audio-context.js b/js/audio-context.js index d70d560..037c1c1 100644 --- a/js/audio-context.js +++ b/js/audio-context.js @@ -113,6 +113,15 @@ class AudioContextManager { // Callbacks for audio graph changes (for visualizers like Butterchurn) this._graphChangeCallbacks = []; + // --- Graphic EQ (16-band, separate chain) --- + this.geqFilters = []; + this.geqPreampNode = null; + this.geqOutputNode = null; + this.isGraphicEQEnabled = equalizerSettings.isGraphicEqEnabled(); + this.geqFrequencies = [25, 40, 63, 100, 160, 250, 400, 630, 1000, 1600, 2500, 4000, 6300, 10000, 16000, 20000]; + this.geqGains = equalizerSettings.getGraphicEqGains(); + this.geqPreamp = equalizerSettings.getGraphicEqPreamp(); + // Load saved settings this._loadSettings(); } @@ -308,17 +317,12 @@ class AudioContextManager { try { const AudioContext = window.AudioContext || window.webkitAudioContext; - const highResOptions = { sampleRate: 192000, latencyHint: 'playback' }; try { - this.audioContext = new AudioContext(highResOptions); - console.log(`[AudioContext] Created with high-res settings: ${this.audioContext.sampleRate}Hz`); + this.audioContext = new AudioContext({ latencyHint: 'playback' }); + console.log(`[AudioContext] Created: ${this.audioContext.sampleRate}Hz`); } catch { - try { - this.audioContext = new AudioContext({ latencyHint: 'playback' }); - } catch { - this.audioContext = new AudioContext(); - } + this.audioContext = new AudioContext(); } if (!this.sources.has(audioElement)) { @@ -331,6 +335,7 @@ class AudioContextManager { this.analyser.smoothingTimeConstant = 0.7; this._createEQ(); + this._createGraphicEQ(); this.outputNode = this.audioContext.createGain(); this.outputNode.gain.value = 1; @@ -342,6 +347,21 @@ class AudioContextManager { this._connectGraph(); + // Auto-recover from unexpected suspensions (e.g. background throttling) + this.audioContext.addEventListener('statechange', () => { + if (this.audioContext.state === 'interrupted' || this.audioContext.state === 'suspended') { + console.log(`[AudioContext] State changed to ${this.audioContext.state}, attempting resume`); + // Use a short delay to let the system settle before resuming + setTimeout(() => { + if (this.audioContext && this.audioContext.state !== 'running' && this.source) { + this.audioContext.resume().catch((e) => { + console.warn('[AudioContext] Auto-resume failed:', e); + }); + } + }, 100); + } + }); + this.isInitialized = true; } catch (e) { console.warn('[AudioContext] Init failed:', e); @@ -380,65 +400,76 @@ class AudioContextManager { } /** - * Connect the audio graph based on EQ and mono audio state + * Connect the audio graph based on EQ and mono audio state. + * Uses connect-before-disconnect ordering to avoid audio dropouts: + * the new chain is wired up first, then the old connections are torn down. */ _connectGraph() { if (!this.isInitialized || !this.source || !this.audioContext) return; - try { - // Disconnect everything first - try { - this.source.disconnect(); - } catch { - // node may already be disconnected - } - this.outputNode.disconnect(); - if (this.volumeNode) { - this.volumeNode.disconnect(); - } - this.analyser.disconnect(); + // Ensure graphic EQ nodes exist + if (this.geqFilters.length === 0 && this.isGraphicEQEnabled) { + this._createGraphicEQ(); + } - if (this.monoMergerNode) { - try { - this.monoMergerNode.disconnect(); - } catch { - // Ignore if not connected + // Helper: connect a chain segment from lastNode through graphic EQ (if enabled) to analyser -> volume -> dest + const connectTail = (lastNode) => { + if (this.isGraphicEQEnabled && this.geqFilters.length > 0) { + lastNode.connect(this.geqPreampNode); + this.geqPreampNode.connect(this.geqFilters[0]); + for (let i = 0; i < this.geqFilters.length - 1; i++) { + this.geqFilters[i].connect(this.geqFilters[i + 1]); } + this.geqFilters[this.geqFilters.length - 1].connect(this.geqOutputNode); + this.geqOutputNode.connect(this.analyser); + } else { + lastNode.connect(this.analyser); + } + this.analyser.connect(this.volumeNode); + this.volumeNode.connect(this.audioContext.destination); + }; + + try { + // Ensure mono gain node exists if needed + if (this.isMonoAudioEnabled && this.monoMergerNode && !this.monoGainNode) { + this.monoGainNode = this.audioContext.createGain(); + this.monoGainNode.gain.value = 0.5; } + // --- 1. Disconnect all existing connections --- + const safeDisconnect = (node) => { + try { + node?.disconnect(); + } catch { + /* */ + } + }; + safeDisconnect(this.source); + safeDisconnect(this.monoGainNode); + safeDisconnect(this.monoMergerNode); + safeDisconnect(this.preampNode); + this.filters.forEach(safeDisconnect); + safeDisconnect(this.outputNode); + safeDisconnect(this.geqPreampNode); + this.geqFilters.forEach(safeDisconnect); + safeDisconnect(this.geqOutputNode); + safeDisconnect(this.analyser); + safeDisconnect(this.volumeNode); + + // --- 2. Reconnect the graph --- let lastNode = this.source; - // Apply mono audio if enabled if (this.isMonoAudioEnabled && this.monoMergerNode) { - // Reuse persistent gain node to avoid leaking AudioNodes - if (!this.monoGainNode) { - this.monoGainNode = this.audioContext.createGain(); - this.monoGainNode.gain.value = 0.5; // Reduce volume to prevent clipping when mixing - } - try { - this.monoGainNode.disconnect(); - } catch { - /* not connected */ - } - - // Connect source to mono gain this.source.connect(this.monoGainNode); - - // Connect mono gain to both inputs of the merger this.monoGainNode.connect(this.monoMergerNode, 0, 0); this.monoGainNode.connect(this.monoMergerNode, 0, 1); - lastNode = this.monoMergerNode; - console.log('[AudioContext] Mono audio enabled'); } if (this.isEQEnabled && this.filters.length > 0) { - // EQ enabled: lastNode -> preamp -> EQ filters -> output -> analyser -> volume -> destination - // Connect filter chain for (let i = 0; i < this.filters.length - 1; i++) { this.filters[i].connect(this.filters[i + 1]); } - // Connect preamp to first filter if (this.preampNode) { lastNode.connect(this.preampNode); this.preampNode.connect(this.filters[0]); @@ -446,22 +477,15 @@ class AudioContextManager { lastNode.connect(this.filters[0]); } this.filters[this.filters.length - 1].connect(this.outputNode); - this.outputNode.connect(this.analyser); - this.analyser.connect(this.volumeNode); - this.volumeNode.connect(this.audioContext.destination); - console.log('[AudioContext] EQ connected'); + connectTail(this.outputNode); } else { - // EQ disabled: lastNode -> analyser -> volume -> destination - lastNode.connect(this.analyser); - this.analyser.connect(this.volumeNode); - this.volumeNode.connect(this.audioContext.destination); + connectTail(lastNode); } // Notify visualizers that graph has been reconnected this._notifyGraphChange(); } catch (e) { console.warn('[AudioContext] Failed to connect graph:', e); - // Fallback: direct connection try { this.source.connect(this.audioContext.destination); } catch { @@ -815,11 +839,22 @@ class AudioContextManager { this.currentQs = newQs; this.currentGains = newGains; - // Rebuild EQ so _createEQ picks up the new types/Qs if (this.isInitialized && this.audioContext) { - this._destroyEQ(); - this._createEQ(); - this._connectGraph(); + // If filter count matches, update params in-place (no graph rebuild) + if (this.filters.length === count) { + const now = this.audioContext.currentTime; + this.filters.forEach((filter, i) => { + filter.type = newTypes[i] || 'peaking'; + filter.frequency.setTargetAtTime(newFrequencies[i], now, 0.005); + filter.gain.setTargetAtTime(newGains[i], now, 0.005); + filter.Q.setTargetAtTime(newQs[i] > 0 ? newQs[i] : this._calculateQ(i), now, 0.005); + }); + } else { + // Band count changed — must rebuild + this._destroyEQ(); + this._createEQ(); + this._connectGraph(); + } } // Apply preamp (skip if caller manages preamp externally) @@ -955,6 +990,97 @@ class AudioContextManager { return false; } } + + // ======================================== + // Graphic EQ (16-band, independent chain) + // ======================================== + + _createGraphicEQ() { + if (!this.audioContext) return; + this.geqPreampNode = this.audioContext.createGain(); + const gainValue = Math.pow(10, (this.geqPreamp || 0) / 20); + this.geqPreampNode.gain.value = gainValue; + + this.geqOutputNode = this.audioContext.createGain(); + this.geqOutputNode.gain.value = 1; + + this.geqFilters = this.geqFrequencies.map((freq, i) => { + const filter = this.audioContext.createBiquadFilter(); + filter.type = 'peaking'; + filter.frequency.value = freq; + filter.Q.value = 2.5; // constant Q for 16-band + filter.gain.value = this.geqGains[i] || 0; + return filter; + }); + } + + _destroyGraphicEQ() { + this.geqFilters.forEach((f) => { + try { + f.disconnect(); + } catch { + /* */ + } + }); + this.geqFilters = []; + if (this.geqPreampNode) { + try { + this.geqPreampNode.disconnect(); + } catch { + /* */ + } + this.geqPreampNode = null; + } + if (this.geqOutputNode) { + try { + this.geqOutputNode.disconnect(); + } catch { + /* */ + } + this.geqOutputNode = null; + } + } + + toggleGraphicEQ(enabled) { + this.isGraphicEQEnabled = enabled; + equalizerSettings.setGraphicEqEnabled(enabled); + if (this.isInitialized) { + this._connectGraph(); + } + } + + setGraphicEqBandGain(bandIndex, gainDb) { + if (bandIndex < 0 || bandIndex >= 16) return; + this.geqGains[bandIndex] = Math.max(-30, Math.min(30, gainDb)); + if (this.geqFilters[bandIndex] && this.audioContext) { + const now = this.audioContext.currentTime; + this.geqFilters[bandIndex].gain.setTargetAtTime(this.geqGains[bandIndex], now, 0.01); + } + equalizerSettings.setGraphicEqGains([...this.geqGains]); + } + + setGraphicEqAllGains(gains) { + if (!Array.isArray(gains)) return; + const now = this.audioContext?.currentTime || 0; + gains.forEach((g, i) => { + if (i >= 16) return; + this.geqGains[i] = Math.max(-30, Math.min(30, g)); + if (this.geqFilters[i]) { + this.geqFilters[i].gain.setTargetAtTime(this.geqGains[i], now, 0.01); + } + }); + equalizerSettings.setGraphicEqGains([...this.geqGains]); + } + + setGraphicEqPreamp(db) { + this.geqPreamp = Math.max(-20, Math.min(20, parseFloat(db) || 0)); + if (this.geqPreampNode && this.audioContext) { + const gainValue = Math.pow(10, this.geqPreamp / 20); + const now = this.audioContext.currentTime; + this.geqPreampNode.gain.setTargetAtTime(gainValue, now, 0.01); + } + equalizerSettings.setGraphicEqPreamp(this.geqPreamp); + } } // Export singleton instance diff --git a/js/lyrics.js b/js/lyrics.js index 59307ca..96a7dee 100644 --- a/js/lyrics.js +++ b/js/lyrics.js @@ -10,7 +10,28 @@ import { SVG_GLOBE, } from './icons.js'; import { sidePanelManager } from './side-panel.js'; -import('@uimaxbai/am-lyrics/am-lyrics.js').catch(console.error); + +const loadAmLyrics = () => { + const images = Array.from(document.images).filter((img) => !img.complete); + if (images.length === 0) { + import('@uimaxbai/am-lyrics/am-lyrics.js').catch(console.error); + } else { + Promise.all( + images.map( + (img) => + new Promise((res) => { + img.onload = img.onerror = res; + }) + ) + ).then(() => import('@uimaxbai/am-lyrics/am-lyrics.js').catch(console.error)); + } +}; + +if (document.readyState === 'complete') { + loadAmLyrics(); +} else { + window.addEventListener('load', loadAmLyrics); +} // Check if text contains Japanese, Chinese, or Korean characters function containsAsianText(text) { @@ -278,13 +299,13 @@ export class LyricsManager { // Load Kuroshiro from CDN if (!window.Kuroshiro) { - await this.loadScript('https://unpkg.com/kuroshiro@1.2.0/dist/kuroshiro.min.js'); + await this.loadScript('https://cdn.jsdelivr.net/npm/kuroshiro@1.2.0/dist/kuroshiro.min.js'); } // Load Kuromoji analyzer from CDN if (!window.KuromojiAnalyzer) { await this.loadScript( - 'https://unpkg.com/kuroshiro-analyzer-kuromoji@1.1.0/dist/kuroshiro-analyzer-kuromoji.min.js' + 'https://cdn.jsdelivr.net/npm/kuroshiro-analyzer-kuromoji@1.1.0/dist/kuroshiro-analyzer-kuromoji.min.js' ); } @@ -1002,7 +1023,6 @@ function applyFullscreenLyricsShadowTweaks(amLyrics, container) { .lyrics-line:not(.active):not(.pre-active) { opacity: 0.44; } - .lyrics-line-container { transition: transform 0.72s cubic-bezier(0.22, 1, 0.36, 1), diff --git a/js/music-api.js b/js/music-api.js index 0888c9f..1590452 100644 --- a/js/music-api.js +++ b/js/music-api.js @@ -223,6 +223,13 @@ export class MusicAPI { return this.tidalAPI.getCoverUrl(this.stripProviderPrefix(id), size); } + getCoverSrcset(id) { + if (typeof id === 'string' && id.startsWith('blob:')) { + return ''; + } + return this.tidalAPI.getCoverSrcset(this.stripProviderPrefix(id)); + } + getVideoCoverUrl(imageId, size = '1280') { if (!imageId) { return null; @@ -260,6 +267,10 @@ export class MusicAPI { return this.tidalAPI.getArtistPictureUrl(this.stripProviderPrefix(id), size); } + getArtistPictureSrcset(id) { + return this.tidalAPI.getArtistPictureSrcset(this.stripProviderPrefix(id)); + } + extractStreamUrlFromManifest(manifest) { return this.tidalAPI.extractStreamUrlFromManifest(manifest); } diff --git a/js/player.js b/js/player.js index 5b779a0..81f57fe 100644 --- a/js/player.js +++ b/js/player.js @@ -98,6 +98,24 @@ export class Player { }); } + const waitForImagesLoading = () => { + const images = Array.from(document.images).filter((img) => !img.complete); + if (images.length === 0) return Promise.resolve(); + return Promise.all( + images.map( + (img) => + new Promise((res) => { + img.onload = img.onerror = res; + }) + ) + ); + }; + + if (document.readyState !== 'complete') { + await new Promise((resolve) => window.addEventListener('load', resolve)); + } + await waitForImagesLoading(); + // Initialize Shaka player const shaka = await import('shaka-player'); shaka.polyfill.installAll(); @@ -148,9 +166,13 @@ export class Player { await this.saveQueueState(); }); - // Handle visibility change for iOS - AudioContext gets suspended when screen locks + // Handle visibility change - AudioContext can be suspended when backgrounded document.addEventListener('visibilitychange', async () => { const el = this.activeElement; + if (document.visibilityState === 'hidden' && !el.paused) { + // Proactively resume context when going to background to prevent suspension + void audioContextManager.resume(); + } if (document.visibilityState === 'visible' && !el.paused) { // Ensure audio context is resumed when user returns to the app if (!audioContextManager.isReady()) { @@ -307,8 +329,9 @@ export class Player { if (coverEl) { const videoCoverUrl = track.videoUrl || track.videoCoverUrl || track.album?.videoCoverUrl || null; - const coverUrl = - videoCoverUrl || this.api.getCoverUrl(track.image || track.cover || track.album?.cover); + const coverId = track.image || track.cover || track.album?.cover; + const coverUrl = videoCoverUrl || this.api.getCoverUrl(coverId); + const coverSrcset = videoCoverUrl ? null : this.api.getCoverSrcset(coverId); if (videoCoverUrl) { if (coverEl.tagName === 'IMG') { @@ -326,14 +349,24 @@ export class Player { coverEl.src = videoCoverUrl; } } else { + const setImgSrcset = (img) => { + if (img.getAttribute('src') !== coverUrl) img.src = coverUrl; + if (coverSrcset) { + img.setAttribute('srcset', coverSrcset); + img.setAttribute('sizes', '(max-width: 640px) 160px, (max-width: 1024px) 320px, 640px'); + } else { + img.removeAttribute('srcset'); + img.removeAttribute('sizes'); + } + }; if (coverEl.tagName === 'VIDEO') { const img = document.createElement('img'); - img.src = coverUrl; img.className = coverEl.className; img.id = coverEl.id; + setImgSrcset(img); coverEl.replaceWith(img); } else { - coverEl.src = coverUrl; + setImgSrcset(coverEl); } } } @@ -870,8 +903,19 @@ export class Player { } else { if (coverEl) { coverEl.style.display = 'block'; - const coverUrl = this.api.getCoverUrl(track.image || track.cover || track.album?.cover); - if (coverEl.src !== coverUrl) coverEl.src = coverUrl; + const coverId = track.image || track.cover || track.album?.cover; + const coverUrl = this.api.getCoverUrl(coverId); + const coverSrcset = this.api.getCoverSrcset(coverId); + if (coverEl.getAttribute('src') !== coverUrl) { + coverEl.src = coverUrl; + if (coverSrcset) { + coverEl.setAttribute('srcset', coverSrcset); + coverEl.setAttribute('sizes', '(max-width: 640px) 160px, (max-width: 1024px) 320px, 640px'); + } else { + coverEl.removeAttribute('srcset'); + coverEl.removeAttribute('sizes'); + } + } } if (this.audio) { const isInFullscreen = document.getElementById('fullscreen-cover-overlay')?.style.display === 'flex'; @@ -2054,7 +2098,41 @@ export class Player { updateMediaSessionPlaybackState() { if (!('mediaSession' in navigator)) return; - navigator.mediaSession.playbackState = this.activeElement.paused ? 'paused' : 'playing'; + const isPlaying = !this.activeElement.paused; + navigator.mediaSession.playbackState = isPlaying ? 'playing' : 'paused'; + + // Start/stop Android foreground service to prevent background audio throttling + this._updateBackgroundAudioService(isPlaying); + } + + /** + * On Android (Capacitor), start or stop the foreground service that keeps + * the WebView alive so Web Audio EQ processing isn't throttled. + */ + _updateBackgroundAudioService(isPlaying) { + if (this._bgAudioPending) return; + this._bgAudioPending = true; + + // Lazy-load Capacitor core; no-op on web/iOS + void (async () => { + try { + const { Capacitor } = await import('@capacitor/core'); + if (Capacitor.getPlatform() !== 'android') return; + const { registerPlugin } = await import('@capacitor/core'); + if (!this._bgAudioPlugin) { + this._bgAudioPlugin = registerPlugin('BackgroundAudio'); + } + if (isPlaying) { + await this._bgAudioPlugin.start(); + } else { + await this._bgAudioPlugin.stop(); + } + } catch { + // Not running in Capacitor or plugin unavailable — ignore + } finally { + this._bgAudioPending = false; + } + })(); } updateMediaSessionPositionState() { diff --git a/js/settings.js b/js/settings.js index 7900e13..2b8b187 100644 --- a/js/settings.js +++ b/js/settings.js @@ -57,6 +57,8 @@ async function getButterchurnPresets(...args) { // Module-level state for AutoEQ (persists across re-initializations) let _autoeqIndex = []; +let _graphAbortController = null; +let _graphResizeObserver = null; export async function initializeSettings(scrobbler, player, api, ui) { // Restore last active settings tab @@ -1231,6 +1233,154 @@ export async function initializeSettings(scrobbler, player, api, ui) { }); } + // ======================================== + // 16-Band Graphic Equalizer (Legacy EQ mode) + // ======================================== + const GEQ_LABELS = [ + '25', + '40', + '63', + '100', + '160', + '250', + '400', + '630', + '1K', + '1.6K', + '2.5K', + '4K', + '6.3K', + '10K', + '16K', + '20K', + ]; + const geqBandsContainer = document.getElementById('graphic-eq-bands'); + const geqPreampSlider = document.getElementById('graphic-eq-preamp-slider'); + const geqPreampValue = document.getElementById('graphic-eq-preamp-value'); + const geqPresetSelect = document.getElementById('graphic-eq-preset-select'); + const geqResetBtn = document.getElementById('graphic-eq-reset-btn'); + + const legacyGeqBandsContainer = document.getElementById('legacy-graphic-eq-bands'); + const legacyGeqPreampSlider = document.getElementById('legacy-graphic-eq-preamp-slider'); + const legacyGeqPreampValue = document.getElementById('legacy-graphic-eq-preamp-value'); + const legacyGeqPresetSelect = document.getElementById('legacy-graphic-eq-preset-select'); + const legacyGeqResetBtn = document.getElementById('legacy-graphic-eq-reset-btn'); + + const geqPreampSliders = [geqPreampSlider, legacyGeqPreampSlider].filter(Boolean); + const geqPreampValues = [geqPreampValue, legacyGeqPreampValue].filter(Boolean); + const geqPresetSelects = [geqPresetSelect, legacyGeqPresetSelect].filter(Boolean); + + let geqGains = equalizerSettings.getGraphicEqGains() || new Array(16).fill(0); + let geqPreamp = equalizerSettings.getGraphicEqPreamp() || 0; + const geqRange = equalizerSettings.getRange(); + + // Sync all slider UIs across both containers + const geqSyncAllSliders = () => { + geqGains.forEach((g, i) => { + ['geq', 'legacy-geq'].forEach((prefix) => { + const sl = document.getElementById(`${prefix}-slider-${i}`); + const vl = document.getElementById(`${prefix}-value-${i}`); + if (sl) sl.value = g; + if (vl) vl.textContent = `${g > 0 ? '+' : ''}${g.toFixed(1)}`; + }); + }); + }; + + // Build 16 vertical slider bands into a container + const buildGeqBands = (container, idPrefix) => { + if (!container) return; + container.innerHTML = ''; + GEQ_LABELS.forEach((_label, i) => { + const band = document.createElement('div'); + band.className = 'graphic-eq-band'; + + const valueLabel = document.createElement('span'); + valueLabel.className = 'graphic-eq-band-value'; + valueLabel.textContent = `${geqGains[i] > 0 ? '+' : ''}${geqGains[i].toFixed(1)}`; + valueLabel.id = `${idPrefix}-value-${i}`; + + const sliderWrap = document.createElement('div'); + sliderWrap.className = 'graphic-eq-band-slider-wrap'; + + const slider = document.createElement('input'); + slider.type = 'range'; + slider.min = geqRange.min; + slider.max = geqRange.max; + slider.step = '0.1'; + slider.value = geqGains[i]; + slider.id = `${idPrefix}-slider-${i}`; + slider.setAttribute('aria-label', `${GEQ_LABELS[i]} Hz`); + + slider.addEventListener('input', () => { + const gain = parseFloat(slider.value); + geqGains[i] = gain; + equalizerSettings.setGraphicEqGains(geqGains); + audioContextManager.setGraphicEqBandGain(i, gain); + geqSyncAllSliders(); + geqPresetSelects.forEach((s) => (s.value = '')); + }); + + sliderWrap.appendChild(slider); + + const freqLabel = document.createElement('span'); + freqLabel.className = 'graphic-eq-band-label'; + freqLabel.textContent = GEQ_LABELS[i]; + + band.appendChild(valueLabel); + band.appendChild(sliderWrap); + band.appendChild(freqLabel); + container.appendChild(band); + }); + }; + + buildGeqBands(geqBandsContainer, 'geq'); + buildGeqBands(legacyGeqBandsContainer, 'legacy-geq'); + + // Wire up preamp sliders + geqPreampSliders.forEach((slider) => { + slider.value = geqPreamp; + slider.addEventListener('input', () => { + geqPreamp = parseFloat(slider.value); + const text = `${geqPreamp.toFixed(1)} dB`; + geqPreampValues.forEach((v) => (v.textContent = text)); + geqPreampSliders.forEach((s) => { + if (s !== slider) s.value = geqPreamp; + }); + equalizerSettings.setGraphicEqPreamp(geqPreamp); + audioContextManager.setGraphicEqPreamp(geqPreamp); + }); + }); + geqPreampValues.forEach((v) => (v.textContent = `${geqPreamp} dB`)); + + // Wire up preset selects + geqPresetSelects.forEach((select) => { + select.addEventListener('change', () => { + const key = select.value; + if (!key) return; + const presets = getPresetsForBandCount(16); + const preset = presets[key]; + if (!preset) return; + geqGains = [...preset.gains]; + equalizerSettings.setGraphicEqGains(geqGains); + audioContextManager.setGraphicEqAllGains(geqGains); + geqSyncAllSliders(); + geqPresetSelects.forEach((s) => { + if (s !== select) s.value = key; + }); + }); + }); + + // Wire up reset buttons + [geqResetBtn, legacyGeqResetBtn].filter(Boolean).forEach((btn) => { + btn.addEventListener('click', () => { + geqGains = new Array(16).fill(0); + equalizerSettings.setGraphicEqGains(geqGains); + audioContextManager.setGraphicEqAllGains(geqGains); + geqSyncAllSliders(); + geqPresetSelects.forEach((s) => (s.value = 'flat')); + }); + }); + // ======================================== // Precision AutoEQ - Redesigned Equalizer // ======================================== @@ -1362,6 +1512,15 @@ export async function initializeSettings(scrobbler, player, api, ui) { /** * Draw the frequency response graph with Original, Target, and Corrected curves */ + let _drawGraphRafId = null; + const scheduleDrawAutoEQGraph = () => { + if (_drawGraphRafId) return; + _drawGraphRafId = requestAnimationFrame(() => { + _drawGraphRafId = null; + drawAutoEQGraph(); + }); + }; + const drawAutoEQGraph = () => { if (!autoeqCanvas) return; const activeBands = getActiveBands(); @@ -1859,18 +2018,82 @@ export async function initializeSettings(scrobbler, player, api, ui) { if (autoeqCanvas) { autoeqCanvas.addEventListener('mousedown', (e) => { const coords = getCanvasCoords(e); - const nodeIdx = findClosestNode(coords.x, coords.y, 18); + let nodeIdx = findClosestNode(coords.x, coords.y, 18); if (nodeIdx >= 0) { + // Clicked directly on a node - start dragging draggedNode = nodeIdx; autoeqCanvas.style.cursor = 'grabbing'; e.preventDefault(); + } else { + // Clicked empty space - find nearest node (no threshold) and snap it + nodeIdx = findClosestNode(coords.x, coords.y, Infinity); + if (nodeIdx >= 0) { + const bands = getActiveBands(); + if (bands && bands[nodeIdx]) { + const rect = autoeqCanvas.getBoundingClientRect(); + const padLeft = 40, + padRight = 10, + padTop = 10, + padBottom = 30; + const w = rect.width - padLeft - padRight; + const h = rect.height - padTop - padBottom; + const isParam = currentMode === 'parametric'; + const dbCenter = isParam ? 0 : 75; + const dbHalf = isParam ? graphDbHalfParametric : graphDbHalfAutoEQ; + const dbMin = dbCenter - dbHalf; + const dbMax = dbCenter + dbHalf; + + // Snap frequency to click position + const freq = xToFreq(coords.x - padLeft, w); + bands[nodeIdx].freq = Math.max(20, Math.min(20000, freq)); + + // Snap gain to click position + if (isParam) { + const newGain = yToDb(coords.y - padTop, h, dbMin, dbMax); + bands[nodeIdx].gain = Math.max(-30, Math.min(30, Math.round(newGain * 10) / 10)); + } else { + const corrGain = interpolate(bands[nodeIdx].freq, autoeqCorrectedCurve || []); + const newDb = yToDb(coords.y - padTop, h, dbMin, dbMax); + const gainDelta = newDb - corrGain; + bands[nodeIdx].gain = Math.max(-30, Math.min(30, bands[nodeIdx].gain + gainDelta * 0.3)); + } + + draggedNode = nodeIdx; + autoeqCanvas.style.cursor = 'grabbing'; + computeCorrectedCurve(); + applyBandsToAudio(bands); + drawAutoEQGraph(); + renderBandControls(bands); + e.preventDefault(); + } + } } }); - autoeqCanvas.addEventListener('mousemove', (e) => { - const coords = getCanvasCoords(e); - const bands = getActiveBands(); - if (draggedNode !== null && bands) { + // Helper to compute canvas-relative coords from any mouse event (even outside the canvas) + const getCanvasCoordsFromEvent = (e) => { + const rect = autoeqCanvas.getBoundingClientRect(); + return { x: e.clientX - rect.left, y: e.clientY - rect.top }; + }; + + // Clean up previous document-level listeners and observer on re-initialization + if (_graphAbortController) _graphAbortController.abort(); + _graphAbortController = new AbortController(); + const graphSignal = _graphAbortController.signal; + if (_graphResizeObserver) { + _graphResizeObserver.disconnect(); + _graphResizeObserver = null; + } + + // Document-level mousemove so dragging continues outside the canvas + document.addEventListener( + 'mousemove', + (e) => { + if (draggedNode === null) return; + const bands = getActiveBands(); + if (!bands) return; + + const coords = getCanvasCoordsFromEvent(e); const rect = autoeqCanvas.getBoundingClientRect(); const padLeft = 40, padRight = 10, @@ -1907,34 +2130,49 @@ export async function initializeSettings(scrobbler, player, api, ui) { graphAnimFrame = null; }); } + }, + { signal: graphSignal } + ); + + // Canvas-only mousemove for hover cursor changes (when not dragging) + autoeqCanvas.addEventListener('mousemove', (e) => { + if (draggedNode !== null) return; // dragging is handled by document listener + const coords = getCanvasCoords(e); + const padLeft = 40; + if (coords.x <= padLeft + 10) { + autoeqCanvas.style.cursor = 'ns-resize'; + if (hoveredNode !== null) { + hoveredNode = null; + drawAutoEQGraph(); + } } else { - const padLeft = 40; - if (coords.x <= padLeft + 10) { - autoeqCanvas.style.cursor = 'ns-resize'; - if (hoveredNode !== null) { - hoveredNode = null; - drawAutoEQGraph(); - } - } else { - const newHovered = findClosestNode(coords.x, coords.y, 18); - if (newHovered !== hoveredNode) { - hoveredNode = newHovered; - autoeqCanvas.style.cursor = hoveredNode >= 0 ? 'grab' : 'crosshair'; - drawAutoEQGraph(); - } + const newHovered = findClosestNode(coords.x, coords.y, 18); + if (newHovered !== hoveredNode) { + hoveredNode = newHovered; + autoeqCanvas.style.cursor = hoveredNode >= 0 ? 'grab' : 'crosshair'; + drawAutoEQGraph(); } } }); - autoeqCanvas.addEventListener('mouseup', () => { - draggedNode = null; - autoeqCanvas.style.cursor = hoveredNode >= 0 ? 'grab' : 'crosshair'; - }); + // Document-level mouseup so drag ends even if cursor is outside the canvas + document.addEventListener( + 'mouseup', + () => { + if (draggedNode !== null) { + draggedNode = null; + autoeqCanvas.style.cursor = hoveredNode >= 0 ? 'grab' : 'crosshair'; + } + }, + { signal: graphSignal } + ); autoeqCanvas.addEventListener('mouseleave', () => { - draggedNode = null; + // Only reset hover state, NOT drag state (drag continues outside canvas) hoveredNode = null; - autoeqCanvas.style.cursor = 'crosshair'; + if (draggedNode === null) { + autoeqCanvas.style.cursor = 'crosshair'; + } drawAutoEQGraph(); }); @@ -2038,74 +2276,131 @@ export async function initializeSettings(scrobbler, player, api, ui) { { passive: false } ); - // Touch support + // Touch support - snap nearest node on empty space touch, continue drag outside canvas let touchNodeIdx = -1; autoeqCanvas.addEventListener( 'touchstart', (e) => { const touch = e.touches[0]; - const coords = { - x: touch.clientX - autoeqCanvas.getBoundingClientRect().left, - y: touch.clientY - autoeqCanvas.getBoundingClientRect().top, - }; + const rect = autoeqCanvas.getBoundingClientRect(); + const coords = { x: touch.clientX - rect.left, y: touch.clientY - rect.top }; touchNodeIdx = findClosestNode(coords.x, coords.y, 25); if (touchNodeIdx >= 0) { draggedNode = touchNodeIdx; e.preventDefault(); + } else { + // Snap nearest node to touch position + touchNodeIdx = findClosestNode(coords.x, coords.y, Infinity); + if (touchNodeIdx >= 0) { + const bands = getActiveBands(); + if (bands && bands[touchNodeIdx]) { + const padLeft = 40, + padRight = 10, + padTop = 10, + padBottom = 30; + const w = rect.width - padLeft - padRight; + const h = rect.height - padTop - padBottom; + const isParam = currentMode === 'parametric'; + const dbCenter = isParam ? 0 : 75; + const dbHalf = isParam ? graphDbHalfParametric : graphDbHalfAutoEQ; + const dbMin = dbCenter - dbHalf; + const dbMax = dbCenter + dbHalf; + + const freq = xToFreq(coords.x - padLeft, w); + bands[touchNodeIdx].freq = Math.max(20, Math.min(20000, freq)); + if (isParam) { + const newGain = yToDb(coords.y - padTop, h, dbMin, dbMax); + bands[touchNodeIdx].gain = Math.max(-30, Math.min(30, Math.round(newGain * 10) / 10)); + } else { + const corrGain = interpolate(bands[touchNodeIdx].freq, autoeqCorrectedCurve || []); + const newDb = yToDb(coords.y - padTop, h, dbMin, dbMax); + const gainDelta = newDb - corrGain; + bands[touchNodeIdx].gain = Math.max( + -30, + Math.min(30, bands[touchNodeIdx].gain + gainDelta * 0.3) + ); + } + draggedNode = touchNodeIdx; + computeCorrectedCurve(); + applyBandsToAudio(bands); + drawAutoEQGraph(); + renderBandControls(bands); + e.preventDefault(); + } + } } }, { passive: false } ); - autoeqCanvas.addEventListener( + // Document-level touchmove so dragging continues outside canvas + document.addEventListener( 'touchmove', (e) => { + if (draggedNode === null) return; const tBands = getActiveBands(); - if (draggedNode !== null && tBands) { - const touch = e.touches[0]; - const rect = autoeqCanvas.getBoundingClientRect(); - const coords = { x: touch.clientX - rect.left, y: touch.clientY - rect.top }; - const padLeft = 40, - padRight = 10, - padTop = 10, - padBottom = 30; - const w = rect.width - padLeft - padRight; - const h = rect.height - padTop - padBottom; + if (!tBands) return; - const freq = xToFreq(coords.x - padLeft, w); - tBands[draggedNode].freq = Math.max(20, Math.min(20000, freq)); + const touch = e.touches[0]; + const rect = autoeqCanvas.getBoundingClientRect(); + const coords = { x: touch.clientX - rect.left, y: touch.clientY - rect.top }; + const padLeft = 40, + padRight = 10, + padTop = 10, + padBottom = 30; + const w = rect.width - padLeft - padRight; + const h = rect.height - padTop - padBottom; - if (currentMode === 'parametric') { - const newGain = yToDb(coords.y - padTop, h, -graphDbHalfParametric, graphDbHalfParametric); - tBands[draggedNode].gain = Math.max(-30, Math.min(30, Math.round(newGain * 10) / 10)); - } + const isParam = currentMode === 'parametric'; + const dbCenter = isParam ? 0 : 75; + const dbHalf = isParam ? graphDbHalfParametric : graphDbHalfAutoEQ; + const dbMin = dbCenter - dbHalf; + const dbMax = dbCenter + dbHalf; - computeCorrectedCurve(); - applyBandsToAudio(tBands); - if (!graphAnimFrame) { - graphAnimFrame = requestAnimationFrame(() => { - drawAutoEQGraph(); - renderBandControls(tBands); - graphAnimFrame = null; - }); - } - e.preventDefault(); + const freq = xToFreq(coords.x - padLeft, w); + tBands[draggedNode].freq = Math.max(20, Math.min(20000, freq)); + + if (isParam) { + const newGain = yToDb(coords.y - padTop, h, dbMin, dbMax); + tBands[draggedNode].gain = Math.max(-30, Math.min(30, Math.round(newGain * 10) / 10)); + } else { + const corrGain = interpolate(tBands[draggedNode].freq, autoeqCorrectedCurve || []); + const newDb = yToDb(coords.y - padTop, h, dbMin, dbMax); + const gainDelta = newDb - corrGain; + tBands[draggedNode].gain = Math.max(-30, Math.min(30, tBands[draggedNode].gain + gainDelta * 0.3)); } + + computeCorrectedCurve(); + applyBandsToAudio(tBands); + if (!graphAnimFrame) { + graphAnimFrame = requestAnimationFrame(() => { + drawAutoEQGraph(); + renderBandControls(tBands); + graphAnimFrame = null; + }); + } + e.preventDefault(); }, - { passive: false } + { passive: false, signal: graphSignal } ); - autoeqCanvas.addEventListener('touchend', () => { - draggedNode = null; - touchNodeIdx = -1; - }); + document.addEventListener( + 'touchend', + () => { + if (draggedNode !== null) { + draggedNode = null; + touchNodeIdx = -1; + } + }, + { signal: graphSignal } + ); // Resize observer for graph if (autoeqGraphWrapper) { - const ro = new ResizeObserver(() => { - drawAutoEQGraph(); + _graphResizeObserver = new ResizeObserver(() => { + scheduleDrawAutoEQGraph(); }); - ro.observe(autoeqGraphWrapper); + _graphResizeObserver.observe(autoeqGraphWrapper); } } @@ -2166,7 +2461,7 @@ export async function initializeSettings(scrobbler, player, api, ui) { freqVal.textContent = `${formatFreq(bands[i].freq)} Hz`; computeCorrectedCurve(); applyBandsToAudio(bands); - drawAutoEQGraph(); + scheduleDrawAutoEQGraph(); }); gainSlider.addEventListener('input', () => { @@ -2176,7 +2471,7 @@ export async function initializeSettings(scrobbler, player, api, ui) { gainVal.textContent = `${bands[i].gain > 0 ? '+' : ''}${bands[i].gain.toFixed(1)} dB`; computeCorrectedCurve(); applyBandsToAudio(bands); - drawAutoEQGraph(); + scheduleDrawAutoEQGraph(); }); qSlider.addEventListener('input', () => { @@ -2186,7 +2481,7 @@ export async function initializeSettings(scrobbler, player, api, ui) { qVal.textContent = bands[i].q.toFixed(2); computeCorrectedCurve(); applyBandsToAudio(bands); - drawAutoEQGraph(); + scheduleDrawAutoEQGraph(); }); const typeSelect = control.querySelector('.autoeq-type-select'); @@ -2196,7 +2491,7 @@ export async function initializeSettings(scrobbler, player, api, ui) { bands[i].type = typeSelect.value; computeCorrectedCurve(); applyBandsToAudio(bands); - drawAutoEQGraph(); + scheduleDrawAutoEQGraph(); }); }); }; @@ -2257,6 +2552,22 @@ export async function initializeSettings(scrobbler, player, api, ui) { }); } + // Database section collapse + const autoeqDatabaseToggle = document.getElementById('autoeq-database-toggle'); + const autoeqDatabaseCollapse = document.getElementById('autoeq-database-collapse'); + const autoeqDatabaseBody = document.getElementById('autoeq-database-body'); + if (autoeqDatabaseToggle) { + autoeqDatabaseToggle.addEventListener('click', () => { + if (autoeqDatabaseCollapse) autoeqDatabaseCollapse.classList.toggle('collapsed'); + if (autoeqDatabaseBody) + autoeqDatabaseBody.style.display = autoeqDatabaseBody.style.display === 'none' ? '' : 'none'; + if (autoeqDatabaseCollapse) { + const isExpanded = !autoeqDatabaseCollapse.classList.contains('collapsed'); + autoeqDatabaseCollapse.setAttribute('aria-expanded', String(isExpanded)); + } + }); + } + // ======================================== // Set Status Message // ======================================== @@ -3113,13 +3424,14 @@ export async function initializeSettings(scrobbler, player, api, ui) { const presetRow = document.getElementById('autoeq-preset-row'); const parametricProfiles = document.getElementById('autoeq-parametric-profiles'); const speakerSavedSection = document.getElementById('speaker-saved-section'); + const legacySection = document.getElementById('graphic-eq-section'); // Reset interactive state on switch draggedNode = null; hoveredNode = null; - // Graph always visible in all modes - if (graphSection) graphSection.style.display = ''; + // Graph visible in all modes except legacy + if (graphSection) graphSection.style.display = mode === 'legacy' ? 'none' : ''; // Only show shared AutoEq button in AutoEQ mode if (autoeqRunBtn) autoeqRunBtn.style.display = mode === 'autoeq' ? '' : 'none'; @@ -3132,6 +3444,20 @@ export async function initializeSettings(scrobbler, player, api, ui) { if (parametricProfiles) parametricProfiles.style.display = 'none'; if (speakerSection) speakerSection.style.display = 'none'; if (speakerSavedSection) speakerSavedSection.style.display = 'none'; + if (legacySection) legacySection.style.display = 'none'; + + if (mode === 'legacy') { + if (legacySection) legacySection.style.display = ''; + // Disable parametric EQ entirely - only graphic EQ active to save resources + audioContextManager.isEQEnabled = false; + audioContextManager.toggleGraphicEQ(equalizerSettings.isEnabled()); + equalizerSettings.setGraphicEqEnabled(true); + } else { + // Disable graphic EQ entirely - only parametric EQ active to save resources + audioContextManager.isEQEnabled = equalizerSettings.isEnabled(); + audioContextManager.toggleGraphicEQ(false); + equalizerSettings.setGraphicEqEnabled(false); + } if (mode === 'autoeq') { if (controlsSection) controlsSection.style.display = ''; @@ -4400,7 +4726,15 @@ export async function initializeSettings(scrobbler, player, api, ui) { equalizerSettings.setEnabled(enabled); updateEQContainerVisibility(enabled); - audioContextManager.toggleEQ(enabled); + if (currentMode === 'legacy') { + // Legacy mode uses graphic EQ chain + audioContextManager.isEQEnabled = false; + audioContextManager.toggleGraphicEQ(enabled); + } else { + // AutoEQ/Parametric/Speaker modes use parametric EQ chain + audioContextManager.toggleEQ(enabled); + audioContextManager.toggleGraphicEQ(false); + } }); } @@ -4515,7 +4849,7 @@ export async function initializeSettings(scrobbler, player, api, ui) { // Restore EQ mode on startup const savedEQMode = localStorage.getItem(EQ_MODE_KEY); - if (savedEQMode && ['autoeq', 'parametric', 'speaker'].includes(savedEQMode)) { + if (savedEQMode && ['autoeq', 'parametric', 'speaker', 'legacy'].includes(savedEQMode)) { setEQMode(savedEQMode); } diff --git a/js/storage.js b/js/storage.js index 743c42c..d7470b5 100644 --- a/js/storage.js +++ b/js/storage.js @@ -1538,6 +1538,71 @@ export const equalizerSettings = { clearLastHeadphone() { localStorage.removeItem(this.AUTOEQ_LAST_HEADPHONE_KEY); }, + + // --- Graphic EQ (16-band) separate storage --- + GEQ_ENABLED_KEY: 'graphic-eq-enabled', + GEQ_GAINS_KEY: 'graphic-eq-gains', + GEQ_PREAMP_KEY: 'graphic-eq-preamp', + + isGraphicEqEnabled() { + try { + return localStorage.getItem(this.GEQ_ENABLED_KEY) === 'true'; + } catch { + return false; + } + }, + + setGraphicEqEnabled(enabled) { + try { + localStorage.setItem(this.GEQ_ENABLED_KEY, String(!!enabled)); + } catch { + /* ignore */ + } + }, + + getGraphicEqGains() { + try { + const stored = localStorage.getItem(this.GEQ_GAINS_KEY); + if (stored) { + const parsed = JSON.parse(stored); + if (Array.isArray(parsed) && parsed.length === 16) { + return parsed.map((v) => (Number.isFinite(v) ? v : 0)); + } + } + } catch { + /* ignore */ + } + return new Array(16).fill(0); + }, + + setGraphicEqGains(gains) { + try { + localStorage.setItem(this.GEQ_GAINS_KEY, JSON.stringify(gains)); + } catch { + /* ignore */ + } + }, + + getGraphicEqPreamp() { + try { + const val = localStorage.getItem(this.GEQ_PREAMP_KEY); + if (val !== null) { + const num = parseFloat(val); + return Number.isFinite(num) ? num : 0; + } + return 0; + } catch { + return 0; + } + }, + + setGraphicEqPreamp(db) { + try { + localStorage.setItem(this.GEQ_PREAMP_KEY, String(db)); + } catch { + /* ignore */ + } + }, }; export const monoAudioSettings = { diff --git a/js/tracker.js b/js/tracker.js index de42676..5b29cd1 100644 --- a/js/tracker.js +++ b/js/tracker.js @@ -304,7 +304,7 @@ export function createProjectCardHTML(era, _artist, sheetId, trackCount) { ${playBtnHTML}
-

${escapeHtml(era.name)}

+

${escapeHtml(era.name)}

${era.timeline || 'Unreleased'} • ${trackCount} tracks

@@ -784,7 +784,7 @@ export async function renderUnreleasedPage(container) { ${artist.name}
-

${artist.name}

+

${artist.name}

Unreleased Music

`; diff --git a/js/ui.js b/js/ui.js index 164c30b..b99466d 100644 --- a/js/ui.js +++ b/js/ui.js @@ -94,7 +94,6 @@ const setFullscreenUIToggleIcon = (button, visualizerOnlyMode) => { }; const isMobileFullscreenViewport = () => window.matchMedia('(max-width: 768px)').matches; - function sortTracks(tracks, sortType) { if (sortType === 'custom') return [...tracks]; const sorted = [...tracks]; @@ -541,11 +540,44 @@ export class UIRenderer { `; } - getCoverHTML(cover, alt, className = 'card-image', loading = 'lazy', videoCoverUrl = null) { - const imageUrl = this.api.getCoverUrl(cover); + getCoverHTML( + cover, + alt, + className = 'card-image', + loading = 'lazy', + videoCoverUrl = null, + isEditorsPick = false, + type = 'album' + ) { + let size = '320'; + if (className === 'track-item-cover') { + size = '80'; + } else if (type === 'artist') { + size = '160'; + } + + const imageUrl = + type === 'artist' ? this.api.getArtistPictureUrl(cover, size) : this.api.getCoverUrl(cover, size); + if (videoCoverUrl) { return ``; } + + if ( + isEditorsPick && + cover && + typeof cover === 'string' && + !cover.startsWith('http') && + !cover.startsWith('blob:') && + !cover.startsWith('assets/') + ) { + const formattedId = String(cover).replace(/-/g, '/'); + const tidalUrl = `https://resources.tidal.com/images/${formattedId}/320x320.jpg`; + const wsrvUrl = `https://wsrv.nl/?url=${encodeURIComponent(tidalUrl)}&w=250&h=250&output=webp`; + const fetchPriorityAttr = loading === 'eager' ? ' fetchpriority="high"' : ''; + return `${alt}`; + } + return `${alt}`; } @@ -575,7 +607,7 @@ export class UIRenderer { const cardContent = `
-

${title}

+

${title}

${subtitle ? `

${subtitle}

` : ''}
`; @@ -656,7 +688,15 @@ export class UIRenderer { createUserPlaylistCardHTML(playlist, customSubtitle = null) { let imageHTML = ''; if (playlist.cover) { - imageHTML = `${playlist.name}`; + imageHTML = this.getCoverHTML( + playlist.cover, + escapeHtml(playlist.name), + 'card-image', + playlist._lazy === false ? 'eager' : 'lazy', + null, + playlist._isEditorsPick || false, + 'album' + ); } else { const tracks = playlist.tracks || []; let uniqueCovers = playlist.images || []; @@ -747,8 +787,10 @@ export class UIRenderer { album.cover, escapeHtml(album.title), 'card-image', - 'lazy', - album.videoCoverUrl + album._lazy === false ? 'eager' : 'lazy', + album.videoCoverUrl, + album._isEditorsPick || false, + 'album' ), actionButtonsHTML: `