From c9ff17558dc23f2b305d061bd40e5c4dc9db413d Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Sun, 20 Jul 2025 12:18:15 +0700 Subject: [PATCH 01/26] prettier formatting, add manifest url in console.log --- .prettierignore | 3 + .prettierrc.json | 8 + background.js | 130 +- content.js | 115 +- frontend/eslint.config.js | 57 +- frontend/index.html | 18 +- frontend/package-lock.json | 6810 +++++++++++++------------- frontend/package.json | 56 +- frontend/src/App.jsx | 121 +- frontend/src/components/results.jsx | 266 +- frontend/src/components/settings.jsx | 263 +- frontend/src/components/sidenav.jsx | 77 +- frontend/src/components/topnav.jsx | 145 +- frontend/src/index.css | 14 +- frontend/src/main.jsx | 18 +- frontend/vite.config.js | 12 +- inject.js | 1001 ++-- manifest.json | 76 +- 18 files changed, 4754 insertions(+), 4436 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc.json diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..474c34a --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +react/ +frontend/dist/ +frontend/src/assets/ \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..eb0d496 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "trailingComma": "es5", + "tabWidth": 4, + "semi": true, + "singleQuote": false, + "useTabs": false, + "printWidth": 100 +} diff --git a/background.js b/background.js index a04773a..331927d 100644 --- a/background.js +++ b/background.js @@ -1,84 +1,84 @@ // Open popout window when the extension icon is clicked chrome.browserAction.onClicked.addListener(() => { - chrome.windows.create({ - url: chrome.runtime.getURL("react/index.html"), - type: "popup", // opens as a floating window - width: 800, - height: 600 - }); + chrome.windows.create({ + url: chrome.runtime.getURL("react/index.html"), + type: "popup", // opens as a floating window + width: 800, + height: 600, + }); }); // Listen for messages and store data in chrome.storage.local chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { - const { type, data } = message; + const { type, data } = message; - switch (type) { - case "DRM_TYPE": - console.log("DRM Type:", data); - chrome.storage.local.set({ drmType: data }); - break; + switch (type) { + case "DRM_TYPE": + console.log("DRM Type:", data); + chrome.storage.local.set({ drmType: data }); + break; - case "PSSH_DATA": - console.log("Storing PSSH:", data); - chrome.storage.local.set({ latestPSSH: data }); - break; + case "PSSH_DATA": + console.log("Storing PSSH:", data); + chrome.storage.local.set({ latestPSSH: data }); + break; - case "KEYS_DATA": - console.log("Storing Decryption Keys:", data); - chrome.storage.local.set({ latestKeys: data }); - break; - - case "LICENSE_URL": - console.log("Storling License URL " + data); - chrome.storage.local.set({licenseURL: data}); - break; + case "KEYS_DATA": + console.log("Storing Decryption Keys:", data); + chrome.storage.local.set({ latestKeys: data }); + break; - default: - console.warn("Unknown message type received:", type); - } + case "LICENSE_URL": + console.log("Storling License URL " + data); + chrome.storage.local.set({ licenseURL: data }); + break; + + default: + console.warn("Unknown message type received:", type); + } }); // Set initial config and injection type on install chrome.runtime.onInstalled.addListener((details) => { - if (details.reason === "install") { - chrome.storage.local.set({ valid_config: false }, () => { - if (chrome.runtime.lastError) { - console.error("Error setting valid_config:", chrome.runtime.lastError); - } else { - console.log("valid_config set to false on first install."); - } - }); + if (details.reason === "install") { + chrome.storage.local.set({ valid_config: false }, () => { + if (chrome.runtime.lastError) { + console.error("Error setting valid_config:", chrome.runtime.lastError); + } else { + console.log("valid_config set to false on first install."); + } + }); - chrome.storage.local.set({ injection_type: "LICENSE" }, () => { - if (chrome.runtime.lastError) { - console.error("Error setting Injection Type:", chrome.runtime.lastError); - } else { - console.log("Injection type set to LICENSE on first install."); - } - }); + chrome.storage.local.set({ injection_type: "LICENSE" }, () => { + if (chrome.runtime.lastError) { + console.error("Error setting Injection Type:", chrome.runtime.lastError); + } else { + console.log("Injection type set to LICENSE on first install."); + } + }); - chrome.storage.local.set({ drm_override: "DISABLED" }, () => { - if (chrome.runtime.lastError) { - console.error("Error setting DRM Override type:", chrome.runtime.lastError); - } else { - console.log("DRM Override type set to DISABLED on first install."); - } - }); + chrome.storage.local.set({ drm_override: "DISABLED" }, () => { + if (chrome.runtime.lastError) { + console.error("Error setting DRM Override type:", chrome.runtime.lastError); + } else { + console.log("DRM Override type set to DISABLED on first install."); + } + }); - chrome.storage.local.set({ cdrm_instance: null }, () => { - if (chrome.runtime.lastError) { - console.error("Error setting CDRM instance:", chrome.runtime.lastError); - } else { - console.log("CDRM instance set to null."); - } - }); + chrome.storage.local.set({ cdrm_instance: null }, () => { + if (chrome.runtime.lastError) { + console.error("Error setting CDRM instance:", chrome.runtime.lastError); + } else { + console.log("CDRM instance set to null."); + } + }); - chrome.storage.local.set({ cdrm_api_key: null }, () => { - if (chrome.runtime.lastError) { - console.error("Error setting CDRM API Key:", chrome.runtime.lastError); - } else { - console.log("CDRM API Key set."); - } - }); - } + chrome.storage.local.set({ cdrm_api_key: null }, () => { + if (chrome.runtime.lastError) { + console.error("Error setting CDRM API Key:", chrome.runtime.lastError); + } else { + console.log("CDRM API Key set."); + } + }); + } }); diff --git a/content.js b/content.js index 31a8e53..55b7891 100644 --- a/content.js +++ b/content.js @@ -1,67 +1,96 @@ // Inject `inject.js` into the page context (function injectScript() { - const script = document.createElement('script'); - script.src = chrome.runtime.getURL('inject.js'); - script.type = 'text/javascript'; - script.onload = () => script.remove(); // Clean up - // Inject directly into or - (document.documentElement || document.head || document.body).appendChild(script); + const script = document.createElement("script"); + script.src = chrome.runtime.getURL("inject.js"); + script.type = "text/javascript"; + script.onload = () => script.remove(); // Clean up + // Inject directly into or + (document.documentElement || document.head || document.body).appendChild(script); })(); // Listen for messages from the injected script -window.addEventListener("message", function(event) { +window.addEventListener("message", function (event) { if (event.source !== window) return; - if (["__DRM_TYPE__", "__PSSH_DATA__", "__KEYS_DATA__", "__LICENSE_URL__"].includes(event.data?.type)) { + if ( + ["__DRM_TYPE__", "__PSSH_DATA__", "__KEYS_DATA__", "__LICENSE_URL__"].includes( + event.data?.type + ) + ) { chrome.runtime.sendMessage({ type: event.data.type.replace("__", "").replace("__", ""), - data: event.data.data + data: event.data.data, }); } if (event.data.type === "__GET_CDM_DEVICES__") { + chrome.storage.local.get(["widevine_device", "playready_device"], (result) => { + const widevine_device = result.widevine_device || null; + const playready_device = result.playready_device || null; - chrome.storage.local.get(["widevine_device", "playready_device"], (result) => { - const widevine_device = result.widevine_device || null; - const playready_device = result.playready_device || null; - - window.postMessage( - { - type: "__CDM_DEVICES__", - widevine_device, - playready_device - }, - "*" - ); - }); + window.postMessage( + { + type: "__CDM_DEVICES__", + widevine_device, + playready_device, + }, + "*" + ); + }); } if (event.data.type === "__GET_INJECTION_TYPE__") { + chrome.storage.local.get("injection_type", (result) => { + const injectionType = result.injection_type || "LICENSE"; - chrome.storage.local.get("injection_type", (result) => { - const injectionType = result.injection_type || "LICENSE"; - - window.postMessage( - { - type: "__INJECTION_TYPE__", - injectionType - }, - "*" - ); - }); + window.postMessage( + { + type: "__INJECTION_TYPE__", + injectionType, + }, + "*" + ); + }); } + if (event.data.type === "__GET_DRM_OVERRIDE__") { + chrome.storage.local.get("drm_override", (result) => { + const drmOverride = result.drm_override || "DISABLED"; - chrome.storage.local.get("drm_override", (result) => { - const drmOverride = result.drm_override || "DISABLED"; + window.postMessage( + { + type: "__DRM_OVERRIDE__", + drmOverride, + }, + "*" + ); + }); + } - window.postMessage( - { - type: "__DRM_OVERRIDE__", - drmOverride - }, - "*" - ); - }); + // Manifest header and URL + + const seenManifestUrls = new Set(); + + if (event.data?.type === "__MANIFEST_URL__") { + const url = event.data.data; + if (seenManifestUrls.has(url)) return; + seenManifestUrls.add(url); + console.log("✅ [Content] Unique manifest URL:", url); + + chrome.runtime.sendMessage({ + type: "MANIFEST_URL_FOUND", + data: url, + }); + } + + if (event.data?.type === "__MANIFEST_HEADERS__") { + const { url, headers } = event.data; + console.log("[Content.js] Manifest Headers:", url, headers); + + chrome.runtime.sendMessage({ + type: "MANIFEST_HEADERS", + url, + headers, + }); } }); diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js index ec2b712..ab358f6 100644 --- a/frontend/eslint.config.js +++ b/frontend/eslint.config.js @@ -1,33 +1,30 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; export default [ - { ignores: ['dist'] }, - { - files: ['**/*.{js,jsx}'], - languageOptions: { - ecmaVersion: 2020, - globals: globals.browser, - parserOptions: { - ecmaVersion: 'latest', - ecmaFeatures: { jsx: true }, - sourceType: 'module', - }, + { ignores: ["dist"] }, + { + files: ["**/*.{js,jsx}"], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + parserOptions: { + ecmaVersion: "latest", + ecmaFeatures: { jsx: true }, + sourceType: "module", + }, + }, + plugins: { + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + }, + rules: { + ...js.configs.recommended.rules, + ...reactHooks.configs.recommended.rules, + "no-unused-vars": ["error", { varsIgnorePattern: "^[A-Z_]" }], + "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], + }, }, - plugins: { - 'react-hooks': reactHooks, - 'react-refresh': reactRefresh, - }, - rules: { - ...js.configs.recommended.rules, - ...reactHooks.configs.recommended.rules, - 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }], - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], - }, - }, -] +]; diff --git a/frontend/index.html b/frontend/index.html index 788ba95..e830ac7 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,12 +1,12 @@ - - - - CDRM Decryption Extension - - -
- - + + + + CDRM Decryption Extension + + +
+ + diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 98c30eb..39b3ac5 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,3411 +1,3411 @@ { - "name": "frontend", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "frontend", - "version": "0.0.0", - "dependencies": { - "@tailwindcss/vite": "^4.1.7", - "react": "^19.1.0", - "react-dom": "^19.1.0", - "react-router-dom": "^7.6.1", - "tailwindcss": "^4.1.7" - }, - "devDependencies": { - "@eslint/js": "^9.25.0", - "@types/react": "^19.1.2", - "@types/react-dom": "^19.1.2", - "@vitejs/plugin-react": "^4.4.1", - "eslint": "^9.25.0", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.19", - "globals": "^16.0.0", - "vite": "^6.3.5" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz", - "integrity": "sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz", - "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.1", - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helpers": "^7.27.1", - "@babel/parser": "^7.27.1", - "@babel/template": "^7.27.1", - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", - "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.27.1", - "@babel/types": "^7.27.1", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz", - "integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", - "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", - "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.1" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", - "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.1", - "@babel/parser": "^7.27.1", - "@babel/template": "^7.27.1", - "@babel/types": "^7.27.1", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/types": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", - "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", - "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", - "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", - "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", - "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", - "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", - "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", - "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", - "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", - "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", - "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", - "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", - "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", - "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", - "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", - "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", - "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", - "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", - "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", - "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", - "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", - "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", - "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", - "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", - "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", - "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", - "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz", - "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", - "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "9.27.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz", - "integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", - "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.14.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.2.tgz", - "integrity": "sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.2.tgz", - "integrity": "sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.2.tgz", - "integrity": "sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.2.tgz", - "integrity": "sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.2.tgz", - "integrity": "sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.2.tgz", - "integrity": "sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.2.tgz", - "integrity": "sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.2.tgz", - "integrity": "sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.2.tgz", - "integrity": "sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.2.tgz", - "integrity": "sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.2.tgz", - "integrity": "sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.2.tgz", - "integrity": "sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.2.tgz", - "integrity": "sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.2.tgz", - "integrity": "sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.2.tgz", - "integrity": "sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.2.tgz", - "integrity": "sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.2.tgz", - "integrity": "sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.2.tgz", - "integrity": "sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.2.tgz", - "integrity": "sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.2.tgz", - "integrity": "sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@tailwindcss/node": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.7.tgz", - "integrity": "sha512-9rsOpdY9idRI2NH6CL4wORFY0+Q6fnx9XP9Ju+iq/0wJwGD5IByIgFmwVbyy4ymuyprj8Qh4ErxMKTUL4uNh3g==", - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.3.0", - "enhanced-resolve": "^5.18.1", - "jiti": "^2.4.2", - "lightningcss": "1.30.1", - "magic-string": "^0.30.17", - "source-map-js": "^1.2.1", - "tailwindcss": "4.1.7" - } - }, - "node_modules/@tailwindcss/oxide": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.7.tgz", - "integrity": "sha512-5SF95Ctm9DFiUyjUPnDGkoKItPX/k+xifcQhcqX5RA85m50jw1pT/KzjdvlqxRja45Y52nR4MR9fD1JYd7f8NQ==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.4", - "tar": "^7.4.3" - }, - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.7", - "@tailwindcss/oxide-darwin-arm64": "4.1.7", - "@tailwindcss/oxide-darwin-x64": "4.1.7", - "@tailwindcss/oxide-freebsd-x64": "4.1.7", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.7", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.7", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.7", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.7", - "@tailwindcss/oxide-linux-x64-musl": "4.1.7", - "@tailwindcss/oxide-wasm32-wasi": "4.1.7", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.7", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.7" - } - }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.7.tgz", - "integrity": "sha512-IWA410JZ8fF7kACus6BrUwY2Z1t1hm0+ZWNEzykKmMNM09wQooOcN/VXr0p/WJdtHZ90PvJf2AIBS/Ceqx1emg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.7.tgz", - "integrity": "sha512-81jUw9To7fimGGkuJ2W5h3/oGonTOZKZ8C2ghm/TTxbwvfSiFSDPd6/A/KE2N7Jp4mv3Ps9OFqg2fEKgZFfsvg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.7.tgz", - "integrity": "sha512-q77rWjEyGHV4PdDBtrzO0tgBBPlQWKY7wZK0cUok/HaGgbNKecegNxCGikuPJn5wFAlIywC3v+WMBt0PEBtwGw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.7.tgz", - "integrity": "sha512-RfmdbbK6G6ptgF4qqbzoxmH+PKfP4KSVs7SRlTwcbRgBwezJkAO3Qta/7gDy10Q2DcUVkKxFLXUQO6J3CRvBGw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.7.tgz", - "integrity": "sha512-OZqsGvpwOa13lVd1z6JVwQXadEobmesxQ4AxhrwRiPuE04quvZHWn/LnihMg7/XkN+dTioXp/VMu/p6A5eZP3g==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.7.tgz", - "integrity": "sha512-voMvBTnJSfKecJxGkoeAyW/2XRToLZ227LxswLAwKY7YslG/Xkw9/tJNH+3IVh5bdYzYE7DfiaPbRkSHFxY1xA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.7.tgz", - "integrity": "sha512-PjGuNNmJeKHnP58M7XyjJyla8LPo+RmwHQpBI+W/OxqrwojyuCQ+GUtygu7jUqTEexejZHr/z3nBc/gTiXBj4A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.7.tgz", - "integrity": "sha512-HMs+Va+ZR3gC3mLZE00gXxtBo3JoSQxtu9lobbZd+DmfkIxR54NO7Z+UQNPsa0P/ITn1TevtFxXTpsRU7qEvWg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.7.tgz", - "integrity": "sha512-MHZ6jyNlutdHH8rd+YTdr3QbXrHXqwIhHw9e7yXEBcQdluGwhpQY2Eku8UZK6ReLaWtQ4gijIv5QoM5eE+qlsA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.7.tgz", - "integrity": "sha512-ANaSKt74ZRzE2TvJmUcbFQ8zS201cIPxUDm5qez5rLEwWkie2SkGtA4P+GPTj+u8N6JbPrC8MtY8RmJA35Oo+A==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@emnapi/wasi-threads": "^1.0.2", - "@napi-rs/wasm-runtime": "^0.2.9", - "@tybys/wasm-util": "^0.9.0", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.7.tgz", - "integrity": "sha512-HUiSiXQ9gLJBAPCMVRk2RT1ZrBjto7WvqsPBwUrNK2BcdSxMnk19h4pjZjI7zgPhDxlAbJSumTC4ljeA9y0tEw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.7.tgz", - "integrity": "sha512-rYHGmvoHiLJ8hWucSfSOEmdCBIGZIq7SpkPRSqLsH2Ab2YUNgKeAPT1Fi2cx3+hnYOrAb0jp9cRyode3bBW4mQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/vite": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.7.tgz", - "integrity": "sha512-tYa2fO3zDe41I7WqijyVbRd8oWT0aEID1Eokz5hMT6wShLIHj3yvwj9XbfuloHP9glZ6H+aG2AN/+ZrxJ1Y5RQ==", - "license": "MIT", - "dependencies": { - "@tailwindcss/node": "4.1.7", - "@tailwindcss/oxide": "4.1.7", - "tailwindcss": "4.1.7" - }, - "peerDependencies": { - "vite": "^5.2.0 || ^6" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", - "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "19.1.4", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.4.tgz", - "integrity": "sha512-EB1yiiYdvySuIITtD5lhW4yPyJ31RkJkkDw794LaQYrxCSaQV/47y5o1FMC4zF9ZyjUjzJMZwbovEnT5yHTW6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.1.5", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.5.tgz", - "integrity": "sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.0.0" - } - }, - "node_modules/@vitejs/plugin-react": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.1.tgz", - "integrity": "sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.26.10", - "@babel/plugin-transform-react-jsx-self": "^7.25.9", - "@babel/plugin-transform-react-jsx-source": "^7.25.9", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.17.0" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" - } - }, - "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "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" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/browserslist": { - "version": "4.24.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz", - "integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" + "name": "frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.0", + "dependencies": { + "@tailwindcss/vite": "^4.1.7", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-router-dom": "^7.6.1", + "tailwindcss": "^4.1.7" + }, + "devDependencies": { + "@eslint/js": "^9.25.0", + "@types/react": "^19.1.2", + "@types/react-dom": "^19.1.2", + "@vitejs/plugin-react": "^4.4.1", + "eslint": "^9.25.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^16.0.0", + "vite": "^6.3.5" + } }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz", + "integrity": "sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz", + "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helpers": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", + "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz", + "integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", + "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", + "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", + "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", + "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", + "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", + "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", + "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", + "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", + "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", + "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", + "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", + "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", + "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", + "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", + "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", + "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", + "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", + "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", + "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", + "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", + "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", + "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", + "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", + "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", + "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", + "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", + "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", + "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", + "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz", + "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", + "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz", + "integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", + "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.14.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.2.tgz", + "integrity": "sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.2.tgz", + "integrity": "sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.2.tgz", + "integrity": "sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.2.tgz", + "integrity": "sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.2.tgz", + "integrity": "sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.2.tgz", + "integrity": "sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.2.tgz", + "integrity": "sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.2.tgz", + "integrity": "sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.2.tgz", + "integrity": "sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.2.tgz", + "integrity": "sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.2.tgz", + "integrity": "sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.2.tgz", + "integrity": "sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.2.tgz", + "integrity": "sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.2.tgz", + "integrity": "sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.2.tgz", + "integrity": "sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.2.tgz", + "integrity": "sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.2.tgz", + "integrity": "sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.2.tgz", + "integrity": "sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.2.tgz", + "integrity": "sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.2.tgz", + "integrity": "sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.7.tgz", + "integrity": "sha512-9rsOpdY9idRI2NH6CL4wORFY0+Q6fnx9XP9Ju+iq/0wJwGD5IByIgFmwVbyy4ymuyprj8Qh4ErxMKTUL4uNh3g==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.30.1", + "magic-string": "^0.30.17", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.7" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.7.tgz", + "integrity": "sha512-5SF95Ctm9DFiUyjUPnDGkoKItPX/k+xifcQhcqX5RA85m50jw1pT/KzjdvlqxRja45Y52nR4MR9fD1JYd7f8NQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.7", + "@tailwindcss/oxide-darwin-arm64": "4.1.7", + "@tailwindcss/oxide-darwin-x64": "4.1.7", + "@tailwindcss/oxide-freebsd-x64": "4.1.7", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.7", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.7", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.7", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.7", + "@tailwindcss/oxide-linux-x64-musl": "4.1.7", + "@tailwindcss/oxide-wasm32-wasi": "4.1.7", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.7", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.7" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.7.tgz", + "integrity": "sha512-IWA410JZ8fF7kACus6BrUwY2Z1t1hm0+ZWNEzykKmMNM09wQooOcN/VXr0p/WJdtHZ90PvJf2AIBS/Ceqx1emg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.7.tgz", + "integrity": "sha512-81jUw9To7fimGGkuJ2W5h3/oGonTOZKZ8C2ghm/TTxbwvfSiFSDPd6/A/KE2N7Jp4mv3Ps9OFqg2fEKgZFfsvg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.7.tgz", + "integrity": "sha512-q77rWjEyGHV4PdDBtrzO0tgBBPlQWKY7wZK0cUok/HaGgbNKecegNxCGikuPJn5wFAlIywC3v+WMBt0PEBtwGw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.7.tgz", + "integrity": "sha512-RfmdbbK6G6ptgF4qqbzoxmH+PKfP4KSVs7SRlTwcbRgBwezJkAO3Qta/7gDy10Q2DcUVkKxFLXUQO6J3CRvBGw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.7.tgz", + "integrity": "sha512-OZqsGvpwOa13lVd1z6JVwQXadEobmesxQ4AxhrwRiPuE04quvZHWn/LnihMg7/XkN+dTioXp/VMu/p6A5eZP3g==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.7.tgz", + "integrity": "sha512-voMvBTnJSfKecJxGkoeAyW/2XRToLZ227LxswLAwKY7YslG/Xkw9/tJNH+3IVh5bdYzYE7DfiaPbRkSHFxY1xA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.7.tgz", + "integrity": "sha512-PjGuNNmJeKHnP58M7XyjJyla8LPo+RmwHQpBI+W/OxqrwojyuCQ+GUtygu7jUqTEexejZHr/z3nBc/gTiXBj4A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.7.tgz", + "integrity": "sha512-HMs+Va+ZR3gC3mLZE00gXxtBo3JoSQxtu9lobbZd+DmfkIxR54NO7Z+UQNPsa0P/ITn1TevtFxXTpsRU7qEvWg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.7.tgz", + "integrity": "sha512-MHZ6jyNlutdHH8rd+YTdr3QbXrHXqwIhHw9e7yXEBcQdluGwhpQY2Eku8UZK6ReLaWtQ4gijIv5QoM5eE+qlsA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.7.tgz", + "integrity": "sha512-ANaSKt74ZRzE2TvJmUcbFQ8zS201cIPxUDm5qez5rLEwWkie2SkGtA4P+GPTj+u8N6JbPrC8MtY8RmJA35Oo+A==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@emnapi/wasi-threads": "^1.0.2", + "@napi-rs/wasm-runtime": "^0.2.9", + "@tybys/wasm-util": "^0.9.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.7.tgz", + "integrity": "sha512-HUiSiXQ9gLJBAPCMVRk2RT1ZrBjto7WvqsPBwUrNK2BcdSxMnk19h4pjZjI7zgPhDxlAbJSumTC4ljeA9y0tEw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.7.tgz", + "integrity": "sha512-rYHGmvoHiLJ8hWucSfSOEmdCBIGZIq7SpkPRSqLsH2Ab2YUNgKeAPT1Fi2cx3+hnYOrAb0jp9cRyode3bBW4mQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.7.tgz", + "integrity": "sha512-tYa2fO3zDe41I7WqijyVbRd8oWT0aEID1Eokz5hMT6wShLIHj3yvwj9XbfuloHP9glZ6H+aG2AN/+ZrxJ1Y5RQ==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.7", + "@tailwindcss/oxide": "4.1.7", + "tailwindcss": "4.1.7" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.1.4", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.4.tgz", + "integrity": "sha512-EB1yiiYdvySuIITtD5lhW4yPyJ31RkJkkDw794LaQYrxCSaQV/47y5o1FMC4zF9ZyjUjzJMZwbovEnT5yHTW6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.5.tgz", + "integrity": "sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.1.tgz", + "integrity": "sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.26.10", + "@babel/plugin-transform-react-jsx-self": "^7.25.9", + "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.24.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz", + "integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001716", + "electron-to-chromium": "^1.5.149", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001718", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz", + "integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.155", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz", + "integrity": "sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/esbuild": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", + "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.4", + "@esbuild/android-arm": "0.25.4", + "@esbuild/android-arm64": "0.25.4", + "@esbuild/android-x64": "0.25.4", + "@esbuild/darwin-arm64": "0.25.4", + "@esbuild/darwin-x64": "0.25.4", + "@esbuild/freebsd-arm64": "0.25.4", + "@esbuild/freebsd-x64": "0.25.4", + "@esbuild/linux-arm": "0.25.4", + "@esbuild/linux-arm64": "0.25.4", + "@esbuild/linux-ia32": "0.25.4", + "@esbuild/linux-loong64": "0.25.4", + "@esbuild/linux-mips64el": "0.25.4", + "@esbuild/linux-ppc64": "0.25.4", + "@esbuild/linux-riscv64": "0.25.4", + "@esbuild/linux-s390x": "0.25.4", + "@esbuild/linux-x64": "0.25.4", + "@esbuild/netbsd-arm64": "0.25.4", + "@esbuild/netbsd-x64": "0.25.4", + "@esbuild/openbsd-arm64": "0.25.4", + "@esbuild/openbsd-x64": "0.25.4", + "@esbuild/sunos-x64": "0.25.4", + "@esbuild/win32-arm64": "0.25.4", + "@esbuild/win32-ia32": "0.25.4", + "@esbuild/win32-x64": "0.25.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz", + "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.14.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.27.0", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.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.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.20", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz", + "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz", + "integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.1.tgz", + "integrity": "sha512-hPJXXxHJZEsPFNVbtATH7+MMX43UDeOauz+EAU4cgqTn7ojdI9qQORqS8Z0qmDlL1TclO/6jLRYUEtbWidtdHQ==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.6.1.tgz", + "integrity": "sha512-vxU7ei//UfPYQ3iZvHuO1D/5fX3/JOqhNTbRR+WjSBWxf9bIvpWK+ftjmdfJHzPOuMQKe2fiEdG+dZX6E8uUpA==", + "license": "MIT", + "dependencies": { + "react-router": "7.6.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rollup": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.2.tgz", + "integrity": "sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.40.2", + "@rollup/rollup-android-arm64": "4.40.2", + "@rollup/rollup-darwin-arm64": "4.40.2", + "@rollup/rollup-darwin-x64": "4.40.2", + "@rollup/rollup-freebsd-arm64": "4.40.2", + "@rollup/rollup-freebsd-x64": "4.40.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.2", + "@rollup/rollup-linux-arm-musleabihf": "4.40.2", + "@rollup/rollup-linux-arm64-gnu": "4.40.2", + "@rollup/rollup-linux-arm64-musl": "4.40.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.2", + "@rollup/rollup-linux-riscv64-gnu": "4.40.2", + "@rollup/rollup-linux-riscv64-musl": "4.40.2", + "@rollup/rollup-linux-s390x-gnu": "4.40.2", + "@rollup/rollup-linux-x64-gnu": "4.40.2", + "@rollup/rollup-linux-x64-musl": "4.40.2", + "@rollup/rollup-win32-arm64-msvc": "4.40.2", + "@rollup/rollup-win32-ia32-msvc": "4.40.2", + "@rollup/rollup-win32-x64-msvc": "4.40.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.7.tgz", + "integrity": "sha512-kr1o/ErIdNhTz8uzAYL7TpaUuzKIE6QPQ4qmSdxnoX/lo+5wmUHQA6h3L5yIqEImSRnAAURDirLu/BgiXGPAhg==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "license": "MIT", + "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" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001716", - "electron-to-chromium": "^1.5.149", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001718", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz", - "integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", - "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true, - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.155", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz", - "integrity": "sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==", - "dev": true, - "license": "ISC" - }, - "node_modules/enhanced-resolve": { - "version": "5.18.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", - "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/esbuild": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", - "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.4", - "@esbuild/android-arm": "0.25.4", - "@esbuild/android-arm64": "0.25.4", - "@esbuild/android-x64": "0.25.4", - "@esbuild/darwin-arm64": "0.25.4", - "@esbuild/darwin-x64": "0.25.4", - "@esbuild/freebsd-arm64": "0.25.4", - "@esbuild/freebsd-x64": "0.25.4", - "@esbuild/linux-arm": "0.25.4", - "@esbuild/linux-arm64": "0.25.4", - "@esbuild/linux-ia32": "0.25.4", - "@esbuild/linux-loong64": "0.25.4", - "@esbuild/linux-mips64el": "0.25.4", - "@esbuild/linux-ppc64": "0.25.4", - "@esbuild/linux-riscv64": "0.25.4", - "@esbuild/linux-s390x": "0.25.4", - "@esbuild/linux-x64": "0.25.4", - "@esbuild/netbsd-arm64": "0.25.4", - "@esbuild/netbsd-x64": "0.25.4", - "@esbuild/openbsd-arm64": "0.25.4", - "@esbuild/openbsd-x64": "0.25.4", - "@esbuild/sunos-x64": "0.25.4", - "@esbuild/win32-arm64": "0.25.4", - "@esbuild/win32-ia32": "0.25.4", - "@esbuild/win32-x64": "0.25.4" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.27.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz", - "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.20.0", - "@eslint/config-helpers": "^0.2.1", - "@eslint/core": "^0.14.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.27.0", - "@eslint/plugin-kit": "^0.3.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.3.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.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.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", - "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" - } - }, - "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.20", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz", - "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "eslint": ">=8.40" - } - }, - "node_modules/eslint-scope": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", - "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.14.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fdir": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", - "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz", - "integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/jiti": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", - "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lightningcss": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", - "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-darwin-arm64": "1.30.1", - "lightningcss-darwin-x64": "1.30.1", - "lightningcss-freebsd-x64": "1.30.1", - "lightningcss-linux-arm-gnueabihf": "1.30.1", - "lightningcss-linux-arm64-gnu": "1.30.1", - "lightningcss-linux-arm64-musl": "1.30.1", - "lightningcss-linux-x64-gnu": "1.30.1", - "lightningcss-linux-x64-musl": "1.30.1", - "lightningcss-win32-arm64-msvc": "1.30.1", - "lightningcss-win32-x64-msvc": "1.30.1" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", - "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", - "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", - "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", - "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", - "cpu": [ - "arm" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", - "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", - "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", - "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", - "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", - "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", - "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true, - "license": "MIT" - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "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" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.8", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", - "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.26.0" - }, - "peerDependencies": { - "react": "^19.1.0" - } - }, - "node_modules/react-refresh": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", - "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-router": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.1.tgz", - "integrity": "sha512-hPJXXxHJZEsPFNVbtATH7+MMX43UDeOauz+EAU4cgqTn7ojdI9qQORqS8Z0qmDlL1TclO/6jLRYUEtbWidtdHQ==", - "license": "MIT", - "dependencies": { - "cookie": "^1.0.1", - "set-cookie-parser": "^2.6.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } - } - }, - "node_modules/react-router-dom": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.6.1.tgz", - "integrity": "sha512-vxU7ei//UfPYQ3iZvHuO1D/5fX3/JOqhNTbRR+WjSBWxf9bIvpWK+ftjmdfJHzPOuMQKe2fiEdG+dZX6E8uUpA==", - "license": "MIT", - "dependencies": { - "react-router": "7.6.1" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/rollup": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.2.tgz", - "integrity": "sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==", - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.7" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.40.2", - "@rollup/rollup-android-arm64": "4.40.2", - "@rollup/rollup-darwin-arm64": "4.40.2", - "@rollup/rollup-darwin-x64": "4.40.2", - "@rollup/rollup-freebsd-arm64": "4.40.2", - "@rollup/rollup-freebsd-x64": "4.40.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.40.2", - "@rollup/rollup-linux-arm-musleabihf": "4.40.2", - "@rollup/rollup-linux-arm64-gnu": "4.40.2", - "@rollup/rollup-linux-arm64-musl": "4.40.2", - "@rollup/rollup-linux-loongarch64-gnu": "4.40.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.40.2", - "@rollup/rollup-linux-riscv64-gnu": "4.40.2", - "@rollup/rollup-linux-riscv64-musl": "4.40.2", - "@rollup/rollup-linux-s390x-gnu": "4.40.2", - "@rollup/rollup-linux-x64-gnu": "4.40.2", - "@rollup/rollup-linux-x64-musl": "4.40.2", - "@rollup/rollup-win32-arm64-msvc": "4.40.2", - "@rollup/rollup-win32-ia32-msvc": "4.40.2", - "@rollup/rollup-win32-x64-msvc": "4.40.2", - "fsevents": "~2.3.2" - } - }, - "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/set-cookie-parser": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", - "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", - "license": "MIT" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tailwindcss": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.7.tgz", - "integrity": "sha512-kr1o/ErIdNhTz8uzAYL7TpaUuzKIE6QPQ4qmSdxnoX/lo+5wmUHQA6h3L5yIqEImSRnAAURDirLu/BgiXGPAhg==", - "license": "MIT" - }, - "node_modules/tapable": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", - "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", - "license": "ISC", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", - "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", - "license": "MIT", - "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/vite": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", - "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", - "license": "MIT", - "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" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "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" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } } - } } diff --git a/frontend/package.json b/frontend/package.json index fe5f67d..2998847 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,30 +1,30 @@ { - "name": "frontend", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "vite build", - "lint": "eslint .", - "preview": "vite preview" - }, - "dependencies": { - "@tailwindcss/vite": "^4.1.7", - "react": "^19.1.0", - "react-dom": "^19.1.0", - "react-router-dom": "^7.6.1", - "tailwindcss": "^4.1.7" - }, - "devDependencies": { - "@eslint/js": "^9.25.0", - "@types/react": "^19.1.2", - "@types/react-dom": "^19.1.2", - "@vitejs/plugin-react": "^4.4.1", - "eslint": "^9.25.0", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.19", - "globals": "^16.0.0", - "vite": "^6.3.5" - } + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@tailwindcss/vite": "^4.1.7", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-router-dom": "^7.6.1", + "tailwindcss": "^4.1.7" + }, + "devDependencies": { + "@eslint/js": "^9.25.0", + "@types/react": "^19.1.2", + "@types/react-dom": "^19.1.2", + "@vitejs/plugin-react": "^4.4.1", + "eslint": "^9.25.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^16.0.0", + "vite": "^6.3.5" + } } diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 2cd84d0..4b5b5ae 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,77 +1,72 @@ import { useState, useEffect } from "react"; -import { - HashRouter as Router, - Routes, - Route, - Navigate, -} from "react-router-dom"; +import { HashRouter as Router, Routes, Route, Navigate } from "react-router-dom"; import TopNav from "./components/topnav"; import SideNav from "./components/sidenav"; import Results from "./components/results"; import Settings from "./components/settings"; function App() { - const [isSideNavOpen, setIsSideNavOpen] = useState(false); - const [validConfig, setValidConfig] = useState(null); // null = loading + const [isSideNavOpen, setIsSideNavOpen] = useState(false); + const [validConfig, setValidConfig] = useState(null); // null = loading - useEffect(() => { - chrome.storage.local.get("valid_config", (result) => { - if (chrome.runtime.lastError) { - console.error("Error reading valid_config:", chrome.runtime.lastError); - setValidConfig(false); // fallback - } else { - setValidConfig(result.valid_config === true); - } - }); - }, []); + useEffect(() => { + chrome.storage.local.get("valid_config", (result) => { + if (chrome.runtime.lastError) { + console.error("Error reading valid_config:", chrome.runtime.lastError); + setValidConfig(false); // fallback + } else { + setValidConfig(result.valid_config === true); + } + }); + }, []); + + if (validConfig === null) { + return ( +
+ Loading... +
+ ); + } - if (validConfig === null) { return ( -
- Loading... -
+ +
+
+ setIsSideNavOpen(true)} /> +
+ +
+ + {!validConfig ? ( + <> + setValidConfig(true)} /> + } + /> + } /> + + ) : ( + <> + } /> + } /> + } /> + + )} + +
+ +
+ setIsSideNavOpen(false)} /> +
+
+
); - } - - return ( - -
-
- setIsSideNavOpen(true)} /> -
- -
- - {!validConfig ? ( - <> - setValidConfig(true)} /> - } - /> - } /> - - ) : ( - <> - } /> - } /> - } /> - - )} - -
- -
- setIsSideNavOpen(false)} /> -
-
-
- ); } export default App; diff --git a/frontend/src/components/results.jsx b/frontend/src/components/results.jsx index ce5550f..1f57730 100644 --- a/frontend/src/components/results.jsx +++ b/frontend/src/components/results.jsx @@ -1,149 +1,139 @@ import React, { useEffect, useState } from "react"; function Results() { - const [drmType, setDrmType] = useState(""); - const [pssh, setPssh] = useState(""); - const [licenseUrl, setLicenseUrl] = useState(""); - const [keys, setKeys] = useState([]); + const [drmType, setDrmType] = useState(""); + const [pssh, setPssh] = useState(""); + const [licenseUrl, setLicenseUrl] = useState(""); + const [keys, setKeys] = useState([]); - useEffect(() => { - chrome.storage.local.get( - [ - "drmType", - "latestPSSH", - "latestLicenseRequest", - "latestKeys", - "licenseURL", - ], - (result) => { - if (result.drmType) setDrmType(result.drmType); - if (result.latestPSSH) setPssh(result.latestPSSH); - if (result.licenseURL) setLicenseUrl(result.licenseURL); - if (result.latestKeys) { - try { - const parsed = Array.isArray(result.latestKeys) - ? result.latestKeys - : JSON.parse(result.latestKeys); - setKeys(parsed); - } catch (e) { - console.error("Failed to parse keys:", e); - setKeys([]); - } - } - } - ); - - const handleChange = (changes, area) => { - if (area === "local") { - if (changes.drmType) { - setDrmType(changes.drmType.newValue); - } - if (changes.latestPSSH) { - setPssh(changes.latestPSSH.newValue); - } - if (changes.licenseURL) { - setLicenseUrl(changes.licenseURL.newValue); - } - if (changes.latestKeys) { - setKeys(changes.latestKeys.newValue); - } - } - }; - - chrome.storage.onChanged.addListener(handleChange); - return () => chrome.storage.onChanged.removeListener(handleChange); - }, []); - - const handleCapture = () => { - // Reset stored values - chrome.storage.local.set({ - drmType: "None", - latestPSSH: "None", - licenseURL: "None", - latestKeys: [], - }); - - // Get all normal windows to exclude your popup - chrome.windows.getAll( - { populate: true, windowTypes: ["normal"] }, - (windows) => { - if (!windows || windows.length === 0) { - console.warn("No normal Chrome windows found"); - return; - } - - // Find the last focused normal window - const lastFocusedWindow = windows.find((w) => w.focused) || windows[0]; - - if (!lastFocusedWindow) { - console.warn("No focused normal window found"); - return; - } - - // Find the active tab in that window (that is a regular webpage) - const activeTab = lastFocusedWindow.tabs.find( - (tab) => tab.active && tab.url && /^https?:\/\//.test(tab.url) + useEffect(() => { + chrome.storage.local.get( + ["drmType", "latestPSSH", "latestLicenseRequest", "latestKeys", "licenseURL"], + (result) => { + if (result.drmType) setDrmType(result.drmType); + if (result.latestPSSH) setPssh(result.latestPSSH); + if (result.licenseURL) setLicenseUrl(result.licenseURL); + if (result.latestKeys) { + try { + const parsed = Array.isArray(result.latestKeys) + ? result.latestKeys + : JSON.parse(result.latestKeys); + setKeys(parsed); + } catch (e) { + console.error("Failed to parse keys:", e); + setKeys([]); + } + } + } ); - if (activeTab?.id) { - chrome.tabs.reload(activeTab.id, () => { - if (chrome.runtime.lastError) { - console.error("Failed to reload tab:", chrome.runtime.lastError); + const handleChange = (changes, area) => { + if (area === "local") { + if (changes.drmType) { + setDrmType(changes.drmType.newValue); + } + if (changes.latestPSSH) { + setPssh(changes.latestPSSH.newValue); + } + if (changes.licenseURL) { + setLicenseUrl(changes.licenseURL.newValue); + } + if (changes.latestKeys) { + setKeys(changes.latestKeys.newValue); + } } - }); - } else { - console.warn("No active tab found in the last focused normal window"); - } - } - ); - }; + }; - return ( -
- -

DRM Type

- -

PSSH

- -

License URL

- -

Keys

-
- {Array.isArray(keys) && - keys.filter((k) => k.type !== "SIGNING").length > 0 ? ( - keys - .filter((k) => k.type !== "SIGNING") - .map((k) => `${k.key_id || k.keyId}:${k.key}`) - .join("\n") - ) : ( - None - )} -
-
- ); + chrome.storage.onChanged.addListener(handleChange); + return () => chrome.storage.onChanged.removeListener(handleChange); + }, []); + + const handleCapture = () => { + // Reset stored values + chrome.storage.local.set({ + drmType: "None", + latestPSSH: "None", + licenseURL: "None", + latestKeys: [], + }); + + // Get all normal windows to exclude your popup + chrome.windows.getAll({ populate: true, windowTypes: ["normal"] }, (windows) => { + if (!windows || windows.length === 0) { + console.warn("No normal Chrome windows found"); + return; + } + + // Find the last focused normal window + const lastFocusedWindow = windows.find((w) => w.focused) || windows[0]; + + if (!lastFocusedWindow) { + console.warn("No focused normal window found"); + return; + } + + // Find the active tab in that window (that is a regular webpage) + const activeTab = lastFocusedWindow.tabs.find( + (tab) => tab.active && tab.url && /^https?:\/\//.test(tab.url) + ); + + if (activeTab?.id) { + chrome.tabs.reload(activeTab.id, () => { + if (chrome.runtime.lastError) { + console.error("Failed to reload tab:", chrome.runtime.lastError); + } + }); + } else { + console.warn("No active tab found in the last focused normal window"); + } + }); + }; + + return ( +
+ +

DRM Type

+ +

PSSH

+ +

License URL

+ +

Keys

+
+ {Array.isArray(keys) && keys.filter((k) => k.type !== "SIGNING").length > 0 ? ( + keys + .filter((k) => k.type !== "SIGNING") + .map((k) => `${k.key_id || k.keyId}:${k.key}`) + .join("\n") + ) : ( + None + )} +
+
+ ); } export default Results; diff --git a/frontend/src/components/settings.jsx b/frontend/src/components/settings.jsx index e618848..7e7ba9a 100644 --- a/frontend/src/components/settings.jsx +++ b/frontend/src/components/settings.jsx @@ -2,149 +2,140 @@ import { useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; function Settings({ onConfigSaved }) { - const [instanceUrl, setInstanceUrl] = useState(""); - const [storedUrl, setStoredUrl] = useState(null); - const [message, setMessage] = useState(null); - const [messageType, setMessageType] = useState(null); - const [loading, setLoading] = useState(false); + const [instanceUrl, setInstanceUrl] = useState(""); + const [storedUrl, setStoredUrl] = useState(null); + const [message, setMessage] = useState(null); + const [messageType, setMessageType] = useState(null); + const [loading, setLoading] = useState(false); - const navigate = useNavigate(); + const navigate = useNavigate(); - useEffect(() => { - chrome.storage.local.get("cdrm_instance", (result) => { - if (chrome.runtime.lastError) { - console.error( - "Error fetching CDRM instance:", - chrome.runtime.lastError - ); - } else if (result.cdrm_instance) { - setStoredUrl(result.cdrm_instance); - } - }); - }, []); - - const handleSave = async () => { - const trimmedUrl = instanceUrl.trim().replace(/\/+$/, ""); - if (!trimmedUrl) { - setMessage("Please enter a valid URL."); - setMessageType("error"); - return; - } - - const endpoint = trimmedUrl + "/api/extension"; - setLoading(true); - setMessage(null); - - try { - const response = await fetch(endpoint, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - }); - - const data = await response.json(); - - if (data.status === true) { - setMessage("Successfully connected to CDRM Instance."); - setMessageType("success"); - - const widevineRes = await fetch( - `${trimmedUrl}/remotecdm/widevine/deviceinfo` - ); - if (!widevineRes.ok) - throw new Error("Failed to fetch Widevine device info"); - const widevineData = await widevineRes.json(); - - const playreadyRes = await fetch( - `${trimmedUrl}/remotecdm/playready/deviceinfo` - ); - if (!playreadyRes.ok) - throw new Error("Failed to fetch PlayReady device info"); - const playreadyData = await playreadyRes.json(); - - chrome.storage.local.set( - { - valid_config: true, - cdrm_instance: trimmedUrl, - widevine_device: { - device_type: widevineData.device_type, - system_id: widevineData.system_id, - security_level: widevineData.security_level, - secret: widevineData.secret, - device_name: widevineData.device_name, - host: trimmedUrl, - }, - playready_device: { - security_level: playreadyData.security_level, - secret: playreadyData.secret, - device_name: playreadyData.device_name, - host: trimmedUrl, - }, - }, - () => { + useEffect(() => { + chrome.storage.local.get("cdrm_instance", (result) => { if (chrome.runtime.lastError) { - console.error( - "Error saving to chrome.storage:", - chrome.runtime.lastError - ); - setMessage("Error saving configuration."); - setMessageType("error"); - } else { - console.log("Configuration saved."); - setStoredUrl(trimmedUrl); - setInstanceUrl(""); - if (onConfigSaved) onConfigSaved(); - navigate("/results"); // Automatically redirect after success + console.error("Error fetching CDRM instance:", chrome.runtime.lastError); + } else if (result.cdrm_instance) { + setStoredUrl(result.cdrm_instance); } - } - ); - } else { - throw new Error("Invalid response from endpoint."); - } - } catch (err) { - console.error("Connection error:", err); - setMessage("Invalid endpoint or device info could not be retrieved."); - setMessageType("error"); - } finally { - setLoading(false); - } - }; + }); + }, []); - return ( -
- setInstanceUrl(e.target.value)} - placeholder={ - storedUrl - ? `Current CDRM Instance: ${storedUrl}` - : "CDRM Instance URL (e.g., https://cdrm-project.com/, http://127.0.0.1:5000/)" + const handleSave = async () => { + const trimmedUrl = instanceUrl.trim().replace(/\/+$/, ""); + if (!trimmedUrl) { + setMessage("Please enter a valid URL."); + setMessageType("error"); + return; } - className="w-full p-4 text-lg bg-gray-800 text-white border border-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 mt-4" - /> - - {message && ( -

- {message} -

- )} -
- ); + const endpoint = trimmedUrl + "/api/extension"; + setLoading(true); + setMessage(null); + + try { + const response = await fetch(endpoint, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + }); + + const data = await response.json(); + + if (data.status === true) { + setMessage("Successfully connected to CDRM Instance."); + setMessageType("success"); + + const widevineRes = await fetch(`${trimmedUrl}/remotecdm/widevine/deviceinfo`); + if (!widevineRes.ok) throw new Error("Failed to fetch Widevine device info"); + const widevineData = await widevineRes.json(); + + const playreadyRes = await fetch(`${trimmedUrl}/remotecdm/playready/deviceinfo`); + if (!playreadyRes.ok) throw new Error("Failed to fetch PlayReady device info"); + const playreadyData = await playreadyRes.json(); + + chrome.storage.local.set( + { + valid_config: true, + cdrm_instance: trimmedUrl, + widevine_device: { + device_type: widevineData.device_type, + system_id: widevineData.system_id, + security_level: widevineData.security_level, + secret: widevineData.secret, + device_name: widevineData.device_name, + host: trimmedUrl, + }, + playready_device: { + security_level: playreadyData.security_level, + secret: playreadyData.secret, + device_name: playreadyData.device_name, + host: trimmedUrl, + }, + }, + () => { + if (chrome.runtime.lastError) { + console.error( + "Error saving to chrome.storage:", + chrome.runtime.lastError + ); + setMessage("Error saving configuration."); + setMessageType("error"); + } else { + console.log("Configuration saved."); + setStoredUrl(trimmedUrl); + setInstanceUrl(""); + if (onConfigSaved) onConfigSaved(); + navigate("/results"); // Automatically redirect after success + } + } + ); + } else { + throw new Error("Invalid response from endpoint."); + } + } catch (err) { + console.error("Connection error:", err); + setMessage("Invalid endpoint or device info could not be retrieved."); + setMessageType("error"); + } finally { + setLoading(false); + } + }; + + return ( +
+ setInstanceUrl(e.target.value)} + placeholder={ + storedUrl + ? `Current CDRM Instance: ${storedUrl}` + : "CDRM Instance URL (e.g., https://cdrm-project.com/, http://127.0.0.1:5000/)" + } + className="w-full p-4 text-lg bg-gray-800 text-white border border-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 mt-4" + /> + + + {message && ( +

+ {message} +

+ )} +
+ ); } export default Settings; diff --git a/frontend/src/components/sidenav.jsx b/frontend/src/components/sidenav.jsx index 4bf4dfe..15a33aa 100644 --- a/frontend/src/components/sidenav.jsx +++ b/frontend/src/components/sidenav.jsx @@ -4,48 +4,45 @@ import settingsIcon from "../assets/settings.svg"; import closeIcon from "../assets/close.svg"; function SideNav({ onClose }) { - return ( -
-
- -
+ return ( +
+
+ +
-
- - Home - Home - -
+
+ + Home + Home + +
-
- - Settings - Settings - -
-
- ); +
+ + Settings + Settings + +
+
+ ); } export default SideNav; diff --git a/frontend/src/components/topnav.jsx b/frontend/src/components/topnav.jsx index 1653a06..aa86dbf 100644 --- a/frontend/src/components/topnav.jsx +++ b/frontend/src/components/topnav.jsx @@ -2,84 +2,81 @@ import { useEffect, useState } from "react"; import hamburgerIcon from "../assets/hamburger.svg"; function TopNav({ onMenuClick }) { - const [injectionType, setInjectionType] = useState("LICENSE"); - const [drmOverride, setDrmOverride] = useState("DISABLED"); + const [injectionType, setInjectionType] = useState("LICENSE"); + const [drmOverride, setDrmOverride] = useState("DISABLED"); - useEffect(() => { - chrome.storage.local.get(["injection_type", "drm_override"], (result) => { - if (result.injection_type !== undefined) { - setInjectionType(result.injection_type); - } - if (result.drm_override !== undefined) { - setDrmOverride(result.drm_override); - } - }); - }, []); + useEffect(() => { + chrome.storage.local.get(["injection_type", "drm_override"], (result) => { + if (result.injection_type !== undefined) { + setInjectionType(result.injection_type); + } + if (result.drm_override !== undefined) { + setDrmOverride(result.drm_override); + } + }); + }, []); - const handleInjectionTypeChange = (type) => { - chrome.storage.local.set({ injection_type: type }, () => { - if (chrome.runtime.lastError) { - console.error( - "Error updating injection_type:", - chrome.runtime.lastError - ); - } else { - setInjectionType(type); - console.log(`Injection type updated to ${type}`); - } - }); - }; + const handleInjectionTypeChange = (type) => { + chrome.storage.local.set({ injection_type: type }, () => { + if (chrome.runtime.lastError) { + console.error("Error updating injection_type:", chrome.runtime.lastError); + } else { + setInjectionType(type); + console.log(`Injection type updated to ${type}`); + } + }); + }; - const handleDrmOverrideChange = (type) => { - chrome.storage.local.set({ drm_override: type }, () => { - if (chrome.runtime.lastError) { - console.error("Error updating drm_override:", chrome.runtime.lastError); - } else { - setDrmOverride(type); - console.log(`DRM Override updated to ${type}`); - } - }); - }; + const handleDrmOverrideChange = (type) => { + chrome.storage.local.set({ drm_override: type }, () => { + if (chrome.runtime.lastError) { + console.error("Error updating drm_override:", chrome.runtime.lastError); + } else { + setDrmOverride(type); + console.log(`DRM Override updated to ${type}`); + } + }); + }; - return ( -
- Menu -
-

- Injection Type: -

- - - -
-
- ); + return ( +
+ Menu +
+

+ Injection Type: +

+ + + +
+
+ ); } export default TopNav; diff --git a/frontend/src/index.css b/frontend/src/index.css index e6d7ec6..032c11f 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1,8 +1,10 @@ @import "tailwindcss"; -html, body, #root { - height: 100%; - width: 100%; - margin: 0; - padding: 0; -} \ No newline at end of file +html, +body, +#root { + height: 100%; + width: 100%; + margin: 0; + padding: 0; +} diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index b9a1a6d..46adf76 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -1,10 +1,10 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import './index.css' -import App from './App.jsx' +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import "./index.css"; +import App from "./App.jsx"; -createRoot(document.getElementById('root')).render( - - - , -) +createRoot(document.getElementById("root")).render( + + + +); diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 44780a5..a4c9f4d 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -1,9 +1,9 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' -import tailwindcss from '@tailwindcss/vite' +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import tailwindcss from "@tailwindcss/vite"; // https://vite.dev/config/ export default defineConfig({ - base: './', - plugins: [react(), tailwindcss()], -}) + base: "./", + plugins: [react(), tailwindcss()], +}); diff --git a/inject.js b/inject.js index 5c49cb2..fdcb029 100644 --- a/inject.js +++ b/inject.js @@ -1,6 +1,6 @@ let widevineDeviceInfo = null; let playreadyDeviceInfo = null; -let originalChallenge = null +let originalChallenge = null; let serviceCertFound = false; let drmType = "NONE"; let psshFound = false; @@ -21,11 +21,11 @@ let keysRetrieved = false; window.postMessage({ type: "__GET_DRM_OVERRIDE__" }, "*"); // Add listener for DRM override messages -window.addEventListener("message", function(event) { - if (event.source !== window) return; +window.addEventListener("message", function (event) { + if (event.source !== window) return; if (event.data.type === "__DRM_OVERRIDE__") { - drmOverride = event.data.drmOverride || "DISABLED"; - console.log("DRM Override set to:", drmOverride); + drmOverride = event.data.drmOverride || "DISABLED"; + console.log("DRM Override set to:", drmOverride); } }); @@ -33,32 +33,148 @@ window.addEventListener("message", function(event) { window.postMessage({ type: "__GET_INJECTION_TYPE__" }, "*"); // Add listener for injection type messages -window.addEventListener("message", function(event) { - if (event.source !== window) return; +window.addEventListener("message", function (event) { + if (event.source !== window) return; - if (event.data.type === "__INJECTION_TYPE__") { - interceptType = event.data.injectionType || "DISABLED"; - console.log("Injection type set to:", interceptType); - } + if (event.data.type === "__INJECTION_TYPE__") { + interceptType = event.data.injectionType || "DISABLED"; + console.log("Injection type set to:", interceptType); + } }); // Post message to get CDM devices window.postMessage({ type: "__GET_CDM_DEVICES__" }, "*"); // Add listener for CDM device messages -window.addEventListener("message", function(event) { - if (event.source !== window) return; +window.addEventListener("message", function (event) { + if (event.source !== window) return; - if (event.data.type === "__CDM_DEVICES__") { - const { widevine_device, playready_device } = event.data; + if (event.data.type === "__CDM_DEVICES__") { + const { widevine_device, playready_device } = event.data; - console.log("Received device info:", widevine_device, playready_device); + console.log("Received device info:", widevine_device, playready_device); - widevineDeviceInfo = widevine_device; - playreadyDeviceInfo = playready_device; - } + widevineDeviceInfo = widevine_device; + playreadyDeviceInfo = playready_device; + } }); +function safeHeaderShellEscape(str) { + return str + .replace(/\\/g, "\\\\") + .replace(/"/g, '\\"') + .replace(/\$/g, "\\$") // escape shell expansion + .replace(/`/g, "\\`") + .replace(/\n/g, ""); // strip newlines +} + +// Intercep network to find manifest +function injectManifestInterceptor() { + const script = document.createElement("script"); + script.textContent = ` + (function() { + function isProbablyManifest(text = "", contentType = "") { + const lowerCT = contentType?.toLowerCase() ?? ""; + const sample = text.slice(0, 2000); + + const isHLSMime = lowerCT.includes("mpegurl"); + const isDASHMime = lowerCT.includes("dash+xml"); + const isSmoothMime = lowerCT.includes("sstr+xml"); + + const isHLSKeyword = sample.includes("#EXTM3U") || sample.includes("#EXT-X-STREAM-INF"); + const isDASHKeyword = sample.includes(" { + headersObj[key] = value; + }); + + const headerFlags = Object.entries(headersObj) + .map(([key, val]) => '--add-headers "' + safeHeaderShellEscape(key) + ': ' + safeHeaderShellEscape(val) + '"') + .join(" "); + + window.postMessage({ + type: "__MANIFEST_HEADERS__", + url, + headers: headerFlags + }, "*"); + } + } catch (e) {} + + return response; + }; + + const originalXHROpen = XMLHttpRequest.prototype.open; + const originalXHRSend = XMLHttpRequest.prototype.send; + + XMLHttpRequest.prototype.open = function(method, url) { + this.__url = url; + return originalXHROpen.apply(this, arguments); + }; + + XMLHttpRequest.prototype.send = function(body) { + this.addEventListener("load", function () { + try { + const contentType = this.getResponseHeader("content-type") || ""; + const text = this.responseText; + + if (isProbablyManifest(text, contentType)) { + window.postMessage({ type: "__MANIFEST_URL__", data: this.__url }, "*"); + console.log("[Manifest][xhr]", this.__url, contentType); + + const xhrHeaders = {}; + const rawHeaders = this.getAllResponseHeaders().trim().split(/\\r?\\n/); + rawHeaders.forEach(line => { + const parts = line.split(": "); + if (parts.length === 2) { + xhrHeaders[parts[0]] = parts[1]; + } + }); + + const headerFlags = Object.entries(xhrHeaders) + .map(([key, val]) => '--add-headers "' + safeHeaderShellEscape(key) + ': ' + safeHeaderShellEscape(val) + '"') + .join(" "); + + window.postMessage({ + type: "__MANIFEST_HEADERS__", + url: this.__url, + headers: headerFlags + }, "*"); + } + } catch (e) {} + }); + return originalXHRSend.apply(this, arguments); + }; + })(); + `; + document.documentElement.appendChild(script); + script.remove(); +} + +injectManifestInterceptor(); // PlayReady Remote CDM Class class remotePlayReadyCDM { @@ -76,8 +192,8 @@ class remotePlayReadyCDM { openSession() { const url = `${this.host}/remotecdm/playready/${this.device_name}/open`; const xhr = new XMLHttpRequest(); - xhr.open('GET', url, false); - xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.open("GET", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); xhr.send(); const jsonData = JSON.parse(xhr.responseText); if (jsonData.data?.session_id) { @@ -93,11 +209,11 @@ class remotePlayReadyCDM { getChallenge(init_data) { const url = `${this.host}/remotecdm/playready/${this.device_name}/get_license_challenge`; const xhr = new XMLHttpRequest(); - xhr.open('POST', url, false); - xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.open("POST", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); const body = { session_id: this.session_id, - init_data: init_data + init_data: init_data, }; xhr.send(JSON.stringify(body)); const jsonData = JSON.parse(xhr.responseText); @@ -114,16 +230,17 @@ class remotePlayReadyCDM { parseLicense(license_message) { const url = `${this.host}/remotecdm/playready/${this.device_name}/parse_license`; const xhr = new XMLHttpRequest(); - xhr.open('POST', url, false); - xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.open("POST", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); const body = { session_id: this.session_id, - license_message: license_message - } + license_message: license_message, + }; xhr.send(JSON.stringify(body)); const jsonData = JSON.parse(xhr.responseText); - if (jsonData.message === "Successfully parsed and loaded the Keys from the License message") - { + if ( + jsonData.message === "Successfully parsed and loaded the Keys from the License message" + ) { console.log("PlayReady license response parsed successfully"); return true; } else { @@ -136,11 +253,11 @@ class remotePlayReadyCDM { getKeys() { const url = `${this.host}/remotecdm/playready/${this.device_name}/get_keys`; const xhr = new XMLHttpRequest(); - xhr.open('POST', url, false); - xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.open("POST", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); const body = { - session_id: this.session_id - } + session_id: this.session_id, + }; xhr.send(JSON.stringify(body)); const jsonData = JSON.parse(xhr.responseText); if (jsonData.data?.keys) { @@ -153,11 +270,11 @@ class remotePlayReadyCDM { } // Close PlayReady session - closeSession () { + closeSession() { const url = `${this.host}/remotecdm/playready/${this.device_name}/close/${this.session_id}`; const xhr = new XMLHttpRequest(); - xhr.open('GET', url, false); - xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.open("GET", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); xhr.send(); const jsonData = JSON.parse(xhr.responseText); if (jsonData) { @@ -171,24 +288,24 @@ class remotePlayReadyCDM { // Widevine Remote CDM Class class remoteWidevineCDM { - constructor(device_type, system_id, security_level, host, secret, device_name) { - this.device_type = device_type; - this.system_id = system_id; - this.security_level = security_level; - this.host = host; - this.secret = secret; - this.device_name = device_name; - this.session_id = null; - this.challenge = null; - this.keys = null; - } + constructor(device_type, system_id, security_level, host, secret, device_name) { + this.device_type = device_type; + this.system_id = system_id; + this.security_level = security_level; + this.host = host; + this.secret = secret; + this.device_name = device_name; + this.session_id = null; + this.challenge = null; + this.keys = null; + } // Open Widevine session - openSession () { + openSession() { const url = `${this.host}/remotecdm/widevine/${this.device_name}/open`; const xhr = new XMLHttpRequest(); - xhr.open('GET', url, false); - xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.open("GET", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); xhr.send(); const jsonData = JSON.parse(xhr.responseText); if (jsonData.data?.session_id) { @@ -204,12 +321,12 @@ class remoteWidevineCDM { setServiceCertificate(certificate) { const url = `${this.host}/remotecdm/widevine/${this.device_name}/set_service_certificate`; const xhr = new XMLHttpRequest(); - xhr.open('POST', url, false); - xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.open("POST", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); const body = { session_id: this.session_id, - certificate: certificate ?? null - } + certificate: certificate ?? null, + }; xhr.send(JSON.stringify(body)); const jsonData = JSON.parse(xhr.responseText); if (jsonData.status === 200) { @@ -221,15 +338,15 @@ class remoteWidevineCDM { } // Get Widevine challenge - getChallenge(init_data, license_type = 'STREAMING') { + getChallenge(init_data, license_type = "STREAMING") { const url = `${this.host}/remotecdm/widevine/${this.device_name}/get_license_challenge/${license_type}`; const xhr = new XMLHttpRequest(); - xhr.open('POST', url, false); - xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.open("POST", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); const body = { session_id: this.session_id, init_data: init_data, - privacy_mode: serviceCertFound + privacy_mode: serviceCertFound, }; xhr.send(JSON.stringify(body)); const jsonData = JSON.parse(xhr.responseText); @@ -244,13 +361,13 @@ class remoteWidevineCDM { // Parse Widevine license response parseLicense(license_message) { - const url = `${this.host}/remotecdm/widevine/${this.device_name}/parse_license`; + const url = `${this.host}/remotecdm/widevine/${this.device_name}/parse_license`; const xhr = new XMLHttpRequest(); - xhr.open('POST', url, false); - xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.open("POST", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); const body = { session_id: this.session_id, - license_message: license_message + license_message: license_message, }; xhr.send(JSON.stringify(body)); const jsonData = JSON.parse(xhr.responseText); @@ -267,10 +384,10 @@ class remoteWidevineCDM { getKeys() { const url = `${this.host}/remotecdm/widevine/${this.device_name}/get_keys/ALL`; const xhr = new XMLHttpRequest(); - xhr.open('POST', url, false); - xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.open("POST", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); const body = { - session_id: this.session_id + session_id: this.session_id, }; xhr.send(JSON.stringify(body)); const jsonData = JSON.parse(xhr.responseText); @@ -287,8 +404,8 @@ class remoteWidevineCDM { closeSession() { const url = `${this.host}/remotecdm/widevine/${this.device_name}/close/${this.session_id}`; const xhr = new XMLHttpRequest(); - xhr.open('GET', url, false); - xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.open("GET", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); xhr.send(); const jsonData = JSON.parse(xhr.responseText); if (jsonData) { @@ -302,22 +419,22 @@ class remoteWidevineCDM { // Utility functions function hexStrToU8(hexString) { - return Uint8Array.from(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16))); + return Uint8Array.from(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))); } function u8ToHexStr(bytes) { - return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), ''); + return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), ""); } function b64ToHexStr(b64) { - return [...atob(b64)].map(c => c.charCodeAt(0).toString(16).padStart(2, '0')).join``; + return [...atob(b64)].map((c) => c.charCodeAt(0).toString(16).padStart(2, "0")).join``; } function jsonContainsValue(obj, prefix = "CAES") { if (typeof obj === "string") return obj.startsWith(prefix); - if (Array.isArray(obj)) return obj.some(val => jsonContainsValue(val, prefix)); + if (Array.isArray(obj)) return obj.some((val) => jsonContainsValue(val, prefix)); if (typeof obj === "object" && obj !== null) { - return Object.values(obj).some(val => jsonContainsValue(val, prefix)); + return Object.values(obj).some((val) => jsonContainsValue(val, prefix)); } return false; } @@ -328,7 +445,7 @@ function jsonReplaceValue(obj, newValue) { } if (Array.isArray(obj)) { - return obj.map(item => jsonReplaceValue(item, newValue)); + return obj.map((item) => jsonReplaceValue(item, newValue)); } if (typeof obj === "object" && obj !== null) { @@ -379,10 +496,10 @@ function getPlayReadyPssh(buffer) { } function getClearkey(response) { - let obj = JSON.parse((new TextDecoder("utf-8")).decode(response)); - return obj["keys"].map(o => ({ - key_id: b64ToHexStr(o["kid"].replace(/-/g, '+').replace(/_/g, '/')), - key: b64ToHexStr(o["k"].replace(/-/g, '+').replace(/_/g, '/')), + let obj = JSON.parse(new TextDecoder("utf-8").decode(response)); + return obj["keys"].map((o) => ({ + key_id: b64ToHexStr(o["kid"].replace(/-/g, "+").replace(/_/g, "/")), + key: b64ToHexStr(o["k"].replace(/-/g, "+").replace(/_/g, "/")), })); } @@ -397,7 +514,7 @@ function base64ToUint8Array(base64) { } function arrayBufferToBase64(uint8array) { - let binary = ''; + let binary = ""; const len = uint8array.length; for (let i = 0; i < len; i++) { @@ -409,20 +526,20 @@ function arrayBufferToBase64(uint8array) { // Challenge generator interceptor const originalGenerateRequest = MediaKeySession.prototype.generateRequest; -MediaKeySession.prototype.generateRequest = function(initDataType, initData) { +MediaKeySession.prototype.generateRequest = function (initDataType, initData) { const session = this; let playReadyPssh = getPlayReadyPssh(initData); if (playReadyPssh) { console.log("[DRM Detected] PlayReady"); foundPlayreadyPssh = playReadyPssh; - console.log("[PlayReady PSSH found] " + playReadyPssh) + console.log("[PlayReady PSSH found] " + playReadyPssh); } - let wideVinePssh = getWidevinePssh(initData) + let wideVinePssh = getWidevinePssh(initData); if (wideVinePssh) { // Widevine code console.log("[DRM Detected] Widevine"); foundWidevinePssh = wideVinePssh; - console.log("[Widevine PSSH found] " + wideVinePssh) + console.log("[Widevine PSSH found] " + wideVinePssh); } // Challenge message interceptor if (!remoteListenerMounted) { @@ -432,10 +549,16 @@ MediaKeySession.prototype.generateRequest = function(initDataType, initData) { const uint8Array = new Uint8Array(event.message); const base64challenge = arrayBufferToBase64(uint8Array); if (base64challenge === "CAQ=" && interceptType !== "DISABLED" && !serviceCertFound) { - const { - device_type, system_id, security_level, host, secret, device_name - } = widevineDeviceInfo; - remoteCDM = new remoteWidevineCDM(device_type, system_id, security_level, host, secret, device_name); + const { device_type, system_id, security_level, host, secret, device_name } = + widevineDeviceInfo; + remoteCDM = new remoteWidevineCDM( + device_type, + system_id, + security_level, + host, + secret, + device_name + ); remoteCDM.openSession(); } if (!injectionSuccess && base64challenge !== "CAQ=" && interceptType !== "DISABLED") { @@ -450,30 +573,53 @@ MediaKeySession.prototype.generateRequest = function(initDataType, initData) { window.postMessage({ type: "__PSSH_DATA__", data: foundWidevinePssh }, "*"); if (interceptType === "EME" && !remoteCDM) { const { - device_type, system_id, security_level, host, secret, device_name + device_type, + system_id, + security_level, + host, + secret, + device_name, } = widevineDeviceInfo; - remoteCDM = new remoteWidevineCDM(device_type, system_id, security_level, host, secret, device_name); + remoteCDM = new remoteWidevineCDM( + device_type, + system_id, + security_level, + host, + secret, + device_name + ); remoteCDM.openSession(); remoteCDM.getChallenge(foundWidevinePssh); - }} + } + } if (!originalChallenge.startsWith("CAES")) { const buffer = event.message; - const decoder = new TextDecoder('utf-16'); + const decoder = new TextDecoder("utf-16"); const decodedText = decoder.decode(buffer); - const match = decodedText.match(/([^<]+)<\/Challenge>/); + const match = decodedText.match( + /([^<]+)<\/Challenge>/ + ); if (match) { window.postMessage({ type: "__DRM_TYPE__", data: "PlayReady" }, "*"); - window.postMessage({ type: "__PSSH_DATA__", data: foundPlayreadyPssh }, "*"); + window.postMessage( + { type: "__PSSH_DATA__", data: foundPlayreadyPssh }, + "*" + ); originalChallenge = match[1]; - if (interceptType === "EME" && !remoteCDM) { - const { - security_level, host, secret, device_name - } = playreadyDeviceInfo; - remoteCDM = new remotePlayReadyCDM(security_level, host, secret, device_name) + if (interceptType === "EME" && !remoteCDM) { + const { security_level, host, secret, device_name } = + playreadyDeviceInfo; + remoteCDM = new remotePlayReadyCDM( + security_level, + host, + secret, + device_name + ); remoteCDM.openSession(); remoteCDM.getChallenge(foundPlayreadyPssh); } - }} + } + } if (interceptType === "EME" && remoteCDM) { const uint8challenge = base64ToUint8Array(remoteCDM.challenge); const challengeBuffer = uint8challenge.buffer; @@ -482,24 +628,24 @@ MediaKeySession.prototype.generateRequest = function(initDataType, initData) { origin: event.origin, lastEventId: event.lastEventId, source: event.source, - ports: event.ports + ports: event.ports, }); Object.defineProperty(syntheticEvent, "message", { - get: () => challengeBuffer + get: () => challengeBuffer, }); - console.log("Intercepted EME Challenge and injected custom one.") + console.log("Intercepted EME Challenge and injected custom one."); session.dispatchEvent(syntheticEvent); } } - }) + }); console.log("Message interceptor mounted."); } -return originalGenerateRequest.call(session, initDataType, initData); + return originalGenerateRequest.call(session, initDataType, initData); }; // Message update interceptors const originalUpdate = MediaKeySession.prototype.update; -MediaKeySession.prototype.update = function(response) { +MediaKeySession.prototype.update = function (response) { const uint8 = response instanceof Uint8Array ? response : new Uint8Array(response); const base64Response = window.btoa(String.fromCharCode(...uint8)); if (base64Response.startsWith("CAUS") && foundWidevinePssh && remoteCDM && !serviceCertFound) { @@ -511,7 +657,11 @@ MediaKeySession.prototype.update = function(response) { window.postMessage({ type: "__PSSH_DATA__", data: foundWidevinePssh }, "*"); serviceCertFound = true; } - if (!base64Response.startsWith("CAUS") && (foundWidevinePssh || foundPlayreadyPssh) && !keysRetrieved) { + if ( + !base64Response.startsWith("CAUS") && + (foundWidevinePssh || foundPlayreadyPssh) && + !keysRetrieved + ) { if (licenseResponseCounter === 1 || foundChallengeInBody) { remoteCDM.parseLicense(base64Response); remoteCDM.getKeys(); @@ -527,20 +677,20 @@ MediaKeySession.prototype.update = function(response) { .then(() => { let clearKeys = getClearkey(response); if (clearKeys && clearKeys.length > 0) { - console.log("[CLEARKEY] ", clearKeys); - const drmType = { - type: "__DRM_TYPE__", - data: 'ClearKey' - }; - window.postMessage(drmType, "*"); - const keysData = { - type: "__KEYS_DATA__", - data: clearKeys - }; - window.postMessage(keysData, "*"); + console.log("[CLEARKEY] ", clearKeys); + const drmType = { + type: "__DRM_TYPE__", + data: "ClearKey", + }; + window.postMessage(drmType, "*"); + const keysData = { + type: "__KEYS_DATA__", + data: clearKeys, + }; + window.postMessage(keysData, "*"); } }) - .catch(e => { + .catch((e) => { console.log("[CLEARKEY] Not found"); }); } @@ -549,265 +699,424 @@ MediaKeySession.prototype.update = function(response) { }; // fetch POST interceptor -(function() { - const originalFetch = window.fetch; +(function () { + const originalFetch = window.fetch; - window.fetch = async function(resource, config = {}) { - const method = (config.method || 'GET').toUpperCase(); + window.fetch = async function (resource, config = {}) { + const method = (config.method || "GET").toUpperCase(); - if (method === 'POST') { - let body = config.body; - if (body) { - if (body instanceof ArrayBuffer || body instanceof Uint8Array) { - const buffer = body instanceof Uint8Array ? body : new Uint8Array(body); - const base64Body = window.btoa(String.fromCharCode(...buffer)); - if ((base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) && (!remoteCDM || remoteCDM.challenge === null || base64Body !== remoteCDM.challenge) && interceptType === "EME") { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); - // Block the request - return; - } - if ((base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) && interceptType == "LICENSE" &&!foundChallengeInBody) { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); - if (!remoteCDM) { - if (base64Body.startsWith("CAES")) { - const { - device_type, system_id, security_level, host, secret, device_name - } = widevineDeviceInfo; - remoteCDM = new remoteWidevineCDM(device_type, system_id, security_level, host, secret, device_name); - remoteCDM.openSession(); + if (method === "POST") { + let body = config.body; + if (body) { + if (body instanceof ArrayBuffer || body instanceof Uint8Array) { + const buffer = body instanceof Uint8Array ? body : new Uint8Array(body); + const base64Body = window.btoa(String.fromCharCode(...buffer)); + if ( + (base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) && + (!remoteCDM || + remoteCDM.challenge === null || + base64Body !== remoteCDM.challenge) && + interceptType === "EME" + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); + // Block the request + return; + } + if ( + (base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) && + interceptType == "LICENSE" && + !foundChallengeInBody + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); + if (!remoteCDM) { + if (base64Body.startsWith("CAES")) { + const { + device_type, + system_id, + security_level, + host, + secret, + device_name, + } = widevineDeviceInfo; + remoteCDM = new remoteWidevineCDM( + device_type, + system_id, + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundWidevinePssh); + } + if (base64Body.startsWith("PD94")) { + const { security_level, host, secret, device_name } = + playreadyDeviceInfo; + remoteCDM = new remotePlayReadyCDM( + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundPlayreadyPssh); + } + } + if (remoteCDM && remoteCDM.challenge === null) { remoteCDM.getChallenge(foundWidevinePssh); } - if (base64Body.startsWith("PD94")) { - const { - security_level, host, secret, device_name - } = playreadyDeviceInfo; - remoteCDM = new remotePlayReadyCDM(security_level, host, secret, device_name); - remoteCDM.openSession(); - remoteCDM.getChallenge(foundPlayreadyPssh); + const injectedBody = base64ToUint8Array(remoteCDM.challenge); + config.body = injectedBody; + return originalFetch(resource, config); + } + } + if (typeof body === "string" && !isJson(body)) { + const base64EncodedBody = btoa(body); + if ( + (base64EncodedBody.startsWith("CAES") || + base64EncodedBody.startsWith("PD94")) && + (!remoteCDM || + remoteCDM.challenge === null || + base64EncodedBody !== remoteCDM.challenge) && + interceptType === "EME" + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); + // Block the request + return; + } + if ( + (base64EncodedBody.startsWith("CAES") || + base64EncodedBody.startsWith("PD94")) && + interceptType == "LICENSE" && + !foundChallengeInBody + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); + if (!remoteCDM) { + if (base64EncodedBody.startsWith("CAES")) { + const { + device_type, + system_id, + security_level, + host, + secret, + device_name, + } = widevineDeviceInfo; + remoteCDM = new remoteWidevineCDM( + device_type, + system_id, + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundWidevinePssh); + } + if (base64EncodedBody.startsWith("PD94")) { + const { security_level, host, secret, device_name } = + playreadyDeviceInfo; + remoteCDM = new remotePlayReadyCDM( + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundPlayreadyPssh); + } } - } - if (remoteCDM && remoteCDM.challenge === null) { - remoteCDM.getChallenge(foundWidevinePssh); - } - const injectedBody = base64ToUint8Array(remoteCDM.challenge); - config.body = injectedBody; - return originalFetch(resource, config); - } - } - if (typeof body === 'string' && !isJson(body)) { - const base64EncodedBody = btoa(body); - if ((base64EncodedBody.startsWith("CAES") || base64EncodedBody.startsWith("PD94")) && (!remoteCDM || remoteCDM.challenge === null || base64EncodedBody !== remoteCDM.challenge) && interceptType === "EME") { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); - // Block the request - return; - } - if ((base64EncodedBody.startsWith("CAES") || base64EncodedBody.startsWith("PD94")) && interceptType == "LICENSE" && !foundChallengeInBody) { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); - if (!remoteCDM) { - if (base64EncodedBody.startsWith("CAES")) { - const { - device_type, system_id, security_level, host, secret, device_name - } = widevineDeviceInfo; - remoteCDM = new remoteWidevineCDM(device_type, system_id, security_level, host, secret, device_name); - remoteCDM.openSession(); + if (remoteCDM && remoteCDM.challenge === null) { remoteCDM.getChallenge(foundWidevinePssh); } - if (base64EncodedBody.startsWith("PD94")) { - const { - security_level, host, secret, device_name - } = playreadyDeviceInfo; - remoteCDM = new remotePlayReadyCDM(security_level, host, secret, device_name); - remoteCDM.openSession(); - remoteCDM.getChallenge(foundPlayreadyPssh); + const injectedBody = atob(remoteCDM.challenge); + config.body = injectedBody; + return originalFetch(resource, config); + } + } + if (typeof body === "string" && isJson(body)) { + const jsonBody = JSON.parse(body); + + if ( + (jsonContainsValue(jsonBody, "CAES") || + jsonContainsValue(jsonBody, "PD94")) && + (!remoteCDM || remoteCDM.challenge === null) && + interceptType === "EME" + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); + // Block the request + return; + } + + if ( + (jsonContainsValue(jsonBody, "CAES") || + jsonContainsValue(jsonBody, "PD94")) && + interceptType === "LICENSE" && + !foundChallengeInBody + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); + if (!remoteCDM) { + if (jsonContainsValue(jsonBody, "CAES")) { + const { + device_type, + system_id, + security_level, + host, + secret, + device_name, + } = widevineDeviceInfo; + remoteCDM = new remoteWidevineCDM( + device_type, + system_id, + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundWidevinePssh); + } + if (jsonContainsValue(jsonBody, "PD94")) { + const { security_level, host, secret, device_name } = + playreadyDeviceInfo; + remoteCDM = new remotePlayReadyCDM( + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundPlayreadyPssh); + } } - } - if (remoteCDM && remoteCDM.challenge === null) { - remoteCDM.getChallenge(foundWidevinePssh); - } - const injectedBody = atob(remoteCDM.challenge); - config.body = injectedBody; - return originalFetch(resource, config); - } - } - if (typeof body === 'string' && isJson(body)) { - const jsonBody = JSON.parse(body); - - if ((jsonContainsValue(jsonBody, "CAES") || jsonContainsValue(jsonBody, "PD94")) && (!remoteCDM || remoteCDM.challenge === null) && interceptType === "EME") { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); - // Block the request - return; - } - - if ((jsonContainsValue(jsonBody, "CAES") || jsonContainsValue(jsonBody, "PD94")) && interceptType === "LICENSE" && !foundChallengeInBody) { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); - if (!remoteCDM) { - if (jsonContainsValue(jsonBody, "CAES")) { - const { - device_type, system_id, security_level, host, secret, device_name - } = widevineDeviceInfo; - remoteCDM = new remoteWidevineCDM(device_type, system_id, security_level, host, secret, device_name); - remoteCDM.openSession(); + if (remoteCDM && remoteCDM.challenge === null) { remoteCDM.getChallenge(foundWidevinePssh); } - if (jsonContainsValue(jsonBody, "PD94")) { - const { - security_level, host, secret, device_name - } = playreadyDeviceInfo; - remoteCDM = new remotePlayReadyCDM(security_level, host, secret, device_name); - remoteCDM.openSession(); - remoteCDM.getChallenge(foundPlayreadyPssh); - } + const injectedBody = jsonReplaceValue(jsonBody, remoteCDM.challenge); + config.body = JSON.stringify(injectedBody); } - if (remoteCDM && remoteCDM.challenge === null) { - remoteCDM.getChallenge(foundWidevinePssh); - } - const injectedBody = jsonReplaceValue(jsonBody, remoteCDM.challenge); - config.body = JSON.stringify(injectedBody); } } } - } - return originalFetch(resource, config); - }; + return originalFetch(resource, config); + }; })(); // XHR POST interceptor -(function() { - const originalOpen = XMLHttpRequest.prototype.open; - const originalSend = XMLHttpRequest.prototype.send; +(function () { + const originalOpen = XMLHttpRequest.prototype.open; + const originalSend = XMLHttpRequest.prototype.send; - XMLHttpRequest.prototype.open = function(method, url, async, user, password) { - this._method = method; - this._url = url; - return originalOpen.apply(this, arguments); - }; + XMLHttpRequest.prototype.open = function (method, url, async, user, password) { + this._method = method; + this._url = url; + return originalOpen.apply(this, arguments); + }; - XMLHttpRequest.prototype.send = function(body) { - if (this._method && this._method.toUpperCase() === 'POST') { - if (body) { - - if (body instanceof ArrayBuffer || body instanceof Uint8Array) { - const buffer = body instanceof Uint8Array ? body : new Uint8Array(body); - const base64Body = window.btoa(String.fromCharCode(...buffer)); - if ((base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) && (!remoteCDM || remoteCDM.challenge === null || base64Body !== remoteCDM.challenge) && interceptType === "EME") { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); - // Block the request - return; - } - if ((base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) && interceptType == "LICENSE" &&!foundChallengeInBody) { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); - if (!remoteCDM) { - if (base64Body.startsWith("CAES")) { - const { - device_type, system_id, security_level, host, secret, device_name - } = widevineDeviceInfo; - remoteCDM = new remoteWidevineCDM(device_type, system_id, security_level, host, secret, device_name); - remoteCDM.openSession(); + XMLHttpRequest.prototype.send = function (body) { + if (this._method && this._method.toUpperCase() === "POST") { + if (body) { + if (body instanceof ArrayBuffer || body instanceof Uint8Array) { + const buffer = body instanceof Uint8Array ? body : new Uint8Array(body); + const base64Body = window.btoa(String.fromCharCode(...buffer)); + if ( + (base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) && + (!remoteCDM || + remoteCDM.challenge === null || + base64Body !== remoteCDM.challenge) && + interceptType === "EME" + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); + // Block the request + return; + } + if ( + (base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) && + interceptType == "LICENSE" && + !foundChallengeInBody + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); + if (!remoteCDM) { + if (base64Body.startsWith("CAES")) { + const { + device_type, + system_id, + security_level, + host, + secret, + device_name, + } = widevineDeviceInfo; + remoteCDM = new remoteWidevineCDM( + device_type, + system_id, + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundWidevinePssh); + } + if (base64Body.startsWith("PD94")) { + const { security_level, host, secret, device_name } = + playreadyDeviceInfo; + remoteCDM = new remotePlayReadyCDM( + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundPlayreadyPssh); + } + } + if (remoteCDM && remoteCDM.challenge === null) { remoteCDM.getChallenge(foundWidevinePssh); } - if (base64Body.startsWith("PD94")) { - const { - security_level, host, secret, device_name - } = playreadyDeviceInfo; - remoteCDM = new remotePlayReadyCDM(security_level, host, secret, device_name); - remoteCDM.openSession(); - remoteCDM.getChallenge(foundPlayreadyPssh); - } + const injectedBody = base64ToUint8Array(remoteCDM.challenge); + return originalSend.call(this, injectedBody); } - if (remoteCDM && remoteCDM.challenge === null) { - remoteCDM.getChallenge(foundWidevinePssh); - } - const injectedBody = base64ToUint8Array(remoteCDM.challenge); - return originalSend.call(this, injectedBody); } - } - if (typeof body === 'string' && !isJson(body)) { - const base64EncodedBody = btoa(body); - if ((base64EncodedBody.startsWith("CAES") || base64EncodedBody.startsWith("PD94")) && (!remoteCDM || remoteCDM.challenge === null || base64EncodedBody !== remoteCDM.challenge) && interceptType === "EME") { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); - // Block the request - return; - } - if ((base64EncodedBody.startsWith("CAES") || base64EncodedBody.startsWith("PD94")) && interceptType == "LICENSE" && !foundChallengeInBody) { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); - if (!remoteCDM) { - if (base64EncodedBody.startsWith("CAES")) { - const { - device_type, system_id, security_level, host, secret, device_name - } = widevineDeviceInfo; - remoteCDM = new remoteWidevineCDM(device_type, system_id, security_level, host, secret, device_name); - remoteCDM.openSession(); + if (typeof body === "string" && !isJson(body)) { + const base64EncodedBody = btoa(body); + if ( + (base64EncodedBody.startsWith("CAES") || + base64EncodedBody.startsWith("PD94")) && + (!remoteCDM || + remoteCDM.challenge === null || + base64EncodedBody !== remoteCDM.challenge) && + interceptType === "EME" + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); + // Block the request + return; + } + if ( + (base64EncodedBody.startsWith("CAES") || + base64EncodedBody.startsWith("PD94")) && + interceptType == "LICENSE" && + !foundChallengeInBody + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); + if (!remoteCDM) { + if (base64EncodedBody.startsWith("CAES")) { + const { + device_type, + system_id, + security_level, + host, + secret, + device_name, + } = widevineDeviceInfo; + remoteCDM = new remoteWidevineCDM( + device_type, + system_id, + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundWidevinePssh); + } + if (base64EncodedBody.startsWith("PD94")) { + const { security_level, host, secret, device_name } = + playreadyDeviceInfo; + remoteCDM = new remotePlayReadyCDM( + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundPlayreadyPssh); + } + } + if (remoteCDM && remoteCDM.challenge === null) { remoteCDM.getChallenge(foundWidevinePssh); } - if (base64EncodedBody.startsWith("PD94")) { - const { - security_level, host, secret, device_name - } = playreadyDeviceInfo; - remoteCDM = new remotePlayReadyCDM(security_level, host, secret, device_name); - remoteCDM.openSession(); - remoteCDM.getChallenge(foundPlayreadyPssh); + const injectedBody = atob(remoteCDM.challenge); + return originalSend.call(this, injectedBody); + } + } + + if (typeof body === "string" && isJson(body)) { + const jsonBody = JSON.parse(body); + + if ( + (jsonContainsValue(jsonBody, "CAES") || + jsonContainsValue(jsonBody, "PD94")) && + (!remoteCDM || remoteCDM.challenge === null) && + interceptType === "EME" + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); + // Block the request + return; + } + + if ( + (jsonContainsValue(jsonBody, "CAES") || + jsonContainsValue(jsonBody, "PD94")) && + interceptType === "LICENSE" && + !foundChallengeInBody + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); + if (!remoteCDM) { + if (jsonContainsValue(jsonBody, "CAES")) { + const { + device_type, + system_id, + security_level, + host, + secret, + device_name, + } = widevineDeviceInfo; + remoteCDM = new remoteWidevineCDM( + device_type, + system_id, + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundWidevinePssh); + } + if (jsonContainsValue(jsonBody, "PD94")) { + const { security_level, host, secret, device_name } = + playreadyDeviceInfo; + remoteCDM = new remotePlayReadyCDM( + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundPlayreadyPssh); + } } - } - if (remoteCDM && remoteCDM.challenge === null) { - remoteCDM.getChallenge(foundWidevinePssh); - } - const injectedBody = atob(remoteCDM.challenge); - return originalSend.call(this, injectedBody); - } - } - - if (typeof body === 'string' && isJson(body)) { - const jsonBody = JSON.parse(body); - - if ((jsonContainsValue(jsonBody, "CAES") || jsonContainsValue(jsonBody, "PD94")) && (!remoteCDM || remoteCDM.challenge === null) && interceptType === "EME") { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); - // Block the request - return; - } - - if ((jsonContainsValue(jsonBody, "CAES") || jsonContainsValue(jsonBody, "PD94")) && interceptType === "LICENSE" && !foundChallengeInBody) { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); - if (!remoteCDM) { - if (jsonContainsValue(jsonBody, "CAES")) { - const { - device_type, system_id, security_level, host, secret, device_name - } = widevineDeviceInfo; - remoteCDM = new remoteWidevineCDM(device_type, system_id, security_level, host, secret, device_name); - remoteCDM.openSession(); + if (remoteCDM && remoteCDM.challenge === null) { remoteCDM.getChallenge(foundWidevinePssh); } - if (jsonContainsValue(jsonBody, "PD94")) { - const { - security_level, host, secret, device_name - } = playreadyDeviceInfo; - remoteCDM = new remotePlayReadyCDM(security_level, host, secret, device_name); - remoteCDM.openSession(); - remoteCDM.getChallenge(foundPlayreadyPssh); - } + const injectedBody = jsonReplaceValue(jsonBody, remoteCDM.challenge); + return originalSend.call(this, JSON.stringify(injectedBody)); } - if (remoteCDM && remoteCDM.challenge === null) { - remoteCDM.getChallenge(foundWidevinePssh); - } - const injectedBody = jsonReplaceValue(jsonBody, remoteCDM.challenge); - return originalSend.call(this, JSON.stringify(injectedBody)); } } } - } - return originalSend.apply(this, arguments); - }; -})(); \ No newline at end of file + return originalSend.apply(this, arguments); + }; +})(); diff --git a/manifest.json b/manifest.json index 78c3145..5c54ba4 100644 --- a/manifest.json +++ b/manifest.json @@ -1,41 +1,41 @@ { - "manifest_version": 2, - "name": "CDRM Extension 2.0", - "version": "2.0", - "description": "Decrypt DRM Protected content", - "permissions": [ - "webRequest", - "webRequestBlocking", - "", - "activeTab", - "storage", - "tabs", - "contextMenus" - ], - "background": { - "scripts": ["background.js"], - "persistent": true - }, - "content_scripts": [ - { - "matches": [""], - "js": ["content.js"], - "run_at": "document_start", - "all_frames": true + "manifest_version": 2, + "name": "CDRM Extension 2.0", + "version": "2.0", + "description": "Decrypt DRM Protected content", + "permissions": [ + "webRequest", + "webRequestBlocking", + "", + "activeTab", + "storage", + "tabs", + "contextMenus" + ], + "background": { + "scripts": ["background.js"], + "persistent": true + }, + "content_scripts": [ + { + "matches": [""], + "js": ["content.js"], + "run_at": "document_start", + "all_frames": true + } + ], + "web_accessible_resources": ["inject.js"], + "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", + "browser_action": { + "default_icon": { + "16": "icons/icon16.png", + "32": "icons/icon32.png", + "128": "icons/icon128.png" + } + }, + "icons": { + "16": "icons/icon16.png", + "32": "icons/icon32.png", + "128": "icons/icon128.png" } - ], - "web_accessible_resources": ["inject.js"], - "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", - "browser_action": { - "default_icon": { - "16": "icons/icon16.png", - "32": "icons/icon32.png", - "128": "icons/icon128.png" - } - }, - "icons": { - "16": "icons/icon16.png", - "32": "icons/icon32.png", - "128": "icons/icon128.png" - } } -- 2.43.0 From 935d235ad9f387cf1bde4e72be11030448e5bcd9 Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Sun, 20 Jul 2025 14:22:04 +0700 Subject: [PATCH 02/26] organize files and folders, remove unnecessary generated build files, add new script to build extension to `extension-release` folder --- .gitignore | 30 ++ buildext.js | 65 ++++ frontend/dist/assets/index-UaipKa9p.css | 1 - frontend/dist/index.html | 26 +- frontend/package-lock.json | 487 ++++++++++++------------ frontend/package.json | 22 +- package.json | 21 + react/assets/index-UaipKa9p.css | 1 - react/assets/index-ydPQKJSy.js | 52 --- react/index.html | 13 - background.js => src/background.js | 2 +- content.js => src/content.js | 0 inject.js => src/inject.js | 0 manifest.json => src/manifest.json | 0 14 files changed, 382 insertions(+), 338 deletions(-) create mode 100644 .gitignore create mode 100644 buildext.js delete mode 100644 frontend/dist/assets/index-UaipKa9p.css create mode 100644 package.json delete mode 100644 react/assets/index-UaipKa9p.css delete mode 100644 react/assets/index-ydPQKJSy.js delete mode 100644 react/index.html rename background.js => src/background.js (98%) rename content.js => src/content.js (100%) rename inject.js => src/inject.js (100%) rename manifest.json => src/manifest.json (100%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65e2b16 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +frontend/node_modules +frontend/dist + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# extension release folder +extension-release diff --git a/buildext.js b/buildext.js new file mode 100644 index 0000000..de5beb0 --- /dev/null +++ b/buildext.js @@ -0,0 +1,65 @@ +import { execSync } from "child_process"; +import fs from "fs"; +import path from "path"; +import url from "url"; + +const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); +const frontendDir = path.join(__dirname, "frontend"); +const distDir = path.join(frontendDir, "dist"); +const srcDir = path.join(__dirname, "src"); +const iconDir = path.join(__dirname, "icons"); +const releaseDir = path.join(__dirname, "extension-release"); + +function run(cmd, cwd) { + console.log(`🛠️ Running: ${cmd}`); + execSync(cmd, { cwd, stdio: "inherit" }); +} + +async function copyDir(src, dest) { + await fs.promises.mkdir(dest, { recursive: true }); + await fs.promises.cp(src, dest, { + recursive: true, + force: true, + filter: (src) => !src.endsWith(".map"), + }); +} + +async function main() { + console.log("🚀 Starting extension build..."); + + // 1. Install frontend deps if needed + if (!fs.existsSync(path.join(frontendDir, "node_modules"))) { + console.log("📦 node_modules not found. Running npm install..."); + run("npm install", frontendDir); + } + + // 2. Build frontend + console.log("📦 Building frontend..."); + run("npm run build", frontendDir); + + // 3. Clean release folder + if (fs.existsSync(releaseDir)) { + console.log("🧹 Cleaning existing extension-release folder..."); + await fs.promises.rm(releaseDir, { recursive: true, force: true }); + } + await fs.promises.mkdir(releaseDir); + + // 4. Copy src files (manifest, background, etc) to release + console.log("📦 Copying src files to extension-release..."); + await copyDir(srcDir, releaseDir); + + // 5. Copy frontend dist files to release (merged at root) + console.log("📦 Copying frontend dist files to extension-release..."); + await copyDir(distDir, releaseDir); + + // 6. Copy icon directory to release (merged at root) + console.log("📦 Copying icon directory to extension-release..."); + await copyDir(iconDir, path.join(releaseDir, "icons")); + + console.log("✅ Build complete! extension-release ready."); +} + +main().catch((e) => { + console.error("❌ Build failed:", e); + process.exit(1); +}); diff --git a/frontend/dist/assets/index-UaipKa9p.css b/frontend/dist/assets/index-UaipKa9p.css deleted file mode 100644 index 7dcc3f4..0000000 --- a/frontend/dist/assets/index-UaipKa9p.css +++ /dev/null @@ -1 +0,0 @@ -/*! tailwindcss v4.1.7 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-font-weight:initial;--tw-duration:initial;--tw-ease:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-sky-500:oklch(68.5% .169 237.323);--color-sky-600:oklch(58.8% .158 241.966);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-slate-800:oklch(27.9% .041 260.031);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-black:#000;--color-white:#fff;--spacing:.25rem;--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--font-weight-bold:700;--radius-md:.375rem;--ease-in-out:cubic-bezier(.4,0,.2,1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.top-0{top:calc(var(--spacing)*0)}.left-0{left:calc(var(--spacing)*0)}.z-20{z-index:20}.z-50{z-index:50}.m-1{margin:calc(var(--spacing)*1)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-4{margin-top:calc(var(--spacing)*4)}.mt-5{margin-top:calc(var(--spacing)*5)}.mr-2{margin-right:calc(var(--spacing)*2)}.ml-auto{margin-left:auto}.flex{display:flex}.h-10{height:calc(var(--spacing)*10)}.h-16{height:calc(var(--spacing)*16)}.h-64{height:calc(var(--spacing)*64)}.h-full{height:100%}.h-screen{height:100vh}.max-h-16{max-height:calc(var(--spacing)*16)}.min-h-16{min-height:calc(var(--spacing)*16)}.min-h-64{min-height:calc(var(--spacing)*64)}.min-h-full{min-height:100%}.w-16{width:calc(var(--spacing)*16)}.w-full{width:100%}.min-w-full{min-width:100%}.shrink-0{flex-shrink:0}.flex-grow,.grow{flex-grow:1}.-translate-x-full{--tw-translate-x:-100%;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-0{--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.items-center{align-items:center}.justify-center{justify-content:center}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-r-2{border-right-style:var(--tw-border-style);border-right-width:2px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-gray-700{border-color:var(--color-gray-700)}.border-r-white{border-right-color:var(--color-white)}.border-b-white{border-bottom-color:var(--color-white)}.border-l-white{border-left-color:var(--color-white)}.bg-black{background-color:var(--color-black)}.bg-black\/95{background-color:#000000f2}@supports (color:color-mix(in lab,red,red)){.bg-black\/95{background-color:color-mix(in oklab,var(--color-black)95%,transparent)}}.bg-blue-400{background-color:var(--color-blue-400)}.bg-blue-600{background-color:var(--color-blue-600)}.bg-gray-800{background-color:var(--color-gray-800)}.bg-green-500\/70{background-color:#00c758b3}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/70{background-color:color-mix(in oklab,var(--color-green-500)70%,transparent)}}.bg-red-500\/70{background-color:#fb2c36b3}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/70{background-color:color-mix(in oklab,var(--color-red-500)70%,transparent)}}.bg-sky-500{background-color:var(--color-sky-500)}.bg-sky-500\/70{background-color:#00a5efb3}@supports (color:color-mix(in lab,red,red)){.bg-sky-500\/70{background-color:color-mix(in oklab,var(--color-sky-500)70%,transparent)}}.bg-slate-800\/50{background-color:#1d293d80}@supports (color:color-mix(in lab,red,red)){.bg-slate-800\/50{background-color:color-mix(in oklab,var(--color-slate-800)50%,transparent)}}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.text-center{text-align:center}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.text-nowrap{text-wrap:nowrap}.whitespace-pre-line{white-space:pre-line}.text-gray-400{color:var(--color-gray-400)}.text-green-400{color:var(--color-green-400)}.text-red-400{color:var(--color-red-400)}.text-white{color:var(--color-white)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,visibility,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-300{--tw-duration:.3s;transition-duration:.3s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}@media (hover:hover){.hover\:cursor-pointer:hover{cursor:pointer}.hover\:border-l-1:hover{border-left-style:var(--tw-border-style);border-left-width:1px}.hover\:bg-black\/50:hover{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.hover\:bg-black\/50:hover{background-color:color-mix(in oklab,var(--color-black)50%,transparent)}}.hover\:bg-blue-700:hover{background-color:var(--color-blue-700)}.hover\:bg-sky-600:hover{background-color:var(--color-sky-600)}}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-blue-500:focus{--tw-ring-color:var(--color-blue-500)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}}html,body,#root{width:100%;height:100%;margin:0;padding:0}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000} diff --git a/frontend/dist/index.html b/frontend/dist/index.html index 0042fc4..7d51312 100644 --- a/frontend/dist/index.html +++ b/frontend/dist/index.html @@ -1,13 +1,13 @@ - - - - - - CDRM Decryption Extension - - - - -
- - + + + + + + CDRM Decryption Extension + + + + +
+ + diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 39b3ac5..c86a3ab 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,22 +8,22 @@ "name": "frontend", "version": "0.0.0", "dependencies": { - "@tailwindcss/vite": "^4.1.7", + "@tailwindcss/vite": "^4.1.11", "react": "^19.1.0", "react-dom": "^19.1.0", - "react-router-dom": "^7.6.1", - "tailwindcss": "^4.1.7" + "react-router-dom": "^7.7.0", + "tailwindcss": "^4.1.11" }, "devDependencies": { - "@eslint/js": "^9.25.0", - "@types/react": "^19.1.2", - "@types/react-dom": "^19.1.2", - "@vitejs/plugin-react": "^4.4.1", - "eslint": "^9.25.0", + "@eslint/js": "^9.31.0", + "@types/react": "^19.1.8", + "@types/react-dom": "^19.1.6", + "@vitejs/plugin-react": "^4.7.0", + "eslint": "^9.31.0", "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.19", - "globals": "^16.0.0", - "vite": "^6.3.5" + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^16.3.0", + "vite": "^7.0.5" } }, "node_modules/@ampproject/remapping": { @@ -65,22 +65,22 @@ } }, "node_modules/@babel/core": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz", - "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.1", - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helpers": "^7.27.1", - "@babel/parser": "^7.27.1", - "@babel/template": "^7.27.1", - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -96,16 +96,16 @@ } }, "node_modules/@babel/generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", - "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", + "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.27.1", - "@babel/types": "^7.27.1", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "@babel/parser": "^7.28.0", + "@babel/types": "^7.28.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { @@ -129,6 +129,16 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-module-imports": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", @@ -144,15 +154,15 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz", - "integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/traverse": "^7.27.3" }, "engines": { "node": ">=6.9.0" @@ -202,27 +212,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", - "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", - "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.27.1" + "@babel/types": "^7.28.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -279,38 +289,28 @@ } }, "node_modules/@babel/traverse": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", - "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", + "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.1", - "@babel/parser": "^7.27.1", - "@babel/template": "^7.27.1", - "@babel/types": "^7.27.1", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/generator": "^7.28.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.0", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/types": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", - "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", + "version": "7.28.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz", + "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", "dev": true, "license": "MIT", "dependencies": { @@ -764,9 +764,9 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", - "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -779,9 +779,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz", - "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -789,9 +789,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", - "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -839,9 +839,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.27.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz", - "integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", "dev": true, "license": "MIT", "engines": { @@ -862,13 +862,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", - "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz", + "integrity": "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.14.0", + "@eslint/core": "^0.15.1", "levn": "^0.4.1" }, "engines": { @@ -954,17 +954,13 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { @@ -976,15 +972,6 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", @@ -992,15 +979,22 @@ "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.40.2", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.2.tgz", @@ -1262,9 +1256,9 @@ ] }, "node_modules/@tailwindcss/node": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.7.tgz", - "integrity": "sha512-9rsOpdY9idRI2NH6CL4wORFY0+Q6fnx9XP9Ju+iq/0wJwGD5IByIgFmwVbyy4ymuyprj8Qh4ErxMKTUL4uNh3g==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", + "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==", "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", @@ -1273,13 +1267,13 @@ "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", - "tailwindcss": "4.1.7" + "tailwindcss": "4.1.11" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.7.tgz", - "integrity": "sha512-5SF95Ctm9DFiUyjUPnDGkoKItPX/k+xifcQhcqX5RA85m50jw1pT/KzjdvlqxRja45Y52nR4MR9fD1JYd7f8NQ==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz", + "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -1290,24 +1284,24 @@ "node": ">= 10" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.7", - "@tailwindcss/oxide-darwin-arm64": "4.1.7", - "@tailwindcss/oxide-darwin-x64": "4.1.7", - "@tailwindcss/oxide-freebsd-x64": "4.1.7", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.7", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.7", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.7", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.7", - "@tailwindcss/oxide-linux-x64-musl": "4.1.7", - "@tailwindcss/oxide-wasm32-wasi": "4.1.7", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.7", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.7" + "@tailwindcss/oxide-android-arm64": "4.1.11", + "@tailwindcss/oxide-darwin-arm64": "4.1.11", + "@tailwindcss/oxide-darwin-x64": "4.1.11", + "@tailwindcss/oxide-freebsd-x64": "4.1.11", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", + "@tailwindcss/oxide-linux-x64-musl": "4.1.11", + "@tailwindcss/oxide-wasm32-wasi": "4.1.11", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.7.tgz", - "integrity": "sha512-IWA410JZ8fF7kACus6BrUwY2Z1t1hm0+ZWNEzykKmMNM09wQooOcN/VXr0p/WJdtHZ90PvJf2AIBS/Ceqx1emg==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz", + "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==", "cpu": [ "arm64" ], @@ -1321,9 +1315,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.7.tgz", - "integrity": "sha512-81jUw9To7fimGGkuJ2W5h3/oGonTOZKZ8C2ghm/TTxbwvfSiFSDPd6/A/KE2N7Jp4mv3Ps9OFqg2fEKgZFfsvg==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz", + "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==", "cpu": [ "arm64" ], @@ -1337,9 +1331,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.7.tgz", - "integrity": "sha512-q77rWjEyGHV4PdDBtrzO0tgBBPlQWKY7wZK0cUok/HaGgbNKecegNxCGikuPJn5wFAlIywC3v+WMBt0PEBtwGw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz", + "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==", "cpu": [ "x64" ], @@ -1353,9 +1347,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.7.tgz", - "integrity": "sha512-RfmdbbK6G6ptgF4qqbzoxmH+PKfP4KSVs7SRlTwcbRgBwezJkAO3Qta/7gDy10Q2DcUVkKxFLXUQO6J3CRvBGw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz", + "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==", "cpu": [ "x64" ], @@ -1369,9 +1363,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.7.tgz", - "integrity": "sha512-OZqsGvpwOa13lVd1z6JVwQXadEobmesxQ4AxhrwRiPuE04quvZHWn/LnihMg7/XkN+dTioXp/VMu/p6A5eZP3g==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz", + "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==", "cpu": [ "arm" ], @@ -1385,9 +1379,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.7.tgz", - "integrity": "sha512-voMvBTnJSfKecJxGkoeAyW/2XRToLZ227LxswLAwKY7YslG/Xkw9/tJNH+3IVh5bdYzYE7DfiaPbRkSHFxY1xA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz", + "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==", "cpu": [ "arm64" ], @@ -1401,9 +1395,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.7.tgz", - "integrity": "sha512-PjGuNNmJeKHnP58M7XyjJyla8LPo+RmwHQpBI+W/OxqrwojyuCQ+GUtygu7jUqTEexejZHr/z3nBc/gTiXBj4A==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz", + "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==", "cpu": [ "arm64" ], @@ -1417,9 +1411,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.7.tgz", - "integrity": "sha512-HMs+Va+ZR3gC3mLZE00gXxtBo3JoSQxtu9lobbZd+DmfkIxR54NO7Z+UQNPsa0P/ITn1TevtFxXTpsRU7qEvWg==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz", + "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==", "cpu": [ "x64" ], @@ -1433,9 +1427,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.7.tgz", - "integrity": "sha512-MHZ6jyNlutdHH8rd+YTdr3QbXrHXqwIhHw9e7yXEBcQdluGwhpQY2Eku8UZK6ReLaWtQ4gijIv5QoM5eE+qlsA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz", + "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==", "cpu": [ "x64" ], @@ -1449,9 +1443,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.7.tgz", - "integrity": "sha512-ANaSKt74ZRzE2TvJmUcbFQ8zS201cIPxUDm5qez5rLEwWkie2SkGtA4P+GPTj+u8N6JbPrC8MtY8RmJA35Oo+A==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz", + "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -1469,7 +1463,7 @@ "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@emnapi/wasi-threads": "^1.0.2", - "@napi-rs/wasm-runtime": "^0.2.9", + "@napi-rs/wasm-runtime": "^0.2.11", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, @@ -1478,9 +1472,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.7.tgz", - "integrity": "sha512-HUiSiXQ9gLJBAPCMVRk2RT1ZrBjto7WvqsPBwUrNK2BcdSxMnk19h4pjZjI7zgPhDxlAbJSumTC4ljeA9y0tEw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", + "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==", "cpu": [ "arm64" ], @@ -1494,9 +1488,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.7.tgz", - "integrity": "sha512-rYHGmvoHiLJ8hWucSfSOEmdCBIGZIq7SpkPRSqLsH2Ab2YUNgKeAPT1Fi2cx3+hnYOrAb0jp9cRyode3bBW4mQ==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz", + "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==", "cpu": [ "x64" ], @@ -1510,17 +1504,17 @@ } }, "node_modules/@tailwindcss/vite": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.7.tgz", - "integrity": "sha512-tYa2fO3zDe41I7WqijyVbRd8oWT0aEID1Eokz5hMT6wShLIHj3yvwj9XbfuloHP9glZ6H+aG2AN/+ZrxJ1Y5RQ==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.11.tgz", + "integrity": "sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==", "license": "MIT", "dependencies": { - "@tailwindcss/node": "4.1.7", - "@tailwindcss/oxide": "4.1.7", - "tailwindcss": "4.1.7" + "@tailwindcss/node": "4.1.11", + "@tailwindcss/oxide": "4.1.11", + "tailwindcss": "4.1.11" }, "peerDependencies": { - "vite": "^5.2.0 || ^6" + "vite": "^5.2.0 || ^6 || ^7" } }, "node_modules/@types/babel__core": { @@ -1582,9 +1576,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.1.4", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.4.tgz", - "integrity": "sha512-EB1yiiYdvySuIITtD5lhW4yPyJ31RkJkkDw794LaQYrxCSaQV/47y5o1FMC4zF9ZyjUjzJMZwbovEnT5yHTW6g==", + "version": "19.1.8", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", + "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", "dev": true, "license": "MIT", "dependencies": { @@ -1592,9 +1586,9 @@ } }, "node_modules/@types/react-dom": { - "version": "19.1.5", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.5.tgz", - "integrity": "sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg==", + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", + "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", "dev": true, "license": "MIT", "peerDependencies": { @@ -1602,15 +1596,16 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.1.tgz", - "integrity": "sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.26.10", - "@babel/plugin-transform-react-jsx-self": "^7.25.9", - "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, @@ -1618,13 +1613,13 @@ "node": "^14.18.0 || >=16.0.0" }, "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", "bin": { @@ -1692,9 +1687,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -1899,9 +1894,9 @@ "license": "ISC" }, "node_modules/enhanced-resolve": { - "version": "5.18.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", - "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "version": "5.18.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", + "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -1975,19 +1970,19 @@ } }, "node_modules/eslint": { - "version": "9.27.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz", - "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.20.0", - "@eslint/config-helpers": "^0.2.1", - "@eslint/core": "^0.14.0", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", + "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.27.0", + "@eslint/js": "9.31.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -1999,9 +1994,9 @@ "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.3.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.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", @@ -2059,9 +2054,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", - "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -2076,9 +2071,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2089,15 +2084,15 @@ } }, "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2174,9 +2169,9 @@ "license": "MIT" }, "node_modules/fdir": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", - "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", "license": "MIT", "peerDependencies": { "picomatch": "^3 || ^4" @@ -2276,9 +2271,9 @@ } }, "node_modules/globals": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz", - "integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", + "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", "dev": true, "license": "MIT", "engines": { @@ -2919,9 +2914,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", "engines": { "node": ">=12" @@ -2931,9 +2926,9 @@ } }, "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "funding": [ { "type": "opencollective", @@ -2950,7 +2945,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.8", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -3010,9 +3005,9 @@ } }, "node_modules/react-router": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.1.tgz", - "integrity": "sha512-hPJXXxHJZEsPFNVbtATH7+MMX43UDeOauz+EAU4cgqTn7ojdI9qQORqS8Z0qmDlL1TclO/6jLRYUEtbWidtdHQ==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.7.0.tgz", + "integrity": "sha512-3FUYSwlvB/5wRJVTL/aavqHmfUKe0+Xm9MllkYgGo9eDwNdkvwlJGjpPxono1kCycLt6AnDTgjmXvK3/B4QGuw==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -3032,12 +3027,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.6.1.tgz", - "integrity": "sha512-vxU7ei//UfPYQ3iZvHuO1D/5fX3/JOqhNTbRR+WjSBWxf9bIvpWK+ftjmdfJHzPOuMQKe2fiEdG+dZX6E8uUpA==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.7.0.tgz", + "integrity": "sha512-wwGS19VkNBkneVh9/YD0pK3IsjWxQUVMDD6drlG7eJpo1rXBtctBqDyBm/k+oKHRAm1x9XWT3JFC82QI9YOXXA==", "license": "MIT", "dependencies": { - "react-router": "7.6.1" + "react-router": "7.7.0" }, "engines": { "node": ">=20.0.0" @@ -3177,9 +3172,9 @@ } }, "node_modules/tailwindcss": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.7.tgz", - "integrity": "sha512-kr1o/ErIdNhTz8uzAYL7TpaUuzKIE6QPQ4qmSdxnoX/lo+5wmUHQA6h3L5yIqEImSRnAAURDirLu/BgiXGPAhg==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", + "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==", "license": "MIT" }, "node_modules/tapable": { @@ -3218,9 +3213,9 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", - "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", "license": "MIT", "dependencies": { "fdir": "^6.4.4", @@ -3288,23 +3283,23 @@ } }, "node_modules/vite": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", - "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.5.tgz", + "integrity": "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==", "license": "MIT", "dependencies": { "esbuild": "^0.25.0", - "fdir": "^6.4.4", + "fdir": "^6.4.6", "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" + "postcss": "^8.5.6", + "rollup": "^4.40.0", + "tinyglobby": "^0.2.14" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -3313,14 +3308,14 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", - "less": "*", + "less": "^4.0.0", "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", + "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" diff --git a/frontend/package.json b/frontend/package.json index 2998847..b4df8fd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,21 +10,21 @@ "preview": "vite preview" }, "dependencies": { - "@tailwindcss/vite": "^4.1.7", + "@tailwindcss/vite": "^4.1.11", "react": "^19.1.0", "react-dom": "^19.1.0", - "react-router-dom": "^7.6.1", - "tailwindcss": "^4.1.7" + "react-router-dom": "^7.7.0", + "tailwindcss": "^4.1.11" }, "devDependencies": { - "@eslint/js": "^9.25.0", - "@types/react": "^19.1.2", - "@types/react-dom": "^19.1.2", - "@vitejs/plugin-react": "^4.4.1", - "eslint": "^9.25.0", + "@eslint/js": "^9.31.0", + "@types/react": "^19.1.8", + "@types/react-dom": "^19.1.6", + "@vitejs/plugin-react": "^4.7.0", + "eslint": "^9.31.0", "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.19", - "globals": "^16.0.0", - "vite": "^6.3.5" + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^16.3.0", + "vite": "^7.0.5" } } diff --git a/package.json b/package.json new file mode 100644 index 0000000..74ba2f6 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "cdrm-extension", + "version": "1.0.0", + "description": "", + "main": "background.js", + "scripts": { + "buildext": "node buildext.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://cdm-project.com/tpd94/CDRM-Extension.git" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "module", + "engines": { + "node": ">=21.0.0" + } +} diff --git a/react/assets/index-UaipKa9p.css b/react/assets/index-UaipKa9p.css deleted file mode 100644 index 7dcc3f4..0000000 --- a/react/assets/index-UaipKa9p.css +++ /dev/null @@ -1 +0,0 @@ -/*! tailwindcss v4.1.7 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-font-weight:initial;--tw-duration:initial;--tw-ease:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-sky-500:oklch(68.5% .169 237.323);--color-sky-600:oklch(58.8% .158 241.966);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-slate-800:oklch(27.9% .041 260.031);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-black:#000;--color-white:#fff;--spacing:.25rem;--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--font-weight-bold:700;--radius-md:.375rem;--ease-in-out:cubic-bezier(.4,0,.2,1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.top-0{top:calc(var(--spacing)*0)}.left-0{left:calc(var(--spacing)*0)}.z-20{z-index:20}.z-50{z-index:50}.m-1{margin:calc(var(--spacing)*1)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-4{margin-top:calc(var(--spacing)*4)}.mt-5{margin-top:calc(var(--spacing)*5)}.mr-2{margin-right:calc(var(--spacing)*2)}.ml-auto{margin-left:auto}.flex{display:flex}.h-10{height:calc(var(--spacing)*10)}.h-16{height:calc(var(--spacing)*16)}.h-64{height:calc(var(--spacing)*64)}.h-full{height:100%}.h-screen{height:100vh}.max-h-16{max-height:calc(var(--spacing)*16)}.min-h-16{min-height:calc(var(--spacing)*16)}.min-h-64{min-height:calc(var(--spacing)*64)}.min-h-full{min-height:100%}.w-16{width:calc(var(--spacing)*16)}.w-full{width:100%}.min-w-full{min-width:100%}.shrink-0{flex-shrink:0}.flex-grow,.grow{flex-grow:1}.-translate-x-full{--tw-translate-x:-100%;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-0{--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.items-center{align-items:center}.justify-center{justify-content:center}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-r-2{border-right-style:var(--tw-border-style);border-right-width:2px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-gray-700{border-color:var(--color-gray-700)}.border-r-white{border-right-color:var(--color-white)}.border-b-white{border-bottom-color:var(--color-white)}.border-l-white{border-left-color:var(--color-white)}.bg-black{background-color:var(--color-black)}.bg-black\/95{background-color:#000000f2}@supports (color:color-mix(in lab,red,red)){.bg-black\/95{background-color:color-mix(in oklab,var(--color-black)95%,transparent)}}.bg-blue-400{background-color:var(--color-blue-400)}.bg-blue-600{background-color:var(--color-blue-600)}.bg-gray-800{background-color:var(--color-gray-800)}.bg-green-500\/70{background-color:#00c758b3}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/70{background-color:color-mix(in oklab,var(--color-green-500)70%,transparent)}}.bg-red-500\/70{background-color:#fb2c36b3}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/70{background-color:color-mix(in oklab,var(--color-red-500)70%,transparent)}}.bg-sky-500{background-color:var(--color-sky-500)}.bg-sky-500\/70{background-color:#00a5efb3}@supports (color:color-mix(in lab,red,red)){.bg-sky-500\/70{background-color:color-mix(in oklab,var(--color-sky-500)70%,transparent)}}.bg-slate-800\/50{background-color:#1d293d80}@supports (color:color-mix(in lab,red,red)){.bg-slate-800\/50{background-color:color-mix(in oklab,var(--color-slate-800)50%,transparent)}}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.text-center{text-align:center}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.text-nowrap{text-wrap:nowrap}.whitespace-pre-line{white-space:pre-line}.text-gray-400{color:var(--color-gray-400)}.text-green-400{color:var(--color-green-400)}.text-red-400{color:var(--color-red-400)}.text-white{color:var(--color-white)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,visibility,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-300{--tw-duration:.3s;transition-duration:.3s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}@media (hover:hover){.hover\:cursor-pointer:hover{cursor:pointer}.hover\:border-l-1:hover{border-left-style:var(--tw-border-style);border-left-width:1px}.hover\:bg-black\/50:hover{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.hover\:bg-black\/50:hover{background-color:color-mix(in oklab,var(--color-black)50%,transparent)}}.hover\:bg-blue-700:hover{background-color:var(--color-blue-700)}.hover\:bg-sky-600:hover{background-color:var(--color-sky-600)}}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-blue-500:focus{--tw-ring-color:var(--color-blue-500)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}}html,body,#root{width:100%;height:100%;margin:0;padding:0}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000} diff --git a/react/assets/index-ydPQKJSy.js b/react/assets/index-ydPQKJSy.js deleted file mode 100644 index e29c9f8..0000000 --- a/react/assets/index-ydPQKJSy.js +++ /dev/null @@ -1,52 +0,0 @@ -(function(){const o=document.createElement("link").relList;if(o&&o.supports&&o.supports("modulepreload"))return;for(const h of document.querySelectorAll('link[rel="modulepreload"]'))r(h);new MutationObserver(h=>{for(const v of h)if(v.type==="childList")for(const T of v.addedNodes)T.tagName==="LINK"&&T.rel==="modulepreload"&&r(T)}).observe(document,{childList:!0,subtree:!0});function s(h){const v={};return h.integrity&&(v.integrity=h.integrity),h.referrerPolicy&&(v.referrerPolicy=h.referrerPolicy),h.crossOrigin==="use-credentials"?v.credentials="include":h.crossOrigin==="anonymous"?v.credentials="omit":v.credentials="same-origin",v}function r(h){if(h.ep)return;h.ep=!0;const v=s(h);fetch(h.href,v)}})();var pf={exports:{}},Ou={};/** - * @license React - * react-jsx-runtime.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var C0;function U1(){if(C0)return Ou;C0=1;var c=Symbol.for("react.transitional.element"),o=Symbol.for("react.fragment");function s(r,h,v){var T=null;if(v!==void 0&&(T=""+v),h.key!==void 0&&(T=""+h.key),"key"in h){v={};for(var O in h)O!=="key"&&(v[O]=h[O])}else v=h;return h=v.ref,{$$typeof:c,type:r,key:T,ref:h!==void 0?h:null,props:v}}return Ou.Fragment=o,Ou.jsx=s,Ou.jsxs=s,Ou}var U0;function N1(){return U0||(U0=1,pf.exports=U1()),pf.exports}var K=N1(),Sf={exports:{}},et={};/** - * @license React - * react.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var N0;function H1(){if(N0)return et;N0=1;var c=Symbol.for("react.transitional.element"),o=Symbol.for("react.portal"),s=Symbol.for("react.fragment"),r=Symbol.for("react.strict_mode"),h=Symbol.for("react.profiler"),v=Symbol.for("react.consumer"),T=Symbol.for("react.context"),O=Symbol.for("react.forward_ref"),p=Symbol.for("react.suspense"),d=Symbol.for("react.memo"),R=Symbol.for("react.lazy"),w=Symbol.iterator;function C(y){return y===null||typeof y!="object"?null:(y=w&&y[w]||y["@@iterator"],typeof y=="function"?y:null)}var B={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},H=Object.assign,G={};function Q(y,N,X){this.props=y,this.context=N,this.refs=G,this.updater=X||B}Q.prototype.isReactComponent={},Q.prototype.setState=function(y,N){if(typeof y!="object"&&typeof y!="function"&&y!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,y,N,"setState")},Q.prototype.forceUpdate=function(y){this.updater.enqueueForceUpdate(this,y,"forceUpdate")};function L(){}L.prototype=Q.prototype;function Y(y,N,X){this.props=y,this.context=N,this.refs=G,this.updater=X||B}var $=Y.prototype=new L;$.constructor=Y,H($,Q.prototype),$.isPureReactComponent=!0;var it=Array.isArray,I={H:null,A:null,T:null,S:null,V:null},zt=Object.prototype.hasOwnProperty;function Rt(y,N,X,q,J,ft){return X=ft.ref,{$$typeof:c,type:y,key:N,ref:X!==void 0?X:null,props:ft}}function Ot(y,N){return Rt(y.type,N,void 0,void 0,void 0,y.props)}function St(y){return typeof y=="object"&&y!==null&&y.$$typeof===c}function Jt(y){var N={"=":"=0",":":"=2"};return"$"+y.replace(/[=:]/g,function(X){return N[X]})}var oe=/\/+/g;function Xt(y,N){return typeof y=="object"&&y!==null&&y.key!=null?Jt(""+y.key):N.toString(36)}function El(){}function Tl(y){switch(y.status){case"fulfilled":return y.value;case"rejected":throw y.reason;default:switch(typeof y.status=="string"?y.then(El,El):(y.status="pending",y.then(function(N){y.status==="pending"&&(y.status="fulfilled",y.value=N)},function(N){y.status==="pending"&&(y.status="rejected",y.reason=N)})),y.status){case"fulfilled":return y.value;case"rejected":throw y.reason}}throw y}function Vt(y,N,X,q,J){var ft=typeof y;(ft==="undefined"||ft==="boolean")&&(y=null);var tt=!1;if(y===null)tt=!0;else switch(ft){case"bigint":case"string":case"number":tt=!0;break;case"object":switch(y.$$typeof){case c:case o:tt=!0;break;case R:return tt=y._init,Vt(tt(y._payload),N,X,q,J)}}if(tt)return J=J(y),tt=q===""?"."+Xt(y,0):q,it(J)?(X="",tt!=null&&(X=tt.replace(oe,"$&/")+"/"),Vt(J,N,X,"",function($e){return $e})):J!=null&&(St(J)&&(J=Ot(J,X+(J.key==null||y&&y.key===J.key?"":(""+J.key).replace(oe,"$&/")+"/")+tt)),N.push(J)),1;tt=0;var te=q===""?".":q+":";if(it(y))for(var bt=0;bt>>1,y=M[yt];if(0>>1;yth(q,F))Jh(ft,q)?(M[yt]=ft,M[J]=F,yt=J):(M[yt]=q,M[X]=F,yt=X);else if(Jh(ft,F))M[yt]=ft,M[J]=F,yt=J;else break t}}return j}function h(M,j){var F=M.sortIndex-j.sortIndex;return F!==0?F:M.id-j.id}if(c.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var v=performance;c.unstable_now=function(){return v.now()}}else{var T=Date,O=T.now();c.unstable_now=function(){return T.now()-O}}var p=[],d=[],R=1,w=null,C=3,B=!1,H=!1,G=!1,Q=!1,L=typeof setTimeout=="function"?setTimeout:null,Y=typeof clearTimeout=="function"?clearTimeout:null,$=typeof setImmediate<"u"?setImmediate:null;function it(M){for(var j=s(d);j!==null;){if(j.callback===null)r(d);else if(j.startTime<=M)r(d),j.sortIndex=j.expirationTime,o(p,j);else break;j=s(d)}}function I(M){if(G=!1,it(M),!H)if(s(p)!==null)H=!0,zt||(zt=!0,Xt());else{var j=s(d);j!==null&&Vt(I,j.startTime-M)}}var zt=!1,Rt=-1,Ot=5,St=-1;function Jt(){return Q?!0:!(c.unstable_now()-StM&&Jt());){var yt=w.callback;if(typeof yt=="function"){w.callback=null,C=w.priorityLevel;var y=yt(w.expirationTime<=M);if(M=c.unstable_now(),typeof y=="function"){w.callback=y,it(M),j=!0;break e}w===s(p)&&r(p),it(M)}else r(p);w=s(p)}if(w!==null)j=!0;else{var N=s(d);N!==null&&Vt(I,N.startTime-M),j=!1}}break t}finally{w=null,C=F,B=!1}j=void 0}}finally{j?Xt():zt=!1}}}var Xt;if(typeof $=="function")Xt=function(){$(oe)};else if(typeof MessageChannel<"u"){var El=new MessageChannel,Tl=El.port2;El.port1.onmessage=oe,Xt=function(){Tl.postMessage(null)}}else Xt=function(){L(oe,0)};function Vt(M,j){Rt=L(function(){M(c.unstable_now())},j)}c.unstable_IdlePriority=5,c.unstable_ImmediatePriority=1,c.unstable_LowPriority=4,c.unstable_NormalPriority=3,c.unstable_Profiling=null,c.unstable_UserBlockingPriority=2,c.unstable_cancelCallback=function(M){M.callback=null},c.unstable_forceFrameRate=function(M){0>M||125yt?(M.sortIndex=F,o(d,M),s(p)===null&&M===s(d)&&(G?(Y(Rt),Rt=-1):G=!0,Vt(I,F-yt))):(M.sortIndex=y,o(p,M),H||B||(H=!0,zt||(zt=!0,Xt()))),M},c.unstable_shouldYield=Jt,c.unstable_wrapCallback=function(M){var j=C;return function(){var F=C;C=j;try{return M.apply(this,arguments)}finally{C=F}}}}(Tf)),Tf}var L0;function L1(){return L0||(L0=1,Ef.exports=w1()),Ef.exports}var xf={exports:{}},Kt={};/** - * @license React - * react-dom.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var B0;function B1(){if(B0)return Kt;B0=1;var c=Mf();function o(p){var d="https://react.dev/errors/"+p;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(c)}catch(o){console.error(o)}}return c(),xf.exports=B1(),xf.exports}/** - * @license React - * react-dom-client.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var j0;function j1(){if(j0)return Du;j0=1;var c=L1(),o=Mf(),s=q1();function r(t){var e="https://react.dev/errors/"+t;if(1y||(t.current=yt[y],yt[y]=null,y--)}function q(t,e){y++,yt[y]=t.current,t.current=e}var J=N(null),ft=N(null),tt=N(null),te=N(null);function bt(t,e){switch(q(tt,e),q(ft,t),q(J,null),e.nodeType){case 9:case 11:t=(t=e.documentElement)&&(t=t.namespaceURI)?n0(t):0;break;default:if(t=e.tagName,e=e.namespaceURI)e=n0(e),t=i0(e,t);else switch(t){case"svg":t=1;break;case"math":t=2;break;default:t=0}}X(J),q(J,t)}function $e(){X(J),X(ft),X(tt)}function li(t){t.memoizedState!==null&&q(te,t);var e=J.current,l=i0(e,t.type);e!==l&&(q(ft,t),q(J,l))}function Hu(t){ft.current===t&&(X(J),X(ft)),te.current===t&&(X(te),Tu._currentValue=F)}var ai=Object.prototype.hasOwnProperty,ui=c.unstable_scheduleCallback,ni=c.unstable_cancelCallback,od=c.unstable_shouldYield,sd=c.unstable_requestPaint,Ae=c.unstable_now,dd=c.unstable_getCurrentPriorityLevel,qf=c.unstable_ImmediatePriority,jf=c.unstable_UserBlockingPriority,wu=c.unstable_NormalPriority,hd=c.unstable_LowPriority,Yf=c.unstable_IdlePriority,md=c.log,vd=c.unstable_setDisableYieldValue,za=null,ee=null;function We(t){if(typeof md=="function"&&vd(t),ee&&typeof ee.setStrictMode=="function")try{ee.setStrictMode(za,t)}catch{}}var le=Math.clz32?Math.clz32:pd,yd=Math.log,gd=Math.LN2;function pd(t){return t>>>=0,t===0?32:31-(yd(t)/gd|0)|0}var Lu=256,Bu=4194304;function xl(t){var e=t&42;if(e!==0)return e;switch(t&-t){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t&4194048;case 4194304:case 8388608:case 16777216:case 33554432:return t&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return t}}function qu(t,e,l){var a=t.pendingLanes;if(a===0)return 0;var u=0,n=t.suspendedLanes,i=t.pingedLanes;t=t.warmLanes;var f=a&134217727;return f!==0?(a=f&~n,a!==0?u=xl(a):(i&=f,i!==0?u=xl(i):l||(l=f&~t,l!==0&&(u=xl(l))))):(f=a&~n,f!==0?u=xl(f):i!==0?u=xl(i):l||(l=a&~t,l!==0&&(u=xl(l)))),u===0?0:e!==0&&e!==u&&(e&n)===0&&(n=u&-u,l=e&-e,n>=l||n===32&&(l&4194048)!==0)?e:u}function Ca(t,e){return(t.pendingLanes&~(t.suspendedLanes&~t.pingedLanes)&e)===0}function Sd(t,e){switch(t){case 1:case 2:case 4:case 8:case 64:return e+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Gf(){var t=Lu;return Lu<<=1,(Lu&4194048)===0&&(Lu=256),t}function Xf(){var t=Bu;return Bu<<=1,(Bu&62914560)===0&&(Bu=4194304),t}function ii(t){for(var e=[],l=0;31>l;l++)e.push(t);return e}function Ua(t,e){t.pendingLanes|=e,e!==268435456&&(t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0)}function bd(t,e,l,a,u,n){var i=t.pendingLanes;t.pendingLanes=l,t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0,t.expiredLanes&=l,t.entangledLanes&=l,t.errorRecoveryDisabledLanes&=l,t.shellSuspendCounter=0;var f=t.entanglements,m=t.expirationTimes,E=t.hiddenUpdates;for(l=i&~l;0)":-1u||m[a]!==E[u]){var D=` -`+m[a].replace(" at new "," at ");return t.displayName&&D.includes("")&&(D=D.replace("",t.displayName)),D}while(1<=a&&0<=u);break}}}finally{di=!1,Error.prepareStackTrace=l}return(l=t?t.displayName||t.name:"")?Jl(l):""}function _d(t){switch(t.tag){case 26:case 27:case 5:return Jl(t.type);case 16:return Jl("Lazy");case 13:return Jl("Suspense");case 19:return Jl("SuspenseList");case 0:case 15:return hi(t.type,!1);case 11:return hi(t.type.render,!1);case 1:return hi(t.type,!0);case 31:return Jl("Activity");default:return""}}function Pf(t){try{var e="";do e+=_d(t),t=t.return;while(t);return e}catch(l){return` -Error generating stack: `+l.message+` -`+l.stack}}function se(t){switch(typeof t){case"bigint":case"boolean":case"number":case"string":case"undefined":return t;case"object":return t;default:return""}}function If(t){var e=t.type;return(t=t.nodeName)&&t.toLowerCase()==="input"&&(e==="checkbox"||e==="radio")}function Od(t){var e=If(t)?"checked":"value",l=Object.getOwnPropertyDescriptor(t.constructor.prototype,e),a=""+t[e];if(!t.hasOwnProperty(e)&&typeof l<"u"&&typeof l.get=="function"&&typeof l.set=="function"){var u=l.get,n=l.set;return Object.defineProperty(t,e,{configurable:!0,get:function(){return u.call(this)},set:function(i){a=""+i,n.call(this,i)}}),Object.defineProperty(t,e,{enumerable:l.enumerable}),{getValue:function(){return a},setValue:function(i){a=""+i},stopTracking:function(){t._valueTracker=null,delete t[e]}}}}function Gu(t){t._valueTracker||(t._valueTracker=Od(t))}function tr(t){if(!t)return!1;var e=t._valueTracker;if(!e)return!0;var l=e.getValue(),a="";return t&&(a=If(t)?t.checked?"true":"false":t.value),t=a,t!==l?(e.setValue(t),!0):!1}function Xu(t){if(t=t||(typeof document<"u"?document:void 0),typeof t>"u")return null;try{return t.activeElement||t.body}catch{return t.body}}var Dd=/[\n"\\]/g;function de(t){return t.replace(Dd,function(e){return"\\"+e.charCodeAt(0).toString(16)+" "})}function mi(t,e,l,a,u,n,i,f){t.name="",i!=null&&typeof i!="function"&&typeof i!="symbol"&&typeof i!="boolean"?t.type=i:t.removeAttribute("type"),e!=null?i==="number"?(e===0&&t.value===""||t.value!=e)&&(t.value=""+se(e)):t.value!==""+se(e)&&(t.value=""+se(e)):i!=="submit"&&i!=="reset"||t.removeAttribute("value"),e!=null?vi(t,i,se(e)):l!=null?vi(t,i,se(l)):a!=null&&t.removeAttribute("value"),u==null&&n!=null&&(t.defaultChecked=!!n),u!=null&&(t.checked=u&&typeof u!="function"&&typeof u!="symbol"),f!=null&&typeof f!="function"&&typeof f!="symbol"&&typeof f!="boolean"?t.name=""+se(f):t.removeAttribute("name")}function er(t,e,l,a,u,n,i,f){if(n!=null&&typeof n!="function"&&typeof n!="symbol"&&typeof n!="boolean"&&(t.type=n),e!=null||l!=null){if(!(n!=="submit"&&n!=="reset"||e!=null))return;l=l!=null?""+se(l):"",e=e!=null?""+se(e):l,f||e===t.value||(t.value=e),t.defaultValue=e}a=a??u,a=typeof a!="function"&&typeof a!="symbol"&&!!a,t.checked=f?t.checked:!!a,t.defaultChecked=!!a,i!=null&&typeof i!="function"&&typeof i!="symbol"&&typeof i!="boolean"&&(t.name=i)}function vi(t,e,l){e==="number"&&Xu(t.ownerDocument)===t||t.defaultValue===""+l||(t.defaultValue=""+l)}function kl(t,e,l,a){if(t=t.options,e){e={};for(var u=0;u"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),bi=!1;if(Ne)try{var La={};Object.defineProperty(La,"passive",{get:function(){bi=!0}}),window.addEventListener("test",La,La),window.removeEventListener("test",La,La)}catch{bi=!1}var Pe=null,Ei=null,Qu=null;function fr(){if(Qu)return Qu;var t,e=Ei,l=e.length,a,u="value"in Pe?Pe.value:Pe.textContent,n=u.length;for(t=0;t=ja),mr=" ",vr=!1;function yr(t,e){switch(t){case"keyup":return lh.indexOf(e.keyCode)!==-1;case"keydown":return e.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function gr(t){return t=t.detail,typeof t=="object"&&"data"in t?t.data:null}var Pl=!1;function uh(t,e){switch(t){case"compositionend":return gr(e);case"keypress":return e.which!==32?null:(vr=!0,mr);case"textInput":return t=e.data,t===mr&&vr?null:t;default:return null}}function nh(t,e){if(Pl)return t==="compositionend"||!_i&&yr(t,e)?(t=fr(),Qu=Ei=Pe=null,Pl=!1,t):null;switch(t){case"paste":return null;case"keypress":if(!(e.ctrlKey||e.altKey||e.metaKey)||e.ctrlKey&&e.altKey){if(e.char&&1=e)return{node:l,offset:e-t};t=a}t:{for(;l;){if(l.nextSibling){l=l.nextSibling;break t}l=l.parentNode}l=void 0}l=Ar(l)}}function Or(t,e){return t&&e?t===e?!0:t&&t.nodeType===3?!1:e&&e.nodeType===3?Or(t,e.parentNode):"contains"in t?t.contains(e):t.compareDocumentPosition?!!(t.compareDocumentPosition(e)&16):!1:!1}function Dr(t){t=t!=null&&t.ownerDocument!=null&&t.ownerDocument.defaultView!=null?t.ownerDocument.defaultView:window;for(var e=Xu(t.document);e instanceof t.HTMLIFrameElement;){try{var l=typeof e.contentWindow.location.href=="string"}catch{l=!1}if(l)t=e.contentWindow;else break;e=Xu(t.document)}return e}function Mi(t){var e=t&&t.nodeName&&t.nodeName.toLowerCase();return e&&(e==="input"&&(t.type==="text"||t.type==="search"||t.type==="tel"||t.type==="url"||t.type==="password")||e==="textarea"||t.contentEditable==="true")}var hh=Ne&&"documentMode"in document&&11>=document.documentMode,Il=null,zi=null,Va=null,Ci=!1;function Mr(t,e,l){var a=l.window===l?l.document:l.nodeType===9?l:l.ownerDocument;Ci||Il==null||Il!==Xu(a)||(a=Il,"selectionStart"in a&&Mi(a)?a={start:a.selectionStart,end:a.selectionEnd}:(a=(a.ownerDocument&&a.ownerDocument.defaultView||window).getSelection(),a={anchorNode:a.anchorNode,anchorOffset:a.anchorOffset,focusNode:a.focusNode,focusOffset:a.focusOffset}),Va&&Xa(Va,a)||(Va=a,a=wn(zi,"onSelect"),0>=i,u-=i,we=1<<32-le(e)+u|l<n?n:8;var i=M.T,f={};M.T=f,yc(t,!1,e,l);try{var m=u(),E=M.S;if(E!==null&&E(f,m),m!==null&&typeof m=="object"&&typeof m.then=="function"){var D=Th(m,a);uu(t,e,D,fe(t))}else uu(t,e,a,fe(t))}catch(U){uu(t,e,{then:function(){},status:"rejected",reason:U},fe())}finally{j.p=n,M.T=i}}function Oh(){}function mc(t,e,l,a){if(t.tag!==5)throw Error(r(476));var u=Co(t).queue;zo(t,u,e,F,l===null?Oh:function(){return Uo(t),l(a)})}function Co(t){var e=t.memoizedState;if(e!==null)return e;e={memoizedState:F,baseState:F,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:je,lastRenderedState:F},next:null};var l={};return e.next={memoizedState:l,baseState:l,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:je,lastRenderedState:l},next:null},t.memoizedState=e,t=t.alternate,t!==null&&(t.memoizedState=e),e}function Uo(t){var e=Co(t).next.queue;uu(t,e,{},fe())}function vc(){return Zt(Tu)}function No(){return Ut().memoizedState}function Ho(){return Ut().memoizedState}function Dh(t){for(var e=t.return;e!==null;){switch(e.tag){case 24:case 3:var l=fe();t=el(l);var a=ll(e,t,l);a!==null&&(re(a,e,l),Pa(a,e,l)),e={cache:Zi()},t.payload=e;return}e=e.return}}function Mh(t,e,l){var a=fe();l={lane:a,revertLane:0,action:l,hasEagerState:!1,eagerState:null,next:null},vn(t)?Lo(e,l):(l=wi(t,e,l,a),l!==null&&(re(l,t,a),Bo(l,e,a)))}function wo(t,e,l){var a=fe();uu(t,e,l,a)}function uu(t,e,l,a){var u={lane:a,revertLane:0,action:l,hasEagerState:!1,eagerState:null,next:null};if(vn(t))Lo(e,u);else{var n=t.alternate;if(t.lanes===0&&(n===null||n.lanes===0)&&(n=e.lastRenderedReducer,n!==null))try{var i=e.lastRenderedState,f=n(i,l);if(u.hasEagerState=!0,u.eagerState=f,ae(f,i))return Fu(t,e,u,0),pt===null&&Wu(),!1}catch{}finally{}if(l=wi(t,e,u,a),l!==null)return re(l,t,a),Bo(l,e,a),!0}return!1}function yc(t,e,l,a){if(a={lane:2,revertLane:kc(),action:a,hasEagerState:!1,eagerState:null,next:null},vn(t)){if(e)throw Error(r(479))}else e=wi(t,l,a,2),e!==null&&re(e,t,2)}function vn(t){var e=t.alternate;return t===lt||e!==null&&e===lt}function Lo(t,e){ra=rn=!0;var l=t.pending;l===null?e.next=e:(e.next=l.next,l.next=e),t.pending=e}function Bo(t,e,l){if((l&4194048)!==0){var a=e.lanes;a&=t.pendingLanes,l|=a,e.lanes=l,Qf(t,l)}}var yn={readContext:Zt,use:sn,useCallback:Dt,useContext:Dt,useEffect:Dt,useImperativeHandle:Dt,useLayoutEffect:Dt,useInsertionEffect:Dt,useMemo:Dt,useReducer:Dt,useRef:Dt,useState:Dt,useDebugValue:Dt,useDeferredValue:Dt,useTransition:Dt,useSyncExternalStore:Dt,useId:Dt,useHostTransitionStatus:Dt,useFormState:Dt,useActionState:Dt,useOptimistic:Dt,useMemoCache:Dt,useCacheRefresh:Dt},qo={readContext:Zt,use:sn,useCallback:function(t,e){return Ft().memoizedState=[t,e===void 0?null:e],t},useContext:Zt,useEffect:Eo,useImperativeHandle:function(t,e,l){l=l!=null?l.concat([t]):null,mn(4194308,4,Ao.bind(null,e,t),l)},useLayoutEffect:function(t,e){return mn(4194308,4,t,e)},useInsertionEffect:function(t,e){mn(4,2,t,e)},useMemo:function(t,e){var l=Ft();e=e===void 0?null:e;var a=t();if(Ll){We(!0);try{t()}finally{We(!1)}}return l.memoizedState=[a,e],a},useReducer:function(t,e,l){var a=Ft();if(l!==void 0){var u=l(e);if(Ll){We(!0);try{l(e)}finally{We(!1)}}}else u=e;return a.memoizedState=a.baseState=u,t={pending:null,lanes:0,dispatch:null,lastRenderedReducer:t,lastRenderedState:u},a.queue=t,t=t.dispatch=Mh.bind(null,lt,t),[a.memoizedState,t]},useRef:function(t){var e=Ft();return t={current:t},e.memoizedState=t},useState:function(t){t=oc(t);var e=t.queue,l=wo.bind(null,lt,e);return e.dispatch=l,[t.memoizedState,l]},useDebugValue:dc,useDeferredValue:function(t,e){var l=Ft();return hc(l,t,e)},useTransition:function(){var t=oc(!1);return t=zo.bind(null,lt,t.queue,!0,!1),Ft().memoizedState=t,[!1,t]},useSyncExternalStore:function(t,e,l){var a=lt,u=Ft();if(ot){if(l===void 0)throw Error(r(407));l=l()}else{if(l=e(),pt===null)throw Error(r(349));(ct&124)!==0||uo(a,e,l)}u.memoizedState=l;var n={value:l,getSnapshot:e};return u.queue=n,Eo(io.bind(null,a,n,t),[t]),a.flags|=2048,sa(9,hn(),no.bind(null,a,n,l,e),null),l},useId:function(){var t=Ft(),e=pt.identifierPrefix;if(ot){var l=Le,a=we;l=(a&~(1<<32-le(a)-1)).toString(32)+l,e="«"+e+"R"+l,l=on++,0W?(qt=Z,Z=null):qt=Z.sibling;var rt=x(S,Z,b[W],z);if(rt===null){Z===null&&(Z=qt);break}t&&Z&&rt.alternate===null&&e(S,Z),g=n(rt,g,W),at===null?V=rt:at.sibling=rt,at=rt,Z=qt}if(W===b.length)return l(S,Z),ot&&zl(S,W),V;if(Z===null){for(;WW?(qt=Z,Z=null):qt=Z.sibling;var Sl=x(S,Z,rt.value,z);if(Sl===null){Z===null&&(Z=qt);break}t&&Z&&Sl.alternate===null&&e(S,Z),g=n(Sl,g,W),at===null?V=Sl:at.sibling=Sl,at=Sl,Z=qt}if(rt.done)return l(S,Z),ot&&zl(S,W),V;if(Z===null){for(;!rt.done;W++,rt=b.next())rt=U(S,rt.value,z),rt!==null&&(g=n(rt,g,W),at===null?V=rt:at.sibling=rt,at=rt);return ot&&zl(S,W),V}for(Z=a(Z);!rt.done;W++,rt=b.next())rt=A(Z,S,W,rt.value,z),rt!==null&&(t&&rt.alternate!==null&&Z.delete(rt.key===null?W:rt.key),g=n(rt,g,W),at===null?V=rt:at.sibling=rt,at=rt);return t&&Z.forEach(function(C1){return e(S,C1)}),ot&&zl(S,W),V}function vt(S,g,b,z){if(typeof b=="object"&&b!==null&&b.type===H&&b.key===null&&(b=b.props.children),typeof b=="object"&&b!==null){switch(b.$$typeof){case C:t:{for(var V=b.key;g!==null;){if(g.key===V){if(V=b.type,V===H){if(g.tag===7){l(S,g.sibling),z=u(g,b.props.children),z.return=S,S=z;break t}}else if(g.elementType===V||typeof V=="object"&&V!==null&&V.$$typeof===Ot&&Yo(V)===g.type){l(S,g.sibling),z=u(g,b.props),iu(z,b),z.return=S,S=z;break t}l(S,g);break}else e(S,g);g=g.sibling}b.type===H?(z=Dl(b.props.children,S.mode,z,b.key),z.return=S,S=z):(z=Iu(b.type,b.key,b.props,null,S.mode,z),iu(z,b),z.return=S,S=z)}return i(S);case B:t:{for(V=b.key;g!==null;){if(g.key===V)if(g.tag===4&&g.stateNode.containerInfo===b.containerInfo&&g.stateNode.implementation===b.implementation){l(S,g.sibling),z=u(g,b.children||[]),z.return=S,S=z;break t}else{l(S,g);break}else e(S,g);g=g.sibling}z=qi(b,S.mode,z),z.return=S,S=z}return i(S);case Ot:return V=b._init,b=V(b._payload),vt(S,g,b,z)}if(Vt(b))return P(S,g,b,z);if(Xt(b)){if(V=Xt(b),typeof V!="function")throw Error(r(150));return b=V.call(b),k(S,g,b,z)}if(typeof b.then=="function")return vt(S,g,gn(b),z);if(b.$$typeof===$)return vt(S,g,an(S,b),z);pn(S,b)}return typeof b=="string"&&b!==""||typeof b=="number"||typeof b=="bigint"?(b=""+b,g!==null&&g.tag===6?(l(S,g.sibling),z=u(g,b),z.return=S,S=z):(l(S,g),z=Bi(b,S.mode,z),z.return=S,S=z),i(S)):l(S,g)}return function(S,g,b,z){try{nu=0;var V=vt(S,g,b,z);return da=null,V}catch(Z){if(Z===Wa||Z===nn)throw Z;var at=ue(29,Z,null,S.mode);return at.lanes=z,at.return=S,at}finally{}}}var ha=Go(!0),Xo=Go(!1),ge=N(null),Oe=null;function ul(t){var e=t.alternate;q(Ht,Ht.current&1),q(ge,t),Oe===null&&(e===null||fa.current!==null||e.memoizedState!==null)&&(Oe=t)}function Vo(t){if(t.tag===22){if(q(Ht,Ht.current),q(ge,t),Oe===null){var e=t.alternate;e!==null&&e.memoizedState!==null&&(Oe=t)}}else nl()}function nl(){q(Ht,Ht.current),q(ge,ge.current)}function Ye(t){X(ge),Oe===t&&(Oe=null),X(Ht)}var Ht=N(0);function Sn(t){for(var e=t;e!==null;){if(e.tag===13){var l=e.memoizedState;if(l!==null&&(l=l.dehydrated,l===null||l.data==="$?"||cf(l)))return e}else if(e.tag===19&&e.memoizedProps.revealOrder!==void 0){if((e.flags&128)!==0)return e}else if(e.child!==null){e.child.return=e,e=e.child;continue}if(e===t)break;for(;e.sibling===null;){if(e.return===null||e.return===t)return null;e=e.return}e.sibling.return=e.return,e=e.sibling}return null}function gc(t,e,l,a){e=t.memoizedState,l=l(a,e),l=l==null?e:R({},e,l),t.memoizedState=l,t.lanes===0&&(t.updateQueue.baseState=l)}var pc={enqueueSetState:function(t,e,l){t=t._reactInternals;var a=fe(),u=el(a);u.payload=e,l!=null&&(u.callback=l),e=ll(t,u,a),e!==null&&(re(e,t,a),Pa(e,t,a))},enqueueReplaceState:function(t,e,l){t=t._reactInternals;var a=fe(),u=el(a);u.tag=1,u.payload=e,l!=null&&(u.callback=l),e=ll(t,u,a),e!==null&&(re(e,t,a),Pa(e,t,a))},enqueueForceUpdate:function(t,e){t=t._reactInternals;var l=fe(),a=el(l);a.tag=2,e!=null&&(a.callback=e),e=ll(t,a,l),e!==null&&(re(e,t,l),Pa(e,t,l))}};function Qo(t,e,l,a,u,n,i){return t=t.stateNode,typeof t.shouldComponentUpdate=="function"?t.shouldComponentUpdate(a,n,i):e.prototype&&e.prototype.isPureReactComponent?!Xa(l,a)||!Xa(u,n):!0}function Zo(t,e,l,a){t=e.state,typeof e.componentWillReceiveProps=="function"&&e.componentWillReceiveProps(l,a),typeof e.UNSAFE_componentWillReceiveProps=="function"&&e.UNSAFE_componentWillReceiveProps(l,a),e.state!==t&&pc.enqueueReplaceState(e,e.state,null)}function Bl(t,e){var l=e;if("ref"in e){l={};for(var a in e)a!=="ref"&&(l[a]=e[a])}if(t=t.defaultProps){l===e&&(l=R({},l));for(var u in t)l[u]===void 0&&(l[u]=t[u])}return l}var bn=typeof reportError=="function"?reportError:function(t){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var e=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof t=="object"&&t!==null&&typeof t.message=="string"?String(t.message):String(t),error:t});if(!window.dispatchEvent(e))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",t);return}console.error(t)};function Ko(t){bn(t)}function Jo(t){console.error(t)}function ko(t){bn(t)}function En(t,e){try{var l=t.onUncaughtError;l(e.value,{componentStack:e.stack})}catch(a){setTimeout(function(){throw a})}}function $o(t,e,l){try{var a=t.onCaughtError;a(l.value,{componentStack:l.stack,errorBoundary:e.tag===1?e.stateNode:null})}catch(u){setTimeout(function(){throw u})}}function Sc(t,e,l){return l=el(l),l.tag=3,l.payload={element:null},l.callback=function(){En(t,e)},l}function Wo(t){return t=el(t),t.tag=3,t}function Fo(t,e,l,a){var u=l.type.getDerivedStateFromError;if(typeof u=="function"){var n=a.value;t.payload=function(){return u(n)},t.callback=function(){$o(e,l,a)}}var i=l.stateNode;i!==null&&typeof i.componentDidCatch=="function"&&(t.callback=function(){$o(e,l,a),typeof u!="function"&&(sl===null?sl=new Set([this]):sl.add(this));var f=a.stack;this.componentDidCatch(a.value,{componentStack:f!==null?f:""})})}function Ch(t,e,l,a,u){if(l.flags|=32768,a!==null&&typeof a=="object"&&typeof a.then=="function"){if(e=l.alternate,e!==null&&Ja(e,l,u,!0),l=ge.current,l!==null){switch(l.tag){case 13:return Oe===null?Vc():l.alternate===null&&_t===0&&(_t=3),l.flags&=-257,l.flags|=65536,l.lanes=u,a===ki?l.flags|=16384:(e=l.updateQueue,e===null?l.updateQueue=new Set([a]):e.add(a),Zc(t,a,u)),!1;case 22:return l.flags|=65536,a===ki?l.flags|=16384:(e=l.updateQueue,e===null?(e={transitions:null,markerInstances:null,retryQueue:new Set([a])},l.updateQueue=e):(l=e.retryQueue,l===null?e.retryQueue=new Set([a]):l.add(a)),Zc(t,a,u)),!1}throw Error(r(435,l.tag))}return Zc(t,a,u),Vc(),!1}if(ot)return e=ge.current,e!==null?((e.flags&65536)===0&&(e.flags|=256),e.flags|=65536,e.lanes=u,a!==Gi&&(t=Error(r(422),{cause:a}),Ka(he(t,l)))):(a!==Gi&&(e=Error(r(423),{cause:a}),Ka(he(e,l))),t=t.current.alternate,t.flags|=65536,u&=-u,t.lanes|=u,a=he(a,l),u=Sc(t.stateNode,a,u),Fi(t,u),_t!==4&&(_t=2)),!1;var n=Error(r(520),{cause:a});if(n=he(n,l),hu===null?hu=[n]:hu.push(n),_t!==4&&(_t=2),e===null)return!0;a=he(a,l),l=e;do{switch(l.tag){case 3:return l.flags|=65536,t=u&-u,l.lanes|=t,t=Sc(l.stateNode,a,t),Fi(l,t),!1;case 1:if(e=l.type,n=l.stateNode,(l.flags&128)===0&&(typeof e.getDerivedStateFromError=="function"||n!==null&&typeof n.componentDidCatch=="function"&&(sl===null||!sl.has(n))))return l.flags|=65536,u&=-u,l.lanes|=u,u=Wo(u),Fo(u,t,l,a),Fi(l,u),!1}l=l.return}while(l!==null);return!1}var Po=Error(r(461)),Lt=!1;function jt(t,e,l,a){e.child=t===null?Xo(e,null,l,a):ha(e,t.child,l,a)}function Io(t,e,l,a,u){l=l.render;var n=e.ref;if("ref"in a){var i={};for(var f in a)f!=="ref"&&(i[f]=a[f])}else i=a;return Hl(e),a=lc(t,e,l,i,n,u),f=ac(),t!==null&&!Lt?(uc(t,e,u),Ge(t,e,u)):(ot&&f&&ji(e),e.flags|=1,jt(t,e,a,u),e.child)}function ts(t,e,l,a,u){if(t===null){var n=l.type;return typeof n=="function"&&!Li(n)&&n.defaultProps===void 0&&l.compare===null?(e.tag=15,e.type=n,es(t,e,n,a,u)):(t=Iu(l.type,null,a,e,e.mode,u),t.ref=e.ref,t.return=e,e.child=t)}if(n=t.child,!Oc(t,u)){var i=n.memoizedProps;if(l=l.compare,l=l!==null?l:Xa,l(i,a)&&t.ref===e.ref)return Ge(t,e,u)}return e.flags|=1,t=He(n,a),t.ref=e.ref,t.return=e,e.child=t}function es(t,e,l,a,u){if(t!==null){var n=t.memoizedProps;if(Xa(n,a)&&t.ref===e.ref)if(Lt=!1,e.pendingProps=a=n,Oc(t,u))(t.flags&131072)!==0&&(Lt=!0);else return e.lanes=t.lanes,Ge(t,e,u)}return bc(t,e,l,a,u)}function ls(t,e,l){var a=e.pendingProps,u=a.children,n=t!==null?t.memoizedState:null;if(a.mode==="hidden"){if((e.flags&128)!==0){if(a=n!==null?n.baseLanes|l:l,t!==null){for(u=e.child=t.child,n=0;u!==null;)n=n|u.lanes|u.childLanes,u=u.sibling;e.childLanes=n&~a}else e.childLanes=0,e.child=null;return as(t,e,a,l)}if((l&536870912)!==0)e.memoizedState={baseLanes:0,cachePool:null},t!==null&&un(e,n!==null?n.cachePool:null),n!==null?to(e,n):Ii(),Vo(e);else return e.lanes=e.childLanes=536870912,as(t,e,n!==null?n.baseLanes|l:l,l)}else n!==null?(un(e,n.cachePool),to(e,n),nl(),e.memoizedState=null):(t!==null&&un(e,null),Ii(),nl());return jt(t,e,u,l),e.child}function as(t,e,l,a){var u=Ji();return u=u===null?null:{parent:Nt._currentValue,pool:u},e.memoizedState={baseLanes:l,cachePool:u},t!==null&&un(e,null),Ii(),Vo(e),t!==null&&Ja(t,e,a,!0),null}function Tn(t,e){var l=e.ref;if(l===null)t!==null&&t.ref!==null&&(e.flags|=4194816);else{if(typeof l!="function"&&typeof l!="object")throw Error(r(284));(t===null||t.ref!==l)&&(e.flags|=4194816)}}function bc(t,e,l,a,u){return Hl(e),l=lc(t,e,l,a,void 0,u),a=ac(),t!==null&&!Lt?(uc(t,e,u),Ge(t,e,u)):(ot&&a&&ji(e),e.flags|=1,jt(t,e,l,u),e.child)}function us(t,e,l,a,u,n){return Hl(e),e.updateQueue=null,l=lo(e,a,l,u),eo(t),a=ac(),t!==null&&!Lt?(uc(t,e,n),Ge(t,e,n)):(ot&&a&&ji(e),e.flags|=1,jt(t,e,l,n),e.child)}function ns(t,e,l,a,u){if(Hl(e),e.stateNode===null){var n=aa,i=l.contextType;typeof i=="object"&&i!==null&&(n=Zt(i)),n=new l(a,n),e.memoizedState=n.state!==null&&n.state!==void 0?n.state:null,n.updater=pc,e.stateNode=n,n._reactInternals=e,n=e.stateNode,n.props=a,n.state=e.memoizedState,n.refs={},$i(e),i=l.contextType,n.context=typeof i=="object"&&i!==null?Zt(i):aa,n.state=e.memoizedState,i=l.getDerivedStateFromProps,typeof i=="function"&&(gc(e,l,i,a),n.state=e.memoizedState),typeof l.getDerivedStateFromProps=="function"||typeof n.getSnapshotBeforeUpdate=="function"||typeof n.UNSAFE_componentWillMount!="function"&&typeof n.componentWillMount!="function"||(i=n.state,typeof n.componentWillMount=="function"&&n.componentWillMount(),typeof n.UNSAFE_componentWillMount=="function"&&n.UNSAFE_componentWillMount(),i!==n.state&&pc.enqueueReplaceState(n,n.state,null),tu(e,a,n,u),Ia(),n.state=e.memoizedState),typeof n.componentDidMount=="function"&&(e.flags|=4194308),a=!0}else if(t===null){n=e.stateNode;var f=e.memoizedProps,m=Bl(l,f);n.props=m;var E=n.context,D=l.contextType;i=aa,typeof D=="object"&&D!==null&&(i=Zt(D));var U=l.getDerivedStateFromProps;D=typeof U=="function"||typeof n.getSnapshotBeforeUpdate=="function",f=e.pendingProps!==f,D||typeof n.UNSAFE_componentWillReceiveProps!="function"&&typeof n.componentWillReceiveProps!="function"||(f||E!==i)&&Zo(e,n,a,i),tl=!1;var x=e.memoizedState;n.state=x,tu(e,a,n,u),Ia(),E=e.memoizedState,f||x!==E||tl?(typeof U=="function"&&(gc(e,l,U,a),E=e.memoizedState),(m=tl||Qo(e,l,m,a,x,E,i))?(D||typeof n.UNSAFE_componentWillMount!="function"&&typeof n.componentWillMount!="function"||(typeof n.componentWillMount=="function"&&n.componentWillMount(),typeof n.UNSAFE_componentWillMount=="function"&&n.UNSAFE_componentWillMount()),typeof n.componentDidMount=="function"&&(e.flags|=4194308)):(typeof n.componentDidMount=="function"&&(e.flags|=4194308),e.memoizedProps=a,e.memoizedState=E),n.props=a,n.state=E,n.context=i,a=m):(typeof n.componentDidMount=="function"&&(e.flags|=4194308),a=!1)}else{n=e.stateNode,Wi(t,e),i=e.memoizedProps,D=Bl(l,i),n.props=D,U=e.pendingProps,x=n.context,E=l.contextType,m=aa,typeof E=="object"&&E!==null&&(m=Zt(E)),f=l.getDerivedStateFromProps,(E=typeof f=="function"||typeof n.getSnapshotBeforeUpdate=="function")||typeof n.UNSAFE_componentWillReceiveProps!="function"&&typeof n.componentWillReceiveProps!="function"||(i!==U||x!==m)&&Zo(e,n,a,m),tl=!1,x=e.memoizedState,n.state=x,tu(e,a,n,u),Ia();var A=e.memoizedState;i!==U||x!==A||tl||t!==null&&t.dependencies!==null&&ln(t.dependencies)?(typeof f=="function"&&(gc(e,l,f,a),A=e.memoizedState),(D=tl||Qo(e,l,D,a,x,A,m)||t!==null&&t.dependencies!==null&&ln(t.dependencies))?(E||typeof n.UNSAFE_componentWillUpdate!="function"&&typeof n.componentWillUpdate!="function"||(typeof n.componentWillUpdate=="function"&&n.componentWillUpdate(a,A,m),typeof n.UNSAFE_componentWillUpdate=="function"&&n.UNSAFE_componentWillUpdate(a,A,m)),typeof n.componentDidUpdate=="function"&&(e.flags|=4),typeof n.getSnapshotBeforeUpdate=="function"&&(e.flags|=1024)):(typeof n.componentDidUpdate!="function"||i===t.memoizedProps&&x===t.memoizedState||(e.flags|=4),typeof n.getSnapshotBeforeUpdate!="function"||i===t.memoizedProps&&x===t.memoizedState||(e.flags|=1024),e.memoizedProps=a,e.memoizedState=A),n.props=a,n.state=A,n.context=m,a=D):(typeof n.componentDidUpdate!="function"||i===t.memoizedProps&&x===t.memoizedState||(e.flags|=4),typeof n.getSnapshotBeforeUpdate!="function"||i===t.memoizedProps&&x===t.memoizedState||(e.flags|=1024),a=!1)}return n=a,Tn(t,e),a=(e.flags&128)!==0,n||a?(n=e.stateNode,l=a&&typeof l.getDerivedStateFromError!="function"?null:n.render(),e.flags|=1,t!==null&&a?(e.child=ha(e,t.child,null,u),e.child=ha(e,null,l,u)):jt(t,e,l,u),e.memoizedState=n.state,t=e.child):t=Ge(t,e,u),t}function is(t,e,l,a){return Za(),e.flags|=256,jt(t,e,l,a),e.child}var Ec={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function Tc(t){return{baseLanes:t,cachePool:Kr()}}function xc(t,e,l){return t=t!==null?t.childLanes&~l:0,e&&(t|=pe),t}function cs(t,e,l){var a=e.pendingProps,u=!1,n=(e.flags&128)!==0,i;if((i=n)||(i=t!==null&&t.memoizedState===null?!1:(Ht.current&2)!==0),i&&(u=!0,e.flags&=-129),i=(e.flags&32)!==0,e.flags&=-33,t===null){if(ot){if(u?ul(e):nl(),ot){var f=At,m;if(m=f){t:{for(m=f,f=_e;m.nodeType!==8;){if(!f){f=null;break t}if(m=xe(m.nextSibling),m===null){f=null;break t}}f=m}f!==null?(e.memoizedState={dehydrated:f,treeContext:Ml!==null?{id:we,overflow:Le}:null,retryLane:536870912,hydrationErrors:null},m=ue(18,null,null,0),m.stateNode=f,m.return=e,e.child=m,kt=e,At=null,m=!0):m=!1}m||Ul(e)}if(f=e.memoizedState,f!==null&&(f=f.dehydrated,f!==null))return cf(f)?e.lanes=32:e.lanes=536870912,null;Ye(e)}return f=a.children,a=a.fallback,u?(nl(),u=e.mode,f=xn({mode:"hidden",children:f},u),a=Dl(a,u,l,null),f.return=e,a.return=e,f.sibling=a,e.child=f,u=e.child,u.memoizedState=Tc(l),u.childLanes=xc(t,i,l),e.memoizedState=Ec,a):(ul(e),Rc(e,f))}if(m=t.memoizedState,m!==null&&(f=m.dehydrated,f!==null)){if(n)e.flags&256?(ul(e),e.flags&=-257,e=Ac(t,e,l)):e.memoizedState!==null?(nl(),e.child=t.child,e.flags|=128,e=null):(nl(),u=a.fallback,f=e.mode,a=xn({mode:"visible",children:a.children},f),u=Dl(u,f,l,null),u.flags|=2,a.return=e,u.return=e,a.sibling=u,e.child=a,ha(e,t.child,null,l),a=e.child,a.memoizedState=Tc(l),a.childLanes=xc(t,i,l),e.memoizedState=Ec,e=u);else if(ul(e),cf(f)){if(i=f.nextSibling&&f.nextSibling.dataset,i)var E=i.dgst;i=E,a=Error(r(419)),a.stack="",a.digest=i,Ka({value:a,source:null,stack:null}),e=Ac(t,e,l)}else if(Lt||Ja(t,e,l,!1),i=(l&t.childLanes)!==0,Lt||i){if(i=pt,i!==null&&(a=l&-l,a=(a&42)!==0?1:ci(a),a=(a&(i.suspendedLanes|l))!==0?0:a,a!==0&&a!==m.retryLane))throw m.retryLane=a,la(t,a),re(i,t,a),Po;f.data==="$?"||Vc(),e=Ac(t,e,l)}else f.data==="$?"?(e.flags|=192,e.child=t.child,e=null):(t=m.treeContext,At=xe(f.nextSibling),kt=e,ot=!0,Cl=null,_e=!1,t!==null&&(ve[ye++]=we,ve[ye++]=Le,ve[ye++]=Ml,we=t.id,Le=t.overflow,Ml=e),e=Rc(e,a.children),e.flags|=4096);return e}return u?(nl(),u=a.fallback,f=e.mode,m=t.child,E=m.sibling,a=He(m,{mode:"hidden",children:a.children}),a.subtreeFlags=m.subtreeFlags&65011712,E!==null?u=He(E,u):(u=Dl(u,f,l,null),u.flags|=2),u.return=e,a.return=e,a.sibling=u,e.child=a,a=u,u=e.child,f=t.child.memoizedState,f===null?f=Tc(l):(m=f.cachePool,m!==null?(E=Nt._currentValue,m=m.parent!==E?{parent:E,pool:E}:m):m=Kr(),f={baseLanes:f.baseLanes|l,cachePool:m}),u.memoizedState=f,u.childLanes=xc(t,i,l),e.memoizedState=Ec,a):(ul(e),l=t.child,t=l.sibling,l=He(l,{mode:"visible",children:a.children}),l.return=e,l.sibling=null,t!==null&&(i=e.deletions,i===null?(e.deletions=[t],e.flags|=16):i.push(t)),e.child=l,e.memoizedState=null,l)}function Rc(t,e){return e=xn({mode:"visible",children:e},t.mode),e.return=t,t.child=e}function xn(t,e){return t=ue(22,t,null,e),t.lanes=0,t.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null},t}function Ac(t,e,l){return ha(e,t.child,null,l),t=Rc(e,e.pendingProps.children),t.flags|=2,e.memoizedState=null,t}function fs(t,e,l){t.lanes|=e;var a=t.alternate;a!==null&&(a.lanes|=e),Vi(t.return,e,l)}function _c(t,e,l,a,u){var n=t.memoizedState;n===null?t.memoizedState={isBackwards:e,rendering:null,renderingStartTime:0,last:a,tail:l,tailMode:u}:(n.isBackwards=e,n.rendering=null,n.renderingStartTime=0,n.last=a,n.tail=l,n.tailMode=u)}function rs(t,e,l){var a=e.pendingProps,u=a.revealOrder,n=a.tail;if(jt(t,e,a.children,l),a=Ht.current,(a&2)!==0)a=a&1|2,e.flags|=128;else{if(t!==null&&(t.flags&128)!==0)t:for(t=e.child;t!==null;){if(t.tag===13)t.memoizedState!==null&&fs(t,l,e);else if(t.tag===19)fs(t,l,e);else if(t.child!==null){t.child.return=t,t=t.child;continue}if(t===e)break t;for(;t.sibling===null;){if(t.return===null||t.return===e)break t;t=t.return}t.sibling.return=t.return,t=t.sibling}a&=1}switch(q(Ht,a),u){case"forwards":for(l=e.child,u=null;l!==null;)t=l.alternate,t!==null&&Sn(t)===null&&(u=l),l=l.sibling;l=u,l===null?(u=e.child,e.child=null):(u=l.sibling,l.sibling=null),_c(e,!1,u,l,n);break;case"backwards":for(l=null,u=e.child,e.child=null;u!==null;){if(t=u.alternate,t!==null&&Sn(t)===null){e.child=u;break}t=u.sibling,u.sibling=l,l=u,u=t}_c(e,!0,l,null,n);break;case"together":_c(e,!1,null,null,void 0);break;default:e.memoizedState=null}return e.child}function Ge(t,e,l){if(t!==null&&(e.dependencies=t.dependencies),ol|=e.lanes,(l&e.childLanes)===0)if(t!==null){if(Ja(t,e,l,!1),(l&e.childLanes)===0)return null}else return null;if(t!==null&&e.child!==t.child)throw Error(r(153));if(e.child!==null){for(t=e.child,l=He(t,t.pendingProps),e.child=l,l.return=e;t.sibling!==null;)t=t.sibling,l=l.sibling=He(t,t.pendingProps),l.return=e;l.sibling=null}return e.child}function Oc(t,e){return(t.lanes&e)!==0?!0:(t=t.dependencies,!!(t!==null&&ln(t)))}function Uh(t,e,l){switch(e.tag){case 3:bt(e,e.stateNode.containerInfo),Ie(e,Nt,t.memoizedState.cache),Za();break;case 27:case 5:li(e);break;case 4:bt(e,e.stateNode.containerInfo);break;case 10:Ie(e,e.type,e.memoizedProps.value);break;case 13:var a=e.memoizedState;if(a!==null)return a.dehydrated!==null?(ul(e),e.flags|=128,null):(l&e.child.childLanes)!==0?cs(t,e,l):(ul(e),t=Ge(t,e,l),t!==null?t.sibling:null);ul(e);break;case 19:var u=(t.flags&128)!==0;if(a=(l&e.childLanes)!==0,a||(Ja(t,e,l,!1),a=(l&e.childLanes)!==0),u){if(a)return rs(t,e,l);e.flags|=128}if(u=e.memoizedState,u!==null&&(u.rendering=null,u.tail=null,u.lastEffect=null),q(Ht,Ht.current),a)break;return null;case 22:case 23:return e.lanes=0,ls(t,e,l);case 24:Ie(e,Nt,t.memoizedState.cache)}return Ge(t,e,l)}function os(t,e,l){if(t!==null)if(t.memoizedProps!==e.pendingProps)Lt=!0;else{if(!Oc(t,l)&&(e.flags&128)===0)return Lt=!1,Uh(t,e,l);Lt=(t.flags&131072)!==0}else Lt=!1,ot&&(e.flags&1048576)!==0&&jr(e,en,e.index);switch(e.lanes=0,e.tag){case 16:t:{t=e.pendingProps;var a=e.elementType,u=a._init;if(a=u(a._payload),e.type=a,typeof a=="function")Li(a)?(t=Bl(a,t),e.tag=1,e=ns(null,e,a,t,l)):(e.tag=0,e=bc(null,e,a,t,l));else{if(a!=null){if(u=a.$$typeof,u===it){e.tag=11,e=Io(null,e,a,t,l);break t}else if(u===Rt){e.tag=14,e=ts(null,e,a,t,l);break t}}throw e=Tl(a)||a,Error(r(306,e,""))}}return e;case 0:return bc(t,e,e.type,e.pendingProps,l);case 1:return a=e.type,u=Bl(a,e.pendingProps),ns(t,e,a,u,l);case 3:t:{if(bt(e,e.stateNode.containerInfo),t===null)throw Error(r(387));a=e.pendingProps;var n=e.memoizedState;u=n.element,Wi(t,e),tu(e,a,null,l);var i=e.memoizedState;if(a=i.cache,Ie(e,Nt,a),a!==n.cache&&Qi(e,[Nt],l,!0),Ia(),a=i.element,n.isDehydrated)if(n={element:a,isDehydrated:!1,cache:i.cache},e.updateQueue.baseState=n,e.memoizedState=n,e.flags&256){e=is(t,e,a,l);break t}else if(a!==u){u=he(Error(r(424)),e),Ka(u),e=is(t,e,a,l);break t}else{switch(t=e.stateNode.containerInfo,t.nodeType){case 9:t=t.body;break;default:t=t.nodeName==="HTML"?t.ownerDocument.body:t}for(At=xe(t.firstChild),kt=e,ot=!0,Cl=null,_e=!0,l=Xo(e,null,a,l),e.child=l;l;)l.flags=l.flags&-3|4096,l=l.sibling}else{if(Za(),a===u){e=Ge(t,e,l);break t}jt(t,e,a,l)}e=e.child}return e;case 26:return Tn(t,e),t===null?(l=m0(e.type,null,e.pendingProps,null))?e.memoizedState=l:ot||(l=e.type,t=e.pendingProps,a=Bn(tt.current).createElement(l),a[Qt]=e,a[$t]=t,Gt(a,l,t),wt(a),e.stateNode=a):e.memoizedState=m0(e.type,t.memoizedProps,e.pendingProps,t.memoizedState),null;case 27:return li(e),t===null&&ot&&(a=e.stateNode=s0(e.type,e.pendingProps,tt.current),kt=e,_e=!0,u=At,ml(e.type)?(ff=u,At=xe(a.firstChild)):At=u),jt(t,e,e.pendingProps.children,l),Tn(t,e),t===null&&(e.flags|=4194304),e.child;case 5:return t===null&&ot&&((u=a=At)&&(a=i1(a,e.type,e.pendingProps,_e),a!==null?(e.stateNode=a,kt=e,At=xe(a.firstChild),_e=!1,u=!0):u=!1),u||Ul(e)),li(e),u=e.type,n=e.pendingProps,i=t!==null?t.memoizedProps:null,a=n.children,af(u,n)?a=null:i!==null&&af(u,i)&&(e.flags|=32),e.memoizedState!==null&&(u=lc(t,e,Rh,null,null,l),Tu._currentValue=u),Tn(t,e),jt(t,e,a,l),e.child;case 6:return t===null&&ot&&((t=l=At)&&(l=c1(l,e.pendingProps,_e),l!==null?(e.stateNode=l,kt=e,At=null,t=!0):t=!1),t||Ul(e)),null;case 13:return cs(t,e,l);case 4:return bt(e,e.stateNode.containerInfo),a=e.pendingProps,t===null?e.child=ha(e,null,a,l):jt(t,e,a,l),e.child;case 11:return Io(t,e,e.type,e.pendingProps,l);case 7:return jt(t,e,e.pendingProps,l),e.child;case 8:return jt(t,e,e.pendingProps.children,l),e.child;case 12:return jt(t,e,e.pendingProps.children,l),e.child;case 10:return a=e.pendingProps,Ie(e,e.type,a.value),jt(t,e,a.children,l),e.child;case 9:return u=e.type._context,a=e.pendingProps.children,Hl(e),u=Zt(u),a=a(u),e.flags|=1,jt(t,e,a,l),e.child;case 14:return ts(t,e,e.type,e.pendingProps,l);case 15:return es(t,e,e.type,e.pendingProps,l);case 19:return rs(t,e,l);case 31:return a=e.pendingProps,l=e.mode,a={mode:a.mode,children:a.children},t===null?(l=xn(a,l),l.ref=e.ref,e.child=l,l.return=e,e=l):(l=He(t.child,a),l.ref=e.ref,e.child=l,l.return=e,e=l),e;case 22:return ls(t,e,l);case 24:return Hl(e),a=Zt(Nt),t===null?(u=Ji(),u===null&&(u=pt,n=Zi(),u.pooledCache=n,n.refCount++,n!==null&&(u.pooledCacheLanes|=l),u=n),e.memoizedState={parent:a,cache:u},$i(e),Ie(e,Nt,u)):((t.lanes&l)!==0&&(Wi(t,e),tu(e,null,null,l),Ia()),u=t.memoizedState,n=e.memoizedState,u.parent!==a?(u={parent:a,cache:a},e.memoizedState=u,e.lanes===0&&(e.memoizedState=e.updateQueue.baseState=u),Ie(e,Nt,a)):(a=n.cache,Ie(e,Nt,a),a!==u.cache&&Qi(e,[Nt],l,!0))),jt(t,e,e.pendingProps.children,l),e.child;case 29:throw e.pendingProps}throw Error(r(156,e.tag))}function Xe(t){t.flags|=4}function ss(t,e){if(e.type!=="stylesheet"||(e.state.loading&4)!==0)t.flags&=-16777217;else if(t.flags|=16777216,!S0(e)){if(e=ge.current,e!==null&&((ct&4194048)===ct?Oe!==null:(ct&62914560)!==ct&&(ct&536870912)===0||e!==Oe))throw Fa=ki,Jr;t.flags|=8192}}function Rn(t,e){e!==null&&(t.flags|=4),t.flags&16384&&(e=t.tag!==22?Xf():536870912,t.lanes|=e,ga|=e)}function cu(t,e){if(!ot)switch(t.tailMode){case"hidden":e=t.tail;for(var l=null;e!==null;)e.alternate!==null&&(l=e),e=e.sibling;l===null?t.tail=null:l.sibling=null;break;case"collapsed":l=t.tail;for(var a=null;l!==null;)l.alternate!==null&&(a=l),l=l.sibling;a===null?e||t.tail===null?t.tail=null:t.tail.sibling=null:a.sibling=null}}function xt(t){var e=t.alternate!==null&&t.alternate.child===t.child,l=0,a=0;if(e)for(var u=t.child;u!==null;)l|=u.lanes|u.childLanes,a|=u.subtreeFlags&65011712,a|=u.flags&65011712,u.return=t,u=u.sibling;else for(u=t.child;u!==null;)l|=u.lanes|u.childLanes,a|=u.subtreeFlags,a|=u.flags,u.return=t,u=u.sibling;return t.subtreeFlags|=a,t.childLanes=l,e}function Nh(t,e,l){var a=e.pendingProps;switch(Yi(e),e.tag){case 31:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return xt(e),null;case 1:return xt(e),null;case 3:return l=e.stateNode,a=null,t!==null&&(a=t.memoizedState.cache),e.memoizedState.cache!==a&&(e.flags|=2048),qe(Nt),$e(),l.pendingContext&&(l.context=l.pendingContext,l.pendingContext=null),(t===null||t.child===null)&&(Qa(e)?Xe(e):t===null||t.memoizedState.isDehydrated&&(e.flags&256)===0||(e.flags|=1024,Xr())),xt(e),null;case 26:return l=e.memoizedState,t===null?(Xe(e),l!==null?(xt(e),ss(e,l)):(xt(e),e.flags&=-16777217)):l?l!==t.memoizedState?(Xe(e),xt(e),ss(e,l)):(xt(e),e.flags&=-16777217):(t.memoizedProps!==a&&Xe(e),xt(e),e.flags&=-16777217),null;case 27:Hu(e),l=tt.current;var u=e.type;if(t!==null&&e.stateNode!=null)t.memoizedProps!==a&&Xe(e);else{if(!a){if(e.stateNode===null)throw Error(r(166));return xt(e),null}t=J.current,Qa(e)?Yr(e):(t=s0(u,a,l),e.stateNode=t,Xe(e))}return xt(e),null;case 5:if(Hu(e),l=e.type,t!==null&&e.stateNode!=null)t.memoizedProps!==a&&Xe(e);else{if(!a){if(e.stateNode===null)throw Error(r(166));return xt(e),null}if(t=J.current,Qa(e))Yr(e);else{switch(u=Bn(tt.current),t){case 1:t=u.createElementNS("http://www.w3.org/2000/svg",l);break;case 2:t=u.createElementNS("http://www.w3.org/1998/Math/MathML",l);break;default:switch(l){case"svg":t=u.createElementNS("http://www.w3.org/2000/svg",l);break;case"math":t=u.createElementNS("http://www.w3.org/1998/Math/MathML",l);break;case"script":t=u.createElement("div"),t.innerHTML=" - - - -
- - diff --git a/background.js b/src/background.js similarity index 98% rename from background.js rename to src/background.js index 331927d..bb1e2be 100644 --- a/background.js +++ b/src/background.js @@ -1,7 +1,7 @@ // Open popout window when the extension icon is clicked chrome.browserAction.onClicked.addListener(() => { chrome.windows.create({ - url: chrome.runtime.getURL("react/index.html"), + url: chrome.runtime.getURL("index.html"), type: "popup", // opens as a floating window width: 800, height: 600, diff --git a/content.js b/src/content.js similarity index 100% rename from content.js rename to src/content.js diff --git a/inject.js b/src/inject.js similarity index 100% rename from inject.js rename to src/inject.js diff --git a/manifest.json b/src/manifest.json similarity index 100% rename from manifest.json rename to src/manifest.json -- 2.43.0 From 53aa7d66e3c73771093f7c6613d03e575a7fd24a Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Sun, 20 Jul 2025 14:24:03 +0700 Subject: [PATCH 03/26] remove leftover dist folder --- frontend/dist/index.html | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 frontend/dist/index.html diff --git a/frontend/dist/index.html b/frontend/dist/index.html deleted file mode 100644 index 7d51312..0000000 --- a/frontend/dist/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - CDRM Decryption Extension - - - - -
- - -- 2.43.0 From 9c738d8a3eff4b6352bb3aff5cfbe8fcf078492c Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Sun, 20 Jul 2025 15:03:43 +0700 Subject: [PATCH 04/26] dynamically add version to extension title bar and manifest.json, add readme --- README.md | 49 ++++++++++++++++++++++++ buildext.js | 15 +++++--- frontend/index.html | 2 +- frontend/package.json | 58 ++++++++++++++-------------- frontend/vite.config.js | 18 +++++++-- package.json | 2 +- src/manifest.json | 84 ++++++++++++++++++++++------------------- syncVersion.js | 46 ++++++++++++++++++++++ 8 files changed, 196 insertions(+), 78 deletions(-) create mode 100644 README.md create mode 100644 syncVersion.js diff --git a/README.md b/README.md new file mode 100644 index 0000000..16ce2fc --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# CDRM Extension + +An extension to show keys from DRM protected content, which are used to decrypt content. + +## Notes + +Keep these extension core files inside `src`: + +- `background.js` +- `content.js` +- `inject.js` +- `manifest.json` + +Frontend React source stays in `frontend`. + +The build process will take care of everything into `extension-release`. + +To update the version across the entire project, simply change the version number in the root `package.json`. The build script will handle version sync automatically to both the extension's version and the frontend's title bar. + +## Build instructions + +### Prerequisites + +- Node.js v21 or higher. [Download Node.js here](https://nodejs.org/en/download). + +### How to build by yourself + +- Open terminal at the project root + +- Run the build script: + +```bash +npm run buildext +``` + +This will: + +- Sync the version number from the root `package.json` to `src/manifest.json` and `frontend/package.json` +- Install frontend dependencies if needed +- Build the React frontend +- Clean and prepare the `extension-release` folder +- Copy extension files in `src`, built frontend assets, and icons into `extension-release` + +### How to load the extension in Google Chrome or Chromium browsers + +1. Go to `chrome://extensions/` +2. Enable **Developer mode** +3. Click **Load unpacked** and select the `extension-release` folder +4. Verify the extension is working by clicking its icon or opening the developer console (F12) to check for any logs or errors. diff --git a/buildext.js b/buildext.js index de5beb0..632d6fb 100644 --- a/buildext.js +++ b/buildext.js @@ -2,6 +2,7 @@ import { execSync } from "child_process"; import fs from "fs"; import path from "path"; import url from "url"; +import syncVersion from "./syncVersion.js"; const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); const frontendDir = path.join(__dirname, "frontend"); @@ -10,21 +11,23 @@ const srcDir = path.join(__dirname, "src"); const iconDir = path.join(__dirname, "icons"); const releaseDir = path.join(__dirname, "extension-release"); -function run(cmd, cwd) { +const run = (cmd, cwd) => { console.log(`🛠️ Running: ${cmd}`); execSync(cmd, { cwd, stdio: "inherit" }); -} +}; -async function copyDir(src, dest) { +const copyDir = async (src, dest) => { await fs.promises.mkdir(dest, { recursive: true }); await fs.promises.cp(src, dest, { recursive: true, force: true, filter: (src) => !src.endsWith(".map"), }); -} +}; + +const main = async () => { + await syncVersion(); -async function main() { console.log("🚀 Starting extension build..."); // 1. Install frontend deps if needed @@ -57,7 +60,7 @@ async function main() { await copyDir(iconDir, path.join(releaseDir, "icons")); console.log("✅ Build complete! extension-release ready."); -} +}; main().catch((e) => { console.error("❌ Build failed:", e); diff --git a/frontend/index.html b/frontend/index.html index e830ac7..cb4ef3c 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -3,7 +3,7 @@ - CDRM Decryption Extension + CDRM Decryption Extension v%APPVERSION%
diff --git a/frontend/package.json b/frontend/package.json index b4df8fd..326d96a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,30 +1,30 @@ { - "name": "frontend", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "vite build", - "lint": "eslint .", - "preview": "vite preview" - }, - "dependencies": { - "@tailwindcss/vite": "^4.1.11", - "react": "^19.1.0", - "react-dom": "^19.1.0", - "react-router-dom": "^7.7.0", - "tailwindcss": "^4.1.11" - }, - "devDependencies": { - "@eslint/js": "^9.31.0", - "@types/react": "^19.1.8", - "@types/react-dom": "^19.1.6", - "@vitejs/plugin-react": "^4.7.0", - "eslint": "^9.31.0", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.20", - "globals": "^16.3.0", - "vite": "^7.0.5" - } -} + "name": "frontend", + "private": true, + "version": "2.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@tailwindcss/vite": "^4.1.11", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-router-dom": "^7.7.0", + "tailwindcss": "^4.1.11" + }, + "devDependencies": { + "@eslint/js": "^9.31.0", + "@types/react": "^19.1.8", + "@types/react-dom": "^19.1.6", + "@vitejs/plugin-react": "^4.7.0", + "eslint": "^9.31.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^16.3.0", + "vite": "^7.0.5" + } +} \ No newline at end of file diff --git a/frontend/vite.config.js b/frontend/vite.config.js index a4c9f4d..01889a6 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -1,9 +1,21 @@ -import { defineConfig } from "vite"; -import react from "@vitejs/plugin-react"; import tailwindcss from "@tailwindcss/vite"; +import react from "@vitejs/plugin-react"; +import { readFileSync } from "fs"; +import { defineConfig } from "vite"; + +const packageJson = JSON.parse(readFileSync("./package.json", "utf8")); + +const replaceVersionPlugin = () => { + return { + name: "replace-version", + transformIndexHtml(html) { + return html.replace("%APPVERSION%", packageJson.version); + }, + }; +}; // https://vite.dev/config/ export default defineConfig({ base: "./", - plugins: [react(), tailwindcss()], + plugins: [react(), tailwindcss(), replaceVersionPlugin()], }); diff --git a/package.json b/package.json index 74ba2f6..2372915 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cdrm-extension", - "version": "1.0.0", + "version": "2.1.0", "description": "", "main": "background.js", "scripts": { diff --git a/src/manifest.json b/src/manifest.json index 5c54ba4..00a5ab7 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,41 +1,49 @@ { - "manifest_version": 2, - "name": "CDRM Extension 2.0", - "version": "2.0", - "description": "Decrypt DRM Protected content", - "permissions": [ - "webRequest", - "webRequestBlocking", - "", - "activeTab", - "storage", - "tabs", - "contextMenus" + "manifest_version": 2, + "name": "CDRM Extension", + "version": "2.1.0", + "description": "Decrypt DRM protected content", + "permissions": [ + "webRequest", + "webRequestBlocking", + "", + "activeTab", + "storage", + "tabs", + "contextMenus" + ], + "background": { + "scripts": [ + "background.js" ], - "background": { - "scripts": ["background.js"], - "persistent": true - }, - "content_scripts": [ - { - "matches": [""], - "js": ["content.js"], - "run_at": "document_start", - "all_frames": true - } - ], - "web_accessible_resources": ["inject.js"], - "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", - "browser_action": { - "default_icon": { - "16": "icons/icon16.png", - "32": "icons/icon32.png", - "128": "icons/icon128.png" - } - }, - "icons": { - "16": "icons/icon16.png", - "32": "icons/icon32.png", - "128": "icons/icon128.png" + "persistent": true + }, + "content_scripts": [ + { + "matches": [ + "" + ], + "js": [ + "content.js" + ], + "run_at": "document_start", + "all_frames": true } -} + ], + "web_accessible_resources": [ + "inject.js" + ], + "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", + "browser_action": { + "default_icon": { + "16": "icons/icon16.png", + "32": "icons/icon32.png", + "128": "icons/icon128.png" + } + }, + "icons": { + "16": "icons/icon16.png", + "32": "icons/icon32.png", + "128": "icons/icon128.png" + } +} \ No newline at end of file diff --git a/syncVersion.js b/syncVersion.js new file mode 100644 index 0000000..7acba5d --- /dev/null +++ b/syncVersion.js @@ -0,0 +1,46 @@ +import fs from "fs/promises"; +import path from "path"; +import { fileURLToPath } from "url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const syncVersion = async () => { + const rootPkgPath = path.join(__dirname, "package.json"); + const frontendPkgPath = path.join(__dirname, "frontend", "package.json"); + const manifestPath = path.join(__dirname, "src", "manifest.json"); + + // Read root package.json version + const rootPkgRaw = await fs.readFile(rootPkgPath, "utf-8"); + const rootPkg = JSON.parse(rootPkgRaw); + const version = rootPkg.version; + + if (!version) { + console.warn("⚠️ No version field found in root package.json, skipping sync."); + return; + } + + // Update frontend/package.json if exists + try { + const frontendPkgRaw = await fs.readFile(frontendPkgPath, "utf-8"); + const frontendPkg = JSON.parse(frontendPkgRaw); + frontendPkg.version = version; + await fs.writeFile(frontendPkgPath, JSON.stringify(frontendPkg, null, 2)); + console.log(`🔄 Updated frontend/package.json version to ${version}`); + } catch { + console.log("ℹ️ frontend/package.json not found or unreadable, skipping version update."); + } + + // Update src/manifest.json version + try { + const manifestRaw = await fs.readFile(manifestPath, "utf-8"); + const manifest = JSON.parse(manifestRaw); + manifest.version = version; + await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2)); + console.log(`🔄 Updated src/manifest.json version to ${version}`); + } catch (err) { + console.error(`❌ Failed to update src/manifest.json version: ${err.message}`); + } +}; + +export default syncVersion; -- 2.43.0 From 5678c9b5da37f141f43a26c484bae010d06297d7 Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Sun, 20 Jul 2025 16:00:59 +0700 Subject: [PATCH 05/26] add manifest URL in frontend --- frontend/index.html | 2 +- frontend/src/components/results.jsx | 44 +++++++++++++++++++++++------ src/background.js | 5 ++++ 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index cb4ef3c..8d5c386 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,7 +2,7 @@ - + CDRM Decryption Extension v%APPVERSION% diff --git a/frontend/src/components/results.jsx b/frontend/src/components/results.jsx index 1f57730..8339113 100644 --- a/frontend/src/components/results.jsx +++ b/frontend/src/components/results.jsx @@ -5,14 +5,23 @@ function Results() { const [pssh, setPssh] = useState(""); const [licenseUrl, setLicenseUrl] = useState(""); const [keys, setKeys] = useState([]); + const [manifestUrl, setManifestUrl] = useState(""); useEffect(() => { chrome.storage.local.get( - ["drmType", "latestPSSH", "latestLicenseRequest", "latestKeys", "licenseURL"], + [ + "drmType", + "latestPSSH", + "latestLicenseRequest", + "latestKeys", + "licenseURL", + "manifestURL", + ], (result) => { if (result.drmType) setDrmType(result.drmType); if (result.latestPSSH) setPssh(result.latestPSSH); if (result.licenseURL) setLicenseUrl(result.licenseURL); + if (result.manifestURL) setManifestUrl(result.manifestURL); if (result.latestKeys) { try { const parsed = Array.isArray(result.latestKeys) @@ -38,6 +47,9 @@ function Results() { if (changes.licenseURL) { setLicenseUrl(changes.licenseURL.newValue); } + if (changes.manifestURL) { + setManifestUrl(changes.manifestURL.newValue); + } if (changes.latestKeys) { setKeys(changes.latestKeys.newValue); } @@ -54,6 +66,7 @@ function Results() { drmType: "None", latestPSSH: "None", licenseURL: "None", + manifestURL: "None", latestKeys: [], }); @@ -97,39 +110,52 @@ function Results() { > Capture current tab +

DRM Type

+ +

Manifest URL

+ +

PSSH

+

License URL

+

Keys

-
+
{Array.isArray(keys) && keys.filter((k) => k.type !== "SIGNING").length > 0 ? ( keys .filter((k) => k.type !== "SIGNING") .map((k) => `${k.key_id || k.keyId}:${k.key}`) .join("\n") ) : ( - None + [Not available] )}
diff --git a/src/background.js b/src/background.js index bb1e2be..da9c10f 100644 --- a/src/background.js +++ b/src/background.js @@ -33,6 +33,11 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { chrome.storage.local.set({ licenseURL: data }); break; + case "MANIFEST_URL_FOUND": + console.log("Storing Manifest URL:", data); + chrome.storage.local.set({ manifestURL: data }); + break; + default: console.warn("Unknown message type received:", type); } -- 2.43.0 From 5949228d4f9e846ca10dbe586871e0d82a061392 Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Sun, 20 Jul 2025 16:27:13 +0700 Subject: [PATCH 06/26] update to manifest v3 --- README.md | 2 + frontend/package.json | 58 +-- mv2/background.js | 89 ++++ mv2/content.js | 96 ++++ mv2/inject.js | 1122 +++++++++++++++++++++++++++++++++++++++++ mv2/manifest.json | 41 ++ src/background.js | 5 +- src/manifest.json | 80 ++- 8 files changed, 1415 insertions(+), 78 deletions(-) create mode 100644 mv2/background.js create mode 100644 mv2/content.js create mode 100644 mv2/inject.js create mode 100644 mv2/manifest.json diff --git a/README.md b/README.md index 16ce2fc..4cb2211 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ Keep these extension core files inside `src`: - `inject.js` - `manifest.json` +The `mv2` folder is for Manifest v2 backup for legacy reasons. + Frontend React source stays in `frontend`. The build process will take care of everything into `extension-release`. diff --git a/frontend/package.json b/frontend/package.json index 326d96a..bae1047 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,30 +1,30 @@ { - "name": "frontend", - "private": true, - "version": "2.1.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "vite build", - "lint": "eslint .", - "preview": "vite preview" - }, - "dependencies": { - "@tailwindcss/vite": "^4.1.11", - "react": "^19.1.0", - "react-dom": "^19.1.0", - "react-router-dom": "^7.7.0", - "tailwindcss": "^4.1.11" - }, - "devDependencies": { - "@eslint/js": "^9.31.0", - "@types/react": "^19.1.8", - "@types/react-dom": "^19.1.6", - "@vitejs/plugin-react": "^4.7.0", - "eslint": "^9.31.0", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.20", - "globals": "^16.3.0", - "vite": "^7.0.5" - } -} \ No newline at end of file + "name": "frontend", + "private": true, + "version": "2.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@tailwindcss/vite": "^4.1.11", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-router-dom": "^7.7.0", + "tailwindcss": "^4.1.11" + }, + "devDependencies": { + "@eslint/js": "^9.31.0", + "@types/react": "^19.1.8", + "@types/react-dom": "^19.1.6", + "@vitejs/plugin-react": "^4.7.0", + "eslint": "^9.31.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^16.3.0", + "vite": "^7.0.5" + } +} diff --git a/mv2/background.js b/mv2/background.js new file mode 100644 index 0000000..da9c10f --- /dev/null +++ b/mv2/background.js @@ -0,0 +1,89 @@ +// Open popout window when the extension icon is clicked +chrome.browserAction.onClicked.addListener(() => { + chrome.windows.create({ + url: chrome.runtime.getURL("index.html"), + type: "popup", // opens as a floating window + width: 800, + height: 600, + }); +}); + +// Listen for messages and store data in chrome.storage.local +chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + const { type, data } = message; + + switch (type) { + case "DRM_TYPE": + console.log("DRM Type:", data); + chrome.storage.local.set({ drmType: data }); + break; + + case "PSSH_DATA": + console.log("Storing PSSH:", data); + chrome.storage.local.set({ latestPSSH: data }); + break; + + case "KEYS_DATA": + console.log("Storing Decryption Keys:", data); + chrome.storage.local.set({ latestKeys: data }); + break; + + case "LICENSE_URL": + console.log("Storling License URL " + data); + chrome.storage.local.set({ licenseURL: data }); + break; + + case "MANIFEST_URL_FOUND": + console.log("Storing Manifest URL:", data); + chrome.storage.local.set({ manifestURL: data }); + break; + + default: + console.warn("Unknown message type received:", type); + } +}); + +// Set initial config and injection type on install +chrome.runtime.onInstalled.addListener((details) => { + if (details.reason === "install") { + chrome.storage.local.set({ valid_config: false }, () => { + if (chrome.runtime.lastError) { + console.error("Error setting valid_config:", chrome.runtime.lastError); + } else { + console.log("valid_config set to false on first install."); + } + }); + + chrome.storage.local.set({ injection_type: "LICENSE" }, () => { + if (chrome.runtime.lastError) { + console.error("Error setting Injection Type:", chrome.runtime.lastError); + } else { + console.log("Injection type set to LICENSE on first install."); + } + }); + + chrome.storage.local.set({ drm_override: "DISABLED" }, () => { + if (chrome.runtime.lastError) { + console.error("Error setting DRM Override type:", chrome.runtime.lastError); + } else { + console.log("DRM Override type set to DISABLED on first install."); + } + }); + + chrome.storage.local.set({ cdrm_instance: null }, () => { + if (chrome.runtime.lastError) { + console.error("Error setting CDRM instance:", chrome.runtime.lastError); + } else { + console.log("CDRM instance set to null."); + } + }); + + chrome.storage.local.set({ cdrm_api_key: null }, () => { + if (chrome.runtime.lastError) { + console.error("Error setting CDRM API Key:", chrome.runtime.lastError); + } else { + console.log("CDRM API Key set."); + } + }); + } +}); diff --git a/mv2/content.js b/mv2/content.js new file mode 100644 index 0000000..55b7891 --- /dev/null +++ b/mv2/content.js @@ -0,0 +1,96 @@ +// Inject `inject.js` into the page context +(function injectScript() { + const script = document.createElement("script"); + script.src = chrome.runtime.getURL("inject.js"); + script.type = "text/javascript"; + script.onload = () => script.remove(); // Clean up + // Inject directly into or + (document.documentElement || document.head || document.body).appendChild(script); +})(); + +// Listen for messages from the injected script +window.addEventListener("message", function (event) { + if (event.source !== window) return; + + if ( + ["__DRM_TYPE__", "__PSSH_DATA__", "__KEYS_DATA__", "__LICENSE_URL__"].includes( + event.data?.type + ) + ) { + chrome.runtime.sendMessage({ + type: event.data.type.replace("__", "").replace("__", ""), + data: event.data.data, + }); + } + + if (event.data.type === "__GET_CDM_DEVICES__") { + chrome.storage.local.get(["widevine_device", "playready_device"], (result) => { + const widevine_device = result.widevine_device || null; + const playready_device = result.playready_device || null; + + window.postMessage( + { + type: "__CDM_DEVICES__", + widevine_device, + playready_device, + }, + "*" + ); + }); + } + + if (event.data.type === "__GET_INJECTION_TYPE__") { + chrome.storage.local.get("injection_type", (result) => { + const injectionType = result.injection_type || "LICENSE"; + + window.postMessage( + { + type: "__INJECTION_TYPE__", + injectionType, + }, + "*" + ); + }); + } + + if (event.data.type === "__GET_DRM_OVERRIDE__") { + chrome.storage.local.get("drm_override", (result) => { + const drmOverride = result.drm_override || "DISABLED"; + + window.postMessage( + { + type: "__DRM_OVERRIDE__", + drmOverride, + }, + "*" + ); + }); + } + + // Manifest header and URL + + const seenManifestUrls = new Set(); + + if (event.data?.type === "__MANIFEST_URL__") { + const url = event.data.data; + if (seenManifestUrls.has(url)) return; + seenManifestUrls.add(url); + console.log("✅ [Content] Unique manifest URL:", url); + + chrome.runtime.sendMessage({ + type: "MANIFEST_URL_FOUND", + data: url, + }); + } + + if (event.data?.type === "__MANIFEST_HEADERS__") { + const { url, headers } = event.data; + console.log("[Content.js] Manifest Headers:", url, headers); + + chrome.runtime.sendMessage({ + type: "MANIFEST_HEADERS", + url, + headers, + }); + } +}); diff --git a/mv2/inject.js b/mv2/inject.js new file mode 100644 index 0000000..fdcb029 --- /dev/null +++ b/mv2/inject.js @@ -0,0 +1,1122 @@ +let widevineDeviceInfo = null; +let playreadyDeviceInfo = null; +let originalChallenge = null; +let serviceCertFound = false; +let drmType = "NONE"; +let psshFound = false; +let foundWidevinePssh = null; +let foundPlayreadyPssh = null; +let drmDecided = null; +let drmOverride = "DISABLED"; +let interceptType = "DISABLED"; +let remoteCDM = null; +let generateRequestCalled = false; +let remoteListenerMounted = false; +let injectionSuccess = false; +let foundChallengeInBody = false; +let licenseResponseCounter = 0; +let keysRetrieved = false; + +// Post message to content.js to get DRM override +window.postMessage({ type: "__GET_DRM_OVERRIDE__" }, "*"); + +// Add listener for DRM override messages +window.addEventListener("message", function (event) { + if (event.source !== window) return; + if (event.data.type === "__DRM_OVERRIDE__") { + drmOverride = event.data.drmOverride || "DISABLED"; + console.log("DRM Override set to:", drmOverride); + } +}); + +// Post message to content.js to get injection type +window.postMessage({ type: "__GET_INJECTION_TYPE__" }, "*"); + +// Add listener for injection type messages +window.addEventListener("message", function (event) { + if (event.source !== window) return; + + if (event.data.type === "__INJECTION_TYPE__") { + interceptType = event.data.injectionType || "DISABLED"; + console.log("Injection type set to:", interceptType); + } +}); + +// Post message to get CDM devices +window.postMessage({ type: "__GET_CDM_DEVICES__" }, "*"); + +// Add listener for CDM device messages +window.addEventListener("message", function (event) { + if (event.source !== window) return; + + if (event.data.type === "__CDM_DEVICES__") { + const { widevine_device, playready_device } = event.data; + + console.log("Received device info:", widevine_device, playready_device); + + widevineDeviceInfo = widevine_device; + playreadyDeviceInfo = playready_device; + } +}); + +function safeHeaderShellEscape(str) { + return str + .replace(/\\/g, "\\\\") + .replace(/"/g, '\\"') + .replace(/\$/g, "\\$") // escape shell expansion + .replace(/`/g, "\\`") + .replace(/\n/g, ""); // strip newlines +} + +// Intercep network to find manifest +function injectManifestInterceptor() { + const script = document.createElement("script"); + script.textContent = ` + (function() { + function isProbablyManifest(text = "", contentType = "") { + const lowerCT = contentType?.toLowerCase() ?? ""; + const sample = text.slice(0, 2000); + + const isHLSMime = lowerCT.includes("mpegurl"); + const isDASHMime = lowerCT.includes("dash+xml"); + const isSmoothMime = lowerCT.includes("sstr+xml"); + + const isHLSKeyword = sample.includes("#EXTM3U") || sample.includes("#EXT-X-STREAM-INF"); + const isDASHKeyword = sample.includes(" { + headersObj[key] = value; + }); + + const headerFlags = Object.entries(headersObj) + .map(([key, val]) => '--add-headers "' + safeHeaderShellEscape(key) + ': ' + safeHeaderShellEscape(val) + '"') + .join(" "); + + window.postMessage({ + type: "__MANIFEST_HEADERS__", + url, + headers: headerFlags + }, "*"); + } + } catch (e) {} + + return response; + }; + + const originalXHROpen = XMLHttpRequest.prototype.open; + const originalXHRSend = XMLHttpRequest.prototype.send; + + XMLHttpRequest.prototype.open = function(method, url) { + this.__url = url; + return originalXHROpen.apply(this, arguments); + }; + + XMLHttpRequest.prototype.send = function(body) { + this.addEventListener("load", function () { + try { + const contentType = this.getResponseHeader("content-type") || ""; + const text = this.responseText; + + if (isProbablyManifest(text, contentType)) { + window.postMessage({ type: "__MANIFEST_URL__", data: this.__url }, "*"); + console.log("[Manifest][xhr]", this.__url, contentType); + + const xhrHeaders = {}; + const rawHeaders = this.getAllResponseHeaders().trim().split(/\\r?\\n/); + rawHeaders.forEach(line => { + const parts = line.split(": "); + if (parts.length === 2) { + xhrHeaders[parts[0]] = parts[1]; + } + }); + + const headerFlags = Object.entries(xhrHeaders) + .map(([key, val]) => '--add-headers "' + safeHeaderShellEscape(key) + ': ' + safeHeaderShellEscape(val) + '"') + .join(" "); + + window.postMessage({ + type: "__MANIFEST_HEADERS__", + url: this.__url, + headers: headerFlags + }, "*"); + } + } catch (e) {} + }); + return originalXHRSend.apply(this, arguments); + }; + })(); + `; + document.documentElement.appendChild(script); + script.remove(); +} + +injectManifestInterceptor(); + +// PlayReady Remote CDM Class +class remotePlayReadyCDM { + constructor(security_level, host, secret, device_name) { + this.security_level = security_level; + this.host = host; + this.secret = secret; + this.device_name = device_name; + this.session_id = null; + this.challenge = null; + this.keys = null; + } + + // Open PlayReady session + openSession() { + const url = `${this.host}/remotecdm/playready/${this.device_name}/open`; + const xhr = new XMLHttpRequest(); + xhr.open("GET", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); + xhr.send(); + const jsonData = JSON.parse(xhr.responseText); + if (jsonData.data?.session_id) { + this.session_id = jsonData.data.session_id; + console.log("PlayReady session opened:", this.session_id); + } else { + console.error("Failed to open PlayReady session:", jsonData.message); + throw new Error("Failed to open PlayReady session"); + } + } + + // Get PlayReady challenge + getChallenge(init_data) { + const url = `${this.host}/remotecdm/playready/${this.device_name}/get_license_challenge`; + const xhr = new XMLHttpRequest(); + xhr.open("POST", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); + const body = { + session_id: this.session_id, + init_data: init_data, + }; + xhr.send(JSON.stringify(body)); + const jsonData = JSON.parse(xhr.responseText); + if (jsonData.data?.challenge) { + this.challenge = btoa(jsonData.data.challenge); + console.log("PlayReady challenge received:", this.challenge); + } else { + console.error("Failed to get PlayReady challenge:", jsonData.message); + throw new Error("Failed to get PlayReady challenge"); + } + } + + // Parse PlayReady license response + parseLicense(license_message) { + const url = `${this.host}/remotecdm/playready/${this.device_name}/parse_license`; + const xhr = new XMLHttpRequest(); + xhr.open("POST", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); + const body = { + session_id: this.session_id, + license_message: license_message, + }; + xhr.send(JSON.stringify(body)); + const jsonData = JSON.parse(xhr.responseText); + if ( + jsonData.message === "Successfully parsed and loaded the Keys from the License message" + ) { + console.log("PlayReady license response parsed successfully"); + return true; + } else { + console.error("Failed to parse PlayReady license response:", jsonData.message); + throw new Error("Failed to parse PlayReady license response"); + } + } + + // Get PlayReady keys + getKeys() { + const url = `${this.host}/remotecdm/playready/${this.device_name}/get_keys`; + const xhr = new XMLHttpRequest(); + xhr.open("POST", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); + const body = { + session_id: this.session_id, + }; + xhr.send(JSON.stringify(body)); + const jsonData = JSON.parse(xhr.responseText); + if (jsonData.data?.keys) { + this.keys = jsonData.data.keys; + console.log("PlayReady keys received:", this.keys); + } else { + console.error("Failed to get PlayReady keys:", jsonData.message); + throw new Error("Failed to get PlayReady keys"); + } + } + + // Close PlayReady session + closeSession() { + const url = `${this.host}/remotecdm/playready/${this.device_name}/close/${this.session_id}`; + const xhr = new XMLHttpRequest(); + xhr.open("GET", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); + xhr.send(); + const jsonData = JSON.parse(xhr.responseText); + if (jsonData) { + console.log("PlayReady session closed successfully"); + } else { + console.error("Failed to close PlayReady session:", jsonData.message); + throw new Error("Failed to close PlayReady session"); + } + } +} + +// Widevine Remote CDM Class +class remoteWidevineCDM { + constructor(device_type, system_id, security_level, host, secret, device_name) { + this.device_type = device_type; + this.system_id = system_id; + this.security_level = security_level; + this.host = host; + this.secret = secret; + this.device_name = device_name; + this.session_id = null; + this.challenge = null; + this.keys = null; + } + + // Open Widevine session + openSession() { + const url = `${this.host}/remotecdm/widevine/${this.device_name}/open`; + const xhr = new XMLHttpRequest(); + xhr.open("GET", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); + xhr.send(); + const jsonData = JSON.parse(xhr.responseText); + if (jsonData.data?.session_id) { + this.session_id = jsonData.data.session_id; + console.log("Widevine session opened:", this.session_id); + } else { + console.error("Failed to open Widevine session:", jsonData.message); + throw new Error("Failed to open Widevine session"); + } + } + + // Set Widevine service certificate + setServiceCertificate(certificate) { + const url = `${this.host}/remotecdm/widevine/${this.device_name}/set_service_certificate`; + const xhr = new XMLHttpRequest(); + xhr.open("POST", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); + const body = { + session_id: this.session_id, + certificate: certificate ?? null, + }; + xhr.send(JSON.stringify(body)); + const jsonData = JSON.parse(xhr.responseText); + if (jsonData.status === 200) { + console.log("Service certificate set successfully"); + } else { + console.error("Failed to set service certificate:", jsonData.message); + throw new Error("Failed to set service certificate"); + } + } + + // Get Widevine challenge + getChallenge(init_data, license_type = "STREAMING") { + const url = `${this.host}/remotecdm/widevine/${this.device_name}/get_license_challenge/${license_type}`; + const xhr = new XMLHttpRequest(); + xhr.open("POST", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); + const body = { + session_id: this.session_id, + init_data: init_data, + privacy_mode: serviceCertFound, + }; + xhr.send(JSON.stringify(body)); + const jsonData = JSON.parse(xhr.responseText); + if (jsonData.data?.challenge_b64) { + this.challenge = jsonData.data.challenge_b64; + console.log("Widevine challenge received:", this.challenge); + } else { + console.error("Failed to get Widevine challenge:", jsonData.message); + throw new Error("Failed to get Widevine challenge"); + } + } + + // Parse Widevine license response + parseLicense(license_message) { + const url = `${this.host}/remotecdm/widevine/${this.device_name}/parse_license`; + const xhr = new XMLHttpRequest(); + xhr.open("POST", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); + const body = { + session_id: this.session_id, + license_message: license_message, + }; + xhr.send(JSON.stringify(body)); + const jsonData = JSON.parse(xhr.responseText); + if (jsonData.status === 200) { + console.log("Widevine license response parsed successfully"); + return true; + } else { + console.error("Failed to parse Widevine license response:", jsonData.message); + throw new Error("Failed to parse Widevine license response"); + } + } + + // Get Widevine keys + getKeys() { + const url = `${this.host}/remotecdm/widevine/${this.device_name}/get_keys/ALL`; + const xhr = new XMLHttpRequest(); + xhr.open("POST", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); + const body = { + session_id: this.session_id, + }; + xhr.send(JSON.stringify(body)); + const jsonData = JSON.parse(xhr.responseText); + if (jsonData.data?.keys) { + this.keys = jsonData.data.keys; + console.log("Widevine keys received:", this.keys); + } else { + console.error("Failed to get Widevine keys:", jsonData.message); + throw new Error("Failed to get Widevine keys"); + } + } + + // Close Widevine session + closeSession() { + const url = `${this.host}/remotecdm/widevine/${this.device_name}/close/${this.session_id}`; + const xhr = new XMLHttpRequest(); + xhr.open("GET", url, false); + xhr.setRequestHeader("Content-Type", "application/json"); + xhr.send(); + const jsonData = JSON.parse(xhr.responseText); + if (jsonData) { + console.log("Widevine session closed successfully"); + } else { + console.error("Failed to close Widevine session:", jsonData.message); + throw new Error("Failed to close Widevine session"); + } + } +} + +// Utility functions +function hexStrToU8(hexString) { + return Uint8Array.from(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))); +} + +function u8ToHexStr(bytes) { + return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), ""); +} + +function b64ToHexStr(b64) { + return [...atob(b64)].map((c) => c.charCodeAt(0).toString(16).padStart(2, "0")).join``; +} + +function jsonContainsValue(obj, prefix = "CAES") { + if (typeof obj === "string") return obj.startsWith(prefix); + if (Array.isArray(obj)) return obj.some((val) => jsonContainsValue(val, prefix)); + if (typeof obj === "object" && obj !== null) { + return Object.values(obj).some((val) => jsonContainsValue(val, prefix)); + } + return false; +} + +function jsonReplaceValue(obj, newValue) { + if (typeof obj === "string") { + return obj.startsWith("CAES") || obj.startsWith("PD94") ? newValue : obj; + } + + if (Array.isArray(obj)) { + return obj.map((item) => jsonReplaceValue(item, newValue)); + } + + if (typeof obj === "object" && obj !== null) { + const newObj = {}; + for (const key in obj) { + if (Object.hasOwn(obj, key)) { + newObj[key] = jsonReplaceValue(obj[key], newValue); + } + } + return newObj; + } + + return obj; +} + +function isJson(str) { + try { + JSON.parse(str); + return true; + } catch (e) { + return false; + } +} + +function getWidevinePssh(buffer) { + const hex = u8ToHexStr(new Uint8Array(buffer)); + const match = hex.match(/000000(..)?70737368.*/); + if (!match) return null; + + const boxHex = match[0]; + const bytes = hexStrToU8(boxHex); + return window.btoa(String.fromCharCode(...bytes)); +} + +function getPlayReadyPssh(buffer) { + const u8 = new Uint8Array(buffer); + const systemId = "9a04f07998404286ab92e65be0885f95"; + const hex = u8ToHexStr(u8); + const index = hex.indexOf(systemId); + if (index === -1) return null; + const psshBoxStart = hex.lastIndexOf("70737368", index); + if (psshBoxStart === -1) return null; + const lenStart = psshBoxStart - 8; + const boxLen = parseInt(hex.substr(lenStart, 8), 16) * 2; + const psshHex = hex.substr(lenStart, boxLen); + const psshBytes = hexStrToU8(psshHex); + return window.btoa(String.fromCharCode(...psshBytes)); +} + +function getClearkey(response) { + let obj = JSON.parse(new TextDecoder("utf-8").decode(response)); + return obj["keys"].map((o) => ({ + key_id: b64ToHexStr(o["kid"].replace(/-/g, "+").replace(/_/g, "/")), + key: b64ToHexStr(o["k"].replace(/-/g, "+").replace(/_/g, "/")), + })); +} + +function base64ToUint8Array(base64) { + const binaryStr = atob(base64); + const len = binaryStr.length; + const bytes = new Uint8Array(len); + for (let i = 0; i < len; i++) { + bytes[i] = binaryStr.charCodeAt(i); + } + return bytes; +} + +function arrayBufferToBase64(uint8array) { + let binary = ""; + const len = uint8array.length; + + for (let i = 0; i < len; i++) { + binary += String.fromCharCode(uint8array[i]); + } + + return window.btoa(binary); +} + +// Challenge generator interceptor +const originalGenerateRequest = MediaKeySession.prototype.generateRequest; +MediaKeySession.prototype.generateRequest = function (initDataType, initData) { + const session = this; + let playReadyPssh = getPlayReadyPssh(initData); + if (playReadyPssh) { + console.log("[DRM Detected] PlayReady"); + foundPlayreadyPssh = playReadyPssh; + console.log("[PlayReady PSSH found] " + playReadyPssh); + } + let wideVinePssh = getWidevinePssh(initData); + if (wideVinePssh) { + // Widevine code + console.log("[DRM Detected] Widevine"); + foundWidevinePssh = wideVinePssh; + console.log("[Widevine PSSH found] " + wideVinePssh); + } + // Challenge message interceptor + if (!remoteListenerMounted) { + remoteListenerMounted = true; + session.addEventListener("message", function messageInterceptor(event) { + event.stopImmediatePropagation(); + const uint8Array = new Uint8Array(event.message); + const base64challenge = arrayBufferToBase64(uint8Array); + if (base64challenge === "CAQ=" && interceptType !== "DISABLED" && !serviceCertFound) { + const { device_type, system_id, security_level, host, secret, device_name } = + widevineDeviceInfo; + remoteCDM = new remoteWidevineCDM( + device_type, + system_id, + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + } + if (!injectionSuccess && base64challenge !== "CAQ=" && interceptType !== "DISABLED") { + if (interceptType === "EME") { + injectionSuccess = true; + } + if (!originalChallenge) { + originalChallenge = base64challenge; + } + if (originalChallenge.startsWith("CAES")) { + window.postMessage({ type: "__DRM_TYPE__", data: "Widevine" }, "*"); + window.postMessage({ type: "__PSSH_DATA__", data: foundWidevinePssh }, "*"); + if (interceptType === "EME" && !remoteCDM) { + const { + device_type, + system_id, + security_level, + host, + secret, + device_name, + } = widevineDeviceInfo; + remoteCDM = new remoteWidevineCDM( + device_type, + system_id, + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundWidevinePssh); + } + } + if (!originalChallenge.startsWith("CAES")) { + const buffer = event.message; + const decoder = new TextDecoder("utf-16"); + const decodedText = decoder.decode(buffer); + const match = decodedText.match( + /([^<]+)<\/Challenge>/ + ); + if (match) { + window.postMessage({ type: "__DRM_TYPE__", data: "PlayReady" }, "*"); + window.postMessage( + { type: "__PSSH_DATA__", data: foundPlayreadyPssh }, + "*" + ); + originalChallenge = match[1]; + if (interceptType === "EME" && !remoteCDM) { + const { security_level, host, secret, device_name } = + playreadyDeviceInfo; + remoteCDM = new remotePlayReadyCDM( + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundPlayreadyPssh); + } + } + } + if (interceptType === "EME" && remoteCDM) { + const uint8challenge = base64ToUint8Array(remoteCDM.challenge); + const challengeBuffer = uint8challenge.buffer; + const syntheticEvent = new MessageEvent("message", { + data: event.data, + origin: event.origin, + lastEventId: event.lastEventId, + source: event.source, + ports: event.ports, + }); + Object.defineProperty(syntheticEvent, "message", { + get: () => challengeBuffer, + }); + console.log("Intercepted EME Challenge and injected custom one."); + session.dispatchEvent(syntheticEvent); + } + } + }); + console.log("Message interceptor mounted."); + } + return originalGenerateRequest.call(session, initDataType, initData); +}; + +// Message update interceptors +const originalUpdate = MediaKeySession.prototype.update; +MediaKeySession.prototype.update = function (response) { + const uint8 = response instanceof Uint8Array ? response : new Uint8Array(response); + const base64Response = window.btoa(String.fromCharCode(...uint8)); + if (base64Response.startsWith("CAUS") && foundWidevinePssh && remoteCDM && !serviceCertFound) { + remoteCDM.setServiceCertificate(base64Response); + if (interceptType === "EME" && !remoteCDM.challenge) { + remoteCDM.getChallenge(foundWidevinePssh); + } + window.postMessage({ type: "__DRM_TYPE__", data: "Widevine" }, "*"); + window.postMessage({ type: "__PSSH_DATA__", data: foundWidevinePssh }, "*"); + serviceCertFound = true; + } + if ( + !base64Response.startsWith("CAUS") && + (foundWidevinePssh || foundPlayreadyPssh) && + !keysRetrieved + ) { + if (licenseResponseCounter === 1 || foundChallengeInBody) { + remoteCDM.parseLicense(base64Response); + remoteCDM.getKeys(); + remoteCDM.closeSession(); + keysRetrieved = true; + window.postMessage({ type: "__KEYS_DATA__", data: remoteCDM.keys }, "*"); + } + licenseResponseCounter++; + } + const updatePromise = originalUpdate.call(this, response); + if (!foundPlayreadyPssh && !foundWidevinePssh) { + updatePromise + .then(() => { + let clearKeys = getClearkey(response); + if (clearKeys && clearKeys.length > 0) { + console.log("[CLEARKEY] ", clearKeys); + const drmType = { + type: "__DRM_TYPE__", + data: "ClearKey", + }; + window.postMessage(drmType, "*"); + const keysData = { + type: "__KEYS_DATA__", + data: clearKeys, + }; + window.postMessage(keysData, "*"); + } + }) + .catch((e) => { + console.log("[CLEARKEY] Not found"); + }); + } + + return updatePromise; +}; + +// fetch POST interceptor +(function () { + const originalFetch = window.fetch; + + window.fetch = async function (resource, config = {}) { + const method = (config.method || "GET").toUpperCase(); + + if (method === "POST") { + let body = config.body; + if (body) { + if (body instanceof ArrayBuffer || body instanceof Uint8Array) { + const buffer = body instanceof Uint8Array ? body : new Uint8Array(body); + const base64Body = window.btoa(String.fromCharCode(...buffer)); + if ( + (base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) && + (!remoteCDM || + remoteCDM.challenge === null || + base64Body !== remoteCDM.challenge) && + interceptType === "EME" + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); + // Block the request + return; + } + if ( + (base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) && + interceptType == "LICENSE" && + !foundChallengeInBody + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); + if (!remoteCDM) { + if (base64Body.startsWith("CAES")) { + const { + device_type, + system_id, + security_level, + host, + secret, + device_name, + } = widevineDeviceInfo; + remoteCDM = new remoteWidevineCDM( + device_type, + system_id, + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundWidevinePssh); + } + if (base64Body.startsWith("PD94")) { + const { security_level, host, secret, device_name } = + playreadyDeviceInfo; + remoteCDM = new remotePlayReadyCDM( + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundPlayreadyPssh); + } + } + if (remoteCDM && remoteCDM.challenge === null) { + remoteCDM.getChallenge(foundWidevinePssh); + } + const injectedBody = base64ToUint8Array(remoteCDM.challenge); + config.body = injectedBody; + return originalFetch(resource, config); + } + } + if (typeof body === "string" && !isJson(body)) { + const base64EncodedBody = btoa(body); + if ( + (base64EncodedBody.startsWith("CAES") || + base64EncodedBody.startsWith("PD94")) && + (!remoteCDM || + remoteCDM.challenge === null || + base64EncodedBody !== remoteCDM.challenge) && + interceptType === "EME" + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); + // Block the request + return; + } + if ( + (base64EncodedBody.startsWith("CAES") || + base64EncodedBody.startsWith("PD94")) && + interceptType == "LICENSE" && + !foundChallengeInBody + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); + if (!remoteCDM) { + if (base64EncodedBody.startsWith("CAES")) { + const { + device_type, + system_id, + security_level, + host, + secret, + device_name, + } = widevineDeviceInfo; + remoteCDM = new remoteWidevineCDM( + device_type, + system_id, + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundWidevinePssh); + } + if (base64EncodedBody.startsWith("PD94")) { + const { security_level, host, secret, device_name } = + playreadyDeviceInfo; + remoteCDM = new remotePlayReadyCDM( + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundPlayreadyPssh); + } + } + if (remoteCDM && remoteCDM.challenge === null) { + remoteCDM.getChallenge(foundWidevinePssh); + } + const injectedBody = atob(remoteCDM.challenge); + config.body = injectedBody; + return originalFetch(resource, config); + } + } + if (typeof body === "string" && isJson(body)) { + const jsonBody = JSON.parse(body); + + if ( + (jsonContainsValue(jsonBody, "CAES") || + jsonContainsValue(jsonBody, "PD94")) && + (!remoteCDM || remoteCDM.challenge === null) && + interceptType === "EME" + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); + // Block the request + return; + } + + if ( + (jsonContainsValue(jsonBody, "CAES") || + jsonContainsValue(jsonBody, "PD94")) && + interceptType === "LICENSE" && + !foundChallengeInBody + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); + if (!remoteCDM) { + if (jsonContainsValue(jsonBody, "CAES")) { + const { + device_type, + system_id, + security_level, + host, + secret, + device_name, + } = widevineDeviceInfo; + remoteCDM = new remoteWidevineCDM( + device_type, + system_id, + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundWidevinePssh); + } + if (jsonContainsValue(jsonBody, "PD94")) { + const { security_level, host, secret, device_name } = + playreadyDeviceInfo; + remoteCDM = new remotePlayReadyCDM( + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundPlayreadyPssh); + } + } + if (remoteCDM && remoteCDM.challenge === null) { + remoteCDM.getChallenge(foundWidevinePssh); + } + const injectedBody = jsonReplaceValue(jsonBody, remoteCDM.challenge); + config.body = JSON.stringify(injectedBody); + } + } + } + } + + return originalFetch(resource, config); + }; +})(); + +// XHR POST interceptor +(function () { + const originalOpen = XMLHttpRequest.prototype.open; + const originalSend = XMLHttpRequest.prototype.send; + + XMLHttpRequest.prototype.open = function (method, url, async, user, password) { + this._method = method; + this._url = url; + return originalOpen.apply(this, arguments); + }; + + XMLHttpRequest.prototype.send = function (body) { + if (this._method && this._method.toUpperCase() === "POST") { + if (body) { + if (body instanceof ArrayBuffer || body instanceof Uint8Array) { + const buffer = body instanceof Uint8Array ? body : new Uint8Array(body); + const base64Body = window.btoa(String.fromCharCode(...buffer)); + if ( + (base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) && + (!remoteCDM || + remoteCDM.challenge === null || + base64Body !== remoteCDM.challenge) && + interceptType === "EME" + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); + // Block the request + return; + } + if ( + (base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) && + interceptType == "LICENSE" && + !foundChallengeInBody + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); + if (!remoteCDM) { + if (base64Body.startsWith("CAES")) { + const { + device_type, + system_id, + security_level, + host, + secret, + device_name, + } = widevineDeviceInfo; + remoteCDM = new remoteWidevineCDM( + device_type, + system_id, + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundWidevinePssh); + } + if (base64Body.startsWith("PD94")) { + const { security_level, host, secret, device_name } = + playreadyDeviceInfo; + remoteCDM = new remotePlayReadyCDM( + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundPlayreadyPssh); + } + } + if (remoteCDM && remoteCDM.challenge === null) { + remoteCDM.getChallenge(foundWidevinePssh); + } + const injectedBody = base64ToUint8Array(remoteCDM.challenge); + return originalSend.call(this, injectedBody); + } + } + + if (typeof body === "string" && !isJson(body)) { + const base64EncodedBody = btoa(body); + if ( + (base64EncodedBody.startsWith("CAES") || + base64EncodedBody.startsWith("PD94")) && + (!remoteCDM || + remoteCDM.challenge === null || + base64EncodedBody !== remoteCDM.challenge) && + interceptType === "EME" + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); + // Block the request + return; + } + if ( + (base64EncodedBody.startsWith("CAES") || + base64EncodedBody.startsWith("PD94")) && + interceptType == "LICENSE" && + !foundChallengeInBody + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); + if (!remoteCDM) { + if (base64EncodedBody.startsWith("CAES")) { + const { + device_type, + system_id, + security_level, + host, + secret, + device_name, + } = widevineDeviceInfo; + remoteCDM = new remoteWidevineCDM( + device_type, + system_id, + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundWidevinePssh); + } + if (base64EncodedBody.startsWith("PD94")) { + const { security_level, host, secret, device_name } = + playreadyDeviceInfo; + remoteCDM = new remotePlayReadyCDM( + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundPlayreadyPssh); + } + } + if (remoteCDM && remoteCDM.challenge === null) { + remoteCDM.getChallenge(foundWidevinePssh); + } + const injectedBody = atob(remoteCDM.challenge); + return originalSend.call(this, injectedBody); + } + } + + if (typeof body === "string" && isJson(body)) { + const jsonBody = JSON.parse(body); + + if ( + (jsonContainsValue(jsonBody, "CAES") || + jsonContainsValue(jsonBody, "PD94")) && + (!remoteCDM || remoteCDM.challenge === null) && + interceptType === "EME" + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); + // Block the request + return; + } + + if ( + (jsonContainsValue(jsonBody, "CAES") || + jsonContainsValue(jsonBody, "PD94")) && + interceptType === "LICENSE" && + !foundChallengeInBody + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); + if (!remoteCDM) { + if (jsonContainsValue(jsonBody, "CAES")) { + const { + device_type, + system_id, + security_level, + host, + secret, + device_name, + } = widevineDeviceInfo; + remoteCDM = new remoteWidevineCDM( + device_type, + system_id, + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundWidevinePssh); + } + if (jsonContainsValue(jsonBody, "PD94")) { + const { security_level, host, secret, device_name } = + playreadyDeviceInfo; + remoteCDM = new remotePlayReadyCDM( + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(foundPlayreadyPssh); + } + } + if (remoteCDM && remoteCDM.challenge === null) { + remoteCDM.getChallenge(foundWidevinePssh); + } + const injectedBody = jsonReplaceValue(jsonBody, remoteCDM.challenge); + return originalSend.call(this, JSON.stringify(injectedBody)); + } + } + } + } + return originalSend.apply(this, arguments); + }; +})(); diff --git a/mv2/manifest.json b/mv2/manifest.json new file mode 100644 index 0000000..fb34e80 --- /dev/null +++ b/mv2/manifest.json @@ -0,0 +1,41 @@ +{ + "manifest_version": 2, + "name": "CDRM Extension", + "version": "2.1.0", + "description": "Decrypt DRM protected content", + "permissions": [ + "webRequest", + "webRequestBlocking", + "", + "activeTab", + "storage", + "tabs", + "contextMenus" + ], + "background": { + "scripts": ["background.js"], + "persistent": true + }, + "content_scripts": [ + { + "matches": [""], + "js": ["content.js"], + "run_at": "document_start", + "all_frames": true + } + ], + "web_accessible_resources": ["inject.js"], + "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", + "browser_action": { + "default_icon": { + "16": "icons/icon16.png", + "32": "icons/icon32.png", + "128": "icons/icon128.png" + } + }, + "icons": { + "16": "icons/icon16.png", + "32": "icons/icon32.png", + "128": "icons/icon128.png" + } +} diff --git a/src/background.js b/src/background.js index da9c10f..3c0fc5d 100644 --- a/src/background.js +++ b/src/background.js @@ -1,8 +1,7 @@ -// Open popout window when the extension icon is clicked -chrome.browserAction.onClicked.addListener(() => { +chrome.action.onClicked.addListener(() => { chrome.windows.create({ url: chrome.runtime.getURL("index.html"), - type: "popup", // opens as a floating window + type: "popup", width: 800, height: 600, }); diff --git a/src/manifest.json b/src/manifest.json index 00a5ab7..a2a5d0a 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,49 +1,37 @@ { - "manifest_version": 2, - "name": "CDRM Extension", - "version": "2.1.0", - "description": "Decrypt DRM protected content", - "permissions": [ - "webRequest", - "webRequestBlocking", - "", - "activeTab", - "storage", - "tabs", - "contextMenus" - ], - "background": { - "scripts": [ - "background.js" + "manifest_version": 3, + "name": "CDRM Extension", + "version": "2.1.0", + "description": "Decrypt DRM protected content", + "permissions": ["storage", "activeTab", "contextMenus"], + "host_permissions": [""], + "background": { + "service_worker": "background.js" + }, + "content_scripts": [ + { + "matches": [""], + "js": ["content.js"], + "run_at": "document_start", + "all_frames": true + } ], - "persistent": true - }, - "content_scripts": [ - { - "matches": [ - "" - ], - "js": [ - "content.js" - ], - "run_at": "document_start", - "all_frames": true + "web_accessible_resources": [ + { + "resources": ["inject.js"], + "matches": [""] + } + ], + "action": { + "default_icon": { + "16": "icons/icon16.png", + "32": "icons/icon32.png", + "128": "icons/icon128.png" + } + }, + "icons": { + "16": "icons/icon16.png", + "32": "icons/icon32.png", + "128": "icons/icon128.png" } - ], - "web_accessible_resources": [ - "inject.js" - ], - "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", - "browser_action": { - "default_icon": { - "16": "icons/icon16.png", - "32": "icons/icon32.png", - "128": "icons/icon128.png" - } - }, - "icons": { - "16": "icons/icon16.png", - "32": "icons/icon32.png", - "128": "icons/icon128.png" - } -} \ No newline at end of file +} -- 2.43.0 From 5cc68345328fa99452380215f707884cd20f9746 Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Sun, 20 Jul 2025 16:56:34 +0700 Subject: [PATCH 07/26] add export to json button --- frontend/src/components/results.jsx | 78 +++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 14 deletions(-) diff --git a/frontend/src/components/results.jsx b/frontend/src/components/results.jsx index 8339113..fe9ffe7 100644 --- a/frontend/src/components/results.jsx +++ b/frontend/src/components/results.jsx @@ -18,10 +18,10 @@ function Results() { "manifestURL", ], (result) => { - if (result.drmType) setDrmType(result.drmType); - if (result.latestPSSH) setPssh(result.latestPSSH); - if (result.licenseURL) setLicenseUrl(result.licenseURL); - if (result.manifestURL) setManifestUrl(result.manifestURL); + if (result.drmType) setDrmType(result.drmType || ""); + if (result.latestPSSH) setPssh(result.latestPSSH || ""); + if (result.licenseURL) setLicenseUrl(result.licenseURL || ""); + if (result.manifestURL) setManifestUrl(result.manifestURL || ""); if (result.latestKeys) { try { const parsed = Array.isArray(result.latestKeys) @@ -39,19 +39,19 @@ function Results() { const handleChange = (changes, area) => { if (area === "local") { if (changes.drmType) { - setDrmType(changes.drmType.newValue); + setDrmType(changes.drmType.newValue || ""); } if (changes.latestPSSH) { - setPssh(changes.latestPSSH.newValue); + setPssh(changes.latestPSSH.newValue || ""); } if (changes.licenseURL) { - setLicenseUrl(changes.licenseURL.newValue); + setLicenseUrl(changes.licenseURL.newValue || ""); } if (changes.manifestURL) { - setManifestUrl(changes.manifestURL.newValue); + setManifestUrl(changes.manifestURL.newValue || ""); } if (changes.latestKeys) { - setKeys(changes.latestKeys.newValue); + setKeys(changes.latestKeys.newValue || []); } } }; @@ -63,10 +63,10 @@ function Results() { const handleCapture = () => { // Reset stored values chrome.storage.local.set({ - drmType: "None", - latestPSSH: "None", - licenseURL: "None", - manifestURL: "None", + drmType: "", + latestPSSH: "", + licenseURL: "", + manifestURL: "", latestKeys: [], }); @@ -102,11 +102,52 @@ function Results() { }); }; + // Export to JSON file + + const hasData = () => { + return ( + drmType || + pssh || + licenseUrl || + manifestUrl || + (Array.isArray(keys) && keys.filter((k) => k.type !== "SIGNING").length > 0) + ); + }; + + const handleExportJSON = () => { + const exportData = { + drmType: drmType || null, + manifestUrl: manifestUrl || null, + pssh: pssh || null, + licenseUrl: licenseUrl || null, + keys: + Array.isArray(keys) && keys.length > 0 + ? keys + .filter((k) => k.type !== "SIGNING") + .map((k) => `${k.key_id || k.keyId}:${k.key}`) + : null, + exportedAt: new Date().toISOString(), + }; + + const blob = new Blob([JSON.stringify(exportData, null, 2)], { + type: "application/json", + }); + + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = `drm-data-${new Date().toISOString().slice(0, 19).replace(/:/g, "-")}.json`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + }; + return (
@@ -158,6 +199,15 @@ function Results() { [Not available] )}
+ + {hasData() && ( + + )} ); } -- 2.43.0 From 918269f42e28a56583fb4646e95281622a8f63d7 Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Sun, 20 Jul 2025 17:16:40 +0700 Subject: [PATCH 08/26] update setting UI, showing current instance in a separate line --- frontend/src/components/settings.jsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/frontend/src/components/settings.jsx b/frontend/src/components/settings.jsx index 7e7ba9a..ba5cd9c 100644 --- a/frontend/src/components/settings.jsx +++ b/frontend/src/components/settings.jsx @@ -104,25 +104,28 @@ function Settings({ onConfigSaved }) { return (
+ {storedUrl && ( +

+ Current instance: {storedUrl} +

+ )} + +

New instance URL:

setInstanceUrl(e.target.value)} - placeholder={ - storedUrl - ? `Current CDRM Instance: ${storedUrl}` - : "CDRM Instance URL (e.g., https://cdrm-project.com/, http://127.0.0.1:5000/)" - } - className="w-full p-4 text-lg bg-gray-800 text-white border border-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 mt-4" + placeholder="https://cdrm-project.com/, http://127.0.0.1:5000/" + className="w-full p-4 text-lg bg-gray-800 text-white border border-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 mt-4 font-mono" /> {message && ( -- 2.43.0 From 889a4c63f310e599ec2dbb20da1b0a2a55778c17 Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Sun, 20 Jul 2025 17:21:02 +0700 Subject: [PATCH 09/26] sync version: only update the "version" instead of parsing whole file --- syncVersion.js | 52 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/syncVersion.js b/syncVersion.js index 7acba5d..6faa01a 100644 --- a/syncVersion.js +++ b/syncVersion.js @@ -5,6 +5,32 @@ import { fileURLToPath } from "url"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); +const updateVersionWithRegex = async (filePath, newVersion) => { + try { + const content = await fs.readFile(filePath, "utf-8"); + + // Regex to match "version": "any.version.number" + const versionRegex = /("version"\s*:\s*")([^"]+)(")/; + + if (!versionRegex.test(content)) { + console.warn(`⚠️ No version field found in ${filePath}`); + return false; + } + + const updatedContent = content.replace(versionRegex, `$1${newVersion}$3`); + + if (content !== updatedContent) { + await fs.writeFile(filePath, updatedContent); + return true; + } + + return false; + } catch (err) { + console.error(`❌ Failed to update ${filePath}: ${err.message}`); + return false; + } +}; + const syncVersion = async () => { const rootPkgPath = path.join(__dirname, "package.json"); const frontendPkgPath = path.join(__dirname, "frontend", "package.json"); @@ -20,26 +46,20 @@ const syncVersion = async () => { return; } - // Update frontend/package.json if exists - try { - const frontendPkgRaw = await fs.readFile(frontendPkgPath, "utf-8"); - const frontendPkg = JSON.parse(frontendPkgRaw); - frontendPkg.version = version; - await fs.writeFile(frontendPkgPath, JSON.stringify(frontendPkg, null, 2)); + // Update frontend/package.json using regex + const frontendUpdated = await updateVersionWithRegex(frontendPkgPath, version); + if (frontendUpdated) { console.log(`🔄 Updated frontend/package.json version to ${version}`); - } catch { - console.log("ℹ️ frontend/package.json not found or unreadable, skipping version update."); + } else { + console.log("ℹ️ frontend/package.json not found or no changes needed."); } - // Update src/manifest.json version - try { - const manifestRaw = await fs.readFile(manifestPath, "utf-8"); - const manifest = JSON.parse(manifestRaw); - manifest.version = version; - await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2)); + // Update src/manifest.json using regex + const manifestUpdated = await updateVersionWithRegex(manifestPath, version); + if (manifestUpdated) { console.log(`🔄 Updated src/manifest.json version to ${version}`); - } catch (err) { - console.error(`❌ Failed to update src/manifest.json version: ${err.message}`); + } else { + console.log("ℹ️ src/manifest.json not found or no changes needed."); } }; -- 2.43.0 From 6e2283704704b4f9d6518220ecf4341ff14956c9 Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Sun, 20 Jul 2025 17:43:13 +0700 Subject: [PATCH 10/26] Fix Trusted Types violation in manifest interceptor injection, which fixes data not showing for YouTube DRM --- mv2/inject.js | 181 ++++++++++++++++++++++++++++---------------------- src/inject.js | 181 ++++++++++++++++++++++++++++---------------------- 2 files changed, 202 insertions(+), 160 deletions(-) diff --git a/mv2/inject.js b/mv2/inject.js index fdcb029..e549c96 100644 --- a/mv2/inject.js +++ b/mv2/inject.js @@ -70,108 +70,129 @@ function safeHeaderShellEscape(str) { // Intercep network to find manifest function injectManifestInterceptor() { - const script = document.createElement("script"); - script.textContent = ` - (function() { - function isProbablyManifest(text = "", contentType = "") { - const lowerCT = contentType?.toLowerCase() ?? ""; - const sample = text.slice(0, 2000); + // Execute the interceptor code directly instead of injecting a script + (function () { + function isProbablyManifest(text = "", contentType = "") { + const lowerCT = contentType?.toLowerCase() ?? ""; + const sample = text.slice(0, 2000); - const isHLSMime = lowerCT.includes("mpegurl"); - const isDASHMime = lowerCT.includes("dash+xml"); - const isSmoothMime = lowerCT.includes("sstr+xml"); + const isHLSMime = lowerCT.includes("mpegurl"); + const isDASHMime = lowerCT.includes("dash+xml"); + const isSmoothMime = lowerCT.includes("sstr+xml"); - const isHLSKeyword = sample.includes("#EXTM3U") || sample.includes("#EXT-X-STREAM-INF"); - const isDASHKeyword = sample.includes(" { - headersObj[key] = value; - }); + const headersObj = {}; + clone.headers.forEach((value, key) => { + headersObj[key] = value; + }); - const headerFlags = Object.entries(headersObj) - .map(([key, val]) => '--add-headers "' + safeHeaderShellEscape(key) + ': ' + safeHeaderShellEscape(val) + '"') - .join(" "); + const headerFlags = Object.entries(headersObj) + .map( + ([key, val]) => + '--add-headers "' + + safeHeaderShellEscape(key) + + ": " + + safeHeaderShellEscape(val) + + '"' + ) + .join(" "); - window.postMessage({ + window.postMessage( + { type: "__MANIFEST_HEADERS__", url, - headers: headerFlags - }, "*"); - } - } catch (e) {} + headers: headerFlags, + }, + "*" + ); + } + } catch (e) {} - return response; - }; + return response; + }; - const originalXHROpen = XMLHttpRequest.prototype.open; - const originalXHRSend = XMLHttpRequest.prototype.send; + const originalXHROpen = XMLHttpRequest.prototype.open; + const originalXHRSend = XMLHttpRequest.prototype.send; - XMLHttpRequest.prototype.open = function(method, url) { - this.__url = url; - return originalXHROpen.apply(this, arguments); - }; + XMLHttpRequest.prototype.open = function (method, url) { + this.__url = url; + return originalXHROpen.apply(this, arguments); + }; - XMLHttpRequest.prototype.send = function(body) { - this.addEventListener("load", function () { - try { - const contentType = this.getResponseHeader("content-type") || ""; - const text = this.responseText; + XMLHttpRequest.prototype.send = function (body) { + this.addEventListener("load", function () { + try { + const contentType = this.getResponseHeader("content-type") || ""; + const text = this.responseText; - if (isProbablyManifest(text, contentType)) { - window.postMessage({ type: "__MANIFEST_URL__", data: this.__url }, "*"); - console.log("[Manifest][xhr]", this.__url, contentType); + if (isProbablyManifest(text, contentType)) { + window.postMessage({ type: "__MANIFEST_URL__", data: this.__url }, "*"); + console.log("[Manifest][xhr]", this.__url, contentType); - const xhrHeaders = {}; - const rawHeaders = this.getAllResponseHeaders().trim().split(/\\r?\\n/); - rawHeaders.forEach(line => { - const parts = line.split(": "); - if (parts.length === 2) { - xhrHeaders[parts[0]] = parts[1]; - } - }); + const xhrHeaders = {}; + const rawHeaders = this.getAllResponseHeaders().trim().split(/\r?\n/); + rawHeaders.forEach((line) => { + const parts = line.split(": "); + if (parts.length === 2) { + xhrHeaders[parts[0]] = parts[1]; + } + }); - const headerFlags = Object.entries(xhrHeaders) - .map(([key, val]) => '--add-headers "' + safeHeaderShellEscape(key) + ': ' + safeHeaderShellEscape(val) + '"') - .join(" "); + const headerFlags = Object.entries(xhrHeaders) + .map( + ([key, val]) => + '--add-headers "' + + safeHeaderShellEscape(key) + + ": " + + safeHeaderShellEscape(val) + + '"' + ) + .join(" "); - window.postMessage({ + window.postMessage( + { type: "__MANIFEST_HEADERS__", url: this.__url, - headers: headerFlags - }, "*"); - } - } catch (e) {} - }); - return originalXHRSend.apply(this, arguments); - }; - })(); - `; - document.documentElement.appendChild(script); - script.remove(); + headers: headerFlags, + }, + "*" + ); + } + } catch (e) {} + }); + return originalXHRSend.apply(this, arguments); + }; + })(); } injectManifestInterceptor(); diff --git a/src/inject.js b/src/inject.js index fdcb029..e549c96 100644 --- a/src/inject.js +++ b/src/inject.js @@ -70,108 +70,129 @@ function safeHeaderShellEscape(str) { // Intercep network to find manifest function injectManifestInterceptor() { - const script = document.createElement("script"); - script.textContent = ` - (function() { - function isProbablyManifest(text = "", contentType = "") { - const lowerCT = contentType?.toLowerCase() ?? ""; - const sample = text.slice(0, 2000); + // Execute the interceptor code directly instead of injecting a script + (function () { + function isProbablyManifest(text = "", contentType = "") { + const lowerCT = contentType?.toLowerCase() ?? ""; + const sample = text.slice(0, 2000); - const isHLSMime = lowerCT.includes("mpegurl"); - const isDASHMime = lowerCT.includes("dash+xml"); - const isSmoothMime = lowerCT.includes("sstr+xml"); + const isHLSMime = lowerCT.includes("mpegurl"); + const isDASHMime = lowerCT.includes("dash+xml"); + const isSmoothMime = lowerCT.includes("sstr+xml"); - const isHLSKeyword = sample.includes("#EXTM3U") || sample.includes("#EXT-X-STREAM-INF"); - const isDASHKeyword = sample.includes(" { - headersObj[key] = value; - }); + const headersObj = {}; + clone.headers.forEach((value, key) => { + headersObj[key] = value; + }); - const headerFlags = Object.entries(headersObj) - .map(([key, val]) => '--add-headers "' + safeHeaderShellEscape(key) + ': ' + safeHeaderShellEscape(val) + '"') - .join(" "); + const headerFlags = Object.entries(headersObj) + .map( + ([key, val]) => + '--add-headers "' + + safeHeaderShellEscape(key) + + ": " + + safeHeaderShellEscape(val) + + '"' + ) + .join(" "); - window.postMessage({ + window.postMessage( + { type: "__MANIFEST_HEADERS__", url, - headers: headerFlags - }, "*"); - } - } catch (e) {} + headers: headerFlags, + }, + "*" + ); + } + } catch (e) {} - return response; - }; + return response; + }; - const originalXHROpen = XMLHttpRequest.prototype.open; - const originalXHRSend = XMLHttpRequest.prototype.send; + const originalXHROpen = XMLHttpRequest.prototype.open; + const originalXHRSend = XMLHttpRequest.prototype.send; - XMLHttpRequest.prototype.open = function(method, url) { - this.__url = url; - return originalXHROpen.apply(this, arguments); - }; + XMLHttpRequest.prototype.open = function (method, url) { + this.__url = url; + return originalXHROpen.apply(this, arguments); + }; - XMLHttpRequest.prototype.send = function(body) { - this.addEventListener("load", function () { - try { - const contentType = this.getResponseHeader("content-type") || ""; - const text = this.responseText; + XMLHttpRequest.prototype.send = function (body) { + this.addEventListener("load", function () { + try { + const contentType = this.getResponseHeader("content-type") || ""; + const text = this.responseText; - if (isProbablyManifest(text, contentType)) { - window.postMessage({ type: "__MANIFEST_URL__", data: this.__url }, "*"); - console.log("[Manifest][xhr]", this.__url, contentType); + if (isProbablyManifest(text, contentType)) { + window.postMessage({ type: "__MANIFEST_URL__", data: this.__url }, "*"); + console.log("[Manifest][xhr]", this.__url, contentType); - const xhrHeaders = {}; - const rawHeaders = this.getAllResponseHeaders().trim().split(/\\r?\\n/); - rawHeaders.forEach(line => { - const parts = line.split(": "); - if (parts.length === 2) { - xhrHeaders[parts[0]] = parts[1]; - } - }); + const xhrHeaders = {}; + const rawHeaders = this.getAllResponseHeaders().trim().split(/\r?\n/); + rawHeaders.forEach((line) => { + const parts = line.split(": "); + if (parts.length === 2) { + xhrHeaders[parts[0]] = parts[1]; + } + }); - const headerFlags = Object.entries(xhrHeaders) - .map(([key, val]) => '--add-headers "' + safeHeaderShellEscape(key) + ': ' + safeHeaderShellEscape(val) + '"') - .join(" "); + const headerFlags = Object.entries(xhrHeaders) + .map( + ([key, val]) => + '--add-headers "' + + safeHeaderShellEscape(key) + + ": " + + safeHeaderShellEscape(val) + + '"' + ) + .join(" "); - window.postMessage({ + window.postMessage( + { type: "__MANIFEST_HEADERS__", url: this.__url, - headers: headerFlags - }, "*"); - } - } catch (e) {} - }); - return originalXHRSend.apply(this, arguments); - }; - })(); - `; - document.documentElement.appendChild(script); - script.remove(); + headers: headerFlags, + }, + "*" + ); + } + } catch (e) {} + }); + return originalXHRSend.apply(this, arguments); + }; + })(); } injectManifestInterceptor(); -- 2.43.0 From 3fae8f296f9ae4dd499e90cab807152e9437094f Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Sun, 20 Jul 2025 20:10:47 +0700 Subject: [PATCH 11/26] show "use yt-dlp" in manifest for youtube, use plugin-react-swc --- frontend/package-lock.json | 245 +++++++++++++++++++++++++++- frontend/package.json | 1 + frontend/src/components/results.jsx | 49 +++++- frontend/vite.config.js | 2 +- 4 files changed, 290 insertions(+), 7 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c86a3ab..e1d1c23 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "frontend", - "version": "0.0.0", + "version": "2.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "frontend", - "version": "0.0.0", + "version": "2.1.0", "dependencies": { "@tailwindcss/vite": "^4.1.11", "react": "^19.1.0", @@ -19,6 +19,7 @@ "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", "@vitejs/plugin-react": "^4.7.0", + "@vitejs/plugin-react-swc": "^3.11.0", "eslint": "^9.31.0", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", @@ -1255,6 +1256,232 @@ "win32" ] }, + "node_modules/@swc/core": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.1.tgz", + "integrity": "sha512-jEKKErLC6uwSqA+p6bmZR08usZM5Fpc+HdEu5CAzvye0q43yf1si1kjhHEa9XMkz0A2SAaal3eKCg/YYmtOsCA==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.23" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.13.1", + "@swc/core-darwin-x64": "1.13.1", + "@swc/core-linux-arm-gnueabihf": "1.13.1", + "@swc/core-linux-arm64-gnu": "1.13.1", + "@swc/core-linux-arm64-musl": "1.13.1", + "@swc/core-linux-x64-gnu": "1.13.1", + "@swc/core-linux-x64-musl": "1.13.1", + "@swc/core-win32-arm64-msvc": "1.13.1", + "@swc/core-win32-ia32-msvc": "1.13.1", + "@swc/core-win32-x64-msvc": "1.13.1" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.17" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.1.tgz", + "integrity": "sha512-zO6SW/jSMTUORPm6dUZFPUwf+EFWZsaXWMGXadRG6akCofYpoQb8pcY2QZkVr43z8TMka6BtXpyoD/DJ0iOPHQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.1.tgz", + "integrity": "sha512-8RjaTZYxrlYKE5PgzZYWSOT4mAsyhIuh30Nu4dnn/2r0Ef68iNCbvX4ynGnFMhOIhqunjQbJf+mJKpwTwdHXhw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.1.tgz", + "integrity": "sha512-jEqK6pECs2m4BpL2JA/4CCkq04p6iFOEtVNXTisO+lJ3zwmxlnIEm9UfJZG6VSu8GS9MHRKGB0ieZ1tEdN1qDA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.1.tgz", + "integrity": "sha512-PbkuIOYXO/gQbWQ7NnYIwm59ygNqmUcF8LBeoKvxhx1VtOwE+9KiTfoplOikkPLhMiTzKsd8qentTslbITIg+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.1.tgz", + "integrity": "sha512-JaqFdBCarIBKiMu5bbAp+kWPMNGg97ej+7KzbKOzWP5pRptqKi86kCDZT3WmjPe8hNG6dvBwbm7Y8JNry5LebQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.1.tgz", + "integrity": "sha512-t4cLkku10YECDaakWUH0452WJHIZtrLPRwezt6BdoMntVMwNjvXRX7C8bGuYcKC3YxRW7enZKFpozLhQIQ37oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.1.tgz", + "integrity": "sha512-fSMwZOaG+3ukUucbEbzz9GhzGhUhXoCPqHe9qW0/Vc2IZRp538xalygKyZynYweH5d9EHux1aj3+IO8/xBaoiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.1.tgz", + "integrity": "sha512-tweCXK/79vAwj1NhAsYgICy8T1z2QEairmN2BFEBYFBFNMEB1iI1YlXwBkBtuihRvgZrTh1ORusKa4jLYzLCZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.1.tgz", + "integrity": "sha512-zi7hO9D+2R2yQN9D7T10/CAI9KhuXkNkz8tcJOW6+dVPtAk/gsIC5NoGPELjgrAlLL9CS38ZQpLDslLfpP15ng==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.1.tgz", + "integrity": "sha512-KubYjzqs/nz3H69ncX/XHKsC8c1xqc7UvonQAj26BhbL22HBsqdAaVutZ+Obho6RMpd3F5qQ95ldavUTWskRrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@swc/types": { + "version": "0.1.23", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.23.tgz", + "integrity": "sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, "node_modules/@tailwindcss/node": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", @@ -1616,6 +1843,20 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, + "node_modules/@vitejs/plugin-react-swc": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.11.0.tgz", + "integrity": "sha512-YTJCGFdNMHCMfjODYtxRNVAYmTWQ1Lb8PulP/2/f/oEEtglw8oKxKIZmmRkyXrVrHfsKOaVkAc3NT9/dMutO5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.27", + "@swc/core": "^1.12.11" + }, + "peerDependencies": { + "vite": "^4 || ^5 || ^6 || ^7" + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index bae1047..3e0425e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,6 +21,7 @@ "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", "@vitejs/plugin-react": "^4.7.0", + "@vitejs/plugin-react-swc": "^3.11.0", "eslint": "^9.31.0", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", diff --git a/frontend/src/components/results.jsx b/frontend/src/components/results.jsx index fe9ffe7..7155419 100644 --- a/frontend/src/components/results.jsx +++ b/frontend/src/components/results.jsx @@ -6,6 +6,7 @@ function Results() { const [licenseUrl, setLicenseUrl] = useState(""); const [keys, setKeys] = useState([]); const [manifestUrl, setManifestUrl] = useState(""); + const [currentTabUrl, setCurrentTabUrl] = useState(""); useEffect(() => { chrome.storage.local.get( @@ -36,6 +37,21 @@ function Results() { } ); + // Get current tab URL when component mounts + chrome.windows.getAll({ populate: true, windowTypes: ["normal"] }, (windows) => { + if (windows && windows.length > 0) { + const lastFocusedWindow = windows.find((w) => w.focused) || windows[0]; + if (lastFocusedWindow) { + const activeTab = lastFocusedWindow.tabs.find( + (tab) => tab.active && tab.url && /^https?:\/\//.test(tab.url) + ); + if (activeTab?.url) { + setCurrentTabUrl(activeTab.url); + } + } + } + }); + const handleChange = (changes, area) => { if (area === "local") { if (changes.drmType) { @@ -102,8 +118,31 @@ function Results() { }); }; - // Export to JSON file + // Check if current tab is YouTube + const isYouTube = () => { + return currentTabUrl.includes("youtube.com") || currentTabUrl.includes("youtu.be"); + }; + // Get manifest URL display value + const getManifestDisplayValue = () => { + if (manifestUrl) { + return manifestUrl; + } + if (isYouTube()) { + return "[Use yt-dlp to download video]"; + } + return ""; + }; + + // Get manifest URL placeholder + const getManifestPlaceholder = () => { + if (isYouTube() && !manifestUrl) { + return "[Use yt-dlp to download video]"; + } + return "[Not available]"; + }; + + // Export to JSON file const hasData = () => { return ( drmType || @@ -164,9 +203,11 @@ function Results() {

Manifest URL

diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 01889a6..971ff39 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -1,5 +1,5 @@ import tailwindcss from "@tailwindcss/vite"; -import react from "@vitejs/plugin-react"; +import react from "@vitejs/plugin-react-swc"; import { readFileSync } from "fs"; import { defineConfig } from "vite"; -- 2.43.0 From 9cff5b44bd295d64b51d5a597fa3ce1f34efd387 Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Mon, 21 Jul 2025 11:39:35 +0700 Subject: [PATCH 12/26] forgot to remove _FOUND in MANIFEST_URL --- src/background.js | 2 +- src/content.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/background.js b/src/background.js index 3c0fc5d..bbd7fd6 100644 --- a/src/background.js +++ b/src/background.js @@ -32,7 +32,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { chrome.storage.local.set({ licenseURL: data }); break; - case "MANIFEST_URL_FOUND": + case "MANIFEST_URL": console.log("Storing Manifest URL:", data); chrome.storage.local.set({ manifestURL: data }); break; diff --git a/src/content.js b/src/content.js index 55b7891..0e12c93 100644 --- a/src/content.js +++ b/src/content.js @@ -78,7 +78,7 @@ window.addEventListener("message", function (event) { console.log("✅ [Content] Unique manifest URL:", url); chrome.runtime.sendMessage({ - type: "MANIFEST_URL_FOUND", + type: "MANIFEST_URL", data: url, }); } -- 2.43.0 From 3bd2e0f4655dd29b4ceeaa6e4bfe3777811c1a1d Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Mon, 21 Jul 2025 11:41:49 +0700 Subject: [PATCH 13/26] refactor: extract redundant code into helpers (part 1) --- src/inject.js | 275 ++++++++++++++++++++------------------------------ 1 file changed, 111 insertions(+), 164 deletions(-) diff --git a/src/inject.js b/src/inject.js index e549c96..915083f 100644 --- a/src/inject.js +++ b/src/inject.js @@ -68,6 +68,35 @@ function safeHeaderShellEscape(str) { .replace(/\n/g, ""); // strip newlines } +function headersToFlags(headersObj) { + return Object.entries(headersObj) + .map( + ([key, val]) => + '--add-headers "' + + safeHeaderShellEscape(key) + + ": " + + safeHeaderShellEscape(val) + + '"' + ) + .join(" "); +} + +function handleManifestDetection(url, headersObj, contentType, source) { + window.postMessage({ type: "__MANIFEST_URL__", data: url }, "*"); + console.log(`[Manifest][${source}]`, url, contentType); + + const headerFlags = headersToFlags(headersObj); + + window.postMessage( + { + type: "__MANIFEST_HEADERS__", + url, + headers: headerFlags, + }, + "*" + ); +} + // Intercep network to find manifest function injectManifestInterceptor() { // Execute the interceptor code directly instead of injecting a script @@ -108,33 +137,11 @@ function injectManifestInterceptor() { const url = typeof input === "string" ? input : input.url; if (isProbablyManifest(text, contentType)) { - window.postMessage({ type: "__MANIFEST_URL__", data: url }, "*"); - console.log("[Manifest][fetch]", url, contentType); - const headersObj = {}; clone.headers.forEach((value, key) => { headersObj[key] = value; }); - - const headerFlags = Object.entries(headersObj) - .map( - ([key, val]) => - '--add-headers "' + - safeHeaderShellEscape(key) + - ": " + - safeHeaderShellEscape(val) + - '"' - ) - .join(" "); - - window.postMessage( - { - type: "__MANIFEST_HEADERS__", - url, - headers: headerFlags, - }, - "*" - ); + handleManifestDetection(url, headersObj, contentType, "fetch"); } } catch (e) {} @@ -156,9 +163,6 @@ function injectManifestInterceptor() { const text = this.responseText; if (isProbablyManifest(text, contentType)) { - window.postMessage({ type: "__MANIFEST_URL__", data: this.__url }, "*"); - console.log("[Manifest][xhr]", this.__url, contentType); - const xhrHeaders = {}; const rawHeaders = this.getAllResponseHeaders().trim().split(/\r?\n/); rawHeaders.forEach((line) => { @@ -167,26 +171,7 @@ function injectManifestInterceptor() { xhrHeaders[parts[0]] = parts[1]; } }); - - const headerFlags = Object.entries(xhrHeaders) - .map( - ([key, val]) => - '--add-headers "' + - safeHeaderShellEscape(key) + - ": " + - safeHeaderShellEscape(val) + - '"' - ) - .join(" "); - - window.postMessage( - { - type: "__MANIFEST_HEADERS__", - url: this.__url, - headers: headerFlags, - }, - "*" - ); + handleManifestDetection(this.__url, xhrHeaders, contentType, "xhr"); } } catch (e) {} }); @@ -197,21 +182,19 @@ function injectManifestInterceptor() { injectManifestInterceptor(); -// PlayReady Remote CDM Class -class remotePlayReadyCDM { - constructor(security_level, host, secret, device_name) { - this.security_level = security_level; +class RemoteCDMBase { + constructor({ host, secret, device_name, security_level }) { this.host = host; this.secret = secret; this.device_name = device_name; + this.security_level = security_level; this.session_id = null; this.challenge = null; this.keys = null; } - // Open PlayReady session - openSession() { - const url = `${this.host}/remotecdm/playready/${this.device_name}/open`; + openSession(path) { + const url = `${this.host}${path}/open`; const xhr = new XMLHttpRequest(); xhr.open("GET", url, false); xhr.setRequestHeader("Content-Type", "application/json"); @@ -219,126 +202,127 @@ class remotePlayReadyCDM { const jsonData = JSON.parse(xhr.responseText); if (jsonData.data?.session_id) { this.session_id = jsonData.data.session_id; - console.log("PlayReady session opened:", this.session_id); + console.log("Session opened:", this.session_id); } else { - console.error("Failed to open PlayReady session:", jsonData.message); - throw new Error("Failed to open PlayReady session"); + console.error("Failed to open session:", jsonData.message); + throw new Error("Failed to open session"); } } - // Get PlayReady challenge - getChallenge(init_data) { - const url = `${this.host}/remotecdm/playready/${this.device_name}/get_license_challenge`; + getChallenge(path, body) { + const url = `${this.host}${path}/get_license_challenge`; const xhr = new XMLHttpRequest(); xhr.open("POST", url, false); xhr.setRequestHeader("Content-Type", "application/json"); - const body = { - session_id: this.session_id, - init_data: init_data, - }; xhr.send(JSON.stringify(body)); const jsonData = JSON.parse(xhr.responseText); if (jsonData.data?.challenge) { this.challenge = btoa(jsonData.data.challenge); - console.log("PlayReady challenge received:", this.challenge); + console.log("Challenge received:", this.challenge); + } else if (jsonData.data?.challenge_b64) { + this.challenge = jsonData.data.challenge_b64; + console.log("Challenge received:", this.challenge); } else { - console.error("Failed to get PlayReady challenge:", jsonData.message); - throw new Error("Failed to get PlayReady challenge"); + console.error("Failed to get challenge:", jsonData.message); + throw new Error("Failed to get challenge"); } } - // Parse PlayReady license response - parseLicense(license_message) { - const url = `${this.host}/remotecdm/playready/${this.device_name}/parse_license`; + parseLicense(path, body) { + const url = `${this.host}${path}/parse_license`; const xhr = new XMLHttpRequest(); xhr.open("POST", url, false); xhr.setRequestHeader("Content-Type", "application/json"); - const body = { - session_id: this.session_id, - license_message: license_message, - }; xhr.send(JSON.stringify(body)); const jsonData = JSON.parse(xhr.responseText); - if ( - jsonData.message === "Successfully parsed and loaded the Keys from the License message" - ) { - console.log("PlayReady license response parsed successfully"); + if (jsonData.status === 200 || jsonData.message?.includes("parsed and loaded")) { + console.log("License response parsed successfully"); return true; } else { - console.error("Failed to parse PlayReady license response:", jsonData.message); - throw new Error("Failed to parse PlayReady license response"); + console.error("Failed to parse license response:", jsonData.message); + throw new Error("Failed to parse license response"); } } - // Get PlayReady keys - getKeys() { - const url = `${this.host}/remotecdm/playready/${this.device_name}/get_keys`; + getKeys(path, body, extraPath = "") { + const url = `${this.host}${path}/get_keys${extraPath}`; const xhr = new XMLHttpRequest(); xhr.open("POST", url, false); xhr.setRequestHeader("Content-Type", "application/json"); - const body = { - session_id: this.session_id, - }; xhr.send(JSON.stringify(body)); const jsonData = JSON.parse(xhr.responseText); if (jsonData.data?.keys) { this.keys = jsonData.data.keys; - console.log("PlayReady keys received:", this.keys); + console.log("Keys received:", this.keys); } else { - console.error("Failed to get PlayReady keys:", jsonData.message); - throw new Error("Failed to get PlayReady keys"); + console.error("Failed to get keys:", jsonData.message); + throw new Error("Failed to get keys"); } } - // Close PlayReady session - closeSession() { - const url = `${this.host}/remotecdm/playready/${this.device_name}/close/${this.session_id}`; + closeSession(path) { + const url = `${this.host}${path}/close/${this.session_id}`; const xhr = new XMLHttpRequest(); xhr.open("GET", url, false); xhr.setRequestHeader("Content-Type", "application/json"); xhr.send(); const jsonData = JSON.parse(xhr.responseText); if (jsonData) { - console.log("PlayReady session closed successfully"); + console.log("Session closed successfully"); } else { - console.error("Failed to close PlayReady session:", jsonData.message); - throw new Error("Failed to close PlayReady session"); + console.error("Failed to close session:", jsonData.message); + throw new Error("Failed to close session"); } } } +// PlayReady Remote CDM Class +class remotePlayReadyCDM extends RemoteCDMBase { + constructor(security_level, host, secret, device_name) { + super({ host, secret, device_name, security_level }); + } + + openSession() { + super.openSession(`/remotecdm/playready/${this.device_name}`); + } + + getChallenge(init_data) { + super.getChallenge(`/remotecdm/playready/${this.device_name}`, { + session_id: this.session_id, + init_data: init_data, + }); + } + + parseLicense(license_message) { + return super.parseLicense(`/remotecdm/playready/${this.device_name}`, { + session_id: this.session_id, + license_message: license_message, + }); + } + + getKeys() { + super.getKeys(`/remotecdm/playready/${this.device_name}`, { + session_id: this.session_id, + }); + } + + closeSession() { + super.closeSession(`/remotecdm/playready/${this.device_name}`); + } +} + // Widevine Remote CDM Class -class remoteWidevineCDM { +class remoteWidevineCDM extends RemoteCDMBase { constructor(device_type, system_id, security_level, host, secret, device_name) { + super({ host, secret, device_name, security_level }); this.device_type = device_type; this.system_id = system_id; - this.security_level = security_level; - this.host = host; - this.secret = secret; - this.device_name = device_name; - this.session_id = null; - this.challenge = null; - this.keys = null; } - // Open Widevine session openSession() { - const url = `${this.host}/remotecdm/widevine/${this.device_name}/open`; - const xhr = new XMLHttpRequest(); - xhr.open("GET", url, false); - xhr.setRequestHeader("Content-Type", "application/json"); - xhr.send(); - const jsonData = JSON.parse(xhr.responseText); - if (jsonData.data?.session_id) { - this.session_id = jsonData.data.session_id; - console.log("Widevine session opened:", this.session_id); - } else { - console.error("Failed to open Widevine session:", jsonData.message); - throw new Error("Failed to open Widevine session"); - } + super.openSession(`/remotecdm/widevine/${this.device_name}`); } - // Set Widevine service certificate setServiceCertificate(certificate) { const url = `${this.host}/remotecdm/widevine/${this.device_name}/set_service_certificate`; const xhr = new XMLHttpRequest(); @@ -358,7 +342,6 @@ class remoteWidevineCDM { } } - // Get Widevine challenge getChallenge(init_data, license_type = "STREAMING") { const url = `${this.host}/remotecdm/widevine/${this.device_name}/get_license_challenge/${license_type}`; const xhr = new XMLHttpRequest(); @@ -380,61 +363,25 @@ class remoteWidevineCDM { } } - // Parse Widevine license response parseLicense(license_message) { - const url = `${this.host}/remotecdm/widevine/${this.device_name}/parse_license`; - const xhr = new XMLHttpRequest(); - xhr.open("POST", url, false); - xhr.setRequestHeader("Content-Type", "application/json"); - const body = { + return super.parseLicense(`/remotecdm/widevine/${this.device_name}`, { session_id: this.session_id, license_message: license_message, - }; - xhr.send(JSON.stringify(body)); - const jsonData = JSON.parse(xhr.responseText); - if (jsonData.status === 200) { - console.log("Widevine license response parsed successfully"); - return true; - } else { - console.error("Failed to parse Widevine license response:", jsonData.message); - throw new Error("Failed to parse Widevine license response"); - } + }); } - // Get Widevine keys getKeys() { - const url = `${this.host}/remotecdm/widevine/${this.device_name}/get_keys/ALL`; - const xhr = new XMLHttpRequest(); - xhr.open("POST", url, false); - xhr.setRequestHeader("Content-Type", "application/json"); - const body = { - session_id: this.session_id, - }; - xhr.send(JSON.stringify(body)); - const jsonData = JSON.parse(xhr.responseText); - if (jsonData.data?.keys) { - this.keys = jsonData.data.keys; - console.log("Widevine keys received:", this.keys); - } else { - console.error("Failed to get Widevine keys:", jsonData.message); - throw new Error("Failed to get Widevine keys"); - } + super.getKeys( + `/remotecdm/widevine/${this.device_name}`, + { + session_id: this.session_id, + }, + "/ALL" + ); } - // Close Widevine session closeSession() { - const url = `${this.host}/remotecdm/widevine/${this.device_name}/close/${this.session_id}`; - const xhr = new XMLHttpRequest(); - xhr.open("GET", url, false); - xhr.setRequestHeader("Content-Type", "application/json"); - xhr.send(); - const jsonData = JSON.parse(xhr.responseText); - if (jsonData) { - console.log("Widevine session closed successfully"); - } else { - console.error("Failed to close Widevine session:", jsonData.message); - throw new Error("Failed to close Widevine session"); - } + super.closeSession(`/remotecdm/widevine/${this.device_name}`); } } -- 2.43.0 From 30e797bc09d4d44dd53b20b5e6e1c8bbab17dede Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Mon, 21 Jul 2025 11:54:40 +0700 Subject: [PATCH 14/26] use global `DRM_SIGNATURES` for readability --- src/inject.js | 106 +++++++++++++++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 40 deletions(-) diff --git a/src/inject.js b/src/inject.js index 915083f..3b16d9d 100644 --- a/src/inject.js +++ b/src/inject.js @@ -17,6 +17,13 @@ let foundChallengeInBody = false; let licenseResponseCounter = 0; let keysRetrieved = false; +const DRM_SIGNATURES = { + WIDEVINE: "CAES", + PLAYREADY: "PD94", + SERVICE_CERT: "CAUS", + WIDEVINE_INIT: "CAQ=", +}; + // Post message to content.js to get DRM override window.postMessage({ type: "__GET_DRM_OVERRIDE__" }, "*"); @@ -398,7 +405,7 @@ function b64ToHexStr(b64) { return [...atob(b64)].map((c) => c.charCodeAt(0).toString(16).padStart(2, "0")).join``; } -function jsonContainsValue(obj, prefix = "CAES") { +function jsonContainsValue(obj, prefix = DRM_SIGNATURES.WIDEVINE) { if (typeof obj === "string") return obj.startsWith(prefix); if (Array.isArray(obj)) return obj.some((val) => jsonContainsValue(val, prefix)); if (typeof obj === "object" && obj !== null) { @@ -409,7 +416,9 @@ function jsonContainsValue(obj, prefix = "CAES") { function jsonReplaceValue(obj, newValue) { if (typeof obj === "string") { - return obj.startsWith("CAES") || obj.startsWith("PD94") ? newValue : obj; + return obj.startsWith(DRM_SIGNATURES.WIDEVINE) || obj.startsWith(DRM_SIGNATURES.PLAYREADY) + ? newValue + : obj; } if (Array.isArray(obj)) { @@ -516,7 +525,11 @@ MediaKeySession.prototype.generateRequest = function (initDataType, initData) { event.stopImmediatePropagation(); const uint8Array = new Uint8Array(event.message); const base64challenge = arrayBufferToBase64(uint8Array); - if (base64challenge === "CAQ=" && interceptType !== "DISABLED" && !serviceCertFound) { + if ( + base64challenge === DRM_SIGNATURES.WIDEVINE_INIT && + interceptType !== "DISABLED" && + !serviceCertFound + ) { const { device_type, system_id, security_level, host, secret, device_name } = widevineDeviceInfo; remoteCDM = new remoteWidevineCDM( @@ -529,14 +542,18 @@ MediaKeySession.prototype.generateRequest = function (initDataType, initData) { ); remoteCDM.openSession(); } - if (!injectionSuccess && base64challenge !== "CAQ=" && interceptType !== "DISABLED") { + if ( + !injectionSuccess && + base64challenge !== DRM_SIGNATURES.WIDEVINE_INIT && + interceptType !== "DISABLED" + ) { if (interceptType === "EME") { injectionSuccess = true; } if (!originalChallenge) { originalChallenge = base64challenge; } - if (originalChallenge.startsWith("CAES")) { + if (originalChallenge.startsWith(DRM_SIGNATURES.WIDEVINE)) { window.postMessage({ type: "__DRM_TYPE__", data: "Widevine" }, "*"); window.postMessage({ type: "__PSSH_DATA__", data: foundWidevinePssh }, "*"); if (interceptType === "EME" && !remoteCDM) { @@ -560,7 +577,7 @@ MediaKeySession.prototype.generateRequest = function (initDataType, initData) { remoteCDM.getChallenge(foundWidevinePssh); } } - if (!originalChallenge.startsWith("CAES")) { + if (!originalChallenge.startsWith(DRM_SIGNATURES.WIDEVINE)) { const buffer = event.message; const decoder = new TextDecoder("utf-16"); const decodedText = decoder.decode(buffer); @@ -616,7 +633,12 @@ const originalUpdate = MediaKeySession.prototype.update; MediaKeySession.prototype.update = function (response) { const uint8 = response instanceof Uint8Array ? response : new Uint8Array(response); const base64Response = window.btoa(String.fromCharCode(...uint8)); - if (base64Response.startsWith("CAUS") && foundWidevinePssh && remoteCDM && !serviceCertFound) { + if ( + base64Response.startsWith(DRM_SIGNATURES.SERVICE_CERT) && + foundWidevinePssh && + remoteCDM && + !serviceCertFound + ) { remoteCDM.setServiceCertificate(base64Response); if (interceptType === "EME" && !remoteCDM.challenge) { remoteCDM.getChallenge(foundWidevinePssh); @@ -626,7 +648,7 @@ MediaKeySession.prototype.update = function (response) { serviceCertFound = true; } if ( - !base64Response.startsWith("CAUS") && + !base64Response.startsWith(DRM_SIGNATURES.SERVICE_CERT) && (foundWidevinePssh || foundPlayreadyPssh) && !keysRetrieved ) { @@ -680,7 +702,8 @@ MediaKeySession.prototype.update = function (response) { const buffer = body instanceof Uint8Array ? body : new Uint8Array(body); const base64Body = window.btoa(String.fromCharCode(...buffer)); if ( - (base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) && + (base64Body.startsWith(DRM_SIGNATURES.WIDEVINE) || + base64Body.startsWith(DRM_SIGNATURES.PLAYREADY)) && (!remoteCDM || remoteCDM.challenge === null || base64Body !== remoteCDM.challenge) && @@ -692,14 +715,15 @@ MediaKeySession.prototype.update = function (response) { return; } if ( - (base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) && + (base64Body.startsWith(DRM_SIGNATURES.WIDEVINE) || + base64Body.startsWith(DRM_SIGNATURES.PLAYREADY)) && interceptType == "LICENSE" && !foundChallengeInBody ) { foundChallengeInBody = true; window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); if (!remoteCDM) { - if (base64Body.startsWith("CAES")) { + if (base64Body.startsWith(DRM_SIGNATURES.WIDEVINE)) { const { device_type, system_id, @@ -719,7 +743,7 @@ MediaKeySession.prototype.update = function (response) { remoteCDM.openSession(); remoteCDM.getChallenge(foundWidevinePssh); } - if (base64Body.startsWith("PD94")) { + if (base64Body.startsWith(DRM_SIGNATURES.PLAYREADY)) { const { security_level, host, secret, device_name } = playreadyDeviceInfo; remoteCDM = new remotePlayReadyCDM( @@ -743,8 +767,8 @@ MediaKeySession.prototype.update = function (response) { if (typeof body === "string" && !isJson(body)) { const base64EncodedBody = btoa(body); if ( - (base64EncodedBody.startsWith("CAES") || - base64EncodedBody.startsWith("PD94")) && + (base64EncodedBody.startsWith(DRM_SIGNATURES.WIDEVINE) || + base64EncodedBody.startsWith(DRM_SIGNATURES.PLAYREADY)) && (!remoteCDM || remoteCDM.challenge === null || base64EncodedBody !== remoteCDM.challenge) && @@ -756,15 +780,15 @@ MediaKeySession.prototype.update = function (response) { return; } if ( - (base64EncodedBody.startsWith("CAES") || - base64EncodedBody.startsWith("PD94")) && + (base64EncodedBody.startsWith(DRM_SIGNATURES.WIDEVINE) || + base64EncodedBody.startsWith(DRM_SIGNATURES.PLAYREADY)) && interceptType == "LICENSE" && !foundChallengeInBody ) { foundChallengeInBody = true; window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); if (!remoteCDM) { - if (base64EncodedBody.startsWith("CAES")) { + if (base64EncodedBody.startsWith(DRM_SIGNATURES.WIDEVINE)) { const { device_type, system_id, @@ -784,7 +808,7 @@ MediaKeySession.prototype.update = function (response) { remoteCDM.openSession(); remoteCDM.getChallenge(foundWidevinePssh); } - if (base64EncodedBody.startsWith("PD94")) { + if (base64EncodedBody.startsWith(DRM_SIGNATURES.PLAYREADY)) { const { security_level, host, secret, device_name } = playreadyDeviceInfo; remoteCDM = new remotePlayReadyCDM( @@ -809,8 +833,8 @@ MediaKeySession.prototype.update = function (response) { const jsonBody = JSON.parse(body); if ( - (jsonContainsValue(jsonBody, "CAES") || - jsonContainsValue(jsonBody, "PD94")) && + (jsonContainsValue(jsonBody, DRM_SIGNATURES.WIDEVINE) || + jsonContainsValue(jsonBody, DRM_SIGNATURES.PLAYREADY)) && (!remoteCDM || remoteCDM.challenge === null) && interceptType === "EME" ) { @@ -821,15 +845,15 @@ MediaKeySession.prototype.update = function (response) { } if ( - (jsonContainsValue(jsonBody, "CAES") || - jsonContainsValue(jsonBody, "PD94")) && + (jsonContainsValue(jsonBody, DRM_SIGNATURES.WIDEVINE) || + jsonContainsValue(jsonBody, DRM_SIGNATURES.PLAYREADY)) && interceptType === "LICENSE" && !foundChallengeInBody ) { foundChallengeInBody = true; window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); if (!remoteCDM) { - if (jsonContainsValue(jsonBody, "CAES")) { + if (jsonContainsValue(jsonBody, DRM_SIGNATURES.WIDEVINE)) { const { device_type, system_id, @@ -849,7 +873,7 @@ MediaKeySession.prototype.update = function (response) { remoteCDM.openSession(); remoteCDM.getChallenge(foundWidevinePssh); } - if (jsonContainsValue(jsonBody, "PD94")) { + if (jsonContainsValue(jsonBody, DRM_SIGNATURES.PLAYREADY)) { const { security_level, host, secret, device_name } = playreadyDeviceInfo; remoteCDM = new remotePlayReadyCDM( @@ -894,7 +918,8 @@ MediaKeySession.prototype.update = function (response) { const buffer = body instanceof Uint8Array ? body : new Uint8Array(body); const base64Body = window.btoa(String.fromCharCode(...buffer)); if ( - (base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) && + (base64Body.startsWith(DRM_SIGNATURES.WIDEVINE) || + base64Body.startsWith(DRM_SIGNATURES.PLAYREADY)) && (!remoteCDM || remoteCDM.challenge === null || base64Body !== remoteCDM.challenge) && @@ -906,14 +931,15 @@ MediaKeySession.prototype.update = function (response) { return; } if ( - (base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) && + (base64Body.startsWith(DRM_SIGNATURES.WIDEVINE) || + base64Body.startsWith(DRM_SIGNATURES.PLAYREADY)) && interceptType == "LICENSE" && !foundChallengeInBody ) { foundChallengeInBody = true; window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); if (!remoteCDM) { - if (base64Body.startsWith("CAES")) { + if (base64Body.startsWith(DRM_SIGNATURES.WIDEVINE)) { const { device_type, system_id, @@ -933,7 +959,7 @@ MediaKeySession.prototype.update = function (response) { remoteCDM.openSession(); remoteCDM.getChallenge(foundWidevinePssh); } - if (base64Body.startsWith("PD94")) { + if (base64Body.startsWith(DRM_SIGNATURES.PLAYREADY)) { const { security_level, host, secret, device_name } = playreadyDeviceInfo; remoteCDM = new remotePlayReadyCDM( @@ -957,8 +983,8 @@ MediaKeySession.prototype.update = function (response) { if (typeof body === "string" && !isJson(body)) { const base64EncodedBody = btoa(body); if ( - (base64EncodedBody.startsWith("CAES") || - base64EncodedBody.startsWith("PD94")) && + (base64EncodedBody.startsWith(DRM_SIGNATURES.WIDEVINE) || + base64EncodedBody.startsWith(DRM_SIGNATURES.PLAYREADY)) && (!remoteCDM || remoteCDM.challenge === null || base64EncodedBody !== remoteCDM.challenge) && @@ -970,15 +996,15 @@ MediaKeySession.prototype.update = function (response) { return; } if ( - (base64EncodedBody.startsWith("CAES") || - base64EncodedBody.startsWith("PD94")) && + (base64EncodedBody.startsWith(DRM_SIGNATURES.WIDEVINE) || + base64EncodedBody.startsWith(DRM_SIGNATURES.PLAYREADY)) && interceptType == "LICENSE" && !foundChallengeInBody ) { foundChallengeInBody = true; window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); if (!remoteCDM) { - if (base64EncodedBody.startsWith("CAES")) { + if (base64EncodedBody.startsWith(DRM_SIGNATURES.WIDEVINE)) { const { device_type, system_id, @@ -998,7 +1024,7 @@ MediaKeySession.prototype.update = function (response) { remoteCDM.openSession(); remoteCDM.getChallenge(foundWidevinePssh); } - if (base64EncodedBody.startsWith("PD94")) { + if (base64EncodedBody.startsWith(DRM_SIGNATURES.PLAYREADY)) { const { security_level, host, secret, device_name } = playreadyDeviceInfo; remoteCDM = new remotePlayReadyCDM( @@ -1023,8 +1049,8 @@ MediaKeySession.prototype.update = function (response) { const jsonBody = JSON.parse(body); if ( - (jsonContainsValue(jsonBody, "CAES") || - jsonContainsValue(jsonBody, "PD94")) && + (jsonContainsValue(jsonBody, DRM_SIGNATURES.WIDEVINE) || + jsonContainsValue(jsonBody, DRM_SIGNATURES.PLAYREADY)) && (!remoteCDM || remoteCDM.challenge === null) && interceptType === "EME" ) { @@ -1035,15 +1061,15 @@ MediaKeySession.prototype.update = function (response) { } if ( - (jsonContainsValue(jsonBody, "CAES") || - jsonContainsValue(jsonBody, "PD94")) && + (jsonContainsValue(jsonBody, DRM_SIGNATURES.WIDEVINE) || + jsonContainsValue(jsonBody, DRM_SIGNATURES.PLAYREADY)) && interceptType === "LICENSE" && !foundChallengeInBody ) { foundChallengeInBody = true; window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); if (!remoteCDM) { - if (jsonContainsValue(jsonBody, "CAES")) { + if (jsonContainsValue(jsonBody, DRM_SIGNATURES.WIDEVINE)) { const { device_type, system_id, @@ -1063,7 +1089,7 @@ MediaKeySession.prototype.update = function (response) { remoteCDM.openSession(); remoteCDM.getChallenge(foundWidevinePssh); } - if (jsonContainsValue(jsonBody, "PD94")) { + if (jsonContainsValue(jsonBody, DRM_SIGNATURES.PLAYREADY)) { const { security_level, host, secret, device_name } = playreadyDeviceInfo; remoteCDM = new remotePlayReadyCDM( -- 2.43.0 From 89f66b25be3daedb6919d09b5da7d3b2388ef334 Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Mon, 21 Jul 2025 16:05:35 +0700 Subject: [PATCH 15/26] add extension prefix and color for ease of debugging and filtering --- src/background.js | 35 +++++++---- src/content.js | 17 +++++- src/inject.js | 149 +++++++++++++++++++++++++++------------------- 3 files changed, 128 insertions(+), 73 deletions(-) diff --git a/src/background.js b/src/background.js index bbd7fd6..b5f70fc 100644 --- a/src/background.js +++ b/src/background.js @@ -13,27 +13,27 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { switch (type) { case "DRM_TYPE": - console.log("DRM Type:", data); + logWithPrefix("DRM Type:", data); chrome.storage.local.set({ drmType: data }); break; case "PSSH_DATA": - console.log("Storing PSSH:", data); + logWithPrefix("Storing PSSH:", data); chrome.storage.local.set({ latestPSSH: data }); break; case "KEYS_DATA": - console.log("Storing Decryption Keys:", data); + logWithPrefix("Storing Decryption Keys:", data); chrome.storage.local.set({ latestKeys: data }); break; case "LICENSE_URL": - console.log("Storling License URL " + data); + logWithPrefix("Storling License URL " + data); chrome.storage.local.set({ licenseURL: data }); break; case "MANIFEST_URL": - console.log("Storing Manifest URL:", data); + logWithPrefix("Storing Manifest URL:", data); chrome.storage.local.set({ manifestURL: data }); break; @@ -44,12 +44,27 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { // Set initial config and injection type on install chrome.runtime.onInstalled.addListener((details) => { + const EXTENSION_PREFIX = "[CDRM EXTENSION]"; + const PREFIX_COLOR = "black"; + const PREFIX_BACKGROUND_COLOR = "yellow"; + + const logWithPrefix = (...args) => { + const style = `color: ${PREFIX_COLOR}; background: ${PREFIX_BACKGROUND_COLOR}; font-weight: bold; padding: 2px 4px; border-radius: 2px;`; + if (typeof args[0] === "string") { + // If the first arg is a string, prepend the prefix + console.log(`%c${EXTENSION_PREFIX}%c ${args[0]}`, style, "", ...args.slice(1)); + } else { + // If not, just log the prefix and the rest + console.log(`%c${EXTENSION_PREFIX}`, style, ...args); + } + }; + if (details.reason === "install") { chrome.storage.local.set({ valid_config: false }, () => { if (chrome.runtime.lastError) { console.error("Error setting valid_config:", chrome.runtime.lastError); } else { - console.log("valid_config set to false on first install."); + logWithPrefix("valid_config set to false on first install."); } }); @@ -57,7 +72,7 @@ chrome.runtime.onInstalled.addListener((details) => { if (chrome.runtime.lastError) { console.error("Error setting Injection Type:", chrome.runtime.lastError); } else { - console.log("Injection type set to LICENSE on first install."); + logWithPrefix("Injection type set to LICENSE on first install."); } }); @@ -65,7 +80,7 @@ chrome.runtime.onInstalled.addListener((details) => { if (chrome.runtime.lastError) { console.error("Error setting DRM Override type:", chrome.runtime.lastError); } else { - console.log("DRM Override type set to DISABLED on first install."); + logWithPrefix("DRM Override type set to DISABLED on first install."); } }); @@ -73,7 +88,7 @@ chrome.runtime.onInstalled.addListener((details) => { if (chrome.runtime.lastError) { console.error("Error setting CDRM instance:", chrome.runtime.lastError); } else { - console.log("CDRM instance set to null."); + logWithPrefix("CDRM instance set to null."); } }); @@ -81,7 +96,7 @@ chrome.runtime.onInstalled.addListener((details) => { if (chrome.runtime.lastError) { console.error("Error setting CDRM API Key:", chrome.runtime.lastError); } else { - console.log("CDRM API Key set."); + logWithPrefix("CDRM API Key set."); } }); } diff --git a/src/content.js b/src/content.js index 0e12c93..221035d 100644 --- a/src/content.js +++ b/src/content.js @@ -68,6 +68,19 @@ window.addEventListener("message", function (event) { } // Manifest header and URL + const EXTENSION_PREFIX = "[CDRM EXTENSION]"; + const PREFIX_COLOR = "black"; + const PREFIX_BACKGROUND_COLOR = "yellow"; + const logWithPrefix = (...args) => { + const style = `color: ${PREFIX_COLOR}; background: ${PREFIX_BACKGROUND_COLOR}; font-weight: bold; padding: 2px 4px; border-radius: 2px;`; + if (typeof args[0] === "string") { + // If the first arg is a string, prepend the prefix + console.log(`%c${EXTENSION_PREFIX}%c ${args[0]}`, style, "", ...args.slice(1)); + } else { + // If not, just log the prefix and the rest + console.log(`%c${EXTENSION_PREFIX}`, style, ...args); + } + }; const seenManifestUrls = new Set(); @@ -75,7 +88,7 @@ window.addEventListener("message", function (event) { const url = event.data.data; if (seenManifestUrls.has(url)) return; seenManifestUrls.add(url); - console.log("✅ [Content] Unique manifest URL:", url); + logWithPrefix("✅ [Content] Unique manifest URL:", url); chrome.runtime.sendMessage({ type: "MANIFEST_URL", @@ -85,7 +98,7 @@ window.addEventListener("message", function (event) { if (event.data?.type === "__MANIFEST_HEADERS__") { const { url, headers } = event.data; - console.log("[Content.js] Manifest Headers:", url, headers); + logWithPrefix("[Content.js] Manifest Headers:", url, headers); chrome.runtime.sendMessage({ type: "MANIFEST_HEADERS", diff --git a/src/inject.js b/src/inject.js index 3b16d9d..3b463bc 100644 --- a/src/inject.js +++ b/src/inject.js @@ -24,6 +24,21 @@ const DRM_SIGNATURES = { WIDEVINE_INIT: "CAQ=", }; +const EXTENSION_PREFIX = "[CDRM EXTENSION]"; +const PREFIX_COLOR = "black"; +const PREFIX_BACKGROUND_COLOR = "yellow"; + +const logWithPrefix = (...args) => { + const style = `color: ${PREFIX_COLOR}; background: ${PREFIX_BACKGROUND_COLOR}; font-weight: bold; padding: 2px 4px; border-radius: 2px;`; + if (typeof args[0] === "string") { + // If the first arg is a string, prepend the prefix + console.log(`%c${EXTENSION_PREFIX}%c ${args[0]}`, style, "", ...args.slice(1)); + } else { + // If not, just log the prefix and the rest + console.log(`%c${EXTENSION_PREFIX}`, style, ...args); + } +}; + // Post message to content.js to get DRM override window.postMessage({ type: "__GET_DRM_OVERRIDE__" }, "*"); @@ -32,7 +47,7 @@ window.addEventListener("message", function (event) { if (event.source !== window) return; if (event.data.type === "__DRM_OVERRIDE__") { drmOverride = event.data.drmOverride || "DISABLED"; - console.log("DRM Override set to:", drmOverride); + logWithPrefix("DRM Override set to:", drmOverride); } }); @@ -45,7 +60,7 @@ window.addEventListener("message", function (event) { if (event.data.type === "__INJECTION_TYPE__") { interceptType = event.data.injectionType || "DISABLED"; - console.log("Injection type set to:", interceptType); + logWithPrefix("Injection type set to:", interceptType); } }); @@ -59,7 +74,7 @@ window.addEventListener("message", function (event) { if (event.data.type === "__CDM_DEVICES__") { const { widevine_device, playready_device } = event.data; - console.log("Received device info:", widevine_device, playready_device); + logWithPrefix("Received device info:", widevine_device, playready_device); widevineDeviceInfo = widevine_device; playreadyDeviceInfo = playready_device; @@ -90,7 +105,7 @@ function headersToFlags(headersObj) { function handleManifestDetection(url, headersObj, contentType, source) { window.postMessage({ type: "__MANIFEST_URL__", data: url }, "*"); - console.log(`[Manifest][${source}]`, url, contentType); + logWithPrefix(`[Manifest][${source}]`, url, contentType); const headerFlags = headersToFlags(headersObj); @@ -209,7 +224,7 @@ class RemoteCDMBase { const jsonData = JSON.parse(xhr.responseText); if (jsonData.data?.session_id) { this.session_id = jsonData.data.session_id; - console.log("Session opened:", this.session_id); + logWithPrefix("Session opened:", this.session_id); } else { console.error("Failed to open session:", jsonData.message); throw new Error("Failed to open session"); @@ -225,10 +240,10 @@ class RemoteCDMBase { const jsonData = JSON.parse(xhr.responseText); if (jsonData.data?.challenge) { this.challenge = btoa(jsonData.data.challenge); - console.log("Challenge received:", this.challenge); + logWithPrefix("Challenge received:", this.challenge); } else if (jsonData.data?.challenge_b64) { this.challenge = jsonData.data.challenge_b64; - console.log("Challenge received:", this.challenge); + logWithPrefix("Challenge received:", this.challenge); } else { console.error("Failed to get challenge:", jsonData.message); throw new Error("Failed to get challenge"); @@ -243,7 +258,7 @@ class RemoteCDMBase { xhr.send(JSON.stringify(body)); const jsonData = JSON.parse(xhr.responseText); if (jsonData.status === 200 || jsonData.message?.includes("parsed and loaded")) { - console.log("License response parsed successfully"); + logWithPrefix("License response parsed successfully"); return true; } else { console.error("Failed to parse license response:", jsonData.message); @@ -260,7 +275,7 @@ class RemoteCDMBase { const jsonData = JSON.parse(xhr.responseText); if (jsonData.data?.keys) { this.keys = jsonData.data.keys; - console.log("Keys received:", this.keys); + logWithPrefix("Keys received:", this.keys); } else { console.error("Failed to get keys:", jsonData.message); throw new Error("Failed to get keys"); @@ -275,7 +290,7 @@ class RemoteCDMBase { xhr.send(); const jsonData = JSON.parse(xhr.responseText); if (jsonData) { - console.log("Session closed successfully"); + logWithPrefix("Session closed successfully"); } else { console.error("Failed to close session:", jsonData.message); throw new Error("Failed to close session"); @@ -342,7 +357,7 @@ class remoteWidevineCDM extends RemoteCDMBase { xhr.send(JSON.stringify(body)); const jsonData = JSON.parse(xhr.responseText); if (jsonData.status === 200) { - console.log("Service certificate set successfully"); + logWithPrefix("Service certificate set successfully"); } else { console.error("Failed to set service certificate:", jsonData.message); throw new Error("Failed to set service certificate"); @@ -363,7 +378,7 @@ class remoteWidevineCDM extends RemoteCDMBase { const jsonData = JSON.parse(xhr.responseText); if (jsonData.data?.challenge_b64) { this.challenge = jsonData.data.challenge_b64; - console.log("Widevine challenge received:", this.challenge); + logWithPrefix("Widevine challenge received:", this.challenge); } else { console.error("Failed to get Widevine challenge:", jsonData.message); throw new Error("Failed to get Widevine challenge"); @@ -501,30 +516,74 @@ function arrayBufferToBase64(uint8array) { return window.btoa(binary); } +function bufferToBase64(buffer) { + const uint8 = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer); + return window.btoa(String.fromCharCode(...uint8)); +} + +// DRM type detection +function isWidevine(base64str) { + return base64str.startsWith(DRM_SIGNATURES.WIDEVINE); +} +function isPlayReady(base64str) { + return base64str.startsWith(DRM_SIGNATURES.PLAYREADY); +} +function isServiceCertificate(base64str) { + return base64str.startsWith(DRM_SIGNATURES.SERVICE_CERT); +} + +function postDRMTypeAndPssh(type, pssh) { + window.postMessage({ type: "__DRM_TYPE__", data: type }, "*"); + window.postMessage({ type: "__PSSH_DATA__", data: pssh }, "*"); +} + +function ensureRemoteCDM(type, deviceInfo, pssh) { + if (!remoteCDM) { + if (type === "Widevine") { + const { device_type, system_id, security_level, host, secret, device_name } = + deviceInfo; + remoteCDM = new remoteWidevineCDM( + device_type, + system_id, + security_level, + host, + secret, + device_name + ); + remoteCDM.openSession(); + remoteCDM.getChallenge(pssh); + } else if (type === "PlayReady") { + const { security_level, host, secret, device_name } = deviceInfo; + remoteCDM = new remotePlayReadyCDM(security_level, host, secret, device_name); + remoteCDM.openSession(); + remoteCDM.getChallenge(pssh); + } + } +} + // Challenge generator interceptor const originalGenerateRequest = MediaKeySession.prototype.generateRequest; MediaKeySession.prototype.generateRequest = function (initDataType, initData) { const session = this; let playReadyPssh = getPlayReadyPssh(initData); if (playReadyPssh) { - console.log("[DRM Detected] PlayReady"); + logWithPrefix("[DRM Detected] PlayReady"); foundPlayreadyPssh = playReadyPssh; - console.log("[PlayReady PSSH found] " + playReadyPssh); + logWithPrefix("[PlayReady PSSH found] " + playReadyPssh); } let wideVinePssh = getWidevinePssh(initData); if (wideVinePssh) { // Widevine code - console.log("[DRM Detected] Widevine"); + logWithPrefix("[DRM Detected] Widevine"); foundWidevinePssh = wideVinePssh; - console.log("[Widevine PSSH found] " + wideVinePssh); + logWithPrefix("[Widevine PSSH found] " + wideVinePssh); } // Challenge message interceptor if (!remoteListenerMounted) { remoteListenerMounted = true; session.addEventListener("message", function messageInterceptor(event) { event.stopImmediatePropagation(); - const uint8Array = new Uint8Array(event.message); - const base64challenge = arrayBufferToBase64(uint8Array); + const base64challenge = bufferToBase64(event.message); if ( base64challenge === DRM_SIGNATURES.WIDEVINE_INIT && interceptType !== "DISABLED" && @@ -554,27 +613,9 @@ MediaKeySession.prototype.generateRequest = function (initDataType, initData) { originalChallenge = base64challenge; } if (originalChallenge.startsWith(DRM_SIGNATURES.WIDEVINE)) { - window.postMessage({ type: "__DRM_TYPE__", data: "Widevine" }, "*"); - window.postMessage({ type: "__PSSH_DATA__", data: foundWidevinePssh }, "*"); - if (interceptType === "EME" && !remoteCDM) { - const { - device_type, - system_id, - security_level, - host, - secret, - device_name, - } = widevineDeviceInfo; - remoteCDM = new remoteWidevineCDM( - device_type, - system_id, - security_level, - host, - secret, - device_name - ); - remoteCDM.openSession(); - remoteCDM.getChallenge(foundWidevinePssh); + postDRMTypeAndPssh("Widevine", foundWidevinePssh); + if (interceptType === "EME") { + ensureRemoteCDM("Widevine", widevineDeviceInfo, foundWidevinePssh); } } if (!originalChallenge.startsWith(DRM_SIGNATURES.WIDEVINE)) { @@ -585,23 +626,10 @@ MediaKeySession.prototype.generateRequest = function (initDataType, initData) { /([^<]+)<\/Challenge>/ ); if (match) { - window.postMessage({ type: "__DRM_TYPE__", data: "PlayReady" }, "*"); - window.postMessage( - { type: "__PSSH_DATA__", data: foundPlayreadyPssh }, - "*" - ); + postDRMTypeAndPssh("PlayReady", foundPlayreadyPssh); originalChallenge = match[1]; - if (interceptType === "EME" && !remoteCDM) { - const { security_level, host, secret, device_name } = - playreadyDeviceInfo; - remoteCDM = new remotePlayReadyCDM( - security_level, - host, - secret, - device_name - ); - remoteCDM.openSession(); - remoteCDM.getChallenge(foundPlayreadyPssh); + if (interceptType === "EME") { + ensureRemoteCDM("PlayReady", playreadyDeviceInfo, foundPlayreadyPssh); } } } @@ -618,12 +646,12 @@ MediaKeySession.prototype.generateRequest = function (initDataType, initData) { Object.defineProperty(syntheticEvent, "message", { get: () => challengeBuffer, }); - console.log("Intercepted EME Challenge and injected custom one."); + logWithPrefix("Intercepted EME Challenge and injected custom one."); session.dispatchEvent(syntheticEvent); } } }); - console.log("Message interceptor mounted."); + logWithPrefix("Message interceptor mounted."); } return originalGenerateRequest.call(session, initDataType, initData); }; @@ -631,8 +659,7 @@ MediaKeySession.prototype.generateRequest = function (initDataType, initData) { // Message update interceptors const originalUpdate = MediaKeySession.prototype.update; MediaKeySession.prototype.update = function (response) { - const uint8 = response instanceof Uint8Array ? response : new Uint8Array(response); - const base64Response = window.btoa(String.fromCharCode(...uint8)); + const base64Response = bufferToBase64(response); if ( base64Response.startsWith(DRM_SIGNATURES.SERVICE_CERT) && foundWidevinePssh && @@ -667,7 +694,7 @@ MediaKeySession.prototype.update = function (response) { .then(() => { let clearKeys = getClearkey(response); if (clearKeys && clearKeys.length > 0) { - console.log("[CLEARKEY] ", clearKeys); + logWithPrefix("[CLEARKEY] ", clearKeys); const drmType = { type: "__DRM_TYPE__", data: "ClearKey", @@ -681,7 +708,7 @@ MediaKeySession.prototype.update = function (response) { } }) .catch((e) => { - console.log("[CLEARKEY] Not found"); + logWithPrefix("[CLEARKEY] Not found"); }); } -- 2.43.0 From 9e071365e372dda6424041f37eff597be50819cb Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Mon, 21 Jul 2025 17:18:18 +0700 Subject: [PATCH 16/26] use regular prefix instead of adding color. fixes not storing to `chrome.storage.local` --- src/background.js | 47 ++++++++++++++++------------------------------- src/content.js | 19 +++---------------- 2 files changed, 19 insertions(+), 47 deletions(-) diff --git a/src/background.js b/src/background.js index b5f70fc..03c5e12 100644 --- a/src/background.js +++ b/src/background.js @@ -13,90 +13,75 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { switch (type) { case "DRM_TYPE": - logWithPrefix("DRM Type:", data); + console.log("[CDRM-Extension] DRM Type:", data); chrome.storage.local.set({ drmType: data }); break; case "PSSH_DATA": - logWithPrefix("Storing PSSH:", data); + console.log("[CDRM-Extension] Storing PSSH:", data); chrome.storage.local.set({ latestPSSH: data }); break; case "KEYS_DATA": - logWithPrefix("Storing Decryption Keys:", data); + console.log("[CDRM-Extension] Storing Decryption Keys:", data); chrome.storage.local.set({ latestKeys: data }); break; case "LICENSE_URL": - logWithPrefix("Storling License URL " + data); + console.log("[CDRM-Extension] Storing License URL " + data); chrome.storage.local.set({ licenseURL: data }); break; case "MANIFEST_URL": - logWithPrefix("Storing Manifest URL:", data); + console.log("[CDRM-Extension] Storing Manifest URL:", data); chrome.storage.local.set({ manifestURL: data }); break; default: - console.warn("Unknown message type received:", type); + console.warn("[CDRM-Extension] Unknown message type received:", type); } }); // Set initial config and injection type on install chrome.runtime.onInstalled.addListener((details) => { - const EXTENSION_PREFIX = "[CDRM EXTENSION]"; - const PREFIX_COLOR = "black"; - const PREFIX_BACKGROUND_COLOR = "yellow"; - - const logWithPrefix = (...args) => { - const style = `color: ${PREFIX_COLOR}; background: ${PREFIX_BACKGROUND_COLOR}; font-weight: bold; padding: 2px 4px; border-radius: 2px;`; - if (typeof args[0] === "string") { - // If the first arg is a string, prepend the prefix - console.log(`%c${EXTENSION_PREFIX}%c ${args[0]}`, style, "", ...args.slice(1)); - } else { - // If not, just log the prefix and the rest - console.log(`%c${EXTENSION_PREFIX}`, style, ...args); - } - }; - if (details.reason === "install") { chrome.storage.local.set({ valid_config: false }, () => { if (chrome.runtime.lastError) { - console.error("Error setting valid_config:", chrome.runtime.lastError); + console.error("[CDRM-Extension] Error setting valid_config:", chrome.runtime.lastError); } else { - logWithPrefix("valid_config set to false on first install."); + console.log("[CDRM-Extension] valid_config set to false on first install."); } }); chrome.storage.local.set({ injection_type: "LICENSE" }, () => { if (chrome.runtime.lastError) { - console.error("Error setting Injection Type:", chrome.runtime.lastError); + console.error("[CDRM-Extension] Error setting Injection Type:", chrome.runtime.lastError); } else { - logWithPrefix("Injection type set to LICENSE on first install."); + console.log("[CDRM-Extension] Injection type set to LICENSE on first install."); } }); chrome.storage.local.set({ drm_override: "DISABLED" }, () => { if (chrome.runtime.lastError) { - console.error("Error setting DRM Override type:", chrome.runtime.lastError); + console.error("[CDRM-Extension] Error setting DRM Override type:", chrome.runtime.lastError); } else { - logWithPrefix("DRM Override type set to DISABLED on first install."); + console.log("[CDRM-Extension] DRM Override type set to DISABLED on first install."); } }); chrome.storage.local.set({ cdrm_instance: null }, () => { if (chrome.runtime.lastError) { - console.error("Error setting CDRM instance:", chrome.runtime.lastError); + console.error("[CDRM-Extension] Error setting CDRM instance:", chrome.runtime.lastError); } else { - logWithPrefix("CDRM instance set to null."); + console.log("[CDRM-Extension] CDRM instance set to null."); } }); chrome.storage.local.set({ cdrm_api_key: null }, () => { if (chrome.runtime.lastError) { - console.error("Error setting CDRM API Key:", chrome.runtime.lastError); + console.error("[CDRM-Extension] Error setting CDRM API Key:", chrome.runtime.lastError); } else { - logWithPrefix("CDRM API Key set."); + console.log("[CDRM-Extension] CDRM API Key set."); } }); } diff --git a/src/content.js b/src/content.js index 221035d..85e397e 100644 --- a/src/content.js +++ b/src/content.js @@ -68,19 +68,6 @@ window.addEventListener("message", function (event) { } // Manifest header and URL - const EXTENSION_PREFIX = "[CDRM EXTENSION]"; - const PREFIX_COLOR = "black"; - const PREFIX_BACKGROUND_COLOR = "yellow"; - const logWithPrefix = (...args) => { - const style = `color: ${PREFIX_COLOR}; background: ${PREFIX_BACKGROUND_COLOR}; font-weight: bold; padding: 2px 4px; border-radius: 2px;`; - if (typeof args[0] === "string") { - // If the first arg is a string, prepend the prefix - console.log(`%c${EXTENSION_PREFIX}%c ${args[0]}`, style, "", ...args.slice(1)); - } else { - // If not, just log the prefix and the rest - console.log(`%c${EXTENSION_PREFIX}`, style, ...args); - } - }; const seenManifestUrls = new Set(); @@ -88,17 +75,17 @@ window.addEventListener("message", function (event) { const url = event.data.data; if (seenManifestUrls.has(url)) return; seenManifestUrls.add(url); - logWithPrefix("✅ [Content] Unique manifest URL:", url); + console.log("[CDRM-Extension] ✅ [content.js] Unique manifest URL:", url); chrome.runtime.sendMessage({ - type: "MANIFEST_URL", + type: "MANIFEST_URL_FOUND", data: url, }); } if (event.data?.type === "__MANIFEST_HEADERS__") { const { url, headers } = event.data; - logWithPrefix("[Content.js] Manifest Headers:", url, headers); + console.log("[CDRM-Extension] [content.js] Manifest headers:", url, headers); chrome.runtime.sendMessage({ type: "MANIFEST_HEADERS", -- 2.43.0 From f8712d7726dcd70e299bda396f1660fcfb6923c0 Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Mon, 21 Jul 2025 18:28:59 +0700 Subject: [PATCH 17/26] refactor: consolidate remote cdm initialization into a helper function --- src/inject.js | 261 +++++++++++++++----------------------------------- 1 file changed, 76 insertions(+), 185 deletions(-) diff --git a/src/inject.js b/src/inject.js index 3b463bc..3156ae5 100644 --- a/src/inject.js +++ b/src/inject.js @@ -537,27 +537,32 @@ function postDRMTypeAndPssh(type, pssh) { window.postMessage({ type: "__PSSH_DATA__", data: pssh }, "*"); } +function createAndOpenRemoteCDM(type, deviceInfo, pssh) { + let cdm; + if (type === "Widevine") { + const { device_type, system_id, security_level, host, secret, device_name } = deviceInfo; + cdm = new remoteWidevineCDM( + device_type, + system_id, + security_level, + host, + secret, + device_name + ); + cdm.openSession(); + cdm.getChallenge(pssh); + } else if (type === "PlayReady") { + const { security_level, host, secret, device_name } = deviceInfo; + cdm = new remotePlayReadyCDM(security_level, host, secret, device_name); + cdm.openSession(); + cdm.getChallenge(pssh); + } + return cdm; +} + function ensureRemoteCDM(type, deviceInfo, pssh) { if (!remoteCDM) { - if (type === "Widevine") { - const { device_type, system_id, security_level, host, secret, device_name } = - deviceInfo; - remoteCDM = new remoteWidevineCDM( - device_type, - system_id, - security_level, - host, - secret, - device_name - ); - remoteCDM.openSession(); - remoteCDM.getChallenge(pssh); - } else if (type === "PlayReady") { - const { security_level, host, secret, device_name } = deviceInfo; - remoteCDM = new remotePlayReadyCDM(security_level, host, secret, device_name); - remoteCDM.openSession(); - remoteCDM.getChallenge(pssh); - } + remoteCDM = createAndOpenRemoteCDM(type, deviceInfo, pssh); } } @@ -589,17 +594,11 @@ MediaKeySession.prototype.generateRequest = function (initDataType, initData) { interceptType !== "DISABLED" && !serviceCertFound ) { - const { device_type, system_id, security_level, host, secret, device_name } = - widevineDeviceInfo; - remoteCDM = new remoteWidevineCDM( - device_type, - system_id, - security_level, - host, - secret, - device_name + remoteCDM = createAndOpenRemoteCDM( + "Widevine", + widevineDeviceInfo, + foundWidevinePssh ); - remoteCDM.openSession(); } if ( !injectionSuccess && @@ -751,36 +750,18 @@ MediaKeySession.prototype.update = function (response) { window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); if (!remoteCDM) { if (base64Body.startsWith(DRM_SIGNATURES.WIDEVINE)) { - const { - device_type, - system_id, - security_level, - host, - secret, - device_name, - } = widevineDeviceInfo; - remoteCDM = new remoteWidevineCDM( - device_type, - system_id, - security_level, - host, - secret, - device_name + remoteCDM = createAndOpenRemoteCDM( + "Widevine", + widevineDeviceInfo, + foundWidevinePssh ); - remoteCDM.openSession(); - remoteCDM.getChallenge(foundWidevinePssh); } if (base64Body.startsWith(DRM_SIGNATURES.PLAYREADY)) { - const { security_level, host, secret, device_name } = - playreadyDeviceInfo; - remoteCDM = new remotePlayReadyCDM( - security_level, - host, - secret, - device_name + remoteCDM = createAndOpenRemoteCDM( + "PlayReady", + playreadyDeviceInfo, + foundPlayreadyPssh ); - remoteCDM.openSession(); - remoteCDM.getChallenge(foundPlayreadyPssh); } } if (remoteCDM && remoteCDM.challenge === null) { @@ -816,36 +797,18 @@ MediaKeySession.prototype.update = function (response) { window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); if (!remoteCDM) { if (base64EncodedBody.startsWith(DRM_SIGNATURES.WIDEVINE)) { - const { - device_type, - system_id, - security_level, - host, - secret, - device_name, - } = widevineDeviceInfo; - remoteCDM = new remoteWidevineCDM( - device_type, - system_id, - security_level, - host, - secret, - device_name + remoteCDM = createAndOpenRemoteCDM( + "Widevine", + widevineDeviceInfo, + foundWidevinePssh ); - remoteCDM.openSession(); - remoteCDM.getChallenge(foundWidevinePssh); } if (base64EncodedBody.startsWith(DRM_SIGNATURES.PLAYREADY)) { - const { security_level, host, secret, device_name } = - playreadyDeviceInfo; - remoteCDM = new remotePlayReadyCDM( - security_level, - host, - secret, - device_name + remoteCDM = createAndOpenRemoteCDM( + "PlayReady", + playreadyDeviceInfo, + foundPlayreadyPssh ); - remoteCDM.openSession(); - remoteCDM.getChallenge(foundPlayreadyPssh); } } if (remoteCDM && remoteCDM.challenge === null) { @@ -881,36 +844,18 @@ MediaKeySession.prototype.update = function (response) { window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); if (!remoteCDM) { if (jsonContainsValue(jsonBody, DRM_SIGNATURES.WIDEVINE)) { - const { - device_type, - system_id, - security_level, - host, - secret, - device_name, - } = widevineDeviceInfo; - remoteCDM = new remoteWidevineCDM( - device_type, - system_id, - security_level, - host, - secret, - device_name + remoteCDM = createAndOpenRemoteCDM( + "Widevine", + widevineDeviceInfo, + foundWidevinePssh ); - remoteCDM.openSession(); - remoteCDM.getChallenge(foundWidevinePssh); } if (jsonContainsValue(jsonBody, DRM_SIGNATURES.PLAYREADY)) { - const { security_level, host, secret, device_name } = - playreadyDeviceInfo; - remoteCDM = new remotePlayReadyCDM( - security_level, - host, - secret, - device_name + remoteCDM = createAndOpenRemoteCDM( + "PlayReady", + playreadyDeviceInfo, + foundPlayreadyPssh ); - remoteCDM.openSession(); - remoteCDM.getChallenge(foundPlayreadyPssh); } } if (remoteCDM && remoteCDM.challenge === null) { @@ -967,36 +912,18 @@ MediaKeySession.prototype.update = function (response) { window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); if (!remoteCDM) { if (base64Body.startsWith(DRM_SIGNATURES.WIDEVINE)) { - const { - device_type, - system_id, - security_level, - host, - secret, - device_name, - } = widevineDeviceInfo; - remoteCDM = new remoteWidevineCDM( - device_type, - system_id, - security_level, - host, - secret, - device_name + remoteCDM = createAndOpenRemoteCDM( + "Widevine", + widevineDeviceInfo, + foundWidevinePssh ); - remoteCDM.openSession(); - remoteCDM.getChallenge(foundWidevinePssh); } if (base64Body.startsWith(DRM_SIGNATURES.PLAYREADY)) { - const { security_level, host, secret, device_name } = - playreadyDeviceInfo; - remoteCDM = new remotePlayReadyCDM( - security_level, - host, - secret, - device_name + remoteCDM = createAndOpenRemoteCDM( + "PlayReady", + playreadyDeviceInfo, + foundPlayreadyPssh ); - remoteCDM.openSession(); - remoteCDM.getChallenge(foundPlayreadyPssh); } } if (remoteCDM && remoteCDM.challenge === null) { @@ -1032,36 +959,18 @@ MediaKeySession.prototype.update = function (response) { window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); if (!remoteCDM) { if (base64EncodedBody.startsWith(DRM_SIGNATURES.WIDEVINE)) { - const { - device_type, - system_id, - security_level, - host, - secret, - device_name, - } = widevineDeviceInfo; - remoteCDM = new remoteWidevineCDM( - device_type, - system_id, - security_level, - host, - secret, - device_name + remoteCDM = createAndOpenRemoteCDM( + "Widevine", + widevineDeviceInfo, + foundWidevinePssh ); - remoteCDM.openSession(); - remoteCDM.getChallenge(foundWidevinePssh); } if (base64EncodedBody.startsWith(DRM_SIGNATURES.PLAYREADY)) { - const { security_level, host, secret, device_name } = - playreadyDeviceInfo; - remoteCDM = new remotePlayReadyCDM( - security_level, - host, - secret, - device_name + remoteCDM = createAndOpenRemoteCDM( + "PlayReady", + playreadyDeviceInfo, + foundPlayreadyPssh ); - remoteCDM.openSession(); - remoteCDM.getChallenge(foundPlayreadyPssh); } } if (remoteCDM && remoteCDM.challenge === null) { @@ -1097,36 +1006,18 @@ MediaKeySession.prototype.update = function (response) { window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); if (!remoteCDM) { if (jsonContainsValue(jsonBody, DRM_SIGNATURES.WIDEVINE)) { - const { - device_type, - system_id, - security_level, - host, - secret, - device_name, - } = widevineDeviceInfo; - remoteCDM = new remoteWidevineCDM( - device_type, - system_id, - security_level, - host, - secret, - device_name + remoteCDM = createAndOpenRemoteCDM( + "Widevine", + widevineDeviceInfo, + foundWidevinePssh ); - remoteCDM.openSession(); - remoteCDM.getChallenge(foundWidevinePssh); } if (jsonContainsValue(jsonBody, DRM_SIGNATURES.PLAYREADY)) { - const { security_level, host, secret, device_name } = - playreadyDeviceInfo; - remoteCDM = new remotePlayReadyCDM( - security_level, - host, - secret, - device_name + remoteCDM = createAndOpenRemoteCDM( + "PlayReady", + playreadyDeviceInfo, + foundPlayreadyPssh ); - remoteCDM.openSession(); - remoteCDM.getChallenge(foundPlayreadyPssh); } } if (remoteCDM && remoteCDM.challenge === null) { -- 2.43.0 From 867294f7f60135ae89d896bb01d809243dc3908b Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Mon, 21 Jul 2025 20:25:47 +0700 Subject: [PATCH 18/26] refactor: extract DRM challenge detection and handling for fetch and XHR requests --- src/inject.js | 406 ++++++++++++++++---------------------------------- 1 file changed, 130 insertions(+), 276 deletions(-) diff --git a/src/inject.js b/src/inject.js index 3156ae5..678a607 100644 --- a/src/inject.js +++ b/src/inject.js @@ -714,6 +714,81 @@ MediaKeySession.prototype.update = function (response) { return updatePromise; }; +// Helpers +function detectDRMChallenge(body) { + // Handles ArrayBuffer, Uint8Array, string, and JSON string + // Returns: { type: "Widevine"|"PlayReady"|null, base64: string|null, bodyType: "buffer"|"string"|"json"|null } + if (body instanceof ArrayBuffer || body instanceof Uint8Array) { + const buffer = body instanceof Uint8Array ? body : new Uint8Array(body); + const base64Body = window.btoa(String.fromCharCode(...buffer)); + if (base64Body.startsWith(DRM_SIGNATURES.WIDEVINE)) { + return { type: "Widevine", base64: base64Body, bodyType: "buffer" }; + } + if (base64Body.startsWith(DRM_SIGNATURES.PLAYREADY)) { + return { type: "PlayReady", base64: base64Body, bodyType: "buffer" }; + } + } else if (typeof body === "string" && !isJson(body)) { + const base64EncodedBody = btoa(body); + if (base64EncodedBody.startsWith(DRM_SIGNATURES.WIDEVINE)) { + return { type: "Widevine", base64: base64EncodedBody, bodyType: "string" }; + } + if (base64EncodedBody.startsWith(DRM_SIGNATURES.PLAYREADY)) { + return { type: "PlayReady", base64: base64EncodedBody, bodyType: "string" }; + } + } else if (typeof body === "string" && isJson(body)) { + const jsonBody = JSON.parse(body); + if (jsonContainsValue(jsonBody, DRM_SIGNATURES.WIDEVINE)) { + return { type: "Widevine", base64: null, bodyType: "json" }; + } + if (jsonContainsValue(jsonBody, DRM_SIGNATURES.PLAYREADY)) { + return { type: "PlayReady", base64: null, bodyType: "json" }; + } + } + return { type: null, base64: null, bodyType: null }; +} + +function handleLicenseMode({ + drmInfo, + body, + setBody, // function to set the new body (for fetch: (b) => config.body = b, for XHR: (b) => originalSend.call(this, b)) + urlOrResource, + getWidevinePssh, + getPlayreadyPssh, + widevineDeviceInfo, + playreadyDeviceInfo, +}) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: urlOrResource }, "*"); + + // Create remoteCDM if needed + if (!remoteCDM) { + if (drmInfo.type === "Widevine") { + remoteCDM = createAndOpenRemoteCDM("Widevine", widevineDeviceInfo, getWidevinePssh()); + } + if (drmInfo.type === "PlayReady") { + remoteCDM = createAndOpenRemoteCDM( + "PlayReady", + playreadyDeviceInfo, + getPlayreadyPssh() + ); + } + } + if (remoteCDM && remoteCDM.challenge === null) { + remoteCDM.getChallenge(getWidevinePssh()); + } + + // Inject the new challenge into the request body + if (drmInfo.bodyType === "json") { + const jsonBody = JSON.parse(body); + const injectedBody = jsonReplaceValue(jsonBody, remoteCDM.challenge); + setBody(JSON.stringify(injectedBody)); + } else if (drmInfo.bodyType === "buffer") { + setBody(base64ToUint8Array(remoteCDM.challenge)); + } else { + setBody(atob(remoteCDM.challenge)); + } +} + // fetch POST interceptor (function () { const originalFetch = window.fetch; @@ -724,146 +799,37 @@ MediaKeySession.prototype.update = function (response) { if (method === "POST") { let body = config.body; if (body) { - if (body instanceof ArrayBuffer || body instanceof Uint8Array) { - const buffer = body instanceof Uint8Array ? body : new Uint8Array(body); - const base64Body = window.btoa(String.fromCharCode(...buffer)); - if ( - (base64Body.startsWith(DRM_SIGNATURES.WIDEVINE) || - base64Body.startsWith(DRM_SIGNATURES.PLAYREADY)) && - (!remoteCDM || - remoteCDM.challenge === null || - base64Body !== remoteCDM.challenge) && - interceptType === "EME" - ) { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); - // Block the request - return; - } - if ( - (base64Body.startsWith(DRM_SIGNATURES.WIDEVINE) || - base64Body.startsWith(DRM_SIGNATURES.PLAYREADY)) && - interceptType == "LICENSE" && - !foundChallengeInBody - ) { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); - if (!remoteCDM) { - if (base64Body.startsWith(DRM_SIGNATURES.WIDEVINE)) { - remoteCDM = createAndOpenRemoteCDM( - "Widevine", - widevineDeviceInfo, - foundWidevinePssh - ); - } - if (base64Body.startsWith(DRM_SIGNATURES.PLAYREADY)) { - remoteCDM = createAndOpenRemoteCDM( - "PlayReady", - playreadyDeviceInfo, - foundPlayreadyPssh - ); - } - } - if (remoteCDM && remoteCDM.challenge === null) { - remoteCDM.getChallenge(foundWidevinePssh); - } - const injectedBody = base64ToUint8Array(remoteCDM.challenge); - config.body = injectedBody; - return originalFetch(resource, config); - } - } - if (typeof body === "string" && !isJson(body)) { - const base64EncodedBody = btoa(body); - if ( - (base64EncodedBody.startsWith(DRM_SIGNATURES.WIDEVINE) || - base64EncodedBody.startsWith(DRM_SIGNATURES.PLAYREADY)) && - (!remoteCDM || - remoteCDM.challenge === null || - base64EncodedBody !== remoteCDM.challenge) && - interceptType === "EME" - ) { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); - // Block the request - return; - } - if ( - (base64EncodedBody.startsWith(DRM_SIGNATURES.WIDEVINE) || - base64EncodedBody.startsWith(DRM_SIGNATURES.PLAYREADY)) && - interceptType == "LICENSE" && - !foundChallengeInBody - ) { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); - if (!remoteCDM) { - if (base64EncodedBody.startsWith(DRM_SIGNATURES.WIDEVINE)) { - remoteCDM = createAndOpenRemoteCDM( - "Widevine", - widevineDeviceInfo, - foundWidevinePssh - ); - } - if (base64EncodedBody.startsWith(DRM_SIGNATURES.PLAYREADY)) { - remoteCDM = createAndOpenRemoteCDM( - "PlayReady", - playreadyDeviceInfo, - foundPlayreadyPssh - ); - } - } - if (remoteCDM && remoteCDM.challenge === null) { - remoteCDM.getChallenge(foundWidevinePssh); - } - const injectedBody = atob(remoteCDM.challenge); - config.body = injectedBody; - return originalFetch(resource, config); - } - } - if (typeof body === "string" && isJson(body)) { - const jsonBody = JSON.parse(body); + const drmInfo = detectDRMChallenge(body); - if ( - (jsonContainsValue(jsonBody, DRM_SIGNATURES.WIDEVINE) || - jsonContainsValue(jsonBody, DRM_SIGNATURES.PLAYREADY)) && - (!remoteCDM || remoteCDM.challenge === null) && - interceptType === "EME" - ) { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); - // Block the request - return; - } + // EME mode: block the request if a DRM challenge is detected + if ( + drmInfo.type && + (!remoteCDM || + remoteCDM.challenge === null || + drmInfo.base64 !== remoteCDM.challenge) && + interceptType === "EME" + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); + // Block the request + return; + } - if ( - (jsonContainsValue(jsonBody, DRM_SIGNATURES.WIDEVINE) || - jsonContainsValue(jsonBody, DRM_SIGNATURES.PLAYREADY)) && - interceptType === "LICENSE" && - !foundChallengeInBody - ) { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); - if (!remoteCDM) { - if (jsonContainsValue(jsonBody, DRM_SIGNATURES.WIDEVINE)) { - remoteCDM = createAndOpenRemoteCDM( - "Widevine", - widevineDeviceInfo, - foundWidevinePssh - ); - } - if (jsonContainsValue(jsonBody, DRM_SIGNATURES.PLAYREADY)) { - remoteCDM = createAndOpenRemoteCDM( - "PlayReady", - playreadyDeviceInfo, - foundPlayreadyPssh - ); - } - } - if (remoteCDM && remoteCDM.challenge === null) { - remoteCDM.getChallenge(foundWidevinePssh); - } - const injectedBody = jsonReplaceValue(jsonBody, remoteCDM.challenge); - config.body = JSON.stringify(injectedBody); - } + // LICENSE mode: replace the challenge in the request + if (drmInfo.type && interceptType === "LICENSE" && !foundChallengeInBody) { + handleLicenseMode({ + drmInfo, + body, + setBody: (b) => { + config.body = b; + }, + urlOrResource: resource, + getWidevinePssh: () => foundWidevinePssh, + getPlayreadyPssh: () => foundPlayreadyPssh, + widevineDeviceInfo, + playreadyDeviceInfo, + }); + return originalFetch(resource, config); } } } @@ -886,146 +852,34 @@ MediaKeySession.prototype.update = function (response) { XMLHttpRequest.prototype.send = function (body) { if (this._method && this._method.toUpperCase() === "POST") { if (body) { - if (body instanceof ArrayBuffer || body instanceof Uint8Array) { - const buffer = body instanceof Uint8Array ? body : new Uint8Array(body); - const base64Body = window.btoa(String.fromCharCode(...buffer)); - if ( - (base64Body.startsWith(DRM_SIGNATURES.WIDEVINE) || - base64Body.startsWith(DRM_SIGNATURES.PLAYREADY)) && - (!remoteCDM || - remoteCDM.challenge === null || - base64Body !== remoteCDM.challenge) && - interceptType === "EME" - ) { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); - // Block the request - return; - } - if ( - (base64Body.startsWith(DRM_SIGNATURES.WIDEVINE) || - base64Body.startsWith(DRM_SIGNATURES.PLAYREADY)) && - interceptType == "LICENSE" && - !foundChallengeInBody - ) { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); - if (!remoteCDM) { - if (base64Body.startsWith(DRM_SIGNATURES.WIDEVINE)) { - remoteCDM = createAndOpenRemoteCDM( - "Widevine", - widevineDeviceInfo, - foundWidevinePssh - ); - } - if (base64Body.startsWith(DRM_SIGNATURES.PLAYREADY)) { - remoteCDM = createAndOpenRemoteCDM( - "PlayReady", - playreadyDeviceInfo, - foundPlayreadyPssh - ); - } - } - if (remoteCDM && remoteCDM.challenge === null) { - remoteCDM.getChallenge(foundWidevinePssh); - } - const injectedBody = base64ToUint8Array(remoteCDM.challenge); - return originalSend.call(this, injectedBody); - } + const drmInfo = detectDRMChallenge(body); + + // EME mode: block the request if a DRM challenge is detected + if ( + drmInfo.type && + (!remoteCDM || + remoteCDM.challenge === null || + drmInfo.base64 !== remoteCDM.challenge) && + interceptType === "EME" + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); + // Block the request + return; } - if (typeof body === "string" && !isJson(body)) { - const base64EncodedBody = btoa(body); - if ( - (base64EncodedBody.startsWith(DRM_SIGNATURES.WIDEVINE) || - base64EncodedBody.startsWith(DRM_SIGNATURES.PLAYREADY)) && - (!remoteCDM || - remoteCDM.challenge === null || - base64EncodedBody !== remoteCDM.challenge) && - interceptType === "EME" - ) { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); - // Block the request - return; - } - if ( - (base64EncodedBody.startsWith(DRM_SIGNATURES.WIDEVINE) || - base64EncodedBody.startsWith(DRM_SIGNATURES.PLAYREADY)) && - interceptType == "LICENSE" && - !foundChallengeInBody - ) { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); - if (!remoteCDM) { - if (base64EncodedBody.startsWith(DRM_SIGNATURES.WIDEVINE)) { - remoteCDM = createAndOpenRemoteCDM( - "Widevine", - widevineDeviceInfo, - foundWidevinePssh - ); - } - if (base64EncodedBody.startsWith(DRM_SIGNATURES.PLAYREADY)) { - remoteCDM = createAndOpenRemoteCDM( - "PlayReady", - playreadyDeviceInfo, - foundPlayreadyPssh - ); - } - } - if (remoteCDM && remoteCDM.challenge === null) { - remoteCDM.getChallenge(foundWidevinePssh); - } - const injectedBody = atob(remoteCDM.challenge); - return originalSend.call(this, injectedBody); - } - } - - if (typeof body === "string" && isJson(body)) { - const jsonBody = JSON.parse(body); - - if ( - (jsonContainsValue(jsonBody, DRM_SIGNATURES.WIDEVINE) || - jsonContainsValue(jsonBody, DRM_SIGNATURES.PLAYREADY)) && - (!remoteCDM || remoteCDM.challenge === null) && - interceptType === "EME" - ) { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); - // Block the request - return; - } - - if ( - (jsonContainsValue(jsonBody, DRM_SIGNATURES.WIDEVINE) || - jsonContainsValue(jsonBody, DRM_SIGNATURES.PLAYREADY)) && - interceptType === "LICENSE" && - !foundChallengeInBody - ) { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); - if (!remoteCDM) { - if (jsonContainsValue(jsonBody, DRM_SIGNATURES.WIDEVINE)) { - remoteCDM = createAndOpenRemoteCDM( - "Widevine", - widevineDeviceInfo, - foundWidevinePssh - ); - } - if (jsonContainsValue(jsonBody, DRM_SIGNATURES.PLAYREADY)) { - remoteCDM = createAndOpenRemoteCDM( - "PlayReady", - playreadyDeviceInfo, - foundPlayreadyPssh - ); - } - } - if (remoteCDM && remoteCDM.challenge === null) { - remoteCDM.getChallenge(foundWidevinePssh); - } - const injectedBody = jsonReplaceValue(jsonBody, remoteCDM.challenge); - return originalSend.call(this, JSON.stringify(injectedBody)); - } + // LICENSE mode: replace the challenge in the request + if (drmInfo.type && interceptType === "LICENSE" && !foundChallengeInBody) { + return handleLicenseMode({ + drmInfo, + body, + setBody: (b) => originalSend.call(this, b), + urlOrResource: this._url, + getWidevinePssh: () => foundWidevinePssh, + getPlayreadyPssh: () => foundPlayreadyPssh, + widevineDeviceInfo, + playreadyDeviceInfo, + }); } } } -- 2.43.0 From d0154fd6c12220b25613f69ea329ef6e39460256 Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Mon, 21 Jul 2025 22:57:28 +0700 Subject: [PATCH 19/26] change MANIFEST_URL_FOUND to MANIFEST_URL --- src/content.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content.js b/src/content.js index 85e397e..35c0497 100644 --- a/src/content.js +++ b/src/content.js @@ -78,7 +78,7 @@ window.addEventListener("message", function (event) { console.log("[CDRM-Extension] ✅ [content.js] Unique manifest URL:", url); chrome.runtime.sendMessage({ - type: "MANIFEST_URL_FOUND", + type: "MANIFEST_URL", data: url, }); } -- 2.43.0 From e03ca633de3236220d53a5100f844a324684054e Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Mon, 21 Jul 2025 23:29:40 +0700 Subject: [PATCH 20/26] do not show export if keys are not present --- frontend/src/components/results.jsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/frontend/src/components/results.jsx b/frontend/src/components/results.jsx index 7155419..4b60d67 100644 --- a/frontend/src/components/results.jsx +++ b/frontend/src/components/results.jsx @@ -144,13 +144,7 @@ function Results() { // Export to JSON file const hasData = () => { - return ( - drmType || - pssh || - licenseUrl || - manifestUrl || - (Array.isArray(keys) && keys.filter((k) => k.type !== "SIGNING").length > 0) - ); + return Array.isArray(keys) && keys.filter((k) => k.type !== "SIGNING").length > 0; }; const handleExportJSON = () => { -- 2.43.0 From 81f44b2f0ed7f1da9d3ec5b63abf25f83391d22e Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:52:12 +0700 Subject: [PATCH 21/26] concatenate message handling for DRM, injection type, and CDM devices --- src/inject.js | 52 +++++++++++++++++++++------------------------------ 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/src/inject.js b/src/inject.js index 678a607..0dbedd9 100644 --- a/src/inject.js +++ b/src/inject.js @@ -39,46 +39,36 @@ const logWithPrefix = (...args) => { } }; -// Post message to content.js to get DRM override window.postMessage({ type: "__GET_DRM_OVERRIDE__" }, "*"); - -// Add listener for DRM override messages -window.addEventListener("message", function (event) { - if (event.source !== window) return; - if (event.data.type === "__DRM_OVERRIDE__") { - drmOverride = event.data.drmOverride || "DISABLED"; - logWithPrefix("DRM Override set to:", drmOverride); - } -}); - -// Post message to content.js to get injection type window.postMessage({ type: "__GET_INJECTION_TYPE__" }, "*"); - -// Add listener for injection type messages -window.addEventListener("message", function (event) { - if (event.source !== window) return; - - if (event.data.type === "__INJECTION_TYPE__") { - interceptType = event.data.injectionType || "DISABLED"; - logWithPrefix("Injection type set to:", interceptType); - } -}); - -// Post message to get CDM devices window.postMessage({ type: "__GET_CDM_DEVICES__" }, "*"); -// Add listener for CDM device messages -window.addEventListener("message", function (event) { - if (event.source !== window) return; +function createMessageHandler(handlers) { + window.addEventListener("message", function (event) { + if (event.source !== window) return; - if (event.data.type === "__CDM_DEVICES__") { - const { widevine_device, playready_device } = event.data; + const handler = handlers[event.data.type]; + if (handler) { + handler(event.data); + } + }); +} +createMessageHandler({ + __DRM_OVERRIDE__: (data) => { + drmOverride = data.drmOverride || "DISABLED"; + logWithPrefix("DRM Override set to:", drmOverride); + }, + __INJECTION_TYPE__: (data) => { + interceptType = data.injectionType || "DISABLED"; + logWithPrefix("Injection type set to:", interceptType); + }, + __CDM_DEVICES__: (data) => { + const { widevine_device, playready_device } = data; logWithPrefix("Received device info:", widevine_device, playready_device); - widevineDeviceInfo = widevine_device; playreadyDeviceInfo = playready_device; - } + }, }); function safeHeaderShellEscape(str) { -- 2.43.0 From a40a6abaf76dea228c2aba8f9d2909b8910e6d9a Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:56:05 +0700 Subject: [PATCH 22/26] extract DRM PSSH detection and storage into a separate function --- src/inject.js | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/inject.js b/src/inject.js index 0dbedd9..652b2a9 100644 --- a/src/inject.js +++ b/src/inject.js @@ -556,23 +556,32 @@ function ensureRemoteCDM(type, deviceInfo, pssh) { } } +function detectAndStorePssh(initData) { + const detections = [ + { + type: "PlayReady", + getter: getPlayReadyPssh, + store: (pssh) => (foundPlayreadyPssh = pssh), + }, + { type: "Widevine", getter: getWidevinePssh, store: (pssh) => (foundWidevinePssh = pssh) }, + ]; + + detections.forEach(({ type, getter, store }) => { + const pssh = getter(initData); + if (pssh) { + logWithPrefix(`[DRM Detected] ${type}`); + store(pssh); + logWithPrefix(`[${type} PSSH found] ${pssh}`); + } + }); +} + // Challenge generator interceptor const originalGenerateRequest = MediaKeySession.prototype.generateRequest; MediaKeySession.prototype.generateRequest = function (initDataType, initData) { const session = this; - let playReadyPssh = getPlayReadyPssh(initData); - if (playReadyPssh) { - logWithPrefix("[DRM Detected] PlayReady"); - foundPlayreadyPssh = playReadyPssh; - logWithPrefix("[PlayReady PSSH found] " + playReadyPssh); - } - let wideVinePssh = getWidevinePssh(initData); - if (wideVinePssh) { - // Widevine code - logWithPrefix("[DRM Detected] Widevine"); - foundWidevinePssh = wideVinePssh; - logWithPrefix("[Widevine PSSH found] " + wideVinePssh); - } + detectAndStorePssh(initData); + // Challenge message interceptor if (!remoteListenerMounted) { remoteListenerMounted = true; -- 2.43.0 From 8d4cd89a025382d3606c93c0da8239ae83564f25 Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:04:17 +0700 Subject: [PATCH 23/26] consolidate DRM interception handling for fetch and XHR requests --- src/inject.js | 120 +++++++++++++++++++++++--------------------------- 1 file changed, 56 insertions(+), 64 deletions(-) diff --git a/src/inject.js b/src/inject.js index 652b2a9..0793cd1 100644 --- a/src/inject.js +++ b/src/inject.js @@ -788,6 +788,37 @@ function handleLicenseMode({ } } +function handleDRMInterception(drmInfo, body, url, setBodyCallback, continueRequestCallback) { + // EME mode: block the request if a DRM challenge is detected + if ( + drmInfo.type && + (!remoteCDM || remoteCDM.challenge === null || drmInfo.base64 !== remoteCDM.challenge) && + interceptType === "EME" + ) { + foundChallengeInBody = true; + window.postMessage({ type: "__LICENSE_URL__", data: url }, "*"); + // Block the request + return { shouldBlock: true }; + } + + // LICENSE mode: replace the challenge in the request + if (drmInfo.type && interceptType === "LICENSE" && !foundChallengeInBody) { + handleLicenseMode({ + drmInfo, + body, + setBody: setBodyCallback, + urlOrResource: url, + getWidevinePssh: () => foundWidevinePssh, + getPlayreadyPssh: () => foundPlayreadyPssh, + widevineDeviceInfo, + playreadyDeviceInfo, + }); + return { shouldIntercept: true, result: continueRequestCallback() }; + } + + return { shouldContinue: true }; +} + // fetch POST interceptor (function () { const originalFetch = window.fetch; @@ -795,42 +826,21 @@ function handleLicenseMode({ window.fetch = async function (resource, config = {}) { const method = (config.method || "GET").toUpperCase(); - if (method === "POST") { - let body = config.body; - if (body) { - const drmInfo = detectDRMChallenge(body); + if (method === "POST" && config.body) { + const drmInfo = detectDRMChallenge(config.body); - // EME mode: block the request if a DRM challenge is detected - if ( - drmInfo.type && - (!remoteCDM || - remoteCDM.challenge === null || - drmInfo.base64 !== remoteCDM.challenge) && - interceptType === "EME" - ) { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); - // Block the request - return; - } + const result = handleDRMInterception( + drmInfo, + config.body, + resource, + (b) => { + config.body = b; + }, + () => originalFetch(resource, config) + ); - // LICENSE mode: replace the challenge in the request - if (drmInfo.type && interceptType === "LICENSE" && !foundChallengeInBody) { - handleLicenseMode({ - drmInfo, - body, - setBody: (b) => { - config.body = b; - }, - urlOrResource: resource, - getWidevinePssh: () => foundWidevinePssh, - getPlayreadyPssh: () => foundPlayreadyPssh, - widevineDeviceInfo, - playreadyDeviceInfo, - }); - return originalFetch(resource, config); - } - } + if (result.shouldBlock) return; + if (result.shouldIntercept) return result.result; } return originalFetch(resource, config); @@ -849,39 +859,21 @@ function handleLicenseMode({ }; XMLHttpRequest.prototype.send = function (body) { - if (this._method && this._method.toUpperCase() === "POST") { - if (body) { - const drmInfo = detectDRMChallenge(body); + if (this._method && this._method.toUpperCase() === "POST" && body) { + const drmInfo = detectDRMChallenge(body); - // EME mode: block the request if a DRM challenge is detected - if ( - drmInfo.type && - (!remoteCDM || - remoteCDM.challenge === null || - drmInfo.base64 !== remoteCDM.challenge) && - interceptType === "EME" - ) { - foundChallengeInBody = true; - window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); - // Block the request - return; - } + const result = handleDRMInterception( + drmInfo, + body, + this._url, + (b) => originalSend.call(this, b), + () => {} // XHR doesn't need continuation callback + ); - // LICENSE mode: replace the challenge in the request - if (drmInfo.type && interceptType === "LICENSE" && !foundChallengeInBody) { - return handleLicenseMode({ - drmInfo, - body, - setBody: (b) => originalSend.call(this, b), - urlOrResource: this._url, - getWidevinePssh: () => foundWidevinePssh, - getPlayreadyPssh: () => foundPlayreadyPssh, - widevineDeviceInfo, - playreadyDeviceInfo, - }); - } - } + if (result.shouldBlock) return; + if (result.shouldIntercept) return result.result; } + return originalSend.apply(this, arguments); }; })(); -- 2.43.0 From f40e1880d6ec45ac657fba0889f2bfb32d0aa4bd Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:13:17 +0700 Subject: [PATCH 24/26] add logging for POST request interception in DRM handling --- src/inject.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/inject.js b/src/inject.js index 0793cd1..982c312 100644 --- a/src/inject.js +++ b/src/inject.js @@ -827,6 +827,7 @@ function handleDRMInterception(drmInfo, body, url, setBodyCallback, continueRequ const method = (config.method || "GET").toUpperCase(); if (method === "POST" && config.body) { + logWithPrefix("[FETCH] Intercepting POST request to:", resource); const drmInfo = detectDRMChallenge(config.body); const result = handleDRMInterception( @@ -860,6 +861,7 @@ function handleDRMInterception(drmInfo, body, url, setBodyCallback, continueRequ XMLHttpRequest.prototype.send = function (body) { if (this._method && this._method.toUpperCase() === "POST" && body) { + logWithPrefix("[XHR] Intercepting POST request to:", this._url); const drmInfo = detectDRMChallenge(body); const result = handleDRMInterception( -- 2.43.0 From 0b59c6b0d6090a5e54d3e840f9bbc09eb4da9109 Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:28:25 +0700 Subject: [PATCH 25/26] feat: implement `resetDRMState` to clear state and session on new manifest detection --- src/inject.js | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/inject.js b/src/inject.js index 982c312..a759d62 100644 --- a/src/inject.js +++ b/src/inject.js @@ -39,6 +39,47 @@ const logWithPrefix = (...args) => { } }; +function resetDRMState() { + logWithPrefix("Resetting DRM state for new manifest..."); + + // Reset DRM detection state + originalChallenge = null; + serviceCertFound = false; + drmType = "NONE"; + psshFound = false; + foundWidevinePssh = null; + foundPlayreadyPssh = null; + drmDecided = null; + + // Reset CDM and session state + if (remoteCDM) { + try { + // Try to close the existing session if it exists + if (remoteCDM.session_id) { + remoteCDM.closeSession(); + } + } catch (e) { + // Ignore errors when closing session + logWithPrefix("Error closing previous CDM session:", e.message); + } + remoteCDM = null; + } + + // Reset interceptor state + generateRequestCalled = false; + remoteListenerMounted = false; + injectionSuccess = false; + foundChallengeInBody = false; + licenseResponseCounter = 0; + keysRetrieved = false; + + // Post reset messages to clear UI state + window.postMessage({ type: "__DRM_TYPE__", data: "" }, "*"); + window.postMessage({ type: "__PSSH_DATA__", data: "" }, "*"); + window.postMessage({ type: "__KEYS_DATA__", data: "" }, "*"); + window.postMessage({ type: "__LICENSE_URL__", data: "" }, "*"); +} + window.postMessage({ type: "__GET_DRM_OVERRIDE__" }, "*"); window.postMessage({ type: "__GET_INJECTION_TYPE__" }, "*"); window.postMessage({ type: "__GET_CDM_DEVICES__" }, "*"); @@ -94,6 +135,9 @@ function headersToFlags(headersObj) { } function handleManifestDetection(url, headersObj, contentType, source) { + // Reset DRM state when new manifest is detected + resetDRMState(); + window.postMessage({ type: "__MANIFEST_URL__", data: url }, "*"); logWithPrefix(`[Manifest][${source}]`, url, contentType); -- 2.43.0 From ee9eeb30ea42661f89a7c194c4cec932cd1de024 Mon Sep 17 00:00:00 2001 From: voldemort <5692900+yell0wsuit@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:55:02 +0700 Subject: [PATCH 26/26] feat: add js minification during file copy in build process --- README.md | 1 + buildext.js | 57 ++++++++++++++++++- package-lock.json | 136 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 3 + 4 files changed, 194 insertions(+), 3 deletions(-) create mode 100644 package-lock.json diff --git a/README.md b/README.md index 4cb2211..48c514f 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ To update the version across the entire project, simply change the version numbe - Run the build script: ```bash +npm install npm run buildext ``` diff --git a/buildext.js b/buildext.js index 632d6fb..c6b7bcb 100644 --- a/buildext.js +++ b/buildext.js @@ -1,6 +1,7 @@ import { execSync } from "child_process"; import fs from "fs"; import path from "path"; +import { minify } from "terser"; import url from "url"; import syncVersion from "./syncVersion.js"; @@ -25,6 +26,56 @@ const copyDir = async (src, dest) => { }); }; +const minifyJS = async (jsContent) => { + try { + const result = await minify(jsContent, { + compress: { + drop_console: false, // Keep console logs for debugging + drop_debugger: true, + pure_funcs: ["console.debug"], + }, + mangle: { + reserved: ["chrome"], // Don't mangle chrome API + }, + }); + return result.code; + } catch (error) { + console.warn("⚠️ Minification failed, using original:", error.message); + return jsContent; + } +}; + +// Copy and minify JavaScript files from src directory +const copyAndMinifySrcFiles = async (src, dest) => { + await fs.promises.mkdir(dest, { recursive: true }); + + const entries = await fs.promises.readdir(src, { withFileTypes: true }); + + for (const entry of entries) { + const srcPath = path.join(src, entry.name); + const destPath = path.join(dest, entry.name); + + if (entry.isDirectory()) { + await copyAndMinifySrcFiles(srcPath, destPath); + } else if (entry.name.endsWith(".js")) { + // Minify JavaScript files + console.log(`🗜️ Minifying ${entry.name}...`); + const content = await fs.promises.readFile(srcPath, "utf8"); + const originalSize = Buffer.byteLength(content, "utf8"); + const minified = await minifyJS(content, entry.name); + const minifiedSize = Buffer.byteLength(minified, "utf8"); + const savings = (((originalSize - minifiedSize) / originalSize) * 100).toFixed(1); + console.log( + ` 📊 ${entry.name}: ${originalSize} → ${minifiedSize} bytes (${savings}% smaller)` + ); + await fs.promises.writeFile(destPath, minified, "utf8"); + } else { + // Copy other files as-is + await fs.promises.copyFile(srcPath, destPath); + } + } +}; + const main = async () => { await syncVersion(); @@ -47,9 +98,9 @@ const main = async () => { } await fs.promises.mkdir(releaseDir); - // 4. Copy src files (manifest, background, etc) to release - console.log("📦 Copying src files to extension-release..."); - await copyDir(srcDir, releaseDir); + // 4. Copy and minify src files + console.log("📦 Copying and minifying src files..."); + await copyAndMinifySrcFiles(srcDir, releaseDir); // 5. Copy frontend dist files to release (merged at root) console.log("📦 Copying frontend dist files to extension-release..."); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..df2901f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,136 @@ +{ + "name": "cdrm-extension", + "version": "2.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cdrm-extension", + "version": "2.1.0", + "license": "ISC", + "devDependencies": { + "terser": "^5.43.1" + }, + "engines": { + "node": ">=21.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.10.tgz", + "integrity": "sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/terser": { + "version": "5.43.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", + "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.14.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + } + } +} diff --git a/package.json b/package.json index 2372915..2288e3a 100644 --- a/package.json +++ b/package.json @@ -17,5 +17,8 @@ "type": "module", "engines": { "node": ">=21.0.0" + }, + "devDependencies": { + "terser": "^5.43.1" } } -- 2.43.0