extremely claude-assisted go game based on atproto! working on cleaning up and giving a more unique design, still has a bit of a slop vibe to it.

Add local development fallbacks for database and OAuth

- Add dotenv back for local development
- Database falls back to better-sqlite3 when D1 not available
- OAuth falls back to MemoryStore when KV not available
- Create .env file with local development settings
- Support hybrid local/production deployment

+806 -21
+723 -8
package-lock.json
··· 21 21 "@cf-wasm/resvg": "^0.3.3", 22 22 "@sveltejs/adapter-cloudflare": "^7.2.6", 23 23 "actor-typeahead": "^0.1.2", 24 + "dotenv": "^17.2.3", 24 25 "jgoboard": "github:jokkebk/jgoboard", 25 26 "kysely": "^0.27.0", 26 27 "kysely-d1": "^0.4.0", ··· 30 31 "devDependencies": { 31 32 "@sveltejs/kit": "^2.0.0", 32 33 "@sveltejs/vite-plugin-svelte": "^4.0.0", 34 + "@types/better-sqlite3": "^7.6.13", 35 + "better-sqlite3": "^12.6.2", 33 36 "svelte": "^5.0.0", 34 37 "svelte-check": "^4.0.0", 35 38 "tsx": "^4.21.0", ··· 2146 2149 "vite": "^5.0.0" 2147 2150 } 2148 2151 }, 2152 + "node_modules/@types/better-sqlite3": { 2153 + "version": "7.6.13", 2154 + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz", 2155 + "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==", 2156 + "dev": true, 2157 + "license": "MIT", 2158 + "dependencies": { 2159 + "@types/node": "*" 2160 + } 2161 + }, 2149 2162 "node_modules/@types/cookie": { 2150 2163 "version": "0.6.0", 2151 2164 "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", ··· 2160 2173 "version": "25.0.10", 2161 2174 "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", 2162 2175 "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", 2163 - "optional": true, 2164 - "peer": true, 2176 + "devOptional": true, 2165 2177 "dependencies": { 2166 2178 "undici-types": "~7.16.0" 2167 2179 } ··· 2257 2269 "url": "https://feross.org/support" 2258 2270 } 2259 2271 ] 2272 + }, 2273 + "node_modules/better-sqlite3": { 2274 + "version": "12.6.2", 2275 + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.6.2.tgz", 2276 + "integrity": "sha512-8VYKM3MjCa9WcaSAI3hzwhmyHVlH8tiGFwf0RlTsZPWJ1I5MkzjiudCo4KC4DxOaL/53A5B1sI/IbldNFDbsKA==", 2277 + "dev": true, 2278 + "hasInstallScript": true, 2279 + "license": "MIT", 2280 + "dependencies": { 2281 + "bindings": "^1.5.0", 2282 + "prebuild-install": "^7.1.1" 2283 + }, 2284 + "engines": { 2285 + "node": "20.x || 22.x || 23.x || 24.x || 25.x" 2286 + } 2287 + }, 2288 + "node_modules/bindings": { 2289 + "version": "1.5.0", 2290 + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", 2291 + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", 2292 + "dev": true, 2293 + "license": "MIT", 2294 + "dependencies": { 2295 + "file-uri-to-path": "1.0.0" 2296 + } 2297 + }, 2298 + "node_modules/bl": { 2299 + "version": "4.1.0", 2300 + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", 2301 + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", 2302 + "dev": true, 2303 + "license": "MIT", 2304 + "dependencies": { 2305 + "buffer": "^5.5.0", 2306 + "inherits": "^2.0.4", 2307 + "readable-stream": "^3.4.0" 2308 + } 2309 + }, 2310 + "node_modules/bl/node_modules/buffer": { 2311 + "version": "5.7.1", 2312 + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 2313 + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 2314 + "dev": true, 2315 + "funding": [ 2316 + { 2317 + "type": "github", 2318 + "url": "https://github.com/sponsors/feross" 2319 + }, 2320 + { 2321 + "type": "patreon", 2322 + "url": "https://www.patreon.com/feross" 2323 + }, 2324 + { 2325 + "type": "consulting", 2326 + "url": "https://feross.org/support" 2327 + } 2328 + ], 2329 + "license": "MIT", 2330 + "dependencies": { 2331 + "base64-js": "^1.3.1", 2332 + "ieee754": "^1.1.13" 2333 + } 2334 + }, 2335 + "node_modules/bl/node_modules/readable-stream": { 2336 + "version": "3.6.2", 2337 + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 2338 + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 2339 + "dev": true, 2340 + "license": "MIT", 2341 + "dependencies": { 2342 + "inherits": "^2.0.3", 2343 + "string_decoder": "^1.1.1", 2344 + "util-deprecate": "^1.0.1" 2345 + }, 2346 + "engines": { 2347 + "node": ">= 6" 2348 + } 2260 2349 }, 2261 2350 "node_modules/blake3-wasm": { 2262 2351 "version": "2.1.5", ··· 2410 2499 "url": "https://paulmillr.com/funding/" 2411 2500 } 2412 2501 }, 2502 + "node_modules/chownr": { 2503 + "version": "1.1.4", 2504 + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", 2505 + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", 2506 + "dev": true, 2507 + "license": "ISC" 2508 + }, 2413 2509 "node_modules/clsx": { 2414 2510 "version": "2.1.1", 2415 2511 "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", ··· 2466 2562 } 2467 2563 } 2468 2564 }, 2565 + "node_modules/decompress-response": { 2566 + "version": "6.0.0", 2567 + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", 2568 + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", 2569 + "dev": true, 2570 + "license": "MIT", 2571 + "dependencies": { 2572 + "mimic-response": "^3.1.0" 2573 + }, 2574 + "engines": { 2575 + "node": ">=10" 2576 + }, 2577 + "funding": { 2578 + "url": "https://github.com/sponsors/sindresorhus" 2579 + } 2580 + }, 2581 + "node_modules/deep-extend": { 2582 + "version": "0.6.0", 2583 + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 2584 + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", 2585 + "dev": true, 2586 + "license": "MIT", 2587 + "engines": { 2588 + "node": ">=4.0.0" 2589 + } 2590 + }, 2469 2591 "node_modules/deepmerge": { 2470 2592 "version": "4.3.1", 2471 2593 "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", ··· 2503 2625 "version": "5.6.2", 2504 2626 "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.2.tgz", 2505 2627 "integrity": "sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg==" 2628 + }, 2629 + "node_modules/dotenv": { 2630 + "version": "17.2.3", 2631 + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", 2632 + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", 2633 + "license": "BSD-2-Clause", 2634 + "engines": { 2635 + "node": ">=12" 2636 + }, 2637 + "funding": { 2638 + "url": "https://dotenvx.com" 2639 + } 2506 2640 }, 2507 2641 "node_modules/dunder-proto": { 2508 2642 "version": "1.0.1", ··· 2530 2664 "node": ">= 0.8" 2531 2665 } 2532 2666 }, 2667 + "node_modules/end-of-stream": { 2668 + "version": "1.4.5", 2669 + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", 2670 + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", 2671 + "dev": true, 2672 + "license": "MIT", 2673 + "dependencies": { 2674 + "once": "^1.4.0" 2675 + } 2676 + }, 2533 2677 "node_modules/error-stack-parser-es": { 2534 2678 "version": "1.0.5", 2535 2679 "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", ··· 3033 3177 "node": ">=0.8.x" 3034 3178 } 3035 3179 }, 3180 + "node_modules/expand-template": { 3181 + "version": "2.0.3", 3182 + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", 3183 + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", 3184 + "dev": true, 3185 + "license": "(MIT OR WTFPL)", 3186 + "engines": { 3187 + "node": ">=6" 3188 + } 3189 + }, 3036 3190 "node_modules/express": { 3037 3191 "version": "4.22.1", 3038 3192 "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", ··· 3124 3278 } 3125 3279 } 3126 3280 }, 3281 + "node_modules/file-uri-to-path": { 3282 + "version": "1.0.0", 3283 + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", 3284 + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", 3285 + "dev": true, 3286 + "license": "MIT" 3287 + }, 3127 3288 "node_modules/finalhandler": { 3128 3289 "version": "1.3.2", 3129 3290 "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", ··· 3170 3331 "node": ">= 0.6" 3171 3332 } 3172 3333 }, 3334 + "node_modules/fs-constants": { 3335 + "version": "1.0.0", 3336 + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", 3337 + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", 3338 + "dev": true, 3339 + "license": "MIT" 3340 + }, 3173 3341 "node_modules/fsevents": { 3174 3342 "version": "2.3.3", 3175 3343 "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", ··· 3238 3406 "funding": { 3239 3407 "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" 3240 3408 } 3409 + }, 3410 + "node_modules/github-from-package": { 3411 + "version": "0.0.0", 3412 + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", 3413 + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", 3414 + "dev": true, 3415 + "license": "MIT" 3241 3416 }, 3242 3417 "node_modules/gopd": { 3243 3418 "version": "1.2.0", ··· 3331 3506 "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 3332 3507 "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 3333 3508 }, 3509 + "node_modules/ini": { 3510 + "version": "1.3.8", 3511 + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", 3512 + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", 3513 + "dev": true, 3514 + "license": "ISC" 3515 + }, 3334 3516 "node_modules/ipaddr.js": { 3335 3517 "version": "2.3.0", 3336 3518 "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz", ··· 3470 3652 "node": ">= 0.6" 3471 3653 } 3472 3654 }, 3655 + "node_modules/mimic-response": { 3656 + "version": "3.1.0", 3657 + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", 3658 + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", 3659 + "dev": true, 3660 + "license": "MIT", 3661 + "engines": { 3662 + "node": ">=10" 3663 + }, 3664 + "funding": { 3665 + "url": "https://github.com/sponsors/sindresorhus" 3666 + } 3667 + }, 3473 3668 "node_modules/miniflare": { 3474 3669 "version": "4.20260131.0", 3475 3670 "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260131.0.tgz", ··· 3520 3715 } 3521 3716 } 3522 3717 }, 3718 + "node_modules/minimist": { 3719 + "version": "1.2.8", 3720 + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", 3721 + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", 3722 + "dev": true, 3723 + "license": "MIT", 3724 + "funding": { 3725 + "url": "https://github.com/sponsors/ljharb" 3726 + } 3727 + }, 3728 + "node_modules/mkdirp-classic": { 3729 + "version": "0.5.3", 3730 + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", 3731 + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", 3732 + "dev": true, 3733 + "license": "MIT" 3734 + }, 3523 3735 "node_modules/mri": { 3524 3736 "version": "1.2.0", 3525 3737 "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", ··· 3563 3775 "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 3564 3776 } 3565 3777 }, 3778 + "node_modules/napi-build-utils": { 3779 + "version": "2.0.0", 3780 + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", 3781 + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", 3782 + "dev": true, 3783 + "license": "MIT" 3784 + }, 3566 3785 "node_modules/negotiator": { 3567 3786 "version": "0.6.3", 3568 3787 "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", ··· 3571 3790 "node": ">= 0.6" 3572 3791 } 3573 3792 }, 3793 + "node_modules/node-abi": { 3794 + "version": "3.87.0", 3795 + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.87.0.tgz", 3796 + "integrity": "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==", 3797 + "dev": true, 3798 + "license": "MIT", 3799 + "dependencies": { 3800 + "semver": "^7.3.5" 3801 + }, 3802 + "engines": { 3803 + "node": ">=10" 3804 + } 3805 + }, 3574 3806 "node_modules/node-gyp-build-optional-packages": { 3575 3807 "version": "5.1.1", 3576 3808 "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz", ··· 3613 3845 }, 3614 3846 "engines": { 3615 3847 "node": ">= 0.8" 3848 + } 3849 + }, 3850 + "node_modules/once": { 3851 + "version": "1.4.0", 3852 + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 3853 + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 3854 + "dev": true, 3855 + "license": "ISC", 3856 + "dependencies": { 3857 + "wrappy": "1" 3616 3858 } 3617 3859 }, 3618 3860 "node_modules/parseurl": { ··· 3701 3943 "node": "^10 || ^12 || >=14" 3702 3944 } 3703 3945 }, 3946 + "node_modules/prebuild-install": { 3947 + "version": "7.1.3", 3948 + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", 3949 + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", 3950 + "dev": true, 3951 + "license": "MIT", 3952 + "dependencies": { 3953 + "detect-libc": "^2.0.0", 3954 + "expand-template": "^2.0.3", 3955 + "github-from-package": "0.0.0", 3956 + "minimist": "^1.2.3", 3957 + "mkdirp-classic": "^0.5.3", 3958 + "napi-build-utils": "^2.0.0", 3959 + "node-abi": "^3.3.0", 3960 + "pump": "^3.0.0", 3961 + "rc": "^1.2.7", 3962 + "simple-get": "^4.0.0", 3963 + "tar-fs": "^2.0.0", 3964 + "tunnel-agent": "^0.6.0" 3965 + }, 3966 + "bin": { 3967 + "prebuild-install": "bin.js" 3968 + }, 3969 + "engines": { 3970 + "node": ">=10" 3971 + } 3972 + }, 3704 3973 "node_modules/process": { 3705 3974 "version": "0.11.10", 3706 3975 "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", ··· 3745 4014 "url": "https://github.com/sponsors/lupomontero" 3746 4015 } 3747 4016 }, 4017 + "node_modules/pump": { 4018 + "version": "3.0.3", 4019 + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", 4020 + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", 4021 + "dev": true, 4022 + "license": "MIT", 4023 + "dependencies": { 4024 + "end-of-stream": "^1.1.0", 4025 + "once": "^1.3.1" 4026 + } 4027 + }, 3748 4028 "node_modules/punycode": { 3749 4029 "version": "2.3.1", 3750 4030 "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", ··· 3797 4077 }, 3798 4078 "engines": { 3799 4079 "node": ">= 0.8" 4080 + } 4081 + }, 4082 + "node_modules/rc": { 4083 + "version": "1.2.8", 4084 + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 4085 + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 4086 + "dev": true, 4087 + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", 4088 + "dependencies": { 4089 + "deep-extend": "^0.6.0", 4090 + "ini": "~1.3.0", 4091 + "minimist": "^1.2.0", 4092 + "strip-json-comments": "~2.0.1" 4093 + }, 4094 + "bin": { 4095 + "rc": "cli.js" 3800 4096 } 3801 4097 }, 3802 4098 "node_modules/readable-stream": { ··· 4122 4418 "url": "https://github.com/sponsors/ljharb" 4123 4419 } 4124 4420 }, 4421 + "node_modules/simple-concat": { 4422 + "version": "1.0.1", 4423 + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", 4424 + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", 4425 + "dev": true, 4426 + "funding": [ 4427 + { 4428 + "type": "github", 4429 + "url": "https://github.com/sponsors/feross" 4430 + }, 4431 + { 4432 + "type": "patreon", 4433 + "url": "https://www.patreon.com/feross" 4434 + }, 4435 + { 4436 + "type": "consulting", 4437 + "url": "https://feross.org/support" 4438 + } 4439 + ], 4440 + "license": "MIT" 4441 + }, 4442 + "node_modules/simple-get": { 4443 + "version": "4.0.1", 4444 + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", 4445 + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", 4446 + "dev": true, 4447 + "funding": [ 4448 + { 4449 + "type": "github", 4450 + "url": "https://github.com/sponsors/feross" 4451 + }, 4452 + { 4453 + "type": "patreon", 4454 + "url": "https://www.patreon.com/feross" 4455 + }, 4456 + { 4457 + "type": "consulting", 4458 + "url": "https://feross.org/support" 4459 + } 4460 + ], 4461 + "license": "MIT", 4462 + "dependencies": { 4463 + "decompress-response": "^6.0.0", 4464 + "once": "^1.3.1", 4465 + "simple-concat": "^1.0.0" 4466 + } 4467 + }, 4125 4468 "node_modules/sirv": { 4126 4469 "version": "3.0.2", 4127 4470 "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", ··· 4173 4516 "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 4174 4517 "dependencies": { 4175 4518 "safe-buffer": "~5.2.0" 4519 + } 4520 + }, 4521 + "node_modules/strip-json-comments": { 4522 + "version": "2.0.1", 4523 + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 4524 + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", 4525 + "dev": true, 4526 + "license": "MIT", 4527 + "engines": { 4528 + "node": ">=0.10.0" 4176 4529 } 4177 4530 }, 4178 4531 "node_modules/supports-color": { ··· 4280 4633 "@jridgewell/sourcemap-codec": "^1.4.15" 4281 4634 } 4282 4635 }, 4636 + "node_modules/tar-fs": { 4637 + "version": "2.1.4", 4638 + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", 4639 + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", 4640 + "dev": true, 4641 + "license": "MIT", 4642 + "dependencies": { 4643 + "chownr": "^1.1.1", 4644 + "mkdirp-classic": "^0.5.2", 4645 + "pump": "^3.0.0", 4646 + "tar-stream": "^2.1.4" 4647 + } 4648 + }, 4649 + "node_modules/tar-stream": { 4650 + "version": "2.2.0", 4651 + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", 4652 + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", 4653 + "dev": true, 4654 + "license": "MIT", 4655 + "dependencies": { 4656 + "bl": "^4.0.3", 4657 + "end-of-stream": "^1.4.1", 4658 + "fs-constants": "^1.0.0", 4659 + "inherits": "^2.0.3", 4660 + "readable-stream": "^3.1.1" 4661 + }, 4662 + "engines": { 4663 + "node": ">=6" 4664 + } 4665 + }, 4666 + "node_modules/tar-stream/node_modules/readable-stream": { 4667 + "version": "3.6.2", 4668 + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 4669 + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 4670 + "dev": true, 4671 + "license": "MIT", 4672 + "dependencies": { 4673 + "inherits": "^2.0.3", 4674 + "string_decoder": "^1.1.1", 4675 + "util-deprecate": "^1.0.1" 4676 + }, 4677 + "engines": { 4678 + "node": ">= 6" 4679 + } 4680 + }, 4283 4681 "node_modules/tenuki": { 4284 4682 "version": "0.3.1", 4285 4683 "resolved": "https://registry.npmjs.org/tenuki/-/tenuki-0.3.1.tgz", ··· 4345 4743 "fsevents": "~2.3.3" 4346 4744 } 4347 4745 }, 4746 + "node_modules/tunnel-agent": { 4747 + "version": "0.6.0", 4748 + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 4749 + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", 4750 + "dev": true, 4751 + "license": "Apache-2.0", 4752 + "dependencies": { 4753 + "safe-buffer": "^5.0.1" 4754 + }, 4755 + "engines": { 4756 + "node": "*" 4757 + } 4758 + }, 4348 4759 "node_modules/twemoji-parser": { 4349 4760 "version": "14.0.0", 4350 4761 "resolved": "https://registry.npmjs.org/twemoji-parser/-/twemoji-parser-14.0.0.tgz", ··· 4395 4806 "version": "7.16.0", 4396 4807 "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", 4397 4808 "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", 4398 - "optional": true, 4399 - "peer": true 4809 + "devOptional": true 4400 4810 }, 4401 4811 "node_modules/unenv": { 4402 4812 "version": "2.0.0-rc.24", ··· 4419 4829 "engines": { 4420 4830 "node": ">= 0.8" 4421 4831 } 4832 + }, 4833 + "node_modules/util-deprecate": { 4834 + "version": "1.0.2", 4835 + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 4836 + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 4837 + "dev": true, 4838 + "license": "MIT" 4422 4839 }, 4423 4840 "node_modules/utils-merge": { 4424 4841 "version": "1.0.1", ··· 5072 5489 "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", 5073 5490 "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", 5074 5491 "license": "MIT" 5492 + }, 5493 + "node_modules/wrappy": { 5494 + "version": "1.0.2", 5495 + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 5496 + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 5497 + "dev": true, 5498 + "license": "ISC" 5075 5499 }, 5076 5500 "node_modules/ws": { 5077 5501 "version": "8.19.0", ··· 6363 6787 "debug": "^4.3.7" 6364 6788 } 6365 6789 }, 6790 + "@types/better-sqlite3": { 6791 + "version": "7.6.13", 6792 + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz", 6793 + "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==", 6794 + "dev": true, 6795 + "requires": { 6796 + "@types/node": "*" 6797 + } 6798 + }, 6366 6799 "@types/cookie": { 6367 6800 "version": "0.6.0", 6368 6801 "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", ··· 6377 6810 "version": "25.0.10", 6378 6811 "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", 6379 6812 "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", 6380 - "optional": true, 6381 - "peer": true, 6813 + "devOptional": true, 6382 6814 "requires": { 6383 6815 "undici-types": "~7.16.0" 6384 6816 } ··· 6440 6872 "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 6441 6873 "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 6442 6874 }, 6875 + "better-sqlite3": { 6876 + "version": "12.6.2", 6877 + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.6.2.tgz", 6878 + "integrity": "sha512-8VYKM3MjCa9WcaSAI3hzwhmyHVlH8tiGFwf0RlTsZPWJ1I5MkzjiudCo4KC4DxOaL/53A5B1sI/IbldNFDbsKA==", 6879 + "dev": true, 6880 + "requires": { 6881 + "bindings": "^1.5.0", 6882 + "prebuild-install": "^7.1.1" 6883 + } 6884 + }, 6885 + "bindings": { 6886 + "version": "1.5.0", 6887 + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", 6888 + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", 6889 + "dev": true, 6890 + "requires": { 6891 + "file-uri-to-path": "1.0.0" 6892 + } 6893 + }, 6894 + "bl": { 6895 + "version": "4.1.0", 6896 + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", 6897 + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", 6898 + "dev": true, 6899 + "requires": { 6900 + "buffer": "^5.5.0", 6901 + "inherits": "^2.0.4", 6902 + "readable-stream": "^3.4.0" 6903 + }, 6904 + "dependencies": { 6905 + "buffer": { 6906 + "version": "5.7.1", 6907 + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 6908 + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 6909 + "dev": true, 6910 + "requires": { 6911 + "base64-js": "^1.3.1", 6912 + "ieee754": "^1.1.13" 6913 + } 6914 + }, 6915 + "readable-stream": { 6916 + "version": "3.6.2", 6917 + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 6918 + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 6919 + "dev": true, 6920 + "requires": { 6921 + "inherits": "^2.0.3", 6922 + "string_decoder": "^1.1.1", 6923 + "util-deprecate": "^1.0.1" 6924 + } 6925 + } 6926 + } 6927 + }, 6443 6928 "blake3-wasm": { 6444 6929 "version": "2.1.5", 6445 6930 "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", ··· 6548 7033 "readdirp": "^4.0.1" 6549 7034 } 6550 7035 }, 7036 + "chownr": { 7037 + "version": "1.1.4", 7038 + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", 7039 + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", 7040 + "dev": true 7041 + }, 6551 7042 "clsx": { 6552 7043 "version": "2.1.1", 6553 7044 "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", ··· 6584 7075 "ms": "^2.1.3" 6585 7076 } 6586 7077 }, 7078 + "decompress-response": { 7079 + "version": "6.0.0", 7080 + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", 7081 + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", 7082 + "dev": true, 7083 + "requires": { 7084 + "mimic-response": "^3.1.0" 7085 + } 7086 + }, 7087 + "deep-extend": { 7088 + "version": "0.6.0", 7089 + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 7090 + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", 7091 + "dev": true 7092 + }, 6587 7093 "deepmerge": { 6588 7094 "version": "4.3.1", 6589 7095 "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", ··· 6609 7115 "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.2.tgz", 6610 7116 "integrity": "sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg==" 6611 7117 }, 7118 + "dotenv": { 7119 + "version": "17.2.3", 7120 + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", 7121 + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==" 7122 + }, 6612 7123 "dunder-proto": { 6613 7124 "version": "1.0.1", 6614 7125 "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", ··· 6628 7139 "version": "2.0.0", 6629 7140 "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", 6630 7141 "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" 7142 + }, 7143 + "end-of-stream": { 7144 + "version": "1.4.5", 7145 + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", 7146 + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", 7147 + "dev": true, 7148 + "requires": { 7149 + "once": "^1.4.0" 7150 + } 6631 7151 }, 6632 7152 "error-stack-parser-es": { 6633 7153 "version": "1.0.5", ··· 6874 7394 "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", 6875 7395 "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" 6876 7396 }, 7397 + "expand-template": { 7398 + "version": "2.0.3", 7399 + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", 7400 + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", 7401 + "dev": true 7402 + }, 6877 7403 "express": { 6878 7404 "version": "4.22.1", 6879 7405 "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", ··· 6944 7470 "dev": true, 6945 7471 "requires": {} 6946 7472 }, 7473 + "file-uri-to-path": { 7474 + "version": "1.0.0", 7475 + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", 7476 + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", 7477 + "dev": true 7478 + }, 6947 7479 "finalhandler": { 6948 7480 "version": "1.3.2", 6949 7481 "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", ··· 6983 7515 "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 6984 7516 "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" 6985 7517 }, 7518 + "fs-constants": { 7519 + "version": "1.0.0", 7520 + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", 7521 + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", 7522 + "dev": true 7523 + }, 6986 7524 "fsevents": { 6987 7525 "version": "2.3.3", 6988 7526 "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", ··· 7029 7567 "resolve-pkg-maps": "^1.0.0" 7030 7568 } 7031 7569 }, 7570 + "github-from-package": { 7571 + "version": "0.0.0", 7572 + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", 7573 + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", 7574 + "dev": true 7575 + }, 7032 7576 "gopd": { 7033 7577 "version": "1.2.0", 7034 7578 "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", ··· 7082 7626 "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 7083 7627 "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 7084 7628 }, 7629 + "ini": { 7630 + "version": "1.3.8", 7631 + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", 7632 + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", 7633 + "dev": true 7634 + }, 7085 7635 "ipaddr.js": { 7086 7636 "version": "2.3.0", 7087 7637 "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz", ··· 7181 7731 "mime-db": "1.52.0" 7182 7732 } 7183 7733 }, 7734 + "mimic-response": { 7735 + "version": "3.1.0", 7736 + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", 7737 + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", 7738 + "dev": true 7739 + }, 7184 7740 "miniflare": { 7185 7741 "version": "4.20260131.0", 7186 7742 "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260131.0.tgz", ··· 7207 7763 } 7208 7764 } 7209 7765 }, 7766 + "minimist": { 7767 + "version": "1.2.8", 7768 + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", 7769 + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", 7770 + "dev": true 7771 + }, 7772 + "mkdirp-classic": { 7773 + "version": "0.5.3", 7774 + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", 7775 + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", 7776 + "dev": true 7777 + }, 7210 7778 "mri": { 7211 7779 "version": "1.2.0", 7212 7780 "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", ··· 7232 7800 "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", 7233 7801 "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==" 7234 7802 }, 7803 + "napi-build-utils": { 7804 + "version": "2.0.0", 7805 + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", 7806 + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", 7807 + "dev": true 7808 + }, 7235 7809 "negotiator": { 7236 7810 "version": "0.6.3", 7237 7811 "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 7238 7812 "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" 7813 + }, 7814 + "node-abi": { 7815 + "version": "3.87.0", 7816 + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.87.0.tgz", 7817 + "integrity": "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==", 7818 + "dev": true, 7819 + "requires": { 7820 + "semver": "^7.3.5" 7821 + } 7239 7822 }, 7240 7823 "node-gyp-build-optional-packages": { 7241 7824 "version": "5.1.1", ··· 7264 7847 "ee-first": "1.1.1" 7265 7848 } 7266 7849 }, 7850 + "once": { 7851 + "version": "1.4.0", 7852 + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 7853 + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 7854 + "dev": true, 7855 + "requires": { 7856 + "wrappy": "1" 7857 + } 7858 + }, 7267 7859 "parseurl": { 7268 7860 "version": "1.3.3", 7269 7861 "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", ··· 7326 7918 "source-map-js": "^1.2.1" 7327 7919 } 7328 7920 }, 7921 + "prebuild-install": { 7922 + "version": "7.1.3", 7923 + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", 7924 + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", 7925 + "dev": true, 7926 + "requires": { 7927 + "detect-libc": "^2.0.0", 7928 + "expand-template": "^2.0.3", 7929 + "github-from-package": "0.0.0", 7930 + "minimist": "^1.2.3", 7931 + "mkdirp-classic": "^0.5.3", 7932 + "napi-build-utils": "^2.0.0", 7933 + "node-abi": "^3.3.0", 7934 + "pump": "^3.0.0", 7935 + "rc": "^1.2.7", 7936 + "simple-get": "^4.0.0", 7937 + "tar-fs": "^2.0.0", 7938 + "tunnel-agent": "^0.6.0" 7939 + } 7940 + }, 7329 7941 "process": { 7330 7942 "version": "0.11.10", 7331 7943 "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", ··· 7360 7972 "punycode": "^2.3.1" 7361 7973 } 7362 7974 }, 7975 + "pump": { 7976 + "version": "3.0.3", 7977 + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", 7978 + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", 7979 + "dev": true, 7980 + "requires": { 7981 + "end-of-stream": "^1.1.0", 7982 + "once": "^1.3.1" 7983 + } 7984 + }, 7363 7985 "punycode": { 7364 7986 "version": "2.3.1", 7365 7987 "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", ··· 7399 8021 "unpipe": "~1.0.0" 7400 8022 } 7401 8023 }, 8024 + "rc": { 8025 + "version": "1.2.8", 8026 + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 8027 + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 8028 + "dev": true, 8029 + "requires": { 8030 + "deep-extend": "^0.6.0", 8031 + "ini": "~1.3.0", 8032 + "minimist": "^1.2.0", 8033 + "strip-json-comments": "~2.0.1" 8034 + } 8035 + }, 7402 8036 "readable-stream": { 7403 8037 "version": "4.7.0", 7404 8038 "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", ··· 7631 8265 "side-channel-map": "^1.0.1" 7632 8266 } 7633 8267 }, 8268 + "simple-concat": { 8269 + "version": "1.0.1", 8270 + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", 8271 + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", 8272 + "dev": true 8273 + }, 8274 + "simple-get": { 8275 + "version": "4.0.1", 8276 + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", 8277 + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", 8278 + "dev": true, 8279 + "requires": { 8280 + "decompress-response": "^6.0.0", 8281 + "once": "^1.3.1", 8282 + "simple-concat": "^1.0.0" 8283 + } 8284 + }, 7634 8285 "sirv": { 7635 8286 "version": "3.0.2", 7636 8287 "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", ··· 7671 8322 "requires": { 7672 8323 "safe-buffer": "~5.2.0" 7673 8324 } 8325 + }, 8326 + "strip-json-comments": { 8327 + "version": "2.0.1", 8328 + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 8329 + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", 8330 + "dev": true 7674 8331 }, 7675 8332 "supports-color": { 7676 8333 "version": "10.2.2", ··· 7761 8418 } 7762 8419 } 7763 8420 }, 8421 + "tar-fs": { 8422 + "version": "2.1.4", 8423 + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", 8424 + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", 8425 + "dev": true, 8426 + "requires": { 8427 + "chownr": "^1.1.1", 8428 + "mkdirp-classic": "^0.5.2", 8429 + "pump": "^3.0.0", 8430 + "tar-stream": "^2.1.4" 8431 + } 8432 + }, 8433 + "tar-stream": { 8434 + "version": "2.2.0", 8435 + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", 8436 + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", 8437 + "dev": true, 8438 + "requires": { 8439 + "bl": "^4.0.3", 8440 + "end-of-stream": "^1.4.1", 8441 + "fs-constants": "^1.0.0", 8442 + "inherits": "^2.0.3", 8443 + "readable-stream": "^3.1.1" 8444 + }, 8445 + "dependencies": { 8446 + "readable-stream": { 8447 + "version": "3.6.2", 8448 + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 8449 + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 8450 + "dev": true, 8451 + "requires": { 8452 + "inherits": "^2.0.3", 8453 + "string_decoder": "^1.1.1", 8454 + "util-deprecate": "^1.0.1" 8455 + } 8456 + } 8457 + } 8458 + }, 7764 8459 "tenuki": { 7765 8460 "version": "0.3.1", 7766 8461 "resolved": "https://registry.npmjs.org/tenuki/-/tenuki-0.3.1.tgz", ··· 7803 8498 "esbuild": "~0.27.0", 7804 8499 "fsevents": "~2.3.3", 7805 8500 "get-tsconfig": "^4.7.5" 8501 + } 8502 + }, 8503 + "tunnel-agent": { 8504 + "version": "0.6.0", 8505 + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 8506 + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", 8507 + "dev": true, 8508 + "requires": { 8509 + "safe-buffer": "^5.0.1" 7806 8510 } 7807 8511 }, 7808 8512 "twemoji-parser": { ··· 7842 8546 "version": "7.16.0", 7843 8547 "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", 7844 8548 "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", 7845 - "optional": true, 7846 - "peer": true 8549 + "devOptional": true 7847 8550 }, 7848 8551 "unenv": { 7849 8552 "version": "2.0.0-rc.24", ··· 7862 8565 "version": "1.0.0", 7863 8566 "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 7864 8567 "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" 8568 + }, 8569 + "util-deprecate": { 8570 + "version": "1.0.2", 8571 + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 8572 + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 8573 + "dev": true 7865 8574 }, 7866 8575 "utils-merge": { 7867 8576 "version": "1.0.1", ··· 8154 8863 "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==" 8155 8864 } 8156 8865 } 8866 + }, 8867 + "wrappy": { 8868 + "version": "1.0.2", 8869 + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 8870 + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 8871 + "dev": true 8157 8872 }, 8158 8873 "ws": { 8159 8874 "version": "8.19.0",
+3
package.json
··· 13 13 "devDependencies": { 14 14 "@sveltejs/kit": "^2.0.0", 15 15 "@sveltejs/vite-plugin-svelte": "^4.0.0", 16 + "@types/better-sqlite3": "^7.6.13", 17 + "better-sqlite3": "^12.6.2", 16 18 "svelte": "^5.0.0", 17 19 "svelte-check": "^4.0.0", 18 20 "tsx": "^4.21.0", ··· 35 37 "@cf-wasm/resvg": "^0.3.3", 36 38 "@sveltejs/adapter-cloudflare": "^7.2.6", 37 39 "actor-typeahead": "^0.1.2", 40 + "dotenv": "^17.2.3", 38 41 "jgoboard": "github:jokkebk/jgoboard", 39 42 "kysely": "^0.27.0", 40 43 "kysely-d1": "^0.4.0",
+4
src/hooks.server.ts
··· 1 1 import { subscribeToFirehose } from '$lib/server/firehose'; 2 2 import type { Handle } from '@sveltejs/kit'; 3 + import { config } from 'dotenv'; 4 + 5 + // Load .env file for local development (ignored in Cloudflare) 6 + config(); 3 7 4 8 // Initialize firehose on server start (platform context will be passed on first request) 5 9 let firehoseInitialized = false;
+14 -7
src/lib/server/auth.ts
··· 2 2 import type { App } from '@sveltejs/kit'; 3 3 import { 4 4 OAuthClient, 5 + MemoryStore, 5 6 type StoredState, 6 7 type OAuthSession, 7 8 } from "@atcute/oauth-node-client"; ··· 77 78 const oauthClients = new Map<string, OAuthClient>(); 78 79 79 80 export async function getOAuthClient(platform: App.Platform | undefined): Promise<OAuthClient> { 80 - if (!platform?.env) { 81 - throw new Error('Platform environment not available'); 82 - } 83 - 84 - const PRIVATE_KEY_JWK = platform.env.PRIVATE_KEY_JWK; 85 - const PUBLIC_BASE_URL = platform.env.PUBLIC_BASE_URL; 81 + // For local development, fall back to process.env 82 + const PRIVATE_KEY_JWK = platform?.env?.PRIVATE_KEY_JWK || process.env.PRIVATE_KEY_JWK; 83 + const PUBLIC_BASE_URL = platform?.env?.PUBLIC_BASE_URL || process.env.PUBLIC_BASE_URL; 86 84 87 85 if (!PRIVATE_KEY_JWK) { 88 86 throw new Error( ··· 135 133 }), 136 134 }), 137 135 138 - stores: { 136 + stores: platform?.env?.SESSIONS_KV && platform?.env?.STATES_KV ? { 137 + // Production: Use KV storage 139 138 sessions: new KVStateStore(platform.env.SESSIONS_KV, 'session') as any, 140 139 states: new KVStateStore<string, StoredState>(platform.env.STATES_KV, 'state', 600) as any, // 10 min TTL 140 + } : { 141 + // Local development: Use MemoryStore 142 + sessions: new MemoryStore({ maxSize: 10_000 }) as any, 143 + states: new MemoryStore<string, StoredState>({ 144 + maxSize: 10_000, 145 + ttl: TEN_MINUTES_MS, 146 + ttlAutopurge: true, 147 + }) as any, 141 148 }, 142 149 }); 143 150
+62 -6
src/lib/server/db.ts
··· 1 - import { Kysely } from 'kysely'; 1 + import { Kysely, SqliteDialect } from 'kysely'; 2 2 import { D1Dialect } from 'kysely-d1'; 3 3 import type { App } from '@sveltejs/kit'; 4 4 ··· 22 22 games: GameRecord; 23 23 } 24 24 25 - export function getDb(platform: App.Platform | undefined): Kysely<Database> { 26 - if (!platform?.env?.DB) { 27 - throw new Error('D1 database binding not available. Make sure wrangler.toml is configured and you are running in the Cloudflare environment.'); 25 + // Local development database (SQLite) 26 + let localDb: Kysely<Database> | null = null; 27 + 28 + function initLocalDb(): Kysely<Database> { 29 + if (localDb) return localDb; 30 + 31 + // Dynamic import for local development only 32 + const Database = require('better-sqlite3'); 33 + const fs = require('fs'); 34 + const path = require('path'); 35 + 36 + const dbPath = process.env.DATABASE_PATH || './data/app.db'; 37 + const dbDir = path.dirname(dbPath); 38 + 39 + if (!fs.existsSync(dbDir)) { 40 + fs.mkdirSync(dbDir, { recursive: true }); 28 41 } 29 42 30 - return new Kysely<Database>({ 31 - dialect: new D1Dialect({ database: platform.env.DB }), 43 + const sqlite = new Database(dbPath); 44 + sqlite.pragma('journal_mode = WAL'); 45 + 46 + // Initialize tables 47 + sqlite.exec(` 48 + CREATE TABLE IF NOT EXISTS games ( 49 + id TEXT PRIMARY KEY, 50 + rkey TEXT NOT NULL, 51 + creator_did TEXT NOT NULL, 52 + player_one TEXT NOT NULL, 53 + player_two TEXT, 54 + board_size INTEGER NOT NULL DEFAULT 19, 55 + status TEXT NOT NULL CHECK(status IN ('waiting', 'active', 'completed')), 56 + action_count INTEGER NOT NULL DEFAULT 0, 57 + last_action_type TEXT, 58 + winner TEXT, 59 + handicap INTEGER DEFAULT 0, 60 + created_at TEXT NOT NULL, 61 + updated_at TEXT NOT NULL 62 + ); 63 + 64 + CREATE INDEX IF NOT EXISTS idx_games_status ON games(status); 65 + CREATE INDEX IF NOT EXISTS idx_games_player_one ON games(player_one); 66 + CREATE INDEX IF NOT EXISTS idx_games_player_two ON games(player_two); 67 + CREATE INDEX IF NOT EXISTS idx_games_rkey ON games(rkey); 68 + CREATE INDEX IF NOT EXISTS idx_games_creator_did ON games(creator_did); 69 + `); 70 + 71 + localDb = new Kysely<Database>({ 72 + dialect: new SqliteDialect({ database: sqlite }), 32 73 }); 74 + 75 + return localDb; 76 + } 77 + 78 + export function getDb(platform: App.Platform | undefined): Kysely<Database> { 79 + // Production: Use Cloudflare D1 80 + if (platform?.env?.DB) { 81 + return new Kysely<Database>({ 82 + dialect: new D1Dialect({ database: platform.env.DB }), 83 + }); 84 + } 85 + 86 + // Local development: Use better-sqlite3 87 + console.log('[DB] Using local SQLite database for development'); 88 + return initLocalDb(); 33 89 }