├── .github ├── buttcoin.png ├── qr.svg ├── style-after.png └── style-before.png ├── .gitignore ├── README.md ├── archive ├── index.js ├── package.json └── wrangler.toml.example ├── buttcoin ├── index.js ├── package.json └── wrangler.toml.example ├── cors ├── index.js ├── package.json └── wrangler.toml.example ├── eruda ├── index.js ├── package.json └── wrangler.toml.example ├── hash ├── index.js ├── package.json └── wrangler.toml.example ├── instagram ├── index.js ├── package.json └── wrangler.toml.example ├── ip ├── index.js ├── package.json └── wrangler.toml.example ├── package-lock.json ├── package.json ├── proxy ├── index.js ├── package.json └── wrangler.toml.example ├── pwned ├── index.js ├── package.json └── wrangler.toml.example ├── qr ├── index.js ├── package.json └── wrangler.toml.example ├── style ├── index.js ├── package.json └── wrangler.toml.example ├── unroll ├── index.js ├── package.json └── wrangler.toml.example └── uuid ├── index.js ├── package.json └── wrangler.toml.example /.github/buttcoin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ardislu/cloudflare-workers/4c71f7e633c14d73efbaee6341456a10597104e4/.github/buttcoin.png -------------------------------------------------------------------------------- /.github/qr.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.github/style-after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ardislu/cloudflare-workers/4c71f7e633c14d73efbaee6341456a10597104e4/.github/style-after.png -------------------------------------------------------------------------------- /.github/style-before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ardislu/cloudflare-workers/4c71f7e633c14d73efbaee6341456a10597104e4/.github/style-before.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | wrangler.toml 3 | dist 4 | worker 5 | .install.lock 6 | */package-lock.json 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cloudflare-workers 2 | 3 | Monorepo for my personal [Cloudflare Workers](https://workers.cloudflare.com/). 4 | 5 | Uses [npm workspaces](https://docs.npmjs.com/cli/v7/using-npm/workspaces) (npm 6 | version 7+) to manage multiple `package.json` files (a separate package is 7 | required for each Cloudflare Worker). 8 | 9 | # Quickstart 10 | 11 | 1. Clone this repo 12 | 13 | ``` 14 | git clone https://github.com/ardislu/cloudflare-workers.git 15 | ``` 16 | 17 | 2. Install dependencies 18 | 19 | ``` 20 | npm i 21 | ``` 22 | 23 | 3. For each Cloudflare Worker, make a copy of `wrangler.toml.example` and rename 24 | it to `wrangler.toml`. 25 | [Customize the fields](https://developers.cloudflare.com/workers/wrangler/configuration/) 26 | as required. 27 | 28 | You can use this bash script to quickly make a `wrangler.toml` file from the 29 | `wrangler.toml.example` file in each folder: 30 | 31 | ```bash 32 | find -name '*.example' | xargs -I {} sh -c 'cp -u "$1" "${1%.*}"' -- {} 33 | ``` 34 | 35 | Or using PowerShell: 36 | 37 | ```PowerShell 38 | Get-ChildItem -Recurse -Filter '*.example' | Where-Object { -Not (Test-Path "$($_.DirectoryName)\$($_.BaseName)" -PathType Leaf) } | ForEach-Object { Copy-Item -LiteralPath "$($_.FullName)" -Destination "$($_.DirectoryName)\$($_.BaseName)" } 39 | ``` 40 | 41 | 4. Test that each Cloudflare Worker works: 42 | 43 | ``` 44 | npm run dev --workspace=[Cloudflare Worker name] 45 | ``` 46 | 47 | 5. Deploy all the Workers to Cloudflare: 48 | 49 | ``` 50 | npm run deploy --workspaces 51 | ``` 52 | 53 | Or deploy an individual Worker: 54 | 55 | ``` 56 | npm run deploy --workspace=[Cloudflare Worker name] 57 | ``` 58 | 59 | # Limitations 60 | 61 | There are 62 | [some permission issues](https://github.com/cloudflare/wrangler/issues/240) when 63 | installing `wrangler` as a dependency in an npm workspace. As a workaround, I've 64 | made `wrangler` a dependency in the top-level `package.json` for the overall 65 | monorepo, and omitted `wrangler` from the dependencies for each workspace. That 66 | means that **the npm scripts in each workspace depend on `wrangler` being 67 | installed outside of the workspace**. So if you wanted to pull a workspace out 68 | of the monorepo and run the workspace on its own, you must re-add `wrangler` to 69 | the workspace dependencies first. 70 | 71 | # Workers 72 | 73 | All examples assume that the worker has been deployed at 74 | **`https://x.y.workers.dev`**. 75 | 76 |

Reverse Proxies

77 | 78 | ## proxy 79 | 80 | This is a minimal, basic reverse proxy. Proxy any request through the Cloudflare 81 | Worker by passing the **entire** URL as a query string to the Cloudflare Worker. 82 | 83 | For example, if the request you want to proxy is: 84 | 85 | ``` 86 | https://api.example.com?param1=test1¶m2=test2 87 | ``` 88 | 89 | then the final request URL should be: 90 | 91 | ``` 92 | https://x.y.workers.dev?https://api.example.com?param1=test1¶m2=test2 93 | ``` 94 | 95 | For simplicity, this worker does not configure the `cf` headers on the `fetch` 96 | request that is sent to the Cloudflare proxy. Set the `cf` request headers to 97 | configure the Cloudflare Worker's behavior on caching. See: 98 | [Cache using fetch](https://developers.cloudflare.com/workers/examples/cache-using-fetch). 99 | 100 | ## cors 101 | 102 | This reverse proxy injects permissive 103 | [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) response headers 104 | to a response. Useful for using APIs that have not enabled CORS. 105 | 106 | ``` 107 | https://x.y.workers.dev?https://api-with-unconfigured-cors.example.com?param1=test1¶m2=test2 108 | ``` 109 | 110 | ## style 111 | 112 | This reverse proxy injects a basic CSS style sheet to a page's `` and 113 | returns the result. Good for quickly "upgrading" old/minimalist web pages with 114 | no styling. 115 | 116 | ``` 117 | https://x.y.workers.dev?https://site-with-no-styles.example.com 118 | ``` 119 | 120 | Before (https://www.cs.virginia.edu/~robins/YouAndYourResearch.html): 121 | An unstyled page. The body text stretches the entire width of the screen, making it hard to read. 122 | 123 | After 124 | (https://x.y.workers.dev?https://www.cs.virginia.edu/~robins/YouAndYourResearch.html): 125 | A styled version of the previous page. The body text is centered with a max width of about 700px, making a much easier reading experience. 126 | 127 | ## buttcoin 128 | 129 | Returns a page where all instances of the word "bitcoin" are replaced with 130 | "buttcoin". 131 | 132 | ``` 133 | https://x.y.workers.dev?https://en.wikipedia.org/wiki/Bitcoin 134 | > (See image below) 135 | ``` 136 | 137 | Snippet of the Wikipedia page for 'Bitcoin' but all instances of the word 'bitcoin' are replaced with 'buttcoin'. 138 | 139 | ## eruda 140 | 141 | This reverse proxy injects [eruda](https://github.com/liriliri/eruda) into a 142 | webpage. Good for quickly injecting DevTools into a webpage. 143 | 144 | ``` 145 | https://x.y.workers.dev?https://example.com 146 | ``` 147 | 148 |

Web Utilities

149 | 150 | ## qr 151 | 152 | Generates a SVG file representing a QR code for the value passed to the 153 | endpoint. 154 | 155 | ``` 156 | https://x.y.workers.dev?https://example.com 157 | > (The SVG file rendered below) 158 | ``` 159 | 160 | QR code for https://example.com 161 | 162 | ## unroll 163 | 164 | Redirects a Twitter thread to the corresponding Threadreader thread. 165 | 166 | ``` 167 | https://x.y.workers.dev?https://twitter.com/{username}/status/{id} 168 | > (HTTP redirect to https://threadreaderapp.com/thread/{id}) 169 | ``` 170 | 171 | ## instagram 172 | 173 | Returns a page with an Instagram post embedded in it so the post can be viewed 174 | without logging in. 175 | 176 | ``` 177 | https://x.y.workers.dev?https://www.instagram.com/p/{POST_ID}/ 178 | > (Page with the Instagram post's embedded view in it) 179 | ``` 180 | 181 | ## archive 182 | 183 | Redirects a webpage to the corresponding [archive.ph](https://archive.ph) 184 | capture. Useful to bypass news article paywalls. 185 | 186 | ``` 187 | https://x.y.workers.dev?https://example.com 188 | > (HTTP redirect to https://archive.ph/https://example.com) 189 | ``` 190 | 191 |

Command Line Utilities

192 | 193 | ## pwned 194 | 195 | > [!CAUTION]
The underlying Pwned Passwords API used in this worker uses a 196 | > [_k_-anonymity model](https://en.wikipedia.org/wiki/K-anonymity) to preserve 197 | > your privacy. This worker negates that benefit. This worker should only be 198 | > referenced for education, not actually used in a production backend.

199 | > [Click here to read more.](https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/#cloudflareprivacyandkanonymity) 200 | 201 | Checks a password against the [have i been pwned?](https://haveibeenpwned.com/) 202 | API and returns a boolean indicating if the password has been pwned or not. This 203 | is a simplified implementation of the 204 | [Pwned Passwords Cloudflare Worker](https://github.com/HaveIBeenPwned/PwnedPasswordsCloudflareWorker). 205 | 206 | In bash: 207 | 208 | ```bash 209 | $ curl https://x.y.workers.dev?hunter2 210 | true 211 | ``` 212 | 213 | In PowerShell: 214 | 215 | ```powershell 216 | PS> irm https://x.y.workers.dev?hunter2 217 | True 218 | ``` 219 | 220 | ## ip 221 | 222 | Returns the requester's IP address. Uses the 223 | [Cloudflare header](https://developers.cloudflare.com/workers/runtime-apis/headers#cloudflare-headers) 224 | `CF-Connecting-IP` to get this value. Inspired by and works like 225 | [ifconfig.me](https://ifconfig.me) and [ifconfig.co](https://ifconfig.co). 226 | 227 | In bash: 228 | 229 | ```bash 230 | $ curl https://x.y.workers.dev 231 | 232 | ``` 233 | 234 | In PowerShell: 235 | 236 | ```powershell 237 | PS> irm https://x.y.workers.dev 238 | 239 | ``` 240 | 241 | ## uuid 242 | 243 | Returns a random version 4 UUID. 244 | 245 | In bash: 246 | 247 | ```bash 248 | $ curl https://x.y.workers.dev 249 | 250 | ``` 251 | 252 | In PowerShell: 253 | 254 | ```powershell 255 | PS> irm https://x.y.workers.dev 256 | 257 | ``` 258 | 259 | ## hash 260 | 261 | Returns the hash of a string or a hexadecimal byte array using the 262 | [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest). 263 | 264 | In bash: 265 | 266 | ```bash 267 | # SHA-256 hash of the string "Hello, world!" 268 | $ curl "https://x.y.workers.dev?algorithm=SHA-256&query=Hello%2C%20world%21" 269 | 315F5BDB76D078C43B8AC0064E4A0164612B1FCE77C869345BFC94C75894EDD3 270 | 271 | # MD5 hash of the literal byte array [00, AA, BB, CC] 272 | $ curl "https://x.y.workers.dev?algorithm=MD5&hex=00AABBCC" 273 | CD7409B51FDE80807E2E1532B7432A00 274 | ``` 275 | 276 | In PowerShell: 277 | 278 | ```powershell 279 | # SHA-256 hash of the string "Hello, world!" 280 | PS> irm "https://x.y.workers.dev?algorithm=SHA-256&query=Hello%2C%20world%21" 281 | 315F5BDB76D078C43B8AC0064E4A0164612B1FCE77C869345BFC94C75894EDD3 282 | 283 | # MD5 hash of the literal byte array [00, AA, BB, CC] 284 | PS> irm "https://x.y.workers.dev?algorithm=MD5&hex=00AABBCC" 285 | CD7409B51FDE80807E2E1532B7432A00 286 | ``` 287 | 288 | ### `algorithm` (alias `a`) 289 | 290 | Supported values are: 291 | 292 | - `MD5` (not part of the Web Crypto API but supported by Cloudflare Workers) 293 | - `SHA-1` 294 | - `SHA-256` 295 | - `SHA-384` 296 | - `SHA-512` 297 | 298 | ### `query` (alias `q`) 299 | 300 | The string you want to get the hash of. 301 | 302 | ### `hex` (alias `h`) 303 | 304 | The hexadecimal byte array you want to get the hash of. An optional "0x" prefix 305 | may be given, the prefix is ignored. 306 | 307 | If both `query` and `hex` are provided, `query` will be used. 308 | -------------------------------------------------------------------------------- /archive/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | async fetch(request) { 3 | const url = new URL(request.url); 4 | const queryString = decodeURIComponent(url.search.substring(1)); 5 | 6 | return Response.redirect(`https://archive.ph/${queryString}`); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /archive/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "archive", 3 | "version": "0.3.0", 4 | "description": "Web utility that automatically sends a webpage to archive.ph.", 5 | "license": "ISC", 6 | "author": "Ardis Lu", 7 | "private": true, 8 | "scripts": { 9 | "dev": "wrangler dev", 10 | "deploy": "wrangler deploy" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/ardislu/cloudflare-workers.git" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /archive/wrangler.toml.example: -------------------------------------------------------------------------------- 1 | name = 'archive' 2 | main = 'index.js' 3 | compatibility_date = '2025-05-23' 4 | route = { pattern = 'archive.example.com', custom_domain = true } 5 | -------------------------------------------------------------------------------- /buttcoin/index.js: -------------------------------------------------------------------------------- 1 | import { decode } from 'html-entities'; 2 | import { SetBase, UpdateLink } from '../proxy'; 3 | 4 | class ReplaceBitcoin { 5 | #buffer = ''; 6 | 7 | text(text) { 8 | this.#buffer += text.text; 9 | 10 | if (text.lastInTextNode) { 11 | const buttText = this.#buffer 12 | .replaceAll('bitcoin', 'buttcoin') 13 | .replaceAll('Bitcoin', 'Buttcoin') 14 | .replaceAll('BITCOIN', 'BUTTCOIN') 15 | .replaceAll(/bitcoin/gi, 'buttcoin'); // Any other mixed case just become buttcoin 16 | text.replace(decode(buttText)); 17 | this.#buffer = ''; 18 | } 19 | else { 20 | text.remove(); 21 | } 22 | } 23 | } 24 | 25 | export default { 26 | async fetch(request) { 27 | const url = /^http(s)?:\/\//i.test(request.url) ? new URL(request.url) : new URL(`https://${request.url}`); 28 | 29 | const queryString = decodeURIComponent(url.search.substring(1)); 30 | let remoteUrl; 31 | try { 32 | remoteUrl = new URL(queryString); 33 | } 34 | catch { 35 | return new Response('Invalid URL provided in query string.', { status: 400 }); 36 | } 37 | 38 | const response = await fetch(remoteUrl); 39 | 40 | return new HTMLRewriter() 41 | .on('head', new SetBase(remoteUrl.origin)) 42 | .on('link, script', new UpdateLink()) 43 | .on('html', new ReplaceBitcoin()) 44 | .transform(response); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /buttcoin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "buttcoin", 3 | "version": "0.3.0", 4 | "description": "Reverse proxy which replaces all instances of the word \"bitcoin\" to \"buttcoin\".", 5 | "license": "ISC", 6 | "author": "Ardis Lu", 7 | "private": true, 8 | "scripts": { 9 | "dev": "wrangler dev", 10 | "deploy": "wrangler deploy" 11 | }, 12 | "dependencies": { 13 | "html-entities": "^2.5.2" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/ardislu/cloudflare-workers.git" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /buttcoin/wrangler.toml.example: -------------------------------------------------------------------------------- 1 | name = 'buttcoin' 2 | main = 'index.js' 3 | compatibility_date = '2025-05-23' 4 | route = { pattern = 'buttcoin.example.com', custom_domain = true } 5 | -------------------------------------------------------------------------------- /cors/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | async fetch(request) { 3 | const url = /^http(s)?:\/\//i.test(request.url) ? new URL(request.url) : new URL(`https://${request.url}`); 4 | const queryString = decodeURIComponent(url.search.substring(1)); 5 | 6 | let response; 7 | if (request.method === 'OPTIONS') { 8 | response = new Response(); 9 | response.headers.set('Access-Control-Allow-Origin', request.headers.get('Origin')); 10 | response.headers.set('Access-Control-Allow-Methods', request.headers.get('Access-Control-Request-Method')); 11 | response.headers.set('Access-Control-Allow-Headers', request.headers.get('Access-Control-Request-Headers')); 12 | response.headers.set('Access-Control-Allow-Credentials', 'true'); 13 | response.headers.set('Access-Control-Max-Age', '86400'); 14 | response.headers.append('Vary', 'Origin'); 15 | } 16 | else { 17 | response = await fetch(queryString); 18 | response = new Response(response.body, response); 19 | response.headers.set('Access-Control-Allow-Origin', request.headers.get('Origin')); 20 | response.headers.set('Access-Control-Allow-Credentials', 'true'); 21 | response.headers.set('Access-Control-Expose-Headers', '*, Authorization'); 22 | response.headers.append('Vary', 'Origin'); 23 | } 24 | 25 | return response; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /cors/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cors", 3 | "version": "0.3.0", 4 | "description": "This reverse proxy injects permissive CORS response headers to a response.", 5 | "license": "ISC", 6 | "author": "Ardis Lu", 7 | "private": true, 8 | "scripts": { 9 | "dev": "wrangler dev", 10 | "deploy": "wrangler deploy" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/ardislu/cloudflare-workers.git" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /cors/wrangler.toml.example: -------------------------------------------------------------------------------- 1 | name = 'cors' 2 | main = 'index.js' 3 | compatibility_date = '2025-05-23' 4 | route = { pattern = 'cors.example.com', custom_domain = true } 5 | -------------------------------------------------------------------------------- /eruda/index.js: -------------------------------------------------------------------------------- 1 | import { SetBase, UpdateLink } from '../proxy'; 2 | 3 | class InjectEruda { 4 | element(element) { 5 | element.prepend('', { html: true }); 6 | } 7 | } 8 | 9 | export default { 10 | async fetch(request) { 11 | const url = /^http(s)?:\/\//i.test(request.url) ? new URL(request.url) : new URL(`https://${request.url}`); 12 | const remoteUrl = new URL(decodeURIComponent(url.search.substring(1))); 13 | 14 | const response = await fetch(remoteUrl); 15 | return new HTMLRewriter() 16 | .on('head', new SetBase(remoteUrl.origin)) 17 | .on('link, script', new UpdateLink()) 18 | .on('body', new InjectEruda()) 19 | .transform(response); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /eruda/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eruda", 3 | "version": "0.3.0", 4 | "description": "Reverse proxy which injects eruda into a webpage.", 5 | "license": "ISC", 6 | "author": "Ardis Lu", 7 | "private": true, 8 | "scripts": { 9 | "dev": "wrangler dev", 10 | "deploy": "wrangler deploy" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/ardislu/cloudflare-workers.git" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /eruda/wrangler.toml.example: -------------------------------------------------------------------------------- 1 | name = 'eruda' 2 | main = 'index.js' 3 | compatibility_date = '2025-05-23' 4 | route = { pattern = 'eruda.example.com', custom_domain = true } 5 | -------------------------------------------------------------------------------- /hash/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | async fetch(request) { 3 | const url = new URL(request.url); 4 | let algorithm; 5 | let query; 6 | let hex; 7 | 8 | // Parse URLSearchParams 9 | for (const [key, value] of url.searchParams) { 10 | const normalizedKey = key.toLowerCase(); 11 | if ((normalizedKey === 'a' || normalizedKey === 'algorithm')) { 12 | if (/^SHA-?(1|256|384|512)$/i.test(value)) { 13 | algorithm = value.includes('-') ? value : `SHA-${value.slice(3)}`; // Insert a '-' character if it's not present; crypto.subtle.digest() requires it. 14 | } 15 | else if (value.toUpperCase() === 'MD5') { 16 | algorithm = 'MD5'; 17 | } 18 | } 19 | else if ((normalizedKey === 'q' || normalizedKey === 'query') && value) { // Intentionally fail for falsy values because a null or empty string is also invalid 20 | query = value; 21 | } 22 | else if ((normalizedKey === 'h' || normalizedKey === 'hex') && value) { 23 | hex = value; 24 | } 25 | } 26 | 27 | // Handling bad params 28 | if (algorithm === undefined) { 29 | return new Response('Invalid algorithm provided. Provide one of the following values for the parameter "a" or "algorithm": MD5, SHA-1, SHA-256, SHA-384, or SHA-512.', { status: 400 }); 30 | } 31 | if (query === undefined && hex === undefined) { 32 | return new Response('No input provided. Provide a string value for the parameter "q" or "query", or a hex value for the parameter "h" or "hex".', { status: 400 }); 33 | } 34 | if (hex !== undefined) { 35 | if (hex.length % 2 === 1 || !/^(0x)?[a-fA-F0-9]+$/.test(hex)) { 36 | return new Response('Invalid hexadecimal input provided.', { status: 400 }); 37 | } 38 | } 39 | 40 | let inputArray; 41 | if (query) { 42 | inputArray = new TextEncoder().encode(query); 43 | } 44 | else { 45 | inputArray = Uint8Array.from(hex.replace('0x', '').match(/.{2}/g), v => parseInt(v, 16)); 46 | } 47 | const digest = await crypto.subtle.digest(algorithm, inputArray); 48 | const outputArray = [...new Uint8Array(digest)]; // Need to spread typed array into a non-typed array to allow .map() to output strings 49 | const hash = outputArray.map(v => v.toString(16).padStart(2, '0')).join('').toUpperCase(); 50 | 51 | return new Response(hash); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /hash/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hash", 3 | "version": "0.3.0", 4 | "description": "Return the hash of the input query.", 5 | "license": "ISC", 6 | "author": "Ardis Lu", 7 | "private": true, 8 | "scripts": { 9 | "dev": "wrangler dev", 10 | "deploy": "wrangler deploy" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/ardislu/cloudflare-workers.git" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /hash/wrangler.toml.example: -------------------------------------------------------------------------------- 1 | name = 'hash' 2 | main = 'index.js' 3 | compatibility_date = '2025-05-23' 4 | route = { pattern = 'hash.example.com', custom_domain = true } 5 | -------------------------------------------------------------------------------- /instagram/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | async fetch(request) { 3 | const url = new URL(request.url); 4 | const queryString = decodeURIComponent(url.search.substring(1)); 5 | 6 | const segments = queryString.split('/'); 7 | const id = segments.pop() || segments.pop(); // If there's a trailing '/' character, need to pop() twice 8 | 9 | const responseBody = ` 10 | 11 |
12 | 13 | `; 14 | 15 | return new Response(responseBody, { 16 | headers: { 17 | "content-type": "text/html;charset=UTF-8", 18 | }, 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /instagram/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "instagram", 3 | "version": "0.3.0", 4 | "description": "This reverse proxy embeds Instagram posts so they can be viewed without logging in.", 5 | "license": "ISC", 6 | "author": "Ardis Lu", 7 | "private": true, 8 | "scripts": { 9 | "dev": "wrangler dev", 10 | "deploy": "wrangler deploy" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/ardislu/cloudflare-workers.git" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /instagram/wrangler.toml.example: -------------------------------------------------------------------------------- 1 | name = 'instagram' 2 | main = 'index.js' 3 | compatibility_date = '2025-05-23' 4 | route = { pattern = 'instagram.example.com', custom_domain = true } 5 | -------------------------------------------------------------------------------- /ip/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | async fetch(request) { 3 | return new Response(request.headers.get('CF-Connecting-IP')); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /ip/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ip", 3 | "version": "0.3.0", 4 | "description": "Cloudflare Worker which returns the requester's IP address.", 5 | "license": "ISC", 6 | "author": "Ardis Lu", 7 | "private": true, 8 | "scripts": { 9 | "dev": "wrangler dev", 10 | "deploy": "wrangler deploy" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/ardislu/cloudflare-workers.git" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ip/wrangler.toml.example: -------------------------------------------------------------------------------- 1 | name = 'ip' 2 | main = 'index.js' 3 | compatibility_date = '2025-05-23' 4 | route = { pattern = 'ip.example.com', custom_domain = true } 5 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloudflare-workers", 3 | "version": "0.3.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "cloudflare-workers", 9 | "version": "0.3.0", 10 | "license": "ISC", 11 | "workspaces": [ 12 | "proxy", 13 | "instagram", 14 | "cors", 15 | "pwned", 16 | "qr", 17 | "ip", 18 | "uuid", 19 | "hash", 20 | "style", 21 | "unroll", 22 | "buttcoin", 23 | "archive", 24 | "eruda" 25 | ], 26 | "devDependencies": { 27 | "wrangler": "^4.18.0" 28 | } 29 | }, 30 | "archive": { 31 | "version": "0.3.0", 32 | "license": "ISC" 33 | }, 34 | "buttcoin": { 35 | "version": "0.3.0", 36 | "license": "ISC", 37 | "dependencies": { 38 | "html-entities": "^2.5.2" 39 | } 40 | }, 41 | "cors": { 42 | "version": "0.3.0", 43 | "license": "ISC" 44 | }, 45 | "eruda": { 46 | "version": "0.3.0", 47 | "license": "ISC" 48 | }, 49 | "hash": { 50 | "version": "0.3.0", 51 | "license": "ISC" 52 | }, 53 | "instagram": { 54 | "version": "0.3.0", 55 | "license": "ISC" 56 | }, 57 | "ip": { 58 | "version": "0.3.0", 59 | "license": "ISC" 60 | }, 61 | "node_modules/@cloudflare/kv-asset-handler": { 62 | "version": "0.4.0", 63 | "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.0.tgz", 64 | "integrity": "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA==", 65 | "dev": true, 66 | "license": "MIT OR Apache-2.0", 67 | "dependencies": { 68 | "mime": "^3.0.0" 69 | }, 70 | "engines": { 71 | "node": ">=18.0.0" 72 | } 73 | }, 74 | "node_modules/@cloudflare/unenv-preset": { 75 | "version": "2.3.2", 76 | "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.3.2.tgz", 77 | "integrity": "sha512-MtUgNl+QkQyhQvv5bbWP+BpBC1N0me4CHHuP2H4ktmOMKdB/6kkz/lo+zqiA4mEazb4y+1cwyNjVrQ2DWeE4mg==", 78 | "dev": true, 79 | "license": "MIT OR Apache-2.0", 80 | "peerDependencies": { 81 | "unenv": "2.0.0-rc.17", 82 | "workerd": "^1.20250508.0" 83 | }, 84 | "peerDependenciesMeta": { 85 | "workerd": { 86 | "optional": true 87 | } 88 | } 89 | }, 90 | "node_modules/@cloudflare/workerd-darwin-64": { 91 | "version": "1.20250525.0", 92 | "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250525.0.tgz", 93 | "integrity": "sha512-L5l+7sSJJT2+riR5rS3Q3PKNNySPjWfRIeaNGMVRi1dPO6QPi4lwuxfRUFNoeUdilZJUVPfSZvTtj9RedsKznQ==", 94 | "cpu": [ 95 | "x64" 96 | ], 97 | "dev": true, 98 | "license": "Apache-2.0", 99 | "optional": true, 100 | "os": [ 101 | "darwin" 102 | ], 103 | "engines": { 104 | "node": ">=16" 105 | } 106 | }, 107 | "node_modules/@cloudflare/workerd-darwin-arm64": { 108 | "version": "1.20250525.0", 109 | "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250525.0.tgz", 110 | "integrity": "sha512-Y3IbIdrF/vJWh/WBvshwcSyUh175VAiLRW7963S1dXChrZ1N5wuKGQm9xY69cIGVtitpMJWWW3jLq7J/Xxwm0Q==", 111 | "cpu": [ 112 | "arm64" 113 | ], 114 | "dev": true, 115 | "license": "Apache-2.0", 116 | "optional": true, 117 | "os": [ 118 | "darwin" 119 | ], 120 | "engines": { 121 | "node": ">=16" 122 | } 123 | }, 124 | "node_modules/@cloudflare/workerd-linux-64": { 125 | "version": "1.20250525.0", 126 | "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250525.0.tgz", 127 | "integrity": "sha512-KSyQPAby+c6cpENoO0ayCQlY6QIh28l/+QID7VC1SLXfiNHy+hPNsH1vVBTST6CilHVAQSsy9tCZ9O9XECB8yg==", 128 | "cpu": [ 129 | "x64" 130 | ], 131 | "dev": true, 132 | "license": "Apache-2.0", 133 | "optional": true, 134 | "os": [ 135 | "linux" 136 | ], 137 | "engines": { 138 | "node": ">=16" 139 | } 140 | }, 141 | "node_modules/@cloudflare/workerd-linux-arm64": { 142 | "version": "1.20250525.0", 143 | "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250525.0.tgz", 144 | "integrity": "sha512-Nt0FUxS2kQhJUea4hMCNPaetkrAFDhPnNX/ntwcqVlGgnGt75iaAhupWJbU0GB+gIWlKeuClUUnDZqKbicoKyg==", 145 | "cpu": [ 146 | "arm64" 147 | ], 148 | "dev": true, 149 | "license": "Apache-2.0", 150 | "optional": true, 151 | "os": [ 152 | "linux" 153 | ], 154 | "engines": { 155 | "node": ">=16" 156 | } 157 | }, 158 | "node_modules/@cloudflare/workerd-windows-64": { 159 | "version": "1.20250525.0", 160 | "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250525.0.tgz", 161 | "integrity": "sha512-mwTj+9f3uIa4NEXR1cOa82PjLa6dbrb3J+KCVJFYIaq7e63VxEzOchCXS4tublT2pmOhmFqkgBMXrxozxNkR2Q==", 162 | "cpu": [ 163 | "x64" 164 | ], 165 | "dev": true, 166 | "license": "Apache-2.0", 167 | "optional": true, 168 | "os": [ 169 | "win32" 170 | ], 171 | "engines": { 172 | "node": ">=16" 173 | } 174 | }, 175 | "node_modules/@cspotcode/source-map-support": { 176 | "version": "0.8.1", 177 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 178 | "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 179 | "dev": true, 180 | "license": "MIT", 181 | "dependencies": { 182 | "@jridgewell/trace-mapping": "0.3.9" 183 | }, 184 | "engines": { 185 | "node": ">=12" 186 | } 187 | }, 188 | "node_modules/@emnapi/runtime": { 189 | "version": "1.4.3", 190 | "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", 191 | "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", 192 | "dev": true, 193 | "license": "MIT", 194 | "optional": true, 195 | "dependencies": { 196 | "tslib": "^2.4.0" 197 | } 198 | }, 199 | "node_modules/@esbuild/aix-ppc64": { 200 | "version": "0.25.4", 201 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", 202 | "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", 203 | "cpu": [ 204 | "ppc64" 205 | ], 206 | "dev": true, 207 | "license": "MIT", 208 | "optional": true, 209 | "os": [ 210 | "aix" 211 | ], 212 | "engines": { 213 | "node": ">=18" 214 | } 215 | }, 216 | "node_modules/@esbuild/android-arm": { 217 | "version": "0.25.4", 218 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", 219 | "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", 220 | "cpu": [ 221 | "arm" 222 | ], 223 | "dev": true, 224 | "license": "MIT", 225 | "optional": true, 226 | "os": [ 227 | "android" 228 | ], 229 | "engines": { 230 | "node": ">=18" 231 | } 232 | }, 233 | "node_modules/@esbuild/android-arm64": { 234 | "version": "0.25.4", 235 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", 236 | "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", 237 | "cpu": [ 238 | "arm64" 239 | ], 240 | "dev": true, 241 | "license": "MIT", 242 | "optional": true, 243 | "os": [ 244 | "android" 245 | ], 246 | "engines": { 247 | "node": ">=18" 248 | } 249 | }, 250 | "node_modules/@esbuild/android-x64": { 251 | "version": "0.25.4", 252 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", 253 | "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", 254 | "cpu": [ 255 | "x64" 256 | ], 257 | "dev": true, 258 | "license": "MIT", 259 | "optional": true, 260 | "os": [ 261 | "android" 262 | ], 263 | "engines": { 264 | "node": ">=18" 265 | } 266 | }, 267 | "node_modules/@esbuild/darwin-arm64": { 268 | "version": "0.25.4", 269 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", 270 | "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", 271 | "cpu": [ 272 | "arm64" 273 | ], 274 | "dev": true, 275 | "license": "MIT", 276 | "optional": true, 277 | "os": [ 278 | "darwin" 279 | ], 280 | "engines": { 281 | "node": ">=18" 282 | } 283 | }, 284 | "node_modules/@esbuild/darwin-x64": { 285 | "version": "0.25.4", 286 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", 287 | "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", 288 | "cpu": [ 289 | "x64" 290 | ], 291 | "dev": true, 292 | "license": "MIT", 293 | "optional": true, 294 | "os": [ 295 | "darwin" 296 | ], 297 | "engines": { 298 | "node": ">=18" 299 | } 300 | }, 301 | "node_modules/@esbuild/freebsd-arm64": { 302 | "version": "0.25.4", 303 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", 304 | "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", 305 | "cpu": [ 306 | "arm64" 307 | ], 308 | "dev": true, 309 | "license": "MIT", 310 | "optional": true, 311 | "os": [ 312 | "freebsd" 313 | ], 314 | "engines": { 315 | "node": ">=18" 316 | } 317 | }, 318 | "node_modules/@esbuild/freebsd-x64": { 319 | "version": "0.25.4", 320 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", 321 | "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", 322 | "cpu": [ 323 | "x64" 324 | ], 325 | "dev": true, 326 | "license": "MIT", 327 | "optional": true, 328 | "os": [ 329 | "freebsd" 330 | ], 331 | "engines": { 332 | "node": ">=18" 333 | } 334 | }, 335 | "node_modules/@esbuild/linux-arm": { 336 | "version": "0.25.4", 337 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", 338 | "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", 339 | "cpu": [ 340 | "arm" 341 | ], 342 | "dev": true, 343 | "license": "MIT", 344 | "optional": true, 345 | "os": [ 346 | "linux" 347 | ], 348 | "engines": { 349 | "node": ">=18" 350 | } 351 | }, 352 | "node_modules/@esbuild/linux-arm64": { 353 | "version": "0.25.4", 354 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", 355 | "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", 356 | "cpu": [ 357 | "arm64" 358 | ], 359 | "dev": true, 360 | "license": "MIT", 361 | "optional": true, 362 | "os": [ 363 | "linux" 364 | ], 365 | "engines": { 366 | "node": ">=18" 367 | } 368 | }, 369 | "node_modules/@esbuild/linux-ia32": { 370 | "version": "0.25.4", 371 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", 372 | "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", 373 | "cpu": [ 374 | "ia32" 375 | ], 376 | "dev": true, 377 | "license": "MIT", 378 | "optional": true, 379 | "os": [ 380 | "linux" 381 | ], 382 | "engines": { 383 | "node": ">=18" 384 | } 385 | }, 386 | "node_modules/@esbuild/linux-loong64": { 387 | "version": "0.25.4", 388 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", 389 | "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", 390 | "cpu": [ 391 | "loong64" 392 | ], 393 | "dev": true, 394 | "license": "MIT", 395 | "optional": true, 396 | "os": [ 397 | "linux" 398 | ], 399 | "engines": { 400 | "node": ">=18" 401 | } 402 | }, 403 | "node_modules/@esbuild/linux-mips64el": { 404 | "version": "0.25.4", 405 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", 406 | "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", 407 | "cpu": [ 408 | "mips64el" 409 | ], 410 | "dev": true, 411 | "license": "MIT", 412 | "optional": true, 413 | "os": [ 414 | "linux" 415 | ], 416 | "engines": { 417 | "node": ">=18" 418 | } 419 | }, 420 | "node_modules/@esbuild/linux-ppc64": { 421 | "version": "0.25.4", 422 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", 423 | "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", 424 | "cpu": [ 425 | "ppc64" 426 | ], 427 | "dev": true, 428 | "license": "MIT", 429 | "optional": true, 430 | "os": [ 431 | "linux" 432 | ], 433 | "engines": { 434 | "node": ">=18" 435 | } 436 | }, 437 | "node_modules/@esbuild/linux-riscv64": { 438 | "version": "0.25.4", 439 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", 440 | "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", 441 | "cpu": [ 442 | "riscv64" 443 | ], 444 | "dev": true, 445 | "license": "MIT", 446 | "optional": true, 447 | "os": [ 448 | "linux" 449 | ], 450 | "engines": { 451 | "node": ">=18" 452 | } 453 | }, 454 | "node_modules/@esbuild/linux-s390x": { 455 | "version": "0.25.4", 456 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", 457 | "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", 458 | "cpu": [ 459 | "s390x" 460 | ], 461 | "dev": true, 462 | "license": "MIT", 463 | "optional": true, 464 | "os": [ 465 | "linux" 466 | ], 467 | "engines": { 468 | "node": ">=18" 469 | } 470 | }, 471 | "node_modules/@esbuild/linux-x64": { 472 | "version": "0.25.4", 473 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", 474 | "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", 475 | "cpu": [ 476 | "x64" 477 | ], 478 | "dev": true, 479 | "license": "MIT", 480 | "optional": true, 481 | "os": [ 482 | "linux" 483 | ], 484 | "engines": { 485 | "node": ">=18" 486 | } 487 | }, 488 | "node_modules/@esbuild/netbsd-arm64": { 489 | "version": "0.25.4", 490 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", 491 | "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", 492 | "cpu": [ 493 | "arm64" 494 | ], 495 | "dev": true, 496 | "license": "MIT", 497 | "optional": true, 498 | "os": [ 499 | "netbsd" 500 | ], 501 | "engines": { 502 | "node": ">=18" 503 | } 504 | }, 505 | "node_modules/@esbuild/netbsd-x64": { 506 | "version": "0.25.4", 507 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", 508 | "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", 509 | "cpu": [ 510 | "x64" 511 | ], 512 | "dev": true, 513 | "license": "MIT", 514 | "optional": true, 515 | "os": [ 516 | "netbsd" 517 | ], 518 | "engines": { 519 | "node": ">=18" 520 | } 521 | }, 522 | "node_modules/@esbuild/openbsd-arm64": { 523 | "version": "0.25.4", 524 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", 525 | "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", 526 | "cpu": [ 527 | "arm64" 528 | ], 529 | "dev": true, 530 | "license": "MIT", 531 | "optional": true, 532 | "os": [ 533 | "openbsd" 534 | ], 535 | "engines": { 536 | "node": ">=18" 537 | } 538 | }, 539 | "node_modules/@esbuild/openbsd-x64": { 540 | "version": "0.25.4", 541 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", 542 | "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", 543 | "cpu": [ 544 | "x64" 545 | ], 546 | "dev": true, 547 | "license": "MIT", 548 | "optional": true, 549 | "os": [ 550 | "openbsd" 551 | ], 552 | "engines": { 553 | "node": ">=18" 554 | } 555 | }, 556 | "node_modules/@esbuild/sunos-x64": { 557 | "version": "0.25.4", 558 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", 559 | "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", 560 | "cpu": [ 561 | "x64" 562 | ], 563 | "dev": true, 564 | "license": "MIT", 565 | "optional": true, 566 | "os": [ 567 | "sunos" 568 | ], 569 | "engines": { 570 | "node": ">=18" 571 | } 572 | }, 573 | "node_modules/@esbuild/win32-arm64": { 574 | "version": "0.25.4", 575 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", 576 | "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", 577 | "cpu": [ 578 | "arm64" 579 | ], 580 | "dev": true, 581 | "license": "MIT", 582 | "optional": true, 583 | "os": [ 584 | "win32" 585 | ], 586 | "engines": { 587 | "node": ">=18" 588 | } 589 | }, 590 | "node_modules/@esbuild/win32-ia32": { 591 | "version": "0.25.4", 592 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", 593 | "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", 594 | "cpu": [ 595 | "ia32" 596 | ], 597 | "dev": true, 598 | "license": "MIT", 599 | "optional": true, 600 | "os": [ 601 | "win32" 602 | ], 603 | "engines": { 604 | "node": ">=18" 605 | } 606 | }, 607 | "node_modules/@esbuild/win32-x64": { 608 | "version": "0.25.4", 609 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", 610 | "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", 611 | "cpu": [ 612 | "x64" 613 | ], 614 | "dev": true, 615 | "license": "MIT", 616 | "optional": true, 617 | "os": [ 618 | "win32" 619 | ], 620 | "engines": { 621 | "node": ">=18" 622 | } 623 | }, 624 | "node_modules/@fastify/busboy": { 625 | "version": "2.1.1", 626 | "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", 627 | "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", 628 | "dev": true, 629 | "license": "MIT", 630 | "engines": { 631 | "node": ">=14" 632 | } 633 | }, 634 | "node_modules/@img/sharp-darwin-arm64": { 635 | "version": "0.33.5", 636 | "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", 637 | "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", 638 | "cpu": [ 639 | "arm64" 640 | ], 641 | "dev": true, 642 | "license": "Apache-2.0", 643 | "optional": true, 644 | "os": [ 645 | "darwin" 646 | ], 647 | "engines": { 648 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 649 | }, 650 | "funding": { 651 | "url": "https://opencollective.com/libvips" 652 | }, 653 | "optionalDependencies": { 654 | "@img/sharp-libvips-darwin-arm64": "1.0.4" 655 | } 656 | }, 657 | "node_modules/@img/sharp-darwin-x64": { 658 | "version": "0.33.5", 659 | "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", 660 | "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", 661 | "cpu": [ 662 | "x64" 663 | ], 664 | "dev": true, 665 | "license": "Apache-2.0", 666 | "optional": true, 667 | "os": [ 668 | "darwin" 669 | ], 670 | "engines": { 671 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 672 | }, 673 | "funding": { 674 | "url": "https://opencollective.com/libvips" 675 | }, 676 | "optionalDependencies": { 677 | "@img/sharp-libvips-darwin-x64": "1.0.4" 678 | } 679 | }, 680 | "node_modules/@img/sharp-libvips-darwin-arm64": { 681 | "version": "1.0.4", 682 | "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", 683 | "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", 684 | "cpu": [ 685 | "arm64" 686 | ], 687 | "dev": true, 688 | "license": "LGPL-3.0-or-later", 689 | "optional": true, 690 | "os": [ 691 | "darwin" 692 | ], 693 | "funding": { 694 | "url": "https://opencollective.com/libvips" 695 | } 696 | }, 697 | "node_modules/@img/sharp-libvips-darwin-x64": { 698 | "version": "1.0.4", 699 | "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", 700 | "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", 701 | "cpu": [ 702 | "x64" 703 | ], 704 | "dev": true, 705 | "license": "LGPL-3.0-or-later", 706 | "optional": true, 707 | "os": [ 708 | "darwin" 709 | ], 710 | "funding": { 711 | "url": "https://opencollective.com/libvips" 712 | } 713 | }, 714 | "node_modules/@img/sharp-libvips-linux-arm": { 715 | "version": "1.0.5", 716 | "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", 717 | "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", 718 | "cpu": [ 719 | "arm" 720 | ], 721 | "dev": true, 722 | "license": "LGPL-3.0-or-later", 723 | "optional": true, 724 | "os": [ 725 | "linux" 726 | ], 727 | "funding": { 728 | "url": "https://opencollective.com/libvips" 729 | } 730 | }, 731 | "node_modules/@img/sharp-libvips-linux-arm64": { 732 | "version": "1.0.4", 733 | "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", 734 | "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", 735 | "cpu": [ 736 | "arm64" 737 | ], 738 | "dev": true, 739 | "license": "LGPL-3.0-or-later", 740 | "optional": true, 741 | "os": [ 742 | "linux" 743 | ], 744 | "funding": { 745 | "url": "https://opencollective.com/libvips" 746 | } 747 | }, 748 | "node_modules/@img/sharp-libvips-linux-s390x": { 749 | "version": "1.0.4", 750 | "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", 751 | "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", 752 | "cpu": [ 753 | "s390x" 754 | ], 755 | "dev": true, 756 | "license": "LGPL-3.0-or-later", 757 | "optional": true, 758 | "os": [ 759 | "linux" 760 | ], 761 | "funding": { 762 | "url": "https://opencollective.com/libvips" 763 | } 764 | }, 765 | "node_modules/@img/sharp-libvips-linux-x64": { 766 | "version": "1.0.4", 767 | "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", 768 | "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", 769 | "cpu": [ 770 | "x64" 771 | ], 772 | "dev": true, 773 | "license": "LGPL-3.0-or-later", 774 | "optional": true, 775 | "os": [ 776 | "linux" 777 | ], 778 | "funding": { 779 | "url": "https://opencollective.com/libvips" 780 | } 781 | }, 782 | "node_modules/@img/sharp-libvips-linuxmusl-arm64": { 783 | "version": "1.0.4", 784 | "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", 785 | "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", 786 | "cpu": [ 787 | "arm64" 788 | ], 789 | "dev": true, 790 | "license": "LGPL-3.0-or-later", 791 | "optional": true, 792 | "os": [ 793 | "linux" 794 | ], 795 | "funding": { 796 | "url": "https://opencollective.com/libvips" 797 | } 798 | }, 799 | "node_modules/@img/sharp-libvips-linuxmusl-x64": { 800 | "version": "1.0.4", 801 | "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", 802 | "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", 803 | "cpu": [ 804 | "x64" 805 | ], 806 | "dev": true, 807 | "license": "LGPL-3.0-or-later", 808 | "optional": true, 809 | "os": [ 810 | "linux" 811 | ], 812 | "funding": { 813 | "url": "https://opencollective.com/libvips" 814 | } 815 | }, 816 | "node_modules/@img/sharp-linux-arm": { 817 | "version": "0.33.5", 818 | "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", 819 | "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", 820 | "cpu": [ 821 | "arm" 822 | ], 823 | "dev": true, 824 | "license": "Apache-2.0", 825 | "optional": true, 826 | "os": [ 827 | "linux" 828 | ], 829 | "engines": { 830 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 831 | }, 832 | "funding": { 833 | "url": "https://opencollective.com/libvips" 834 | }, 835 | "optionalDependencies": { 836 | "@img/sharp-libvips-linux-arm": "1.0.5" 837 | } 838 | }, 839 | "node_modules/@img/sharp-linux-arm64": { 840 | "version": "0.33.5", 841 | "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", 842 | "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", 843 | "cpu": [ 844 | "arm64" 845 | ], 846 | "dev": true, 847 | "license": "Apache-2.0", 848 | "optional": true, 849 | "os": [ 850 | "linux" 851 | ], 852 | "engines": { 853 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 854 | }, 855 | "funding": { 856 | "url": "https://opencollective.com/libvips" 857 | }, 858 | "optionalDependencies": { 859 | "@img/sharp-libvips-linux-arm64": "1.0.4" 860 | } 861 | }, 862 | "node_modules/@img/sharp-linux-s390x": { 863 | "version": "0.33.5", 864 | "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", 865 | "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", 866 | "cpu": [ 867 | "s390x" 868 | ], 869 | "dev": true, 870 | "license": "Apache-2.0", 871 | "optional": true, 872 | "os": [ 873 | "linux" 874 | ], 875 | "engines": { 876 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 877 | }, 878 | "funding": { 879 | "url": "https://opencollective.com/libvips" 880 | }, 881 | "optionalDependencies": { 882 | "@img/sharp-libvips-linux-s390x": "1.0.4" 883 | } 884 | }, 885 | "node_modules/@img/sharp-linux-x64": { 886 | "version": "0.33.5", 887 | "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", 888 | "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", 889 | "cpu": [ 890 | "x64" 891 | ], 892 | "dev": true, 893 | "license": "Apache-2.0", 894 | "optional": true, 895 | "os": [ 896 | "linux" 897 | ], 898 | "engines": { 899 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 900 | }, 901 | "funding": { 902 | "url": "https://opencollective.com/libvips" 903 | }, 904 | "optionalDependencies": { 905 | "@img/sharp-libvips-linux-x64": "1.0.4" 906 | } 907 | }, 908 | "node_modules/@img/sharp-linuxmusl-arm64": { 909 | "version": "0.33.5", 910 | "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", 911 | "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", 912 | "cpu": [ 913 | "arm64" 914 | ], 915 | "dev": true, 916 | "license": "Apache-2.0", 917 | "optional": true, 918 | "os": [ 919 | "linux" 920 | ], 921 | "engines": { 922 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 923 | }, 924 | "funding": { 925 | "url": "https://opencollective.com/libvips" 926 | }, 927 | "optionalDependencies": { 928 | "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" 929 | } 930 | }, 931 | "node_modules/@img/sharp-linuxmusl-x64": { 932 | "version": "0.33.5", 933 | "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", 934 | "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", 935 | "cpu": [ 936 | "x64" 937 | ], 938 | "dev": true, 939 | "license": "Apache-2.0", 940 | "optional": true, 941 | "os": [ 942 | "linux" 943 | ], 944 | "engines": { 945 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 946 | }, 947 | "funding": { 948 | "url": "https://opencollective.com/libvips" 949 | }, 950 | "optionalDependencies": { 951 | "@img/sharp-libvips-linuxmusl-x64": "1.0.4" 952 | } 953 | }, 954 | "node_modules/@img/sharp-wasm32": { 955 | "version": "0.33.5", 956 | "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", 957 | "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", 958 | "cpu": [ 959 | "wasm32" 960 | ], 961 | "dev": true, 962 | "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", 963 | "optional": true, 964 | "dependencies": { 965 | "@emnapi/runtime": "^1.2.0" 966 | }, 967 | "engines": { 968 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 969 | }, 970 | "funding": { 971 | "url": "https://opencollective.com/libvips" 972 | } 973 | }, 974 | "node_modules/@img/sharp-win32-ia32": { 975 | "version": "0.33.5", 976 | "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", 977 | "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", 978 | "cpu": [ 979 | "ia32" 980 | ], 981 | "dev": true, 982 | "license": "Apache-2.0 AND LGPL-3.0-or-later", 983 | "optional": true, 984 | "os": [ 985 | "win32" 986 | ], 987 | "engines": { 988 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 989 | }, 990 | "funding": { 991 | "url": "https://opencollective.com/libvips" 992 | } 993 | }, 994 | "node_modules/@img/sharp-win32-x64": { 995 | "version": "0.33.5", 996 | "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", 997 | "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", 998 | "cpu": [ 999 | "x64" 1000 | ], 1001 | "dev": true, 1002 | "license": "Apache-2.0 AND LGPL-3.0-or-later", 1003 | "optional": true, 1004 | "os": [ 1005 | "win32" 1006 | ], 1007 | "engines": { 1008 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1009 | }, 1010 | "funding": { 1011 | "url": "https://opencollective.com/libvips" 1012 | } 1013 | }, 1014 | "node_modules/@jridgewell/resolve-uri": { 1015 | "version": "3.1.2", 1016 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 1017 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 1018 | "dev": true, 1019 | "license": "MIT", 1020 | "engines": { 1021 | "node": ">=6.0.0" 1022 | } 1023 | }, 1024 | "node_modules/@jridgewell/sourcemap-codec": { 1025 | "version": "1.5.0", 1026 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", 1027 | "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", 1028 | "dev": true, 1029 | "license": "MIT" 1030 | }, 1031 | "node_modules/@jridgewell/trace-mapping": { 1032 | "version": "0.3.9", 1033 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 1034 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 1035 | "dev": true, 1036 | "license": "MIT", 1037 | "dependencies": { 1038 | "@jridgewell/resolve-uri": "^3.0.3", 1039 | "@jridgewell/sourcemap-codec": "^1.4.10" 1040 | } 1041 | }, 1042 | "node_modules/@paulmillr/qr": { 1043 | "version": "0.3.0", 1044 | "resolved": "https://registry.npmjs.org/@paulmillr/qr/-/qr-0.3.0.tgz", 1045 | "integrity": "sha512-3s/cagXuoXTA2gWSfSfJNanNgm2ifmqgoX8WLOs5//3qrIJ3WWHFjqFqCxvYGf46Afwv6PctT9eAOXLDGwp96Q==", 1046 | "license": "(MIT OR Apache-2.0)", 1047 | "funding": { 1048 | "url": "https://paulmillr.com/funding/" 1049 | } 1050 | }, 1051 | "node_modules/acorn": { 1052 | "version": "8.14.0", 1053 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", 1054 | "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", 1055 | "dev": true, 1056 | "license": "MIT", 1057 | "bin": { 1058 | "acorn": "bin/acorn" 1059 | }, 1060 | "engines": { 1061 | "node": ">=0.4.0" 1062 | } 1063 | }, 1064 | "node_modules/acorn-walk": { 1065 | "version": "8.3.2", 1066 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", 1067 | "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", 1068 | "dev": true, 1069 | "license": "MIT", 1070 | "engines": { 1071 | "node": ">=0.4.0" 1072 | } 1073 | }, 1074 | "node_modules/archive": { 1075 | "resolved": "archive", 1076 | "link": true 1077 | }, 1078 | "node_modules/as-table": { 1079 | "version": "1.0.55", 1080 | "resolved": "https://registry.npmjs.org/as-table/-/as-table-1.0.55.tgz", 1081 | "integrity": "sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==", 1082 | "dev": true, 1083 | "license": "MIT", 1084 | "dependencies": { 1085 | "printable-characters": "^1.0.42" 1086 | } 1087 | }, 1088 | "node_modules/blake3-wasm": { 1089 | "version": "2.1.5", 1090 | "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", 1091 | "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==", 1092 | "dev": true 1093 | }, 1094 | "node_modules/buttcoin": { 1095 | "resolved": "buttcoin", 1096 | "link": true 1097 | }, 1098 | "node_modules/color": { 1099 | "version": "4.2.3", 1100 | "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", 1101 | "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", 1102 | "dev": true, 1103 | "license": "MIT", 1104 | "dependencies": { 1105 | "color-convert": "^2.0.1", 1106 | "color-string": "^1.9.0" 1107 | }, 1108 | "engines": { 1109 | "node": ">=12.5.0" 1110 | } 1111 | }, 1112 | "node_modules/color-convert": { 1113 | "version": "2.0.1", 1114 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1115 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1116 | "dev": true, 1117 | "license": "MIT", 1118 | "dependencies": { 1119 | "color-name": "~1.1.4" 1120 | }, 1121 | "engines": { 1122 | "node": ">=7.0.0" 1123 | } 1124 | }, 1125 | "node_modules/color-name": { 1126 | "version": "1.1.4", 1127 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1128 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1129 | "dev": true, 1130 | "license": "MIT" 1131 | }, 1132 | "node_modules/color-string": { 1133 | "version": "1.9.1", 1134 | "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", 1135 | "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", 1136 | "dev": true, 1137 | "license": "MIT", 1138 | "dependencies": { 1139 | "color-name": "^1.0.0", 1140 | "simple-swizzle": "^0.2.2" 1141 | } 1142 | }, 1143 | "node_modules/cookie": { 1144 | "version": "0.7.2", 1145 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", 1146 | "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", 1147 | "dev": true, 1148 | "license": "MIT", 1149 | "engines": { 1150 | "node": ">= 0.6" 1151 | } 1152 | }, 1153 | "node_modules/cors": { 1154 | "resolved": "cors", 1155 | "link": true 1156 | }, 1157 | "node_modules/data-uri-to-buffer": { 1158 | "version": "2.0.2", 1159 | "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz", 1160 | "integrity": "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==", 1161 | "dev": true, 1162 | "license": "MIT" 1163 | }, 1164 | "node_modules/defu": { 1165 | "version": "6.1.4", 1166 | "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", 1167 | "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", 1168 | "dev": true, 1169 | "license": "MIT" 1170 | }, 1171 | "node_modules/detect-libc": { 1172 | "version": "2.0.4", 1173 | "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", 1174 | "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", 1175 | "dev": true, 1176 | "license": "Apache-2.0", 1177 | "engines": { 1178 | "node": ">=8" 1179 | } 1180 | }, 1181 | "node_modules/eruda": { 1182 | "resolved": "eruda", 1183 | "link": true 1184 | }, 1185 | "node_modules/esbuild": { 1186 | "version": "0.25.4", 1187 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", 1188 | "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", 1189 | "dev": true, 1190 | "hasInstallScript": true, 1191 | "license": "MIT", 1192 | "bin": { 1193 | "esbuild": "bin/esbuild" 1194 | }, 1195 | "engines": { 1196 | "node": ">=18" 1197 | }, 1198 | "optionalDependencies": { 1199 | "@esbuild/aix-ppc64": "0.25.4", 1200 | "@esbuild/android-arm": "0.25.4", 1201 | "@esbuild/android-arm64": "0.25.4", 1202 | "@esbuild/android-x64": "0.25.4", 1203 | "@esbuild/darwin-arm64": "0.25.4", 1204 | "@esbuild/darwin-x64": "0.25.4", 1205 | "@esbuild/freebsd-arm64": "0.25.4", 1206 | "@esbuild/freebsd-x64": "0.25.4", 1207 | "@esbuild/linux-arm": "0.25.4", 1208 | "@esbuild/linux-arm64": "0.25.4", 1209 | "@esbuild/linux-ia32": "0.25.4", 1210 | "@esbuild/linux-loong64": "0.25.4", 1211 | "@esbuild/linux-mips64el": "0.25.4", 1212 | "@esbuild/linux-ppc64": "0.25.4", 1213 | "@esbuild/linux-riscv64": "0.25.4", 1214 | "@esbuild/linux-s390x": "0.25.4", 1215 | "@esbuild/linux-x64": "0.25.4", 1216 | "@esbuild/netbsd-arm64": "0.25.4", 1217 | "@esbuild/netbsd-x64": "0.25.4", 1218 | "@esbuild/openbsd-arm64": "0.25.4", 1219 | "@esbuild/openbsd-x64": "0.25.4", 1220 | "@esbuild/sunos-x64": "0.25.4", 1221 | "@esbuild/win32-arm64": "0.25.4", 1222 | "@esbuild/win32-ia32": "0.25.4", 1223 | "@esbuild/win32-x64": "0.25.4" 1224 | } 1225 | }, 1226 | "node_modules/exit-hook": { 1227 | "version": "2.2.1", 1228 | "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz", 1229 | "integrity": "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==", 1230 | "dev": true, 1231 | "license": "MIT", 1232 | "engines": { 1233 | "node": ">=6" 1234 | }, 1235 | "funding": { 1236 | "url": "https://github.com/sponsors/sindresorhus" 1237 | } 1238 | }, 1239 | "node_modules/exsolve": { 1240 | "version": "1.0.5", 1241 | "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.5.tgz", 1242 | "integrity": "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==", 1243 | "dev": true, 1244 | "license": "MIT" 1245 | }, 1246 | "node_modules/fsevents": { 1247 | "version": "2.3.3", 1248 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 1249 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 1250 | "dev": true, 1251 | "hasInstallScript": true, 1252 | "license": "MIT", 1253 | "optional": true, 1254 | "os": [ 1255 | "darwin" 1256 | ], 1257 | "engines": { 1258 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 1259 | } 1260 | }, 1261 | "node_modules/get-source": { 1262 | "version": "2.0.12", 1263 | "resolved": "https://registry.npmjs.org/get-source/-/get-source-2.0.12.tgz", 1264 | "integrity": "sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==", 1265 | "dev": true, 1266 | "license": "Unlicense", 1267 | "dependencies": { 1268 | "data-uri-to-buffer": "^2.0.0", 1269 | "source-map": "^0.6.1" 1270 | } 1271 | }, 1272 | "node_modules/glob-to-regexp": { 1273 | "version": "0.4.1", 1274 | "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", 1275 | "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", 1276 | "dev": true, 1277 | "license": "BSD-2-Clause" 1278 | }, 1279 | "node_modules/hash": { 1280 | "resolved": "hash", 1281 | "link": true 1282 | }, 1283 | "node_modules/html-entities": { 1284 | "version": "2.5.2", 1285 | "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", 1286 | "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", 1287 | "funding": [ 1288 | { 1289 | "type": "github", 1290 | "url": "https://github.com/sponsors/mdevils" 1291 | }, 1292 | { 1293 | "type": "patreon", 1294 | "url": "https://patreon.com/mdevils" 1295 | } 1296 | ], 1297 | "license": "MIT" 1298 | }, 1299 | "node_modules/instagram": { 1300 | "resolved": "instagram", 1301 | "link": true 1302 | }, 1303 | "node_modules/ip": { 1304 | "resolved": "ip", 1305 | "link": true 1306 | }, 1307 | "node_modules/is-arrayish": { 1308 | "version": "0.3.2", 1309 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", 1310 | "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", 1311 | "dev": true, 1312 | "license": "MIT" 1313 | }, 1314 | "node_modules/mime": { 1315 | "version": "3.0.0", 1316 | "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", 1317 | "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", 1318 | "dev": true, 1319 | "license": "MIT", 1320 | "bin": { 1321 | "mime": "cli.js" 1322 | }, 1323 | "engines": { 1324 | "node": ">=10.0.0" 1325 | } 1326 | }, 1327 | "node_modules/miniflare": { 1328 | "version": "4.20250525.0", 1329 | "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250525.0.tgz", 1330 | "integrity": "sha512-F5XRDn9WqxUaHphUT8qwy5WXC/3UwbBRJTdjjP5uwHX82vypxIlHNyHziZnplPLhQa1kbSdIY7wfuP1XJyyYZw==", 1331 | "dev": true, 1332 | "license": "MIT", 1333 | "dependencies": { 1334 | "@cspotcode/source-map-support": "0.8.1", 1335 | "acorn": "8.14.0", 1336 | "acorn-walk": "8.3.2", 1337 | "exit-hook": "2.2.1", 1338 | "glob-to-regexp": "0.4.1", 1339 | "sharp": "^0.33.5", 1340 | "stoppable": "1.1.0", 1341 | "undici": "^5.28.5", 1342 | "workerd": "1.20250525.0", 1343 | "ws": "8.18.0", 1344 | "youch": "3.3.4", 1345 | "zod": "3.22.3" 1346 | }, 1347 | "bin": { 1348 | "miniflare": "bootstrap.js" 1349 | }, 1350 | "engines": { 1351 | "node": ">=18.0.0" 1352 | } 1353 | }, 1354 | "node_modules/mustache": { 1355 | "version": "4.2.0", 1356 | "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", 1357 | "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", 1358 | "dev": true, 1359 | "license": "MIT", 1360 | "bin": { 1361 | "mustache": "bin/mustache" 1362 | } 1363 | }, 1364 | "node_modules/ohash": { 1365 | "version": "2.0.11", 1366 | "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", 1367 | "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", 1368 | "dev": true, 1369 | "license": "MIT" 1370 | }, 1371 | "node_modules/path-to-regexp": { 1372 | "version": "6.3.0", 1373 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", 1374 | "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", 1375 | "dev": true, 1376 | "license": "MIT" 1377 | }, 1378 | "node_modules/pathe": { 1379 | "version": "2.0.3", 1380 | "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", 1381 | "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", 1382 | "dev": true, 1383 | "license": "MIT" 1384 | }, 1385 | "node_modules/printable-characters": { 1386 | "version": "1.0.42", 1387 | "resolved": "https://registry.npmjs.org/printable-characters/-/printable-characters-1.0.42.tgz", 1388 | "integrity": "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==", 1389 | "dev": true, 1390 | "license": "Unlicense" 1391 | }, 1392 | "node_modules/proxy": { 1393 | "resolved": "proxy", 1394 | "link": true 1395 | }, 1396 | "node_modules/pwned": { 1397 | "resolved": "pwned", 1398 | "link": true 1399 | }, 1400 | "node_modules/qr": { 1401 | "resolved": "qr", 1402 | "link": true 1403 | }, 1404 | "node_modules/semver": { 1405 | "version": "7.7.2", 1406 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", 1407 | "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", 1408 | "dev": true, 1409 | "license": "ISC", 1410 | "bin": { 1411 | "semver": "bin/semver.js" 1412 | }, 1413 | "engines": { 1414 | "node": ">=10" 1415 | } 1416 | }, 1417 | "node_modules/sharp": { 1418 | "version": "0.33.5", 1419 | "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", 1420 | "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", 1421 | "dev": true, 1422 | "hasInstallScript": true, 1423 | "license": "Apache-2.0", 1424 | "dependencies": { 1425 | "color": "^4.2.3", 1426 | "detect-libc": "^2.0.3", 1427 | "semver": "^7.6.3" 1428 | }, 1429 | "engines": { 1430 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1431 | }, 1432 | "funding": { 1433 | "url": "https://opencollective.com/libvips" 1434 | }, 1435 | "optionalDependencies": { 1436 | "@img/sharp-darwin-arm64": "0.33.5", 1437 | "@img/sharp-darwin-x64": "0.33.5", 1438 | "@img/sharp-libvips-darwin-arm64": "1.0.4", 1439 | "@img/sharp-libvips-darwin-x64": "1.0.4", 1440 | "@img/sharp-libvips-linux-arm": "1.0.5", 1441 | "@img/sharp-libvips-linux-arm64": "1.0.4", 1442 | "@img/sharp-libvips-linux-s390x": "1.0.4", 1443 | "@img/sharp-libvips-linux-x64": "1.0.4", 1444 | "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", 1445 | "@img/sharp-libvips-linuxmusl-x64": "1.0.4", 1446 | "@img/sharp-linux-arm": "0.33.5", 1447 | "@img/sharp-linux-arm64": "0.33.5", 1448 | "@img/sharp-linux-s390x": "0.33.5", 1449 | "@img/sharp-linux-x64": "0.33.5", 1450 | "@img/sharp-linuxmusl-arm64": "0.33.5", 1451 | "@img/sharp-linuxmusl-x64": "0.33.5", 1452 | "@img/sharp-wasm32": "0.33.5", 1453 | "@img/sharp-win32-ia32": "0.33.5", 1454 | "@img/sharp-win32-x64": "0.33.5" 1455 | } 1456 | }, 1457 | "node_modules/simple-swizzle": { 1458 | "version": "0.2.2", 1459 | "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", 1460 | "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", 1461 | "dev": true, 1462 | "license": "MIT", 1463 | "dependencies": { 1464 | "is-arrayish": "^0.3.1" 1465 | } 1466 | }, 1467 | "node_modules/source-map": { 1468 | "version": "0.6.1", 1469 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1470 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1471 | "dev": true, 1472 | "license": "BSD-3-Clause", 1473 | "engines": { 1474 | "node": ">=0.10.0" 1475 | } 1476 | }, 1477 | "node_modules/stacktracey": { 1478 | "version": "2.1.8", 1479 | "resolved": "https://registry.npmjs.org/stacktracey/-/stacktracey-2.1.8.tgz", 1480 | "integrity": "sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==", 1481 | "dev": true, 1482 | "license": "Unlicense", 1483 | "dependencies": { 1484 | "as-table": "^1.0.36", 1485 | "get-source": "^2.0.12" 1486 | } 1487 | }, 1488 | "node_modules/stoppable": { 1489 | "version": "1.1.0", 1490 | "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", 1491 | "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", 1492 | "dev": true, 1493 | "license": "MIT", 1494 | "engines": { 1495 | "node": ">=4", 1496 | "npm": ">=6" 1497 | } 1498 | }, 1499 | "node_modules/style": { 1500 | "resolved": "style", 1501 | "link": true 1502 | }, 1503 | "node_modules/tslib": { 1504 | "version": "2.8.1", 1505 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", 1506 | "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", 1507 | "dev": true, 1508 | "license": "0BSD", 1509 | "optional": true 1510 | }, 1511 | "node_modules/ufo": { 1512 | "version": "1.6.1", 1513 | "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", 1514 | "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", 1515 | "dev": true, 1516 | "license": "MIT" 1517 | }, 1518 | "node_modules/undici": { 1519 | "version": "5.29.0", 1520 | "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", 1521 | "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", 1522 | "dev": true, 1523 | "license": "MIT", 1524 | "dependencies": { 1525 | "@fastify/busboy": "^2.0.0" 1526 | }, 1527 | "engines": { 1528 | "node": ">=14.0" 1529 | } 1530 | }, 1531 | "node_modules/unenv": { 1532 | "version": "2.0.0-rc.17", 1533 | "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.17.tgz", 1534 | "integrity": "sha512-B06u0wXkEd+o5gOCMl/ZHl5cfpYbDZKAT+HWTL+Hws6jWu7dCiqBBXXXzMFcFVJb8D4ytAnYmxJA83uwOQRSsg==", 1535 | "dev": true, 1536 | "license": "MIT", 1537 | "dependencies": { 1538 | "defu": "^6.1.4", 1539 | "exsolve": "^1.0.4", 1540 | "ohash": "^2.0.11", 1541 | "pathe": "^2.0.3", 1542 | "ufo": "^1.6.1" 1543 | } 1544 | }, 1545 | "node_modules/unroll": { 1546 | "resolved": "unroll", 1547 | "link": true 1548 | }, 1549 | "node_modules/uuid": { 1550 | "resolved": "uuid", 1551 | "link": true 1552 | }, 1553 | "node_modules/workerd": { 1554 | "version": "1.20250525.0", 1555 | "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250525.0.tgz", 1556 | "integrity": "sha512-SXJgLREy/Aqw2J71Oah0Pbu+SShbqbTExjVQyRBTM1r7MG7fS5NUlknhnt6sikjA/t4cO09Bi8OJqHdTkrcnYQ==", 1557 | "dev": true, 1558 | "hasInstallScript": true, 1559 | "license": "Apache-2.0", 1560 | "bin": { 1561 | "workerd": "bin/workerd" 1562 | }, 1563 | "engines": { 1564 | "node": ">=16" 1565 | }, 1566 | "optionalDependencies": { 1567 | "@cloudflare/workerd-darwin-64": "1.20250525.0", 1568 | "@cloudflare/workerd-darwin-arm64": "1.20250525.0", 1569 | "@cloudflare/workerd-linux-64": "1.20250525.0", 1570 | "@cloudflare/workerd-linux-arm64": "1.20250525.0", 1571 | "@cloudflare/workerd-windows-64": "1.20250525.0" 1572 | } 1573 | }, 1574 | "node_modules/wrangler": { 1575 | "version": "4.18.0", 1576 | "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.18.0.tgz", 1577 | "integrity": "sha512-/ng0KI9io97SNsBU1rheADBLLTE5Djybgsi4gXuvH1RBKJGpyj1xWvZ2fuWu8vAonit3EiZkwtERTm6kESHP3A==", 1578 | "dev": true, 1579 | "license": "MIT OR Apache-2.0", 1580 | "dependencies": { 1581 | "@cloudflare/kv-asset-handler": "0.4.0", 1582 | "@cloudflare/unenv-preset": "2.3.2", 1583 | "blake3-wasm": "2.1.5", 1584 | "esbuild": "0.25.4", 1585 | "miniflare": "4.20250525.0", 1586 | "path-to-regexp": "6.3.0", 1587 | "unenv": "2.0.0-rc.17", 1588 | "workerd": "1.20250525.0" 1589 | }, 1590 | "bin": { 1591 | "wrangler": "bin/wrangler.js", 1592 | "wrangler2": "bin/wrangler.js" 1593 | }, 1594 | "engines": { 1595 | "node": ">=18.0.0" 1596 | }, 1597 | "optionalDependencies": { 1598 | "fsevents": "~2.3.2" 1599 | }, 1600 | "peerDependencies": { 1601 | "@cloudflare/workers-types": "^4.20250525.0" 1602 | }, 1603 | "peerDependenciesMeta": { 1604 | "@cloudflare/workers-types": { 1605 | "optional": true 1606 | } 1607 | } 1608 | }, 1609 | "node_modules/ws": { 1610 | "version": "8.18.0", 1611 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", 1612 | "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", 1613 | "dev": true, 1614 | "license": "MIT", 1615 | "engines": { 1616 | "node": ">=10.0.0" 1617 | }, 1618 | "peerDependencies": { 1619 | "bufferutil": "^4.0.1", 1620 | "utf-8-validate": ">=5.0.2" 1621 | }, 1622 | "peerDependenciesMeta": { 1623 | "bufferutil": { 1624 | "optional": true 1625 | }, 1626 | "utf-8-validate": { 1627 | "optional": true 1628 | } 1629 | } 1630 | }, 1631 | "node_modules/youch": { 1632 | "version": "3.3.4", 1633 | "resolved": "https://registry.npmjs.org/youch/-/youch-3.3.4.tgz", 1634 | "integrity": "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg==", 1635 | "dev": true, 1636 | "license": "MIT", 1637 | "dependencies": { 1638 | "cookie": "^0.7.1", 1639 | "mustache": "^4.2.0", 1640 | "stacktracey": "^2.1.8" 1641 | } 1642 | }, 1643 | "node_modules/zod": { 1644 | "version": "3.22.3", 1645 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.3.tgz", 1646 | "integrity": "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==", 1647 | "dev": true, 1648 | "license": "MIT", 1649 | "funding": { 1650 | "url": "https://github.com/sponsors/colinhacks" 1651 | } 1652 | }, 1653 | "proxy": { 1654 | "version": "0.3.0", 1655 | "license": "ISC" 1656 | }, 1657 | "pwned": { 1658 | "version": "0.3.0", 1659 | "license": "ISC" 1660 | }, 1661 | "qr": { 1662 | "version": "0.3.0", 1663 | "license": "ISC", 1664 | "dependencies": { 1665 | "@paulmillr/qr": "^0.3.0" 1666 | } 1667 | }, 1668 | "style": { 1669 | "version": "0.3.0", 1670 | "license": "ISC" 1671 | }, 1672 | "unroll": { 1673 | "version": "0.3.0", 1674 | "license": "ISC" 1675 | }, 1676 | "uuid": { 1677 | "version": "0.3.0", 1678 | "license": "ISC" 1679 | } 1680 | } 1681 | } 1682 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloudflare-workers", 3 | "version": "0.3.0", 4 | "description": "Monorepo for my personal Cloudflare Workers.", 5 | "license": "ISC", 6 | "author": "Ardis Lu", 7 | "private": true, 8 | "scripts": { 9 | "dev": "wrangler dev", 10 | "deploy": "wrangler deploy" 11 | }, 12 | "devDependencies": { 13 | "wrangler": "^4.18.0" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/ardislu/cloudflare-workers.git" 18 | }, 19 | "workspaces": [ 20 | "proxy", 21 | "instagram", 22 | "cors", 23 | "pwned", 24 | "qr", 25 | "ip", 26 | "uuid", 27 | "hash", 28 | "style", 29 | "unroll", 30 | "buttcoin", 31 | "archive", 32 | "eruda" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /proxy/index.js: -------------------------------------------------------------------------------- 1 | export class SetBase { 2 | constructor(origin) { 3 | this.origin = origin; 4 | } 5 | 6 | element(element) { 7 | element.prepend(``, { html: true }); 8 | } 9 | } 10 | 11 | export class UpdateLink { 12 | element(element) { 13 | for (const attrName of ['href', 'src']) { 14 | const attrValue = element.getAttribute(attrName); 15 | if (attrValue !== null && attrValue[0] === '/') { 16 | element.setAttribute(attrName, attrValue.substring(1)); 17 | } 18 | } 19 | } 20 | } 21 | 22 | export default { 23 | async fetch(request) { 24 | const url = /^http(s)?:\/\//i.test(request.url) ? new URL(request.url) : new URL(`https://${request.url}`); 25 | const remoteUrl = new URL(decodeURIComponent(url.search.substring(1))); 26 | const response = await fetch(remoteUrl); 27 | return new HTMLRewriter() 28 | .on('head', new SetBase(remoteUrl.origin)) 29 | .on('link, script', new UpdateLink()) 30 | .transform(response); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /proxy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "proxy", 3 | "version": "0.3.0", 4 | "description": "This is a minimal, basic reverse proxy.", 5 | "license": "ISC", 6 | "author": "Ardis Lu", 7 | "private": true, 8 | "scripts": { 9 | "dev": "wrangler dev", 10 | "deploy": "wrangler deploy" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/ardislu/cloudflare-workers.git" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /proxy/wrangler.toml.example: -------------------------------------------------------------------------------- 1 | name = 'proxy' 2 | main = 'index.js' 3 | compatibility_date = '2025-05-23' 4 | route = { pattern = 'proxy.example.com', custom_domain = true } 5 | -------------------------------------------------------------------------------- /pwned/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | async fetch(request) { 3 | const url = new URL(request.url); 4 | const queryString = decodeURIComponent(url.search.substring(1)); 5 | 6 | // Convert password string to byte array and hash it, then convert hashed byte array to hex string 7 | const inputArray = new TextEncoder().encode(queryString); 8 | const digest = await crypto.subtle.digest('SHA-1', inputArray); 9 | const outputArray = [...new Uint8Array(digest)]; // Need to spread typed array into a non-typed array to allow .map() to output strings 10 | const hash = outputArray.map(v => v.toString(16).padStart(2, '0')).join('').toUpperCase(); 11 | 12 | // Split the hash as required by the HIBP API (k-Anonymity model) 13 | const range = hash.substring(0, 5); 14 | const index = hash.substring(5); 15 | 16 | const result = fetch(`https://api.pwnedpasswords.com/range/${range}`) 17 | .then(response => response.text()) 18 | .then(body => body.includes(index)) 19 | .then(bool => new Response(bool)); 20 | 21 | return result; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /pwned/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pwned", 3 | "version": "0.3.0", 4 | "description": "This is a re-implementation of the Pwned Passwords Cloudflare Worker.", 5 | "license": "ISC", 6 | "author": "Ardis Lu", 7 | "private": true, 8 | "scripts": { 9 | "dev": "wrangler dev", 10 | "deploy": "wrangler deploy" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/ardislu/cloudflare-workers.git" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /pwned/wrangler.toml.example: -------------------------------------------------------------------------------- 1 | name = 'pwned' 2 | main = 'index.js' 3 | compatibility_date = '2025-05-23' 4 | route = { pattern = 'pwned.example.com', custom_domain = true } 5 | -------------------------------------------------------------------------------- /qr/index.js: -------------------------------------------------------------------------------- 1 | import writeQR from '@paulmillr/qr'; 2 | 3 | export default { 4 | async fetch(request) { 5 | const url = new URL(request.url); 6 | const queryString = decodeURIComponent(url.search.substring(1)); 7 | const qrImage = writeQR(queryString || 'https://example.com', 'svg'); 8 | 9 | return new Response(qrImage, { 10 | headers: { 11 | 'Content-Type': 'image/svg+xml' 12 | } 13 | }) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /qr/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "qr", 3 | "version": "0.3.0", 4 | "description": "QR code generator on a Cloudflare Worker.", 5 | "license": "ISC", 6 | "author": "Ardis Lu", 7 | "private": true, 8 | "scripts": { 9 | "dev": "wrangler dev", 10 | "deploy": "wrangler deploy" 11 | }, 12 | "dependencies": { 13 | "@paulmillr/qr": "^0.3.0" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/ardislu/cloudflare-workers.git" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /qr/wrangler.toml.example: -------------------------------------------------------------------------------- 1 | name = 'qr' 2 | main = 'index.js' 3 | compatibility_date = '2025-05-23' 4 | route = { pattern = 'qr.example.com', custom_domain = true } 5 | -------------------------------------------------------------------------------- /style/index.js: -------------------------------------------------------------------------------- 1 | import { SetBase, UpdateLink } from '../proxy'; 2 | 3 | class InjectStyle { 4 | element(element) { 5 | element.prepend(``, { html: true }); 6 | } 7 | } 8 | 9 | export default { 10 | async fetch(request) { 11 | const url = /^http(s)?:\/\//i.test(request.url) ? new URL(request.url) : new URL(`https://${request.url}`); 12 | const remoteUrl = new URL(decodeURIComponent(url.search.substring(1))); 13 | 14 | const response = await fetch(remoteUrl); 15 | return new HTMLRewriter() 16 | .on('head', new SetBase(remoteUrl.origin)) 17 | .on('link, script', new UpdateLink()) 18 | .on('html', new InjectStyle()) 19 | .transform(response); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /style/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "style", 3 | "version": "0.3.0", 4 | "description": "Reverse proxy which injects a basic CSS style sheet.", 5 | "license": "ISC", 6 | "author": "Ardis Lu", 7 | "private": true, 8 | "scripts": { 9 | "dev": "wrangler dev", 10 | "deploy": "wrangler deploy" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/ardislu/cloudflare-workers.git" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /style/wrangler.toml.example: -------------------------------------------------------------------------------- 1 | name = 'style' 2 | main = 'index.js' 3 | compatibility_date = '2025-05-23' 4 | route = { pattern = 'style.example.com', custom_domain = true } 5 | -------------------------------------------------------------------------------- /unroll/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | async fetch(request) { 3 | const url = new URL(request.url); 4 | const queryString = decodeURIComponent(url.search.substring(1)); 5 | const tweet = queryString.split('/').pop(); 6 | 7 | return Response.redirect(`https://threadreaderapp.com/thread/${tweet}`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /unroll/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "unroll", 3 | "version": "0.3.0", 4 | "description": "Redirects a tweet thread to the corresponding Threadreader thread.", 5 | "license": "ISC", 6 | "author": "Ardis Lu", 7 | "private": true, 8 | "scripts": { 9 | "dev": "wrangler dev", 10 | "deploy": "wrangler deploy" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/ardislu/cloudflare-workers.git" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /unroll/wrangler.toml.example: -------------------------------------------------------------------------------- 1 | name = 'unroll' 2 | main = 'index.js' 3 | compatibility_date = '2025-05-23' 4 | route = { pattern = 'unroll.example.com', custom_domain = true } 5 | -------------------------------------------------------------------------------- /uuid/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | async fetch(request) { 3 | return new Response(crypto.randomUUID()); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /uuid/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uuid", 3 | "version": "0.3.0", 4 | "description": "Cloudflare Worker which returns a random version 4 UUID.", 5 | "license": "ISC", 6 | "author": "Ardis Lu", 7 | "private": true, 8 | "scripts": { 9 | "dev": "wrangler dev", 10 | "deploy": "wrangler deploy" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/ardislu/cloudflare-workers.git" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /uuid/wrangler.toml.example: -------------------------------------------------------------------------------- 1 | name = 'uuid' 2 | main = 'index.js' 3 | compatibility_date = '2025-05-23' 4 | route = { pattern = 'uuid.example.com', custom_domain = true } 5 | --------------------------------------------------------------------------------