├── .prettierrc ├── .gitignore ├── .doc ├── marquee.png └── chrome-web-store-badge.png ├── assets ├── icon16.png ├── icon32.png ├── icon48.png └── icon128.png ├── .vscode ├── cspell.json └── settings.json ├── tsconfig.json ├── package.json ├── manifest.json ├── README.md ├── src └── worker.ts └── pnpm-lock.yaml /.prettierrc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | _metadata/ 4 | 5 | dist/* 6 | -------------------------------------------------------------------------------- /.doc/marquee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni554n/redium/HEAD/.doc/marquee.png -------------------------------------------------------------------------------- /assets/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni554n/redium/HEAD/assets/icon16.png -------------------------------------------------------------------------------- /assets/icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni554n/redium/HEAD/assets/icon32.png -------------------------------------------------------------------------------- /assets/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni554n/redium/HEAD/assets/icon48.png -------------------------------------------------------------------------------- /assets/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni554n/redium/HEAD/assets/icon128.png -------------------------------------------------------------------------------- /.doc/chrome-web-store-badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni554n/redium/HEAD/.doc/chrome-web-store-badge.png -------------------------------------------------------------------------------- /.vscode/cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json", 3 | "words": ["Freedium", "outdir", "readmedium", "Redium"] 4 | } 5 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "noEmit": true, 6 | "strict": true, 7 | "types": ["chrome-types"] 8 | }, 9 | "include": ["src/**/*"] 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "esbenp.prettier-vscode", 3 | "json.schemas": [ 4 | { 5 | "fileMatch": ["manifest.json"], 6 | "url": "https://json.schemastore.org/chrome-manifest" 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "esbuild --minify src/*.ts --outdir=dist --watch", 4 | "build": "esbuild --minify src/*.ts --outdir=dist", 5 | "zip": "powershell Compress-Archive -Path assets,dist,manifest.json -DestinationPath chrome-web-store.zip" 6 | }, 7 | "devDependencies": { 8 | "chrome-types": "0.1.297", 9 | "esbuild": "0.23.0", 10 | "prettier": "3.3.3" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "Redium", 4 | "version": "3.0", 5 | "description": "Redirect medium articles to proxies.", 6 | "icons": { 7 | "16": "assets/icon16.png", 8 | "32": "assets/icon32.png", 9 | "48": "assets/icon48.png", 10 | "128": "assets/icon128.png" 11 | }, 12 | "action": {}, 13 | "commands": { 14 | "_execute_action": { 15 | "suggested_key": { 16 | "default": "Alt+R" 17 | } 18 | } 19 | }, 20 | "host_permissions": ["*://*.medium.com/*"], 21 | "permissions": [ 22 | "activeTab", 23 | "contentSettings", 24 | "contextMenus", 25 | "management", 26 | "storage" 27 | ], 28 | "background": { 29 | "service_worker": "dist/worker.js" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Redium Icon
Redium

2 | 3 |
Chrome Web Store Users
4 | 5 |
6 |
Redium Banner
7 |
8 | 9 |

Automatically unblock medium articles through proxies such as ReadMedium, Freedium, and Archive.today.

10 | 11 | > [!TIP] 12 | > Click the extension icon or press `Alt + R` to redirect manually. 13 | 14 | ## Build 15 | 16 | Install Redium from Chrome Web Store 17 | 18 | 1. [Download this repo](https://github.com/ni554n/redium/archive/refs/heads/main.zip) and extract it somewhere permanent 19 | 2. `cd` into the extracted **_redium-main_** folder and run `npm install` and `npm run build` 20 | 3. Go to [Chrome Extensions](chrome://extensions/) page and enable `Developer Mode` from the top right corner 21 | 4. Click `Load Unpacked` button and select the folder 22 | 23 | ## Changelog 24 | 25 | ### v3.0 26 | 27 | - Remove proxy services that no longer works 28 | - Added ReadMedium, Freedium, and Archive.today 29 | 30 | ### v2.0 31 | 32 | - Added a keyboard shortcut: `Alt + R` to manually redirect any website to the proxy. 33 | - Added Google Web Cache as the new default proxy. Other proxy services are currently failing to unblock premium articles due to the recent changes made to Medium. However, we can still read the cached version of a premium article through Google Web Cache. But it has some limitations: 34 | - Recent articles may take a few days before Google caches them. 35 | - Dynamic iframes inside articles won't be loaded, so we are limited to only texts and images. 36 | 37 | ### v1.0 38 | 39 | - Initial release. 40 | 41 | ## Information 42 | 43 | **Author:** [Nissan Ahmed](https://anissan.com) ([@ni554n](https://twitter.com/ni554n)) 44 | 45 | **Donate:** [PayPal](https://paypal.me/ni554n) 46 | 47 | -------------------------------------------------------------------------------- /src/worker.ts: -------------------------------------------------------------------------------- 1 | type ProxyService = "ReadMedium" | "Freedium" | "Archive.today"; 2 | 3 | const domain: Readonly> = { 4 | ReadMedium: "readmedium.com", 5 | Freedium: "freedium.cfd", 6 | "Archive.today": "archive.today", 7 | }; 8 | 9 | const AUTO_REDIRECTION_MENU_ID = "auto_redirection_toggle"; 10 | 11 | chrome.runtime.onInstalled.addListener(async ({ reason }) => { 12 | let { 13 | selected_proxy: selectedService, 14 | should_redirect: isAutoRedirectionEnabled, 15 | } = await readStorage(); 16 | 17 | if (reason === "update") { 18 | await chrome.storage.local.remove("selected_proxy"); 19 | selectedService = defaultValue.selected_proxy; 20 | 21 | // TODO: Remove the contentSettings permission on the next update 22 | await chrome.contentSettings.javascript.clear({}); 23 | } 24 | 25 | chrome.contextMenus.removeAll(); 26 | 27 | /* Up to 6 menu items can be added. */ 28 | 29 | chrome.contextMenus.create({ 30 | id: AUTO_REDIRECTION_MENU_ID, 31 | title: "Auto-redirect medium articles on new tab", 32 | contexts: ["action"], 33 | type: "checkbox", 34 | checked: isAutoRedirectionEnabled, 35 | }); 36 | 37 | for (const service of Object.keys(domain)) { 38 | chrome.contextMenus.create({ 39 | id: service, 40 | title: service, 41 | contexts: ["action"], 42 | type: "radio", 43 | checked: selectedService === service, 44 | }); 45 | } 46 | 47 | chrome.contextMenus.create({ 48 | id: "tip", 49 | title: "Tip: Press Alt + R or the extension icon to redirect manually", 50 | contexts: ["action"], 51 | type: "normal", 52 | }); 53 | 54 | init(); 55 | }); 56 | 57 | /** 58 | * Handles the click event on the context menu items. 59 | */ 60 | chrome.contextMenus.onClicked.addListener( 61 | async (menuItem: chrome.contextMenus.OnClickData) => { 62 | if (menuItem.menuItemId === AUTO_REDIRECTION_MENU_ID) { 63 | await writeStorage("should_redirect", menuItem.checked ?? true); 64 | } else if (menuItem.menuItemId in domain) { 65 | await writeStorage("selected_proxy", menuItem.menuItemId as ProxyService); 66 | } 67 | 68 | init(); 69 | }, 70 | ); 71 | 72 | chrome.runtime.onStartup.addListener(init); 73 | chrome.management.onEnabled.addListener(init); 74 | 75 | async function init() { 76 | const { 77 | selected_proxy: selectedService, 78 | should_redirect: isAutoRedirectionEnabled, 79 | } = await readStorage(); 80 | 81 | chrome.action.setTitle({ 82 | title: `Redirect to ${selectedService} (Alt + R)`, 83 | }); 84 | 85 | if (isAutoRedirectionEnabled) chrome.tabs.onUpdated.addListener(autoRedirect); 86 | else chrome.tabs.onUpdated.removeListener(autoRedirect); 87 | } 88 | 89 | /** 90 | * Auto redirects to proxy service if enabled. 91 | */ 92 | async function autoRedirect( 93 | tabId: number, 94 | changeInfo: { status?: chrome.tabs.TabStatus }, 95 | tab: chrome.tabs.Tab, 96 | ) { 97 | if (!tab.url || !changeInfo.status) return; 98 | if (changeInfo.status !== "loading") return; 99 | if (!(await readStorage("should_redirect"))) return; 100 | 101 | // Assumes only medium.com urls will be here as set by host_permissions in manifest.json 102 | const tabUrl = new URL(tab.url); 103 | 104 | if (!tabUrl.hostname.includes("medium.com")) return; 105 | if ( 106 | tabUrl.pathname.startsWith("/m/") || // Used for 3xx redirection 107 | !/^.+-[0-9a-f]{8,}.*$/.test(tabUrl.pathname) // Matches medium post slug hash 108 | ) { 109 | return; 110 | } 111 | 112 | await chrome.tabs.update(tabId, { url: await selectRedirectUrl(tabUrl) }); 113 | } 114 | 115 | /** 116 | * Handles the click event on the extension icon. 117 | */ 118 | chrome.action.onClicked.addListener(async (tab: chrome.tabs.Tab) => { 119 | if (!tab.url?.startsWith("http")) return; 120 | 121 | const redirectUrl = await selectRedirectUrl(new URL(tab.url)); 122 | await chrome.tabs.create({ index: tab.index + 1, url: redirectUrl }); 123 | }); 124 | 125 | async function selectRedirectUrl(tabUrl: URL): Promise { 126 | const selectedService: ProxyService = await readStorage("selected_proxy"); 127 | const serviceDomain: string = domain[selectedService]; 128 | 129 | if (tabUrl.hostname.includes(serviceDomain)) return tabUrl.href; 130 | 131 | switch (selectedService) { 132 | case "ReadMedium": 133 | return `https://${serviceDomain}/en/${tabUrl.href}`; 134 | 135 | case "Archive.today": { 136 | return `https://${serviceDomain}?url=${tabUrl.href}&run=1`; 137 | } 138 | 139 | default: 140 | return `https://${serviceDomain}${tabUrl.pathname}`; 141 | } 142 | } 143 | 144 | /* Storage Helpers */ 145 | 146 | type LocalKv = { 147 | selected_proxy: ProxyService; 148 | should_redirect: boolean; 149 | }; 150 | 151 | const defaultValue: Readonly = { 152 | selected_proxy: "ReadMedium", 153 | should_redirect: true, 154 | }; 155 | 156 | function readStorage(key: "selected_proxy"): Promise; 157 | function readStorage(key: "should_redirect"): Promise; 158 | function readStorage(): Promise; 159 | 160 | async function readStorage(key?: keyof LocalKv) { 161 | const kVs = await chrome.storage.local.get(key); 162 | 163 | if (key !== undefined) return kVs[key] ?? defaultValue[key]; 164 | 165 | return kVs; 166 | } 167 | 168 | async function writeStorage( 169 | key: K, 170 | value: LocalKv[K], 171 | ) { 172 | await chrome.storage.local.set({ [key]: value }); 173 | } 174 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | devDependencies: 11 | chrome-types: 12 | specifier: 0.1.297 13 | version: 0.1.297 14 | esbuild: 15 | specifier: 0.23.0 16 | version: 0.23.0 17 | prettier: 18 | specifier: 3.3.3 19 | version: 3.3.3 20 | 21 | packages: 22 | 23 | '@esbuild/aix-ppc64@0.23.0': 24 | resolution: {integrity: sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==} 25 | engines: {node: '>=18'} 26 | cpu: [ppc64] 27 | os: [aix] 28 | 29 | '@esbuild/android-arm64@0.23.0': 30 | resolution: {integrity: sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==} 31 | engines: {node: '>=18'} 32 | cpu: [arm64] 33 | os: [android] 34 | 35 | '@esbuild/android-arm@0.23.0': 36 | resolution: {integrity: sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==} 37 | engines: {node: '>=18'} 38 | cpu: [arm] 39 | os: [android] 40 | 41 | '@esbuild/android-x64@0.23.0': 42 | resolution: {integrity: sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==} 43 | engines: {node: '>=18'} 44 | cpu: [x64] 45 | os: [android] 46 | 47 | '@esbuild/darwin-arm64@0.23.0': 48 | resolution: {integrity: sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==} 49 | engines: {node: '>=18'} 50 | cpu: [arm64] 51 | os: [darwin] 52 | 53 | '@esbuild/darwin-x64@0.23.0': 54 | resolution: {integrity: sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==} 55 | engines: {node: '>=18'} 56 | cpu: [x64] 57 | os: [darwin] 58 | 59 | '@esbuild/freebsd-arm64@0.23.0': 60 | resolution: {integrity: sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==} 61 | engines: {node: '>=18'} 62 | cpu: [arm64] 63 | os: [freebsd] 64 | 65 | '@esbuild/freebsd-x64@0.23.0': 66 | resolution: {integrity: sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==} 67 | engines: {node: '>=18'} 68 | cpu: [x64] 69 | os: [freebsd] 70 | 71 | '@esbuild/linux-arm64@0.23.0': 72 | resolution: {integrity: sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==} 73 | engines: {node: '>=18'} 74 | cpu: [arm64] 75 | os: [linux] 76 | 77 | '@esbuild/linux-arm@0.23.0': 78 | resolution: {integrity: sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==} 79 | engines: {node: '>=18'} 80 | cpu: [arm] 81 | os: [linux] 82 | 83 | '@esbuild/linux-ia32@0.23.0': 84 | resolution: {integrity: sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==} 85 | engines: {node: '>=18'} 86 | cpu: [ia32] 87 | os: [linux] 88 | 89 | '@esbuild/linux-loong64@0.23.0': 90 | resolution: {integrity: sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==} 91 | engines: {node: '>=18'} 92 | cpu: [loong64] 93 | os: [linux] 94 | 95 | '@esbuild/linux-mips64el@0.23.0': 96 | resolution: {integrity: sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==} 97 | engines: {node: '>=18'} 98 | cpu: [mips64el] 99 | os: [linux] 100 | 101 | '@esbuild/linux-ppc64@0.23.0': 102 | resolution: {integrity: sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==} 103 | engines: {node: '>=18'} 104 | cpu: [ppc64] 105 | os: [linux] 106 | 107 | '@esbuild/linux-riscv64@0.23.0': 108 | resolution: {integrity: sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==} 109 | engines: {node: '>=18'} 110 | cpu: [riscv64] 111 | os: [linux] 112 | 113 | '@esbuild/linux-s390x@0.23.0': 114 | resolution: {integrity: sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==} 115 | engines: {node: '>=18'} 116 | cpu: [s390x] 117 | os: [linux] 118 | 119 | '@esbuild/linux-x64@0.23.0': 120 | resolution: {integrity: sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==} 121 | engines: {node: '>=18'} 122 | cpu: [x64] 123 | os: [linux] 124 | 125 | '@esbuild/netbsd-x64@0.23.0': 126 | resolution: {integrity: sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==} 127 | engines: {node: '>=18'} 128 | cpu: [x64] 129 | os: [netbsd] 130 | 131 | '@esbuild/openbsd-arm64@0.23.0': 132 | resolution: {integrity: sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==} 133 | engines: {node: '>=18'} 134 | cpu: [arm64] 135 | os: [openbsd] 136 | 137 | '@esbuild/openbsd-x64@0.23.0': 138 | resolution: {integrity: sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==} 139 | engines: {node: '>=18'} 140 | cpu: [x64] 141 | os: [openbsd] 142 | 143 | '@esbuild/sunos-x64@0.23.0': 144 | resolution: {integrity: sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==} 145 | engines: {node: '>=18'} 146 | cpu: [x64] 147 | os: [sunos] 148 | 149 | '@esbuild/win32-arm64@0.23.0': 150 | resolution: {integrity: sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==} 151 | engines: {node: '>=18'} 152 | cpu: [arm64] 153 | os: [win32] 154 | 155 | '@esbuild/win32-ia32@0.23.0': 156 | resolution: {integrity: sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==} 157 | engines: {node: '>=18'} 158 | cpu: [ia32] 159 | os: [win32] 160 | 161 | '@esbuild/win32-x64@0.23.0': 162 | resolution: {integrity: sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==} 163 | engines: {node: '>=18'} 164 | cpu: [x64] 165 | os: [win32] 166 | 167 | chrome-types@0.1.297: 168 | resolution: {integrity: sha512-pVvyKx+JBZ5jh08bU6qL2IepJItfVOyGeMJtxfi9T5/LBXGbRlbCm7wMUvW51VqqGniPwJjtU20/M0oxH/wsFg==} 169 | 170 | esbuild@0.23.0: 171 | resolution: {integrity: sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==} 172 | engines: {node: '>=18'} 173 | hasBin: true 174 | 175 | prettier@3.3.3: 176 | resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} 177 | engines: {node: '>=14'} 178 | hasBin: true 179 | 180 | snapshots: 181 | 182 | '@esbuild/aix-ppc64@0.23.0': 183 | optional: true 184 | 185 | '@esbuild/android-arm64@0.23.0': 186 | optional: true 187 | 188 | '@esbuild/android-arm@0.23.0': 189 | optional: true 190 | 191 | '@esbuild/android-x64@0.23.0': 192 | optional: true 193 | 194 | '@esbuild/darwin-arm64@0.23.0': 195 | optional: true 196 | 197 | '@esbuild/darwin-x64@0.23.0': 198 | optional: true 199 | 200 | '@esbuild/freebsd-arm64@0.23.0': 201 | optional: true 202 | 203 | '@esbuild/freebsd-x64@0.23.0': 204 | optional: true 205 | 206 | '@esbuild/linux-arm64@0.23.0': 207 | optional: true 208 | 209 | '@esbuild/linux-arm@0.23.0': 210 | optional: true 211 | 212 | '@esbuild/linux-ia32@0.23.0': 213 | optional: true 214 | 215 | '@esbuild/linux-loong64@0.23.0': 216 | optional: true 217 | 218 | '@esbuild/linux-mips64el@0.23.0': 219 | optional: true 220 | 221 | '@esbuild/linux-ppc64@0.23.0': 222 | optional: true 223 | 224 | '@esbuild/linux-riscv64@0.23.0': 225 | optional: true 226 | 227 | '@esbuild/linux-s390x@0.23.0': 228 | optional: true 229 | 230 | '@esbuild/linux-x64@0.23.0': 231 | optional: true 232 | 233 | '@esbuild/netbsd-x64@0.23.0': 234 | optional: true 235 | 236 | '@esbuild/openbsd-arm64@0.23.0': 237 | optional: true 238 | 239 | '@esbuild/openbsd-x64@0.23.0': 240 | optional: true 241 | 242 | '@esbuild/sunos-x64@0.23.0': 243 | optional: true 244 | 245 | '@esbuild/win32-arm64@0.23.0': 246 | optional: true 247 | 248 | '@esbuild/win32-ia32@0.23.0': 249 | optional: true 250 | 251 | '@esbuild/win32-x64@0.23.0': 252 | optional: true 253 | 254 | chrome-types@0.1.297: {} 255 | 256 | esbuild@0.23.0: 257 | optionalDependencies: 258 | '@esbuild/aix-ppc64': 0.23.0 259 | '@esbuild/android-arm': 0.23.0 260 | '@esbuild/android-arm64': 0.23.0 261 | '@esbuild/android-x64': 0.23.0 262 | '@esbuild/darwin-arm64': 0.23.0 263 | '@esbuild/darwin-x64': 0.23.0 264 | '@esbuild/freebsd-arm64': 0.23.0 265 | '@esbuild/freebsd-x64': 0.23.0 266 | '@esbuild/linux-arm': 0.23.0 267 | '@esbuild/linux-arm64': 0.23.0 268 | '@esbuild/linux-ia32': 0.23.0 269 | '@esbuild/linux-loong64': 0.23.0 270 | '@esbuild/linux-mips64el': 0.23.0 271 | '@esbuild/linux-ppc64': 0.23.0 272 | '@esbuild/linux-riscv64': 0.23.0 273 | '@esbuild/linux-s390x': 0.23.0 274 | '@esbuild/linux-x64': 0.23.0 275 | '@esbuild/netbsd-x64': 0.23.0 276 | '@esbuild/openbsd-arm64': 0.23.0 277 | '@esbuild/openbsd-x64': 0.23.0 278 | '@esbuild/sunos-x64': 0.23.0 279 | '@esbuild/win32-arm64': 0.23.0 280 | '@esbuild/win32-ia32': 0.23.0 281 | '@esbuild/win32-x64': 0.23.0 282 | 283 | prettier@3.3.3: {} 284 | --------------------------------------------------------------------------------