├── .gitignore ├── CLAUDE.md ├── README.md ├── package-lock.json ├── package.json ├── public └── index.html ├── src └── index.ts ├── tsconfig.json ├── worker-configuration.d.ts └── wrangler.jsonc /.gitignore: -------------------------------------------------------------------------------- 1 | # prod 2 | dist/ 3 | 4 | # dev 5 | .yarn/ 6 | !.yarn/releases 7 | .vscode/* 8 | !.vscode/launch.json 9 | !.vscode/*.code-snippets 10 | .idea/workspace.xml 11 | .idea/usage.statistics.xml 12 | .idea/shelf 13 | 14 | # deps 15 | node_modules/ 16 | .wrangler 17 | 18 | # env 19 | .env 20 | .env.production 21 | .dev.vars 22 | 23 | # logs 24 | logs/ 25 | *.log 26 | npm-debug.log* 27 | yarn-debug.log* 28 | yarn-error.log* 29 | pnpm-debug.log* 30 | lerna-debug.log* 31 | 32 | # misc 33 | .DS_Store 34 | -------------------------------------------------------------------------------- /CLAUDE.md: -------------------------------------------------------------------------------- 1 | # CLAUDE.md 2 | 3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. 4 | 5 | ## Build Commands 6 | - `npm run dev` - Start development server 7 | - `npm run deploy` - Deploy to Cloudflare Workers 8 | - `npm run cf-typegen` - Generate TypeScript types for Cloudflare bindings 9 | 10 | ## Code Style Guidelines 11 | - **Formatting**: 2-space indentation, semicolons, double quotes 12 | - **Imports**: Group imports at top of file, starting with framework imports 13 | - **Types**: 14 | - Use TypeScript with strict mode 15 | - Use generics for Hono app: `new Hono<{ Bindings: CloudflareBindings }>()` 16 | - Use explicit return types for functions 17 | - **Naming**: 18 | - camelCase for variables, functions, methods 19 | - PascalCase for types, interfaces, classes 20 | - **Documentation**: Use JSDoc-style comments for exported functions 21 | - **Error Handling**: Use try/catch for async operations 22 | - **Framework**: Cloudflare Workers with Hono, following their best practices -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Time Travel Camera 2 | 3 | An example web application that uses Cloudflare Workers, R2 and OpenAI's image generation capabilities to transform your photos into different time periods. 4 | 5 | [![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/rickyrobinett/timetravel-camera) 6 | 7 | ## Overview 8 | 9 | Time Travel Camera is a web-based camera application that runs on [Cloudflare Workers](https://workers.cloudflare.com/) and lets you take photos and "time travel" them to different historical and futuristic periods. The app features. 10 | ## Prerequisites 11 | 12 | - [Node.js](https://nodejs.org/) (v16 or newer) 13 | - npm or yarn 14 | - A [Cloudflare account](https://dash.cloudflare.com/sign-up) (free tier works fine) 15 | - [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/install-and-update/) (`npm install -g wrangler`) 16 | - An [OpenAI API key](https://platform.openai.com/api-keys) 17 | 18 | ## Setup 19 | 20 | 1. Clone the repository: 21 | ```bash 22 | git clone https://github.com/rickyrobinett/timetravel-camera.git 23 | cd timetravel-camera 24 | ``` 25 | 26 | 2. Install dependencies: 27 | ```bash 28 | npm install 29 | ``` 30 | 31 | 3. Create an R2 bucket using Wrangler: 32 | ```bash 33 | npx wrangler r2 bucket create timetravel-images 34 | ``` 35 | 36 | 4. Set up your environment variables by creating a `.dev.vars` file in the root directory with: 37 | ``` 38 | OPENAI_API_KEY=your_openai_api_key_here 39 | ``` 40 | 41 | 5. Configure your `wrangler.jsonc` file if needed (the defaults should work for most cases) 42 | 43 | ## Development 44 | 45 | To run the application in development mode: 46 | 47 | ```bash 48 | npm run dev 49 | ``` 50 | 51 | This will start a local development server at `http://localhost:8787`. The Wrangler dev environment automatically provides HTTPS which is required for camera access. 52 | 53 | ## API Endpoints 54 | 55 | The application provides the following API endpoints through Cloudflare Workers: 56 | 57 | - `/api/health` - Health check endpoint 58 | - `/api/time-periods` - Returns a list of available time periods 59 | - `/api/time-travel` - Transforms an image to a selected time period using OpenAI's GPT-Image-1 60 | - `/api/images/:filename` - Retrieves a stored image from R2 by filename 61 | - `/api/images` - Lists all stored images in the R2 bucket 62 | 63 | ## Deployment 64 | 65 | To deploy the application to Cloudflare Workers: 66 | 67 | 1. Make sure you're logged in to Cloudflare: 68 | ```bash 69 | npx wrangler login 70 | ``` 71 | 72 | 2. Deploy to Cloudflare Workers: 73 | ```bash 74 | npm run deploy 75 | ``` 76 | 77 | 3. Set your environment variable in Cloudflare: 78 | ```bash 79 | npx wrangler secret put OPENAI_API_KEY 80 | ``` 81 | 82 | Once deployed, your application will be available at a `*.workers.dev` domain provided by Cloudflare, or you can configure a custom domain through the Cloudflare dashboard. 83 | 84 | ## Cloudflare Workers Configuration 85 | 86 | This project uses Cloudflare Workers for serverless deployment. Key configuration: 87 | 88 | - `wrangler.jsonc` - Contains Cloudflare Workers configuration 89 | - `worker-configuration.d.ts` - TypeScript definitions for the Worker environment 90 | 91 | For more information on Cloudflare Workers, see the [official documentation](https://developers.cloudflare.com/workers/). 92 | 93 | ## Generating Types 94 | 95 | For generating/synchronizing types based on your Worker configuration: 96 | 97 | ```bash 98 | npm run cf-typegen 99 | ``` 100 | 101 | ## How It Works 102 | 103 | 1. The application captures an image from your device's camera 104 | 2. The image is sent to the Cloudflare Worker backend API with the selected time period 105 | 3. The backend uses OpenAI's gpt-image-1 model to transform the photo 106 | 4. The transformed image is stored in Cloudflare R2 for persistent storage 107 | 5. The image URL is returned to the frontend and displayed 108 | 6. Future requests for the same image are served directly from R2 storage 109 | 110 | ## Technologies Used 111 | 112 | - **Frontend**: HTML, CSS, JavaScript 113 | - **Backend**: [Cloudflare Workers](https://workers.cloudflare.com/) (serverless platform) 114 | - **Storage**: [Cloudflare R2](https://www.cloudflare.com/products/r2/) (object storage for transformed images) 115 | - **Framework**: [Hono](https://hono.dev/) (lightweight web framework for Cloudflare Workers) 116 | - **AI**: [OpenAI GPT-Image-1](https://platform.openai.com/docs/guides/images) model 117 | 118 | ## Resources 119 | 120 | - [Cloudflare Workers Documentation](https://developers.cloudflare.com/workers/) 121 | - [Hono Documentation](https://hono.dev/) 122 | - [OpenAI API Documentation](https://platform.openai.com/docs/api-reference) 123 | - [Wrangler CLI Documentation](https://developers.cloudflare.com/workers/wrangler/) 124 | 125 | Vibe coded with ❤️ in Brooklyn NYC using Claude Code and Cursor -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "timetravel-camera", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "name": "timetravel-camera", 8 | "dependencies": { 9 | "hono": "^4.7.7", 10 | "openai": "^4.96.0" 11 | }, 12 | "devDependencies": { 13 | "@cloudflare/workers-types": "^4.20250424.0", 14 | "wrangler": "^4.13.0" 15 | } 16 | }, 17 | "node_modules/@cloudflare/kv-asset-handler": { 18 | "version": "0.4.0", 19 | "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.0.tgz", 20 | "integrity": "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA==", 21 | "dev": true, 22 | "license": "MIT OR Apache-2.0", 23 | "dependencies": { 24 | "mime": "^3.0.0" 25 | }, 26 | "engines": { 27 | "node": ">=18.0.0" 28 | } 29 | }, 30 | "node_modules/@cloudflare/unenv-preset": { 31 | "version": "2.3.1", 32 | "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.3.1.tgz", 33 | "integrity": "sha512-Xq57Qd+ADpt6hibcVBO0uLG9zzRgyRhfCUgBT9s+g3+3Ivg5zDyVgLFy40ES1VdNcu8rPNSivm9A+kGP5IVaPg==", 34 | "dev": true, 35 | "license": "MIT OR Apache-2.0", 36 | "peerDependencies": { 37 | "unenv": "2.0.0-rc.15", 38 | "workerd": "^1.20250320.0" 39 | }, 40 | "peerDependenciesMeta": { 41 | "workerd": { 42 | "optional": true 43 | } 44 | } 45 | }, 46 | "node_modules/@cloudflare/workerd-darwin-64": { 47 | "version": "1.20250422.0", 48 | "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250422.0.tgz", 49 | "integrity": "sha512-2FWl8TLpC4Knuyw8GmNgUSoJCNJNNGJ7Xv90j2n8FiXR5Clp9jSpm2ovK8RP9P751yX1/iIp8e7QufR/XDB6ow==", 50 | "cpu": [ 51 | "x64" 52 | ], 53 | "dev": true, 54 | "license": "Apache-2.0", 55 | "optional": true, 56 | "os": [ 57 | "darwin" 58 | ], 59 | "engines": { 60 | "node": ">=16" 61 | } 62 | }, 63 | "node_modules/@cloudflare/workerd-darwin-arm64": { 64 | "version": "1.20250422.0", 65 | "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250422.0.tgz", 66 | "integrity": "sha512-GY3W74ivqxsYldacEbMtcSbG7LsS9hPo5UybKIw4RO9GzP7UC5WGnPfuI4PE2SnJOnw7nwSrBLuhGRPe/QQHkQ==", 67 | "cpu": [ 68 | "arm64" 69 | ], 70 | "dev": true, 71 | "license": "Apache-2.0", 72 | "optional": true, 73 | "os": [ 74 | "darwin" 75 | ], 76 | "engines": { 77 | "node": ">=16" 78 | } 79 | }, 80 | "node_modules/@cloudflare/workerd-linux-64": { 81 | "version": "1.20250422.0", 82 | "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250422.0.tgz", 83 | "integrity": "sha512-mtNkEygKtlRq9pMRlm9J4nX4uVHU1AtJ3mSkdNwPwhisTpo989O5Zd0SH9CYwAk8+NmlZsXELpODUVQxQ7FJgw==", 84 | "cpu": [ 85 | "x64" 86 | ], 87 | "dev": true, 88 | "license": "Apache-2.0", 89 | "optional": true, 90 | "os": [ 91 | "linux" 92 | ], 93 | "engines": { 94 | "node": ">=16" 95 | } 96 | }, 97 | "node_modules/@cloudflare/workerd-linux-arm64": { 98 | "version": "1.20250422.0", 99 | "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250422.0.tgz", 100 | "integrity": "sha512-ILlW4/kAoFJvSryrr/QJsiHBdMTf/fjUrIM0hxeuQue8zIEvAVqM1tzpUh8bPJT6AQEbk5ziwkfucA939Z6Tnw==", 101 | "cpu": [ 102 | "arm64" 103 | ], 104 | "dev": true, 105 | "license": "Apache-2.0", 106 | "optional": true, 107 | "os": [ 108 | "linux" 109 | ], 110 | "engines": { 111 | "node": ">=16" 112 | } 113 | }, 114 | "node_modules/@cloudflare/workerd-windows-64": { 115 | "version": "1.20250422.0", 116 | "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250422.0.tgz", 117 | "integrity": "sha512-O2f6f7oxU/oaWX/3/5d/9qvzNSKsw72RsQFjpew2va7KwnnUciI2LnbYR6KYOqRGYrEoiMJxpWPQaYaFVj8t1w==", 118 | "cpu": [ 119 | "x64" 120 | ], 121 | "dev": true, 122 | "license": "Apache-2.0", 123 | "optional": true, 124 | "os": [ 125 | "win32" 126 | ], 127 | "engines": { 128 | "node": ">=16" 129 | } 130 | }, 131 | "node_modules/@cloudflare/workers-types": { 132 | "version": "4.20250424.0", 133 | "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20250424.0.tgz", 134 | "integrity": "sha512-tolHPBVlYSIZq5GWlGbbSqXg1P79u059YJ19cFULwRCF/KpElb9YDq/D9oPxqpw/niS9AvzVBCR5RCxsWv4LDQ==", 135 | "dev": true, 136 | "license": "MIT OR Apache-2.0" 137 | }, 138 | "node_modules/@cspotcode/source-map-support": { 139 | "version": "0.8.1", 140 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 141 | "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 142 | "dev": true, 143 | "license": "MIT", 144 | "dependencies": { 145 | "@jridgewell/trace-mapping": "0.3.9" 146 | }, 147 | "engines": { 148 | "node": ">=12" 149 | } 150 | }, 151 | "node_modules/@emnapi/runtime": { 152 | "version": "1.4.3", 153 | "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", 154 | "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", 155 | "dev": true, 156 | "license": "MIT", 157 | "optional": true, 158 | "dependencies": { 159 | "tslib": "^2.4.0" 160 | } 161 | }, 162 | "node_modules/@esbuild/aix-ppc64": { 163 | "version": "0.25.2", 164 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", 165 | "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", 166 | "cpu": [ 167 | "ppc64" 168 | ], 169 | "dev": true, 170 | "license": "MIT", 171 | "optional": true, 172 | "os": [ 173 | "aix" 174 | ], 175 | "engines": { 176 | "node": ">=18" 177 | } 178 | }, 179 | "node_modules/@esbuild/android-arm": { 180 | "version": "0.25.2", 181 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", 182 | "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", 183 | "cpu": [ 184 | "arm" 185 | ], 186 | "dev": true, 187 | "license": "MIT", 188 | "optional": true, 189 | "os": [ 190 | "android" 191 | ], 192 | "engines": { 193 | "node": ">=18" 194 | } 195 | }, 196 | "node_modules/@esbuild/android-arm64": { 197 | "version": "0.25.2", 198 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", 199 | "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", 200 | "cpu": [ 201 | "arm64" 202 | ], 203 | "dev": true, 204 | "license": "MIT", 205 | "optional": true, 206 | "os": [ 207 | "android" 208 | ], 209 | "engines": { 210 | "node": ">=18" 211 | } 212 | }, 213 | "node_modules/@esbuild/android-x64": { 214 | "version": "0.25.2", 215 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", 216 | "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", 217 | "cpu": [ 218 | "x64" 219 | ], 220 | "dev": true, 221 | "license": "MIT", 222 | "optional": true, 223 | "os": [ 224 | "android" 225 | ], 226 | "engines": { 227 | "node": ">=18" 228 | } 229 | }, 230 | "node_modules/@esbuild/darwin-arm64": { 231 | "version": "0.25.2", 232 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", 233 | "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", 234 | "cpu": [ 235 | "arm64" 236 | ], 237 | "dev": true, 238 | "license": "MIT", 239 | "optional": true, 240 | "os": [ 241 | "darwin" 242 | ], 243 | "engines": { 244 | "node": ">=18" 245 | } 246 | }, 247 | "node_modules/@esbuild/darwin-x64": { 248 | "version": "0.25.2", 249 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", 250 | "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", 251 | "cpu": [ 252 | "x64" 253 | ], 254 | "dev": true, 255 | "license": "MIT", 256 | "optional": true, 257 | "os": [ 258 | "darwin" 259 | ], 260 | "engines": { 261 | "node": ">=18" 262 | } 263 | }, 264 | "node_modules/@esbuild/freebsd-arm64": { 265 | "version": "0.25.2", 266 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", 267 | "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", 268 | "cpu": [ 269 | "arm64" 270 | ], 271 | "dev": true, 272 | "license": "MIT", 273 | "optional": true, 274 | "os": [ 275 | "freebsd" 276 | ], 277 | "engines": { 278 | "node": ">=18" 279 | } 280 | }, 281 | "node_modules/@esbuild/freebsd-x64": { 282 | "version": "0.25.2", 283 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", 284 | "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", 285 | "cpu": [ 286 | "x64" 287 | ], 288 | "dev": true, 289 | "license": "MIT", 290 | "optional": true, 291 | "os": [ 292 | "freebsd" 293 | ], 294 | "engines": { 295 | "node": ">=18" 296 | } 297 | }, 298 | "node_modules/@esbuild/linux-arm": { 299 | "version": "0.25.2", 300 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", 301 | "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", 302 | "cpu": [ 303 | "arm" 304 | ], 305 | "dev": true, 306 | "license": "MIT", 307 | "optional": true, 308 | "os": [ 309 | "linux" 310 | ], 311 | "engines": { 312 | "node": ">=18" 313 | } 314 | }, 315 | "node_modules/@esbuild/linux-arm64": { 316 | "version": "0.25.2", 317 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", 318 | "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", 319 | "cpu": [ 320 | "arm64" 321 | ], 322 | "dev": true, 323 | "license": "MIT", 324 | "optional": true, 325 | "os": [ 326 | "linux" 327 | ], 328 | "engines": { 329 | "node": ">=18" 330 | } 331 | }, 332 | "node_modules/@esbuild/linux-ia32": { 333 | "version": "0.25.2", 334 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", 335 | "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", 336 | "cpu": [ 337 | "ia32" 338 | ], 339 | "dev": true, 340 | "license": "MIT", 341 | "optional": true, 342 | "os": [ 343 | "linux" 344 | ], 345 | "engines": { 346 | "node": ">=18" 347 | } 348 | }, 349 | "node_modules/@esbuild/linux-loong64": { 350 | "version": "0.25.2", 351 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", 352 | "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", 353 | "cpu": [ 354 | "loong64" 355 | ], 356 | "dev": true, 357 | "license": "MIT", 358 | "optional": true, 359 | "os": [ 360 | "linux" 361 | ], 362 | "engines": { 363 | "node": ">=18" 364 | } 365 | }, 366 | "node_modules/@esbuild/linux-mips64el": { 367 | "version": "0.25.2", 368 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", 369 | "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", 370 | "cpu": [ 371 | "mips64el" 372 | ], 373 | "dev": true, 374 | "license": "MIT", 375 | "optional": true, 376 | "os": [ 377 | "linux" 378 | ], 379 | "engines": { 380 | "node": ">=18" 381 | } 382 | }, 383 | "node_modules/@esbuild/linux-ppc64": { 384 | "version": "0.25.2", 385 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", 386 | "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", 387 | "cpu": [ 388 | "ppc64" 389 | ], 390 | "dev": true, 391 | "license": "MIT", 392 | "optional": true, 393 | "os": [ 394 | "linux" 395 | ], 396 | "engines": { 397 | "node": ">=18" 398 | } 399 | }, 400 | "node_modules/@esbuild/linux-riscv64": { 401 | "version": "0.25.2", 402 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", 403 | "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", 404 | "cpu": [ 405 | "riscv64" 406 | ], 407 | "dev": true, 408 | "license": "MIT", 409 | "optional": true, 410 | "os": [ 411 | "linux" 412 | ], 413 | "engines": { 414 | "node": ">=18" 415 | } 416 | }, 417 | "node_modules/@esbuild/linux-s390x": { 418 | "version": "0.25.2", 419 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", 420 | "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", 421 | "cpu": [ 422 | "s390x" 423 | ], 424 | "dev": true, 425 | "license": "MIT", 426 | "optional": true, 427 | "os": [ 428 | "linux" 429 | ], 430 | "engines": { 431 | "node": ">=18" 432 | } 433 | }, 434 | "node_modules/@esbuild/linux-x64": { 435 | "version": "0.25.2", 436 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", 437 | "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", 438 | "cpu": [ 439 | "x64" 440 | ], 441 | "dev": true, 442 | "license": "MIT", 443 | "optional": true, 444 | "os": [ 445 | "linux" 446 | ], 447 | "engines": { 448 | "node": ">=18" 449 | } 450 | }, 451 | "node_modules/@esbuild/netbsd-arm64": { 452 | "version": "0.25.2", 453 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", 454 | "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", 455 | "cpu": [ 456 | "arm64" 457 | ], 458 | "dev": true, 459 | "license": "MIT", 460 | "optional": true, 461 | "os": [ 462 | "netbsd" 463 | ], 464 | "engines": { 465 | "node": ">=18" 466 | } 467 | }, 468 | "node_modules/@esbuild/netbsd-x64": { 469 | "version": "0.25.2", 470 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", 471 | "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", 472 | "cpu": [ 473 | "x64" 474 | ], 475 | "dev": true, 476 | "license": "MIT", 477 | "optional": true, 478 | "os": [ 479 | "netbsd" 480 | ], 481 | "engines": { 482 | "node": ">=18" 483 | } 484 | }, 485 | "node_modules/@esbuild/openbsd-arm64": { 486 | "version": "0.25.2", 487 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", 488 | "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", 489 | "cpu": [ 490 | "arm64" 491 | ], 492 | "dev": true, 493 | "license": "MIT", 494 | "optional": true, 495 | "os": [ 496 | "openbsd" 497 | ], 498 | "engines": { 499 | "node": ">=18" 500 | } 501 | }, 502 | "node_modules/@esbuild/openbsd-x64": { 503 | "version": "0.25.2", 504 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", 505 | "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", 506 | "cpu": [ 507 | "x64" 508 | ], 509 | "dev": true, 510 | "license": "MIT", 511 | "optional": true, 512 | "os": [ 513 | "openbsd" 514 | ], 515 | "engines": { 516 | "node": ">=18" 517 | } 518 | }, 519 | "node_modules/@esbuild/sunos-x64": { 520 | "version": "0.25.2", 521 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", 522 | "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", 523 | "cpu": [ 524 | "x64" 525 | ], 526 | "dev": true, 527 | "license": "MIT", 528 | "optional": true, 529 | "os": [ 530 | "sunos" 531 | ], 532 | "engines": { 533 | "node": ">=18" 534 | } 535 | }, 536 | "node_modules/@esbuild/win32-arm64": { 537 | "version": "0.25.2", 538 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", 539 | "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", 540 | "cpu": [ 541 | "arm64" 542 | ], 543 | "dev": true, 544 | "license": "MIT", 545 | "optional": true, 546 | "os": [ 547 | "win32" 548 | ], 549 | "engines": { 550 | "node": ">=18" 551 | } 552 | }, 553 | "node_modules/@esbuild/win32-ia32": { 554 | "version": "0.25.2", 555 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", 556 | "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", 557 | "cpu": [ 558 | "ia32" 559 | ], 560 | "dev": true, 561 | "license": "MIT", 562 | "optional": true, 563 | "os": [ 564 | "win32" 565 | ], 566 | "engines": { 567 | "node": ">=18" 568 | } 569 | }, 570 | "node_modules/@esbuild/win32-x64": { 571 | "version": "0.25.2", 572 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", 573 | "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", 574 | "cpu": [ 575 | "x64" 576 | ], 577 | "dev": true, 578 | "license": "MIT", 579 | "optional": true, 580 | "os": [ 581 | "win32" 582 | ], 583 | "engines": { 584 | "node": ">=18" 585 | } 586 | }, 587 | "node_modules/@fastify/busboy": { 588 | "version": "2.1.1", 589 | "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", 590 | "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", 591 | "dev": true, 592 | "license": "MIT", 593 | "engines": { 594 | "node": ">=14" 595 | } 596 | }, 597 | "node_modules/@img/sharp-darwin-arm64": { 598 | "version": "0.33.5", 599 | "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", 600 | "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", 601 | "cpu": [ 602 | "arm64" 603 | ], 604 | "dev": true, 605 | "license": "Apache-2.0", 606 | "optional": true, 607 | "os": [ 608 | "darwin" 609 | ], 610 | "engines": { 611 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 612 | }, 613 | "funding": { 614 | "url": "https://opencollective.com/libvips" 615 | }, 616 | "optionalDependencies": { 617 | "@img/sharp-libvips-darwin-arm64": "1.0.4" 618 | } 619 | }, 620 | "node_modules/@img/sharp-darwin-x64": { 621 | "version": "0.33.5", 622 | "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", 623 | "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", 624 | "cpu": [ 625 | "x64" 626 | ], 627 | "dev": true, 628 | "license": "Apache-2.0", 629 | "optional": true, 630 | "os": [ 631 | "darwin" 632 | ], 633 | "engines": { 634 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 635 | }, 636 | "funding": { 637 | "url": "https://opencollective.com/libvips" 638 | }, 639 | "optionalDependencies": { 640 | "@img/sharp-libvips-darwin-x64": "1.0.4" 641 | } 642 | }, 643 | "node_modules/@img/sharp-libvips-darwin-arm64": { 644 | "version": "1.0.4", 645 | "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", 646 | "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", 647 | "cpu": [ 648 | "arm64" 649 | ], 650 | "dev": true, 651 | "license": "LGPL-3.0-or-later", 652 | "optional": true, 653 | "os": [ 654 | "darwin" 655 | ], 656 | "funding": { 657 | "url": "https://opencollective.com/libvips" 658 | } 659 | }, 660 | "node_modules/@img/sharp-libvips-darwin-x64": { 661 | "version": "1.0.4", 662 | "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", 663 | "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", 664 | "cpu": [ 665 | "x64" 666 | ], 667 | "dev": true, 668 | "license": "LGPL-3.0-or-later", 669 | "optional": true, 670 | "os": [ 671 | "darwin" 672 | ], 673 | "funding": { 674 | "url": "https://opencollective.com/libvips" 675 | } 676 | }, 677 | "node_modules/@img/sharp-libvips-linux-arm": { 678 | "version": "1.0.5", 679 | "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", 680 | "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", 681 | "cpu": [ 682 | "arm" 683 | ], 684 | "dev": true, 685 | "license": "LGPL-3.0-or-later", 686 | "optional": true, 687 | "os": [ 688 | "linux" 689 | ], 690 | "funding": { 691 | "url": "https://opencollective.com/libvips" 692 | } 693 | }, 694 | "node_modules/@img/sharp-libvips-linux-arm64": { 695 | "version": "1.0.4", 696 | "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", 697 | "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", 698 | "cpu": [ 699 | "arm64" 700 | ], 701 | "dev": true, 702 | "license": "LGPL-3.0-or-later", 703 | "optional": true, 704 | "os": [ 705 | "linux" 706 | ], 707 | "funding": { 708 | "url": "https://opencollective.com/libvips" 709 | } 710 | }, 711 | "node_modules/@img/sharp-libvips-linux-s390x": { 712 | "version": "1.0.4", 713 | "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", 714 | "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", 715 | "cpu": [ 716 | "s390x" 717 | ], 718 | "dev": true, 719 | "license": "LGPL-3.0-or-later", 720 | "optional": true, 721 | "os": [ 722 | "linux" 723 | ], 724 | "funding": { 725 | "url": "https://opencollective.com/libvips" 726 | } 727 | }, 728 | "node_modules/@img/sharp-libvips-linux-x64": { 729 | "version": "1.0.4", 730 | "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", 731 | "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", 732 | "cpu": [ 733 | "x64" 734 | ], 735 | "dev": true, 736 | "license": "LGPL-3.0-or-later", 737 | "optional": true, 738 | "os": [ 739 | "linux" 740 | ], 741 | "funding": { 742 | "url": "https://opencollective.com/libvips" 743 | } 744 | }, 745 | "node_modules/@img/sharp-libvips-linuxmusl-arm64": { 746 | "version": "1.0.4", 747 | "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", 748 | "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", 749 | "cpu": [ 750 | "arm64" 751 | ], 752 | "dev": true, 753 | "license": "LGPL-3.0-or-later", 754 | "optional": true, 755 | "os": [ 756 | "linux" 757 | ], 758 | "funding": { 759 | "url": "https://opencollective.com/libvips" 760 | } 761 | }, 762 | "node_modules/@img/sharp-libvips-linuxmusl-x64": { 763 | "version": "1.0.4", 764 | "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", 765 | "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", 766 | "cpu": [ 767 | "x64" 768 | ], 769 | "dev": true, 770 | "license": "LGPL-3.0-or-later", 771 | "optional": true, 772 | "os": [ 773 | "linux" 774 | ], 775 | "funding": { 776 | "url": "https://opencollective.com/libvips" 777 | } 778 | }, 779 | "node_modules/@img/sharp-linux-arm": { 780 | "version": "0.33.5", 781 | "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", 782 | "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", 783 | "cpu": [ 784 | "arm" 785 | ], 786 | "dev": true, 787 | "license": "Apache-2.0", 788 | "optional": true, 789 | "os": [ 790 | "linux" 791 | ], 792 | "engines": { 793 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 794 | }, 795 | "funding": { 796 | "url": "https://opencollective.com/libvips" 797 | }, 798 | "optionalDependencies": { 799 | "@img/sharp-libvips-linux-arm": "1.0.5" 800 | } 801 | }, 802 | "node_modules/@img/sharp-linux-arm64": { 803 | "version": "0.33.5", 804 | "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", 805 | "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", 806 | "cpu": [ 807 | "arm64" 808 | ], 809 | "dev": true, 810 | "license": "Apache-2.0", 811 | "optional": true, 812 | "os": [ 813 | "linux" 814 | ], 815 | "engines": { 816 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 817 | }, 818 | "funding": { 819 | "url": "https://opencollective.com/libvips" 820 | }, 821 | "optionalDependencies": { 822 | "@img/sharp-libvips-linux-arm64": "1.0.4" 823 | } 824 | }, 825 | "node_modules/@img/sharp-linux-s390x": { 826 | "version": "0.33.5", 827 | "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", 828 | "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", 829 | "cpu": [ 830 | "s390x" 831 | ], 832 | "dev": true, 833 | "license": "Apache-2.0", 834 | "optional": true, 835 | "os": [ 836 | "linux" 837 | ], 838 | "engines": { 839 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 840 | }, 841 | "funding": { 842 | "url": "https://opencollective.com/libvips" 843 | }, 844 | "optionalDependencies": { 845 | "@img/sharp-libvips-linux-s390x": "1.0.4" 846 | } 847 | }, 848 | "node_modules/@img/sharp-linux-x64": { 849 | "version": "0.33.5", 850 | "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", 851 | "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", 852 | "cpu": [ 853 | "x64" 854 | ], 855 | "dev": true, 856 | "license": "Apache-2.0", 857 | "optional": true, 858 | "os": [ 859 | "linux" 860 | ], 861 | "engines": { 862 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 863 | }, 864 | "funding": { 865 | "url": "https://opencollective.com/libvips" 866 | }, 867 | "optionalDependencies": { 868 | "@img/sharp-libvips-linux-x64": "1.0.4" 869 | } 870 | }, 871 | "node_modules/@img/sharp-linuxmusl-arm64": { 872 | "version": "0.33.5", 873 | "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", 874 | "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", 875 | "cpu": [ 876 | "arm64" 877 | ], 878 | "dev": true, 879 | "license": "Apache-2.0", 880 | "optional": true, 881 | "os": [ 882 | "linux" 883 | ], 884 | "engines": { 885 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 886 | }, 887 | "funding": { 888 | "url": "https://opencollective.com/libvips" 889 | }, 890 | "optionalDependencies": { 891 | "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" 892 | } 893 | }, 894 | "node_modules/@img/sharp-linuxmusl-x64": { 895 | "version": "0.33.5", 896 | "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", 897 | "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", 898 | "cpu": [ 899 | "x64" 900 | ], 901 | "dev": true, 902 | "license": "Apache-2.0", 903 | "optional": true, 904 | "os": [ 905 | "linux" 906 | ], 907 | "engines": { 908 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 909 | }, 910 | "funding": { 911 | "url": "https://opencollective.com/libvips" 912 | }, 913 | "optionalDependencies": { 914 | "@img/sharp-libvips-linuxmusl-x64": "1.0.4" 915 | } 916 | }, 917 | "node_modules/@img/sharp-wasm32": { 918 | "version": "0.33.5", 919 | "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", 920 | "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", 921 | "cpu": [ 922 | "wasm32" 923 | ], 924 | "dev": true, 925 | "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", 926 | "optional": true, 927 | "dependencies": { 928 | "@emnapi/runtime": "^1.2.0" 929 | }, 930 | "engines": { 931 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 932 | }, 933 | "funding": { 934 | "url": "https://opencollective.com/libvips" 935 | } 936 | }, 937 | "node_modules/@img/sharp-win32-ia32": { 938 | "version": "0.33.5", 939 | "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", 940 | "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", 941 | "cpu": [ 942 | "ia32" 943 | ], 944 | "dev": true, 945 | "license": "Apache-2.0 AND LGPL-3.0-or-later", 946 | "optional": true, 947 | "os": [ 948 | "win32" 949 | ], 950 | "engines": { 951 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 952 | }, 953 | "funding": { 954 | "url": "https://opencollective.com/libvips" 955 | } 956 | }, 957 | "node_modules/@img/sharp-win32-x64": { 958 | "version": "0.33.5", 959 | "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", 960 | "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", 961 | "cpu": [ 962 | "x64" 963 | ], 964 | "dev": true, 965 | "license": "Apache-2.0 AND LGPL-3.0-or-later", 966 | "optional": true, 967 | "os": [ 968 | "win32" 969 | ], 970 | "engines": { 971 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 972 | }, 973 | "funding": { 974 | "url": "https://opencollective.com/libvips" 975 | } 976 | }, 977 | "node_modules/@jridgewell/resolve-uri": { 978 | "version": "3.1.2", 979 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 980 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 981 | "dev": true, 982 | "license": "MIT", 983 | "engines": { 984 | "node": ">=6.0.0" 985 | } 986 | }, 987 | "node_modules/@jridgewell/sourcemap-codec": { 988 | "version": "1.5.0", 989 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", 990 | "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", 991 | "dev": true, 992 | "license": "MIT" 993 | }, 994 | "node_modules/@jridgewell/trace-mapping": { 995 | "version": "0.3.9", 996 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 997 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 998 | "dev": true, 999 | "license": "MIT", 1000 | "dependencies": { 1001 | "@jridgewell/resolve-uri": "^3.0.3", 1002 | "@jridgewell/sourcemap-codec": "^1.4.10" 1003 | } 1004 | }, 1005 | "node_modules/@types/node": { 1006 | "version": "18.19.86", 1007 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.86.tgz", 1008 | "integrity": "sha512-fifKayi175wLyKyc5qUfyENhQ1dCNI1UNjp653d8kuYcPQN5JhX3dGuP/XmvPTg/xRBn1VTLpbmi+H/Mr7tLfQ==", 1009 | "license": "MIT", 1010 | "dependencies": { 1011 | "undici-types": "~5.26.4" 1012 | } 1013 | }, 1014 | "node_modules/@types/node-fetch": { 1015 | "version": "2.6.12", 1016 | "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", 1017 | "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", 1018 | "license": "MIT", 1019 | "dependencies": { 1020 | "@types/node": "*", 1021 | "form-data": "^4.0.0" 1022 | } 1023 | }, 1024 | "node_modules/abort-controller": { 1025 | "version": "3.0.0", 1026 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 1027 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 1028 | "license": "MIT", 1029 | "dependencies": { 1030 | "event-target-shim": "^5.0.0" 1031 | }, 1032 | "engines": { 1033 | "node": ">=6.5" 1034 | } 1035 | }, 1036 | "node_modules/acorn": { 1037 | "version": "8.14.0", 1038 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", 1039 | "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", 1040 | "dev": true, 1041 | "license": "MIT", 1042 | "bin": { 1043 | "acorn": "bin/acorn" 1044 | }, 1045 | "engines": { 1046 | "node": ">=0.4.0" 1047 | } 1048 | }, 1049 | "node_modules/acorn-walk": { 1050 | "version": "8.3.2", 1051 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", 1052 | "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", 1053 | "dev": true, 1054 | "license": "MIT", 1055 | "engines": { 1056 | "node": ">=0.4.0" 1057 | } 1058 | }, 1059 | "node_modules/agentkeepalive": { 1060 | "version": "4.6.0", 1061 | "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", 1062 | "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", 1063 | "license": "MIT", 1064 | "dependencies": { 1065 | "humanize-ms": "^1.2.1" 1066 | }, 1067 | "engines": { 1068 | "node": ">= 8.0.0" 1069 | } 1070 | }, 1071 | "node_modules/as-table": { 1072 | "version": "1.0.55", 1073 | "resolved": "https://registry.npmjs.org/as-table/-/as-table-1.0.55.tgz", 1074 | "integrity": "sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==", 1075 | "dev": true, 1076 | "license": "MIT", 1077 | "dependencies": { 1078 | "printable-characters": "^1.0.42" 1079 | } 1080 | }, 1081 | "node_modules/asynckit": { 1082 | "version": "0.4.0", 1083 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 1084 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", 1085 | "license": "MIT" 1086 | }, 1087 | "node_modules/blake3-wasm": { 1088 | "version": "2.1.5", 1089 | "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", 1090 | "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==", 1091 | "dev": true, 1092 | "license": "MIT" 1093 | }, 1094 | "node_modules/call-bind-apply-helpers": { 1095 | "version": "1.0.2", 1096 | "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", 1097 | "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", 1098 | "license": "MIT", 1099 | "dependencies": { 1100 | "es-errors": "^1.3.0", 1101 | "function-bind": "^1.1.2" 1102 | }, 1103 | "engines": { 1104 | "node": ">= 0.4" 1105 | } 1106 | }, 1107 | "node_modules/color": { 1108 | "version": "4.2.3", 1109 | "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", 1110 | "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", 1111 | "dev": true, 1112 | "license": "MIT", 1113 | "optional": true, 1114 | "dependencies": { 1115 | "color-convert": "^2.0.1", 1116 | "color-string": "^1.9.0" 1117 | }, 1118 | "engines": { 1119 | "node": ">=12.5.0" 1120 | } 1121 | }, 1122 | "node_modules/color-convert": { 1123 | "version": "2.0.1", 1124 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1125 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1126 | "dev": true, 1127 | "license": "MIT", 1128 | "optional": true, 1129 | "dependencies": { 1130 | "color-name": "~1.1.4" 1131 | }, 1132 | "engines": { 1133 | "node": ">=7.0.0" 1134 | } 1135 | }, 1136 | "node_modules/color-name": { 1137 | "version": "1.1.4", 1138 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1139 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1140 | "dev": true, 1141 | "license": "MIT", 1142 | "optional": true 1143 | }, 1144 | "node_modules/color-string": { 1145 | "version": "1.9.1", 1146 | "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", 1147 | "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", 1148 | "dev": true, 1149 | "license": "MIT", 1150 | "optional": true, 1151 | "dependencies": { 1152 | "color-name": "^1.0.0", 1153 | "simple-swizzle": "^0.2.2" 1154 | } 1155 | }, 1156 | "node_modules/combined-stream": { 1157 | "version": "1.0.8", 1158 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 1159 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 1160 | "license": "MIT", 1161 | "dependencies": { 1162 | "delayed-stream": "~1.0.0" 1163 | }, 1164 | "engines": { 1165 | "node": ">= 0.8" 1166 | } 1167 | }, 1168 | "node_modules/cookie": { 1169 | "version": "0.7.2", 1170 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", 1171 | "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", 1172 | "dev": true, 1173 | "license": "MIT", 1174 | "engines": { 1175 | "node": ">= 0.6" 1176 | } 1177 | }, 1178 | "node_modules/data-uri-to-buffer": { 1179 | "version": "2.0.2", 1180 | "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz", 1181 | "integrity": "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==", 1182 | "dev": true, 1183 | "license": "MIT" 1184 | }, 1185 | "node_modules/defu": { 1186 | "version": "6.1.4", 1187 | "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", 1188 | "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", 1189 | "dev": true, 1190 | "license": "MIT" 1191 | }, 1192 | "node_modules/delayed-stream": { 1193 | "version": "1.0.0", 1194 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 1195 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 1196 | "license": "MIT", 1197 | "engines": { 1198 | "node": ">=0.4.0" 1199 | } 1200 | }, 1201 | "node_modules/detect-libc": { 1202 | "version": "2.0.4", 1203 | "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", 1204 | "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", 1205 | "dev": true, 1206 | "license": "Apache-2.0", 1207 | "optional": true, 1208 | "engines": { 1209 | "node": ">=8" 1210 | } 1211 | }, 1212 | "node_modules/dunder-proto": { 1213 | "version": "1.0.1", 1214 | "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", 1215 | "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", 1216 | "license": "MIT", 1217 | "dependencies": { 1218 | "call-bind-apply-helpers": "^1.0.1", 1219 | "es-errors": "^1.3.0", 1220 | "gopd": "^1.2.0" 1221 | }, 1222 | "engines": { 1223 | "node": ">= 0.4" 1224 | } 1225 | }, 1226 | "node_modules/es-define-property": { 1227 | "version": "1.0.1", 1228 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", 1229 | "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", 1230 | "license": "MIT", 1231 | "engines": { 1232 | "node": ">= 0.4" 1233 | } 1234 | }, 1235 | "node_modules/es-errors": { 1236 | "version": "1.3.0", 1237 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 1238 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 1239 | "license": "MIT", 1240 | "engines": { 1241 | "node": ">= 0.4" 1242 | } 1243 | }, 1244 | "node_modules/es-object-atoms": { 1245 | "version": "1.1.1", 1246 | "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", 1247 | "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", 1248 | "license": "MIT", 1249 | "dependencies": { 1250 | "es-errors": "^1.3.0" 1251 | }, 1252 | "engines": { 1253 | "node": ">= 0.4" 1254 | } 1255 | }, 1256 | "node_modules/es-set-tostringtag": { 1257 | "version": "2.1.0", 1258 | "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", 1259 | "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", 1260 | "license": "MIT", 1261 | "dependencies": { 1262 | "es-errors": "^1.3.0", 1263 | "get-intrinsic": "^1.2.6", 1264 | "has-tostringtag": "^1.0.2", 1265 | "hasown": "^2.0.2" 1266 | }, 1267 | "engines": { 1268 | "node": ">= 0.4" 1269 | } 1270 | }, 1271 | "node_modules/esbuild": { 1272 | "version": "0.25.2", 1273 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", 1274 | "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", 1275 | "dev": true, 1276 | "hasInstallScript": true, 1277 | "license": "MIT", 1278 | "bin": { 1279 | "esbuild": "bin/esbuild" 1280 | }, 1281 | "engines": { 1282 | "node": ">=18" 1283 | }, 1284 | "optionalDependencies": { 1285 | "@esbuild/aix-ppc64": "0.25.2", 1286 | "@esbuild/android-arm": "0.25.2", 1287 | "@esbuild/android-arm64": "0.25.2", 1288 | "@esbuild/android-x64": "0.25.2", 1289 | "@esbuild/darwin-arm64": "0.25.2", 1290 | "@esbuild/darwin-x64": "0.25.2", 1291 | "@esbuild/freebsd-arm64": "0.25.2", 1292 | "@esbuild/freebsd-x64": "0.25.2", 1293 | "@esbuild/linux-arm": "0.25.2", 1294 | "@esbuild/linux-arm64": "0.25.2", 1295 | "@esbuild/linux-ia32": "0.25.2", 1296 | "@esbuild/linux-loong64": "0.25.2", 1297 | "@esbuild/linux-mips64el": "0.25.2", 1298 | "@esbuild/linux-ppc64": "0.25.2", 1299 | "@esbuild/linux-riscv64": "0.25.2", 1300 | "@esbuild/linux-s390x": "0.25.2", 1301 | "@esbuild/linux-x64": "0.25.2", 1302 | "@esbuild/netbsd-arm64": "0.25.2", 1303 | "@esbuild/netbsd-x64": "0.25.2", 1304 | "@esbuild/openbsd-arm64": "0.25.2", 1305 | "@esbuild/openbsd-x64": "0.25.2", 1306 | "@esbuild/sunos-x64": "0.25.2", 1307 | "@esbuild/win32-arm64": "0.25.2", 1308 | "@esbuild/win32-ia32": "0.25.2", 1309 | "@esbuild/win32-x64": "0.25.2" 1310 | } 1311 | }, 1312 | "node_modules/event-target-shim": { 1313 | "version": "5.0.1", 1314 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 1315 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", 1316 | "license": "MIT", 1317 | "engines": { 1318 | "node": ">=6" 1319 | } 1320 | }, 1321 | "node_modules/exit-hook": { 1322 | "version": "2.2.1", 1323 | "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz", 1324 | "integrity": "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==", 1325 | "dev": true, 1326 | "license": "MIT", 1327 | "engines": { 1328 | "node": ">=6" 1329 | }, 1330 | "funding": { 1331 | "url": "https://github.com/sponsors/sindresorhus" 1332 | } 1333 | }, 1334 | "node_modules/exsolve": { 1335 | "version": "1.0.5", 1336 | "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.5.tgz", 1337 | "integrity": "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==", 1338 | "dev": true, 1339 | "license": "MIT" 1340 | }, 1341 | "node_modules/form-data": { 1342 | "version": "4.0.2", 1343 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", 1344 | "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", 1345 | "license": "MIT", 1346 | "dependencies": { 1347 | "asynckit": "^0.4.0", 1348 | "combined-stream": "^1.0.8", 1349 | "es-set-tostringtag": "^2.1.0", 1350 | "mime-types": "^2.1.12" 1351 | }, 1352 | "engines": { 1353 | "node": ">= 6" 1354 | } 1355 | }, 1356 | "node_modules/form-data-encoder": { 1357 | "version": "1.7.2", 1358 | "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", 1359 | "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", 1360 | "license": "MIT" 1361 | }, 1362 | "node_modules/formdata-node": { 1363 | "version": "4.4.1", 1364 | "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", 1365 | "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", 1366 | "license": "MIT", 1367 | "dependencies": { 1368 | "node-domexception": "1.0.0", 1369 | "web-streams-polyfill": "4.0.0-beta.3" 1370 | }, 1371 | "engines": { 1372 | "node": ">= 12.20" 1373 | } 1374 | }, 1375 | "node_modules/fsevents": { 1376 | "version": "2.3.3", 1377 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 1378 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 1379 | "dev": true, 1380 | "hasInstallScript": true, 1381 | "license": "MIT", 1382 | "optional": true, 1383 | "os": [ 1384 | "darwin" 1385 | ], 1386 | "engines": { 1387 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 1388 | } 1389 | }, 1390 | "node_modules/function-bind": { 1391 | "version": "1.1.2", 1392 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 1393 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 1394 | "license": "MIT", 1395 | "funding": { 1396 | "url": "https://github.com/sponsors/ljharb" 1397 | } 1398 | }, 1399 | "node_modules/get-intrinsic": { 1400 | "version": "1.3.0", 1401 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", 1402 | "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", 1403 | "license": "MIT", 1404 | "dependencies": { 1405 | "call-bind-apply-helpers": "^1.0.2", 1406 | "es-define-property": "^1.0.1", 1407 | "es-errors": "^1.3.0", 1408 | "es-object-atoms": "^1.1.1", 1409 | "function-bind": "^1.1.2", 1410 | "get-proto": "^1.0.1", 1411 | "gopd": "^1.2.0", 1412 | "has-symbols": "^1.1.0", 1413 | "hasown": "^2.0.2", 1414 | "math-intrinsics": "^1.1.0" 1415 | }, 1416 | "engines": { 1417 | "node": ">= 0.4" 1418 | }, 1419 | "funding": { 1420 | "url": "https://github.com/sponsors/ljharb" 1421 | } 1422 | }, 1423 | "node_modules/get-proto": { 1424 | "version": "1.0.1", 1425 | "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", 1426 | "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", 1427 | "license": "MIT", 1428 | "dependencies": { 1429 | "dunder-proto": "^1.0.1", 1430 | "es-object-atoms": "^1.0.0" 1431 | }, 1432 | "engines": { 1433 | "node": ">= 0.4" 1434 | } 1435 | }, 1436 | "node_modules/get-source": { 1437 | "version": "2.0.12", 1438 | "resolved": "https://registry.npmjs.org/get-source/-/get-source-2.0.12.tgz", 1439 | "integrity": "sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==", 1440 | "dev": true, 1441 | "license": "Unlicense", 1442 | "dependencies": { 1443 | "data-uri-to-buffer": "^2.0.0", 1444 | "source-map": "^0.6.1" 1445 | } 1446 | }, 1447 | "node_modules/glob-to-regexp": { 1448 | "version": "0.4.1", 1449 | "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", 1450 | "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", 1451 | "dev": true, 1452 | "license": "BSD-2-Clause" 1453 | }, 1454 | "node_modules/gopd": { 1455 | "version": "1.2.0", 1456 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", 1457 | "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", 1458 | "license": "MIT", 1459 | "engines": { 1460 | "node": ">= 0.4" 1461 | }, 1462 | "funding": { 1463 | "url": "https://github.com/sponsors/ljharb" 1464 | } 1465 | }, 1466 | "node_modules/has-symbols": { 1467 | "version": "1.1.0", 1468 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", 1469 | "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", 1470 | "license": "MIT", 1471 | "engines": { 1472 | "node": ">= 0.4" 1473 | }, 1474 | "funding": { 1475 | "url": "https://github.com/sponsors/ljharb" 1476 | } 1477 | }, 1478 | "node_modules/has-tostringtag": { 1479 | "version": "1.0.2", 1480 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", 1481 | "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", 1482 | "license": "MIT", 1483 | "dependencies": { 1484 | "has-symbols": "^1.0.3" 1485 | }, 1486 | "engines": { 1487 | "node": ">= 0.4" 1488 | }, 1489 | "funding": { 1490 | "url": "https://github.com/sponsors/ljharb" 1491 | } 1492 | }, 1493 | "node_modules/hasown": { 1494 | "version": "2.0.2", 1495 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 1496 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 1497 | "license": "MIT", 1498 | "dependencies": { 1499 | "function-bind": "^1.1.2" 1500 | }, 1501 | "engines": { 1502 | "node": ">= 0.4" 1503 | } 1504 | }, 1505 | "node_modules/hono": { 1506 | "version": "4.7.7", 1507 | "resolved": "https://registry.npmjs.org/hono/-/hono-4.7.7.tgz", 1508 | "integrity": "sha512-2PCpQRbN87Crty8/L/7akZN3UyZIAopSoRxCwRbJgUuV1+MHNFHzYFxZTg4v/03cXUm+jce/qa2VSBZpKBm3Qw==", 1509 | "license": "MIT", 1510 | "engines": { 1511 | "node": ">=16.9.0" 1512 | } 1513 | }, 1514 | "node_modules/humanize-ms": { 1515 | "version": "1.2.1", 1516 | "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", 1517 | "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", 1518 | "license": "MIT", 1519 | "dependencies": { 1520 | "ms": "^2.0.0" 1521 | } 1522 | }, 1523 | "node_modules/is-arrayish": { 1524 | "version": "0.3.2", 1525 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", 1526 | "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", 1527 | "dev": true, 1528 | "license": "MIT", 1529 | "optional": true 1530 | }, 1531 | "node_modules/math-intrinsics": { 1532 | "version": "1.1.0", 1533 | "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", 1534 | "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", 1535 | "license": "MIT", 1536 | "engines": { 1537 | "node": ">= 0.4" 1538 | } 1539 | }, 1540 | "node_modules/mime": { 1541 | "version": "3.0.0", 1542 | "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", 1543 | "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", 1544 | "dev": true, 1545 | "license": "MIT", 1546 | "bin": { 1547 | "mime": "cli.js" 1548 | }, 1549 | "engines": { 1550 | "node": ">=10.0.0" 1551 | } 1552 | }, 1553 | "node_modules/mime-db": { 1554 | "version": "1.52.0", 1555 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 1556 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 1557 | "license": "MIT", 1558 | "engines": { 1559 | "node": ">= 0.6" 1560 | } 1561 | }, 1562 | "node_modules/mime-types": { 1563 | "version": "2.1.35", 1564 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 1565 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 1566 | "license": "MIT", 1567 | "dependencies": { 1568 | "mime-db": "1.52.0" 1569 | }, 1570 | "engines": { 1571 | "node": ">= 0.6" 1572 | } 1573 | }, 1574 | "node_modules/miniflare": { 1575 | "version": "4.20250422.0", 1576 | "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250422.0.tgz", 1577 | "integrity": "sha512-3frXK9EZEWQkHMDyppeIbUKwd7OQkNOm2gBtQQzjQ4gtzQmh+yxkyJiiylf+fGbz86djQTLKKQdQ1FC4yM3AMg==", 1578 | "dev": true, 1579 | "license": "MIT", 1580 | "dependencies": { 1581 | "@cspotcode/source-map-support": "0.8.1", 1582 | "acorn": "8.14.0", 1583 | "acorn-walk": "8.3.2", 1584 | "exit-hook": "2.2.1", 1585 | "glob-to-regexp": "0.4.1", 1586 | "stoppable": "1.1.0", 1587 | "undici": "^5.28.5", 1588 | "workerd": "1.20250422.0", 1589 | "ws": "8.18.0", 1590 | "youch": "3.3.4", 1591 | "zod": "3.22.3" 1592 | }, 1593 | "bin": { 1594 | "miniflare": "bootstrap.js" 1595 | }, 1596 | "engines": { 1597 | "node": ">=18.0.0" 1598 | } 1599 | }, 1600 | "node_modules/miniflare/node_modules/zod": { 1601 | "version": "3.22.3", 1602 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.3.tgz", 1603 | "integrity": "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==", 1604 | "dev": true, 1605 | "license": "MIT", 1606 | "funding": { 1607 | "url": "https://github.com/sponsors/colinhacks" 1608 | } 1609 | }, 1610 | "node_modules/ms": { 1611 | "version": "2.1.3", 1612 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1613 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 1614 | "license": "MIT" 1615 | }, 1616 | "node_modules/mustache": { 1617 | "version": "4.2.0", 1618 | "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", 1619 | "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", 1620 | "dev": true, 1621 | "license": "MIT", 1622 | "bin": { 1623 | "mustache": "bin/mustache" 1624 | } 1625 | }, 1626 | "node_modules/node-domexception": { 1627 | "version": "1.0.0", 1628 | "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", 1629 | "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", 1630 | "deprecated": "Use your platform's native DOMException instead", 1631 | "funding": [ 1632 | { 1633 | "type": "github", 1634 | "url": "https://github.com/sponsors/jimmywarting" 1635 | }, 1636 | { 1637 | "type": "github", 1638 | "url": "https://paypal.me/jimmywarting" 1639 | } 1640 | ], 1641 | "license": "MIT", 1642 | "engines": { 1643 | "node": ">=10.5.0" 1644 | } 1645 | }, 1646 | "node_modules/node-fetch": { 1647 | "version": "2.7.0", 1648 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", 1649 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", 1650 | "license": "MIT", 1651 | "dependencies": { 1652 | "whatwg-url": "^5.0.0" 1653 | }, 1654 | "engines": { 1655 | "node": "4.x || >=6.0.0" 1656 | }, 1657 | "peerDependencies": { 1658 | "encoding": "^0.1.0" 1659 | }, 1660 | "peerDependenciesMeta": { 1661 | "encoding": { 1662 | "optional": true 1663 | } 1664 | } 1665 | }, 1666 | "node_modules/ohash": { 1667 | "version": "2.0.11", 1668 | "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", 1669 | "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", 1670 | "dev": true, 1671 | "license": "MIT" 1672 | }, 1673 | "node_modules/openai": { 1674 | "version": "4.96.0", 1675 | "resolved": "https://registry.npmjs.org/openai/-/openai-4.96.0.tgz", 1676 | "integrity": "sha512-dKoW56i02Prv2XQolJ9Rl9Svqubqkzg3QpwEOBuSVZLk05Shelu7s+ErRTwFc1Bs3JZ2qBqBfVpXQiJhwOGG8A==", 1677 | "license": "Apache-2.0", 1678 | "dependencies": { 1679 | "@types/node": "^18.11.18", 1680 | "@types/node-fetch": "^2.6.4", 1681 | "abort-controller": "^3.0.0", 1682 | "agentkeepalive": "^4.2.1", 1683 | "form-data-encoder": "1.7.2", 1684 | "formdata-node": "^4.3.2", 1685 | "node-fetch": "^2.6.7" 1686 | }, 1687 | "bin": { 1688 | "openai": "bin/cli" 1689 | }, 1690 | "peerDependencies": { 1691 | "ws": "^8.18.0", 1692 | "zod": "^3.23.8" 1693 | }, 1694 | "peerDependenciesMeta": { 1695 | "ws": { 1696 | "optional": true 1697 | }, 1698 | "zod": { 1699 | "optional": true 1700 | } 1701 | } 1702 | }, 1703 | "node_modules/path-to-regexp": { 1704 | "version": "6.3.0", 1705 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", 1706 | "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", 1707 | "dev": true, 1708 | "license": "MIT" 1709 | }, 1710 | "node_modules/pathe": { 1711 | "version": "2.0.3", 1712 | "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", 1713 | "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", 1714 | "dev": true, 1715 | "license": "MIT" 1716 | }, 1717 | "node_modules/printable-characters": { 1718 | "version": "1.0.42", 1719 | "resolved": "https://registry.npmjs.org/printable-characters/-/printable-characters-1.0.42.tgz", 1720 | "integrity": "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==", 1721 | "dev": true, 1722 | "license": "Unlicense" 1723 | }, 1724 | "node_modules/semver": { 1725 | "version": "7.7.1", 1726 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", 1727 | "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", 1728 | "dev": true, 1729 | "license": "ISC", 1730 | "optional": true, 1731 | "bin": { 1732 | "semver": "bin/semver.js" 1733 | }, 1734 | "engines": { 1735 | "node": ">=10" 1736 | } 1737 | }, 1738 | "node_modules/sharp": { 1739 | "version": "0.33.5", 1740 | "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", 1741 | "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", 1742 | "dev": true, 1743 | "hasInstallScript": true, 1744 | "license": "Apache-2.0", 1745 | "optional": true, 1746 | "dependencies": { 1747 | "color": "^4.2.3", 1748 | "detect-libc": "^2.0.3", 1749 | "semver": "^7.6.3" 1750 | }, 1751 | "engines": { 1752 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1753 | }, 1754 | "funding": { 1755 | "url": "https://opencollective.com/libvips" 1756 | }, 1757 | "optionalDependencies": { 1758 | "@img/sharp-darwin-arm64": "0.33.5", 1759 | "@img/sharp-darwin-x64": "0.33.5", 1760 | "@img/sharp-libvips-darwin-arm64": "1.0.4", 1761 | "@img/sharp-libvips-darwin-x64": "1.0.4", 1762 | "@img/sharp-libvips-linux-arm": "1.0.5", 1763 | "@img/sharp-libvips-linux-arm64": "1.0.4", 1764 | "@img/sharp-libvips-linux-s390x": "1.0.4", 1765 | "@img/sharp-libvips-linux-x64": "1.0.4", 1766 | "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", 1767 | "@img/sharp-libvips-linuxmusl-x64": "1.0.4", 1768 | "@img/sharp-linux-arm": "0.33.5", 1769 | "@img/sharp-linux-arm64": "0.33.5", 1770 | "@img/sharp-linux-s390x": "0.33.5", 1771 | "@img/sharp-linux-x64": "0.33.5", 1772 | "@img/sharp-linuxmusl-arm64": "0.33.5", 1773 | "@img/sharp-linuxmusl-x64": "0.33.5", 1774 | "@img/sharp-wasm32": "0.33.5", 1775 | "@img/sharp-win32-ia32": "0.33.5", 1776 | "@img/sharp-win32-x64": "0.33.5" 1777 | } 1778 | }, 1779 | "node_modules/simple-swizzle": { 1780 | "version": "0.2.2", 1781 | "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", 1782 | "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", 1783 | "dev": true, 1784 | "license": "MIT", 1785 | "optional": true, 1786 | "dependencies": { 1787 | "is-arrayish": "^0.3.1" 1788 | } 1789 | }, 1790 | "node_modules/source-map": { 1791 | "version": "0.6.1", 1792 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1793 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1794 | "dev": true, 1795 | "license": "BSD-3-Clause", 1796 | "engines": { 1797 | "node": ">=0.10.0" 1798 | } 1799 | }, 1800 | "node_modules/stacktracey": { 1801 | "version": "2.1.8", 1802 | "resolved": "https://registry.npmjs.org/stacktracey/-/stacktracey-2.1.8.tgz", 1803 | "integrity": "sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==", 1804 | "dev": true, 1805 | "license": "Unlicense", 1806 | "dependencies": { 1807 | "as-table": "^1.0.36", 1808 | "get-source": "^2.0.12" 1809 | } 1810 | }, 1811 | "node_modules/stoppable": { 1812 | "version": "1.1.0", 1813 | "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", 1814 | "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", 1815 | "dev": true, 1816 | "license": "MIT", 1817 | "engines": { 1818 | "node": ">=4", 1819 | "npm": ">=6" 1820 | } 1821 | }, 1822 | "node_modules/tr46": { 1823 | "version": "0.0.3", 1824 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 1825 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", 1826 | "license": "MIT" 1827 | }, 1828 | "node_modules/tslib": { 1829 | "version": "2.8.1", 1830 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", 1831 | "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", 1832 | "dev": true, 1833 | "license": "0BSD", 1834 | "optional": true 1835 | }, 1836 | "node_modules/ufo": { 1837 | "version": "1.6.1", 1838 | "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", 1839 | "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", 1840 | "dev": true, 1841 | "license": "MIT" 1842 | }, 1843 | "node_modules/undici": { 1844 | "version": "5.29.0", 1845 | "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", 1846 | "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", 1847 | "dev": true, 1848 | "license": "MIT", 1849 | "dependencies": { 1850 | "@fastify/busboy": "^2.0.0" 1851 | }, 1852 | "engines": { 1853 | "node": ">=14.0" 1854 | } 1855 | }, 1856 | "node_modules/undici-types": { 1857 | "version": "5.26.5", 1858 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 1859 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", 1860 | "license": "MIT" 1861 | }, 1862 | "node_modules/unenv": { 1863 | "version": "2.0.0-rc.15", 1864 | "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.15.tgz", 1865 | "integrity": "sha512-J/rEIZU8w6FOfLNz/hNKsnY+fFHWnu9MH4yRbSZF3xbbGHovcetXPs7sD+9p8L6CeNC//I9bhRYAOsBt2u7/OA==", 1866 | "dev": true, 1867 | "license": "MIT", 1868 | "dependencies": { 1869 | "defu": "^6.1.4", 1870 | "exsolve": "^1.0.4", 1871 | "ohash": "^2.0.11", 1872 | "pathe": "^2.0.3", 1873 | "ufo": "^1.5.4" 1874 | } 1875 | }, 1876 | "node_modules/web-streams-polyfill": { 1877 | "version": "4.0.0-beta.3", 1878 | "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", 1879 | "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", 1880 | "license": "MIT", 1881 | "engines": { 1882 | "node": ">= 14" 1883 | } 1884 | }, 1885 | "node_modules/webidl-conversions": { 1886 | "version": "3.0.1", 1887 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 1888 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", 1889 | "license": "BSD-2-Clause" 1890 | }, 1891 | "node_modules/whatwg-url": { 1892 | "version": "5.0.0", 1893 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 1894 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 1895 | "license": "MIT", 1896 | "dependencies": { 1897 | "tr46": "~0.0.3", 1898 | "webidl-conversions": "^3.0.0" 1899 | } 1900 | }, 1901 | "node_modules/workerd": { 1902 | "version": "1.20250422.0", 1903 | "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250422.0.tgz", 1904 | "integrity": "sha512-q3ws6MIa9GJQqq1Q52qoD7vCx1203fjKNPmtRV1vvplrsfYphjr5pOAnZGUODFB1BnsDWypr71Luy7OonT0vug==", 1905 | "dev": true, 1906 | "hasInstallScript": true, 1907 | "license": "Apache-2.0", 1908 | "bin": { 1909 | "workerd": "bin/workerd" 1910 | }, 1911 | "engines": { 1912 | "node": ">=16" 1913 | }, 1914 | "optionalDependencies": { 1915 | "@cloudflare/workerd-darwin-64": "1.20250422.0", 1916 | "@cloudflare/workerd-darwin-arm64": "1.20250422.0", 1917 | "@cloudflare/workerd-linux-64": "1.20250422.0", 1918 | "@cloudflare/workerd-linux-arm64": "1.20250422.0", 1919 | "@cloudflare/workerd-windows-64": "1.20250422.0" 1920 | } 1921 | }, 1922 | "node_modules/wrangler": { 1923 | "version": "4.13.0", 1924 | "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.13.0.tgz", 1925 | "integrity": "sha512-CVRNL0unLmzhVeUkW+9neZHFITSo7UDROz8VYxi8YhitV9Rr1xMojS1cGjQTaQX8F3nAEsTRJXTwwTZ0JoJm6g==", 1926 | "dev": true, 1927 | "license": "MIT OR Apache-2.0", 1928 | "dependencies": { 1929 | "@cloudflare/kv-asset-handler": "0.4.0", 1930 | "@cloudflare/unenv-preset": "2.3.1", 1931 | "blake3-wasm": "2.1.5", 1932 | "esbuild": "0.25.2", 1933 | "miniflare": "4.20250422.0", 1934 | "path-to-regexp": "6.3.0", 1935 | "unenv": "2.0.0-rc.15", 1936 | "workerd": "1.20250422.0" 1937 | }, 1938 | "bin": { 1939 | "wrangler": "bin/wrangler.js", 1940 | "wrangler2": "bin/wrangler.js" 1941 | }, 1942 | "engines": { 1943 | "node": ">=18.0.0" 1944 | }, 1945 | "optionalDependencies": { 1946 | "fsevents": "~2.3.2", 1947 | "sharp": "^0.33.5" 1948 | }, 1949 | "peerDependencies": { 1950 | "@cloudflare/workers-types": "^4.20250422.0" 1951 | }, 1952 | "peerDependenciesMeta": { 1953 | "@cloudflare/workers-types": { 1954 | "optional": true 1955 | } 1956 | } 1957 | }, 1958 | "node_modules/ws": { 1959 | "version": "8.18.0", 1960 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", 1961 | "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", 1962 | "devOptional": true, 1963 | "license": "MIT", 1964 | "engines": { 1965 | "node": ">=10.0.0" 1966 | }, 1967 | "peerDependencies": { 1968 | "bufferutil": "^4.0.1", 1969 | "utf-8-validate": ">=5.0.2" 1970 | }, 1971 | "peerDependenciesMeta": { 1972 | "bufferutil": { 1973 | "optional": true 1974 | }, 1975 | "utf-8-validate": { 1976 | "optional": true 1977 | } 1978 | } 1979 | }, 1980 | "node_modules/youch": { 1981 | "version": "3.3.4", 1982 | "resolved": "https://registry.npmjs.org/youch/-/youch-3.3.4.tgz", 1983 | "integrity": "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg==", 1984 | "dev": true, 1985 | "license": "MIT", 1986 | "dependencies": { 1987 | "cookie": "^0.7.1", 1988 | "mustache": "^4.2.0", 1989 | "stacktracey": "^2.1.8" 1990 | } 1991 | }, 1992 | "node_modules/zod": { 1993 | "version": "3.24.3", 1994 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz", 1995 | "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", 1996 | "license": "MIT", 1997 | "optional": true, 1998 | "peer": true, 1999 | "funding": { 2000 | "url": "https://github.com/sponsors/colinhacks" 2001 | } 2002 | } 2003 | } 2004 | } 2005 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "timetravel-camera", 3 | "scripts": { 4 | "dev": "wrangler dev", 5 | "deploy": "wrangler deploy --minify", 6 | "cf-typegen": "wrangler types --env-interface CloudflareBindings" 7 | }, 8 | "dependencies": { 9 | "hono": "^4.7.7", 10 | "openai": "^4.96.0" 11 | }, 12 | "devDependencies": { 13 | "@cloudflare/workers-types": "^4.20250424.0", 14 | "wrangler": "^4.13.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Time Travel Camera 10 | 684 | 685 | 686 |
687 | 688 | 698 |
699 | 700 | 701 |
702 |
703 |
704 |
Time traveling...
705 |
706 | 707 |
708 | 709 |
710 | 711 |
712 | 713 | 714 | 715 | 716 |
717 |
718 |
719 | 720 |
721 |
722 | 723 |
724 | 725 | 726 | 735 |
736 |
737 | 738 |
739 |
Debug info will appear here
740 |
741 |
742 | 743 | 744 | 753 | 754 | 1810 | 1811 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "hono"; 2 | import OpenAI from "openai"; 3 | import { Buffer } from "node:buffer"; 4 | 5 | const app = new Hono<{ Bindings: CloudflareBindings }>(); 6 | 7 | // Define time periods (examples removed) 8 | interface TimePeriod { 9 | id: string; 10 | name: string; 11 | prompt: string; 12 | // examples property removed 13 | } 14 | 15 | const TIME_PERIODS: TimePeriod[] = [ 16 | { 17 | id: "prehistoric", 18 | name: "Prehistoric", 19 | prompt: "prehistoric era", 20 | }, 21 | { 22 | id: "ancient-egypt", 23 | name: "Ancient Egypt", 24 | prompt: "ancient Egyptian civilization", 25 | }, 26 | { 27 | id: "roman-empire", 28 | name: "Roman Empire", 29 | prompt: "ancient Roman civilization", 30 | }, 31 | { 32 | id: "medieval", 33 | name: "Medieval", 34 | prompt: "medieval Europe", 35 | }, 36 | { 37 | id: "renaissance", 38 | name: "Renaissance", 39 | prompt: "Renaissance period", 40 | }, 41 | { 42 | id: "wild-west", 43 | name: "Wild West", 44 | prompt: "American Wild West era", 45 | }, 46 | { 47 | id: "roaring-twenties", 48 | name: "1920s", 49 | prompt: "Roaring Twenties", 50 | }, 51 | { 52 | id: "future", 53 | name: "Future (Year 3030)", 54 | prompt: "year 3030", 55 | } 56 | ]; 57 | 58 | // Health check endpoint 59 | app.get("/api/health", (c) => { 60 | return c.json({ status: "ok", message: "Server is running" }); 61 | }); 62 | 63 | // Get available time periods 64 | app.get("/api/time-periods", (c) => { 65 | console.log("Returning time periods:", TIME_PERIODS.length); 66 | return c.json(TIME_PERIODS); 67 | }); 68 | 69 | // API endpoint to generate a time travel image 70 | app.post("/api/time-travel", async (c) => { 71 | try { 72 | const body = await c.req.json(); 73 | const { timeperiod, imageData } = body; 74 | 75 | console.log(`Received time travel request for period: ${timeperiod}`); 76 | 77 | if (!timeperiod || !imageData) { 78 | console.log("Missing required parameters"); 79 | return c.json({ error: "Missing required parameters" }, 400); 80 | } 81 | 82 | // Get OpenAI API key from environment variable 83 | const apiKey = c.env.OPENAI_API_KEY; 84 | 85 | if (!apiKey) { 86 | console.log("OpenAI API key not configured"); 87 | return c.json({ 88 | error: "OpenAI API key not configured", 89 | details: "Please add OPENAI_API_KEY to your environment variables" 90 | }, 500); 91 | } 92 | 93 | // Find the selected time period 94 | const period = TIME_PERIODS.find(p => p.id === timeperiod); 95 | 96 | if (!period) { 97 | console.log(`Invalid time period: ${timeperiod}`); 98 | return c.json({ error: "Invalid time period" }, 400); 99 | } 100 | 101 | console.log(`Generating image for period: ${period.name}`); 102 | 103 | // Initialize OpenAI client with API key from environment 104 | const openai = new OpenAI({ 105 | apiKey: apiKey, 106 | }); 107 | 108 | // Revised prompt without examples, emphasizing adaptation 109 | const promptText = `Transform the provided image into a photorealistic scene taking place in the ${period.prompt}. **It is essential to retain the original subjects' identity, features, and the overall composition of the scene.** Modify the setting, clothing, and surrounding elements to accurately reflect the style and atmosphere of the ${period.prompt}. The final image should clearly show the original subject transported to this different time period.`; 110 | console.log("Using prompt:", promptText); 111 | 112 | try { 113 | // Process image data 114 | console.log("Processing image data for OpenAI"); 115 | const base64Data = imageData.replace(/^data:image\/\w+;base64,/, ""); 116 | const imageBuffer = Buffer.from(base64Data, 'base64'); 117 | 118 | // Create a blob from the buffer 119 | const blob = new Blob([imageBuffer], { type: 'image/png' }); 120 | const imageFile = new File([blob], 'image.png', { type: 'image/png' }); 121 | 122 | // Use the OpenAI SDK to call the image edit endpoint 123 | const result = await openai.images.edit({ 124 | model: "gpt-image-1", 125 | image: imageFile, 126 | prompt: promptText, 127 | moderation: "low" 128 | }); 129 | 130 | console.log("Successfully generated image"); 131 | 132 | // Check if we have a valid response structure 133 | if (!result.data || !result.data[0]) { 134 | console.error("Unexpected OpenAI response format"); 135 | throw new Error("OpenAI returned invalid response format"); 136 | } 137 | 138 | // Check if we get the base64 content directly 139 | if (result.data[0].b64_json) { 140 | try { 141 | // Store the image in R2 if IMAGES_BUCKET is available 142 | if (c.env.IMAGES_BUCKET) { 143 | const timestamp = Date.now(); 144 | const filename = `timetravel-${period.id}-${timestamp}.png`; 145 | 146 | // Convert base64 to binary for R2 storage using Node.js Buffer 147 | const b64 = result.data[0].b64_json; 148 | const binaryData = Buffer.from(b64, 'base64'); 149 | 150 | // Save to R2 151 | console.log(`Storing image in R2 bucket with key: ${filename}`); 152 | await c.env.IMAGES_BUCKET.put(filename, binaryData, { 153 | httpMetadata: { contentType: "image/png" } 154 | }); 155 | 156 | // Return R2 image URL 157 | return c.json({ 158 | success: true, 159 | image: `/api/images/${filename}`, 160 | stored: true, 161 | filename: filename, 162 | timeperiod: period 163 | }); 164 | } else { 165 | console.log("No R2 bucket available, returning data URI"); 166 | // Still return the data URI if no R2 bucket is available 167 | const dataUri = `data:image/png;base64,${result.data[0].b64_json}`; 168 | return c.json({ 169 | success: true, 170 | image: dataUri, 171 | stored: false, 172 | timeperiod: period 173 | }); 174 | } 175 | } catch (storageError) { 176 | console.error("Error storing image in R2:", storageError); 177 | // Fall back to data URI if R2 storage fails 178 | const dataUri = `data:image/png;base64,${result.data[0].b64_json}`; 179 | return c.json({ 180 | success: true, 181 | image: dataUri, 182 | stored: false, 183 | storageError: storageError.message, 184 | timeperiod: period 185 | }); 186 | } 187 | } 188 | // No valid data found 189 | else { 190 | console.error("No image data in OpenAI response"); 191 | return c.json({ 192 | error: "No image returned from OpenAI", 193 | details: "The API response did not include image data" 194 | }, 500); 195 | } 196 | } catch (openaiError: any) { 197 | console.error("OpenAI API error:", openaiError); 198 | return c.json({ 199 | error: "Failed to generate image with OpenAI", 200 | details: openaiError.message || "Unknown OpenAI error" 201 | }, 500); 202 | } 203 | } catch (error) { 204 | console.error("Error processing request:", error); 205 | return c.json({ error: "Failed to process request" }, 500); 206 | } 207 | }); 208 | 209 | // Add API route to serve images from R2 210 | app.get("/api/images/:filename", async (c) => { 211 | try { 212 | const filename = c.req.param("filename"); 213 | console.log(`Retrieving image from R2: ${filename}`); 214 | 215 | // Check if IMAGES_BUCKET is available 216 | if (!c.env.IMAGES_BUCKET) { 217 | return c.json({ error: "R2 bucket not configured" }, 500); 218 | } 219 | 220 | // Get the object from R2 221 | const object = await c.env.IMAGES_BUCKET.get(filename); 222 | 223 | if (!object) { 224 | console.log(`Image not found: ${filename}`); 225 | return c.json({ error: "Image not found" }, 404); 226 | } 227 | 228 | // Set up response headers 229 | const headers = new Headers(); 230 | object.writeHttpMetadata(headers); 231 | headers.set("etag", object.httpEtag); 232 | headers.set("Cache-Control", "public, max-age=31536000"); // Cache for 1 year 233 | 234 | // Return the image 235 | return new Response(object.body, { 236 | headers, 237 | }); 238 | } catch (error) { 239 | console.error("Error serving image from R2:", error); 240 | return c.json({ 241 | error: "Failed to retrieve image", 242 | details: error.message 243 | }, 500); 244 | } 245 | }); 246 | 247 | // Add API route to list all stored images 248 | app.get("/api/images", async (c) => { 249 | try { 250 | console.log("Listing all images in R2 bucket"); 251 | 252 | // Check if IMAGES_BUCKET is available 253 | if (!c.env.IMAGES_BUCKET) { 254 | return c.json({ error: "R2 bucket not configured" }, 500); 255 | } 256 | 257 | // List objects in the bucket 258 | const objects = await c.env.IMAGES_BUCKET.list(); 259 | 260 | // Format the response 261 | const images = objects.objects.map(obj => ({ 262 | key: obj.key, 263 | url: `/api/images/${obj.key}`, 264 | size: obj.size, 265 | uploaded: obj.uploaded 266 | })); 267 | 268 | return c.json({ 269 | images: images, 270 | count: images.length 271 | }); 272 | } catch (error) { 273 | console.error("Error listing images from R2:", error); 274 | return c.json({ 275 | error: "Failed to list images", 276 | details: error.message 277 | }, 500); 278 | } 279 | }); 280 | 281 | export default app; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "Bundler", 6 | "strict": true, 7 | "skipLibCheck": true, 8 | "lib": [ 9 | "ESNext" 10 | ], 11 | "jsx": "react-jsx", 12 | "jsxImportSource": "hono/jsx", 13 | "types": [ 14 | "@cloudflare/workers-types/2023-07-01" 15 | ] 16 | }, 17 | } -------------------------------------------------------------------------------- /worker-configuration.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by Wrangler 2 | // After adding bindings to `wrangler.jsonc`, regenerate this interface via `npm run cf-typegen` 3 | interface CloudflareBindings { 4 | OPENAI_API_KEY: string; 5 | APP_ENV: string; 6 | ASSETS: Fetcher; 7 | IMAGES_BUCKET: R2Bucket; 8 | } -------------------------------------------------------------------------------- /wrangler.jsonc: -------------------------------------------------------------------------------- 1 | /** 2 | * For more details on how to configure Wrangler, refer to: 3 | * https://developers.cloudflare.com/workers/wrangler/configuration/ 4 | */ 5 | { 6 | "$schema": "node_modules/wrangler/config-schema.json", 7 | "name": "timetravel-camera", 8 | "main": "src/index.ts", 9 | "compatibility_date": "2025-04-24", 10 | "compatibility_flags": ["nodejs_compat"], 11 | "assets": { 12 | "binding": "ASSETS", 13 | "directory": "./public" 14 | }, 15 | "observability": { 16 | "enabled": true 17 | }, 18 | 19 | /** 20 | * Environment Variables 21 | * The OPENAI_API_KEY is stored in .dev.vars for local development 22 | * and as a secret for production 23 | */ 24 | "vars": { 25 | "APP_ENV": "production" 26 | }, 27 | 28 | /** 29 | * R2 Storage Configuration 30 | * This binds an R2 bucket to store and serve the time travel images 31 | */ 32 | "r2_buckets": [ 33 | { 34 | "binding": "IMAGES_BUCKET", 35 | "bucket_name": "timetravel-images" 36 | } 37 | ] 38 | } --------------------------------------------------------------------------------