Superpowered to do lists. No signup required.

Compare changes

Choose any two refs to compare.

+176 -36
bun.lock
··· 4 4 "": { 5 5 "name": "easytodo.link", 6 6 "dependencies": { 7 - "@tailwindcss/vite": "^4.1.13", 8 - "oslo": "^1.2.1", 9 - "svelte-french-toast": "^1.2.0", 10 - "tailwindcss": "^4.1.13", 7 + "@atproto/api": "latest", 8 + "@atproto/oauth-client-node": "latest", 9 + "@oslojs/encoding": "latest", 10 + "@tailwindcss/vite": "latest", 11 + "drizzle-orm": "latest", 12 + "oslo": "latest", 13 + "postgres": "latest", 14 + "svelte-french-toast": "latest", 15 + "tailwindcss": "latest", 11 16 }, 12 17 "devDependencies": { 13 - "@deno/svelte-adapter": "^0.1.0", 14 - "@sveltejs/kit": "^2.43.5", 15 - "@sveltejs/vite-plugin-svelte": "^6.2.1", 16 - "svelte": "^5.39.6", 17 - "svelte-check": "^4.3.2", 18 - "tslib": "^2.8.1", 19 - "typescript": "^5.9.2", 20 - "vite": "^7.1.7", 18 + "@sveltejs/adapter-netlify": "latest", 19 + "@sveltejs/kit": "latest", 20 + "@sveltejs/vite-plugin-svelte": "latest", 21 + "drizzle-kit": "latest", 22 + "svelte": "latest", 23 + "svelte-check": "latest", 24 + "tslib": "latest", 25 + "typescript": "latest", 26 + "vite": "latest", 21 27 }, 22 28 }, 23 29 }, 24 30 "packages": { 25 - "@deno/experimental-route-config": ["@deno/experimental-route-config@0.0.5", "", { "dependencies": { "urlpattern-polyfill": "^10.0.0" } }, "sha512-0PN4qij3sC3Qm8WbiOBGlOQz8WtB0AENGkzsTHOYyPenf40iW7OGFid8QT3L8lGApnz3t6ufET0c2XgagV8Jjw=="], 31 + "@atproto-labs/did-resolver": ["@atproto-labs/did-resolver@0.2.1", "", { "dependencies": { "@atproto-labs/fetch": "0.2.3", "@atproto-labs/pipe": "0.1.1", "@atproto-labs/simple-store": "0.3.0", "@atproto-labs/simple-store-memory": "0.1.4", "@atproto/did": "0.2.0", "zod": "^3.23.8" } }, "sha512-zSoHyqwwRYUtMNLW+RrWsImt1U5S47nJv5FfmAXTmon6wVKjxKD/PFrD1pg/4G6THqJmQHTs1Hj+54XVupYnvQ=="], 32 + 33 + "@atproto-labs/fetch": ["@atproto-labs/fetch@0.2.3", "", { "dependencies": { "@atproto-labs/pipe": "0.1.1" } }, "sha512-NZtbJOCbxKUFRFKMpamT38PUQMY0hX0p7TG5AEYOPhZKZEP7dHZ1K2s1aB8MdVH0qxmqX7nQleNrrvLf09Zfdw=="], 34 + 35 + "@atproto-labs/fetch-node": ["@atproto-labs/fetch-node@0.1.10", "", { "dependencies": { "@atproto-labs/fetch": "0.2.3", "@atproto-labs/pipe": "0.1.1", "ipaddr.js": "^2.1.0", "undici": "^6.14.1" } }, "sha512-o7hGaonA71A6p7O107VhM6UBUN/g9tTyYohMp1q0Kf6xQ4npnuZYRSHSf2g6reSfGQJ1GoFNjBObETTT1ge/jQ=="], 26 36 27 - "@deno/svelte-adapter": ["@deno/svelte-adapter@0.1.0", "", { "dependencies": { "@deno/experimental-route-config": "^0.0.5" }, "peerDependencies": { "@sveltejs/kit": "2.x" } }, "sha512-fx4Kj1lSx1rJvjtPr3cXO7qKveI1vUyGW8jX+clJHQEqEeIDnEcBW0FpMZEVORPlay2SGydQWSGbwh4j2iENzg=="], 37 + "@atproto-labs/handle-resolver": ["@atproto-labs/handle-resolver@0.3.1", "", { "dependencies": { "@atproto-labs/simple-store": "0.3.0", "@atproto-labs/simple-store-memory": "0.1.4", "@atproto/did": "0.2.0", "zod": "^3.23.8" } }, "sha512-mLZdMNvwomgnn9sffKO1/xr02ctgeiT0FUVw7JekbchTckub2RM7qMu8Rw1mC4bpCpW+i7DXDiOxpoajkppwYQ=="], 38 + 39 + "@atproto-labs/handle-resolver-node": ["@atproto-labs/handle-resolver-node@0.1.19", "", { "dependencies": { "@atproto-labs/fetch-node": "0.1.10", "@atproto-labs/handle-resolver": "0.3.1", "@atproto/did": "0.2.0" } }, "sha512-nNVCfiKudvMYfDcWCa9koOMOpCYaC0wG4Uys5dZev99s/Nka7tRlIZIV+u+GWivnG9lqCupKATkoyCd6Per8Gw=="], 40 + 41 + "@atproto-labs/identity-resolver": ["@atproto-labs/identity-resolver@0.3.1", "", { "dependencies": { "@atproto-labs/did-resolver": "0.2.1", "@atproto-labs/handle-resolver": "0.3.1" } }, "sha512-jCgotRRqPykPwh4gh0FBLOqeofv1G8OH/DZ5s88HWm7biUZeksZwDrEvL5TnqEFUpXT3O9Hcyp/XEpfCAplRoQ=="], 42 + 43 + "@atproto-labs/pipe": ["@atproto-labs/pipe@0.1.1", "", {}, "sha512-hdNw2oUs2B6BN1lp+32pF7cp8EMKuIN5Qok2Vvv/aOpG/3tNSJ9YkvfI0k6Zd188LeDDYRUpYpxcoFIcGH/FNg=="], 44 + 45 + "@atproto-labs/simple-store": ["@atproto-labs/simple-store@0.3.0", "", {}, "sha512-nOb6ONKBRJHRlukW1sVawUkBqReLlLx6hT35VS3imaNPwiXDxLnTK7lxw3Lrl9k5yugSBDQAkZAq3MPTEFSUBQ=="], 46 + 47 + "@atproto-labs/simple-store-memory": ["@atproto-labs/simple-store-memory@0.1.4", "", { "dependencies": { "@atproto-labs/simple-store": "0.3.0", "lru-cache": "^10.2.0" } }, "sha512-3mKY4dP8I7yKPFj9VKpYyCRzGJOi5CEpOLPlRhoJyLmgs3J4RzDrjn323Oakjz2Aj2JzRU/AIvWRAZVhpYNJHw=="], 48 + 49 + "@atproto/api": ["@atproto/api@0.17.0", "", { "dependencies": { "@atproto/common-web": "^0.4.3", "@atproto/lexicon": "^0.5.1", "@atproto/syntax": "^0.4.1", "@atproto/xrpc": "^0.7.5", "await-lock": "^2.2.2", "multiformats": "^9.9.0", "tlds": "^1.234.0", "zod": "^3.23.8" } }, "sha512-FNS9SW7/3kslAnJH7F4fO9/jPjXzC0NMD6u9NjJ/h4EnaIEpWHZQPkmD9Q2hvAwD6+Uo2boYZEPKkOa55Lr5Dg=="], 50 + 51 + "@atproto/common-web": ["@atproto/common-web@0.4.3", "", { "dependencies": { "graphemer": "^1.4.0", "multiformats": "^9.9.0", "uint8arrays": "3.0.0", "zod": "^3.23.8" } }, "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg=="], 52 + 53 + "@atproto/did": ["@atproto/did@0.2.0", "", { "dependencies": { "zod": "^3.23.8" } }, "sha512-BskT39KYbwY1DUsWekkHh47xS+wvJpFq5F9acsicNfYniinyAMnNTzGKQEhnjQuG7K0qQItg/SnmC+y0tJXV7Q=="], 54 + 55 + "@atproto/jwk": ["@atproto/jwk@0.5.0", "", { "dependencies": { "multiformats": "^9.9.0", "zod": "^3.23.8" } }, "sha512-Qi2NtEqhkG+uz3CKia4+H05WMV/z//dz3ESo5+cyBKrOnxVTJ5ZubMyltWjoYvy6v/jLhorXdDWcjn07yky7MQ=="], 56 + 57 + "@atproto/jwk-jose": ["@atproto/jwk-jose@0.1.10", "", { "dependencies": { "@atproto/jwk": "0.5.0", "jose": "^5.2.0" } }, "sha512-Eiu/u4tZHz3IIhHZt0zneYEffSAO3Oqk/ToKwlu1TqKte6sjtPs/4uquSiAAGFYozqgo92JC/AQclWzzkHI5QQ=="], 58 + 59 + "@atproto/jwk-webcrypto": ["@atproto/jwk-webcrypto@0.1.10", "", { "dependencies": { "@atproto/jwk": "0.5.0", "@atproto/jwk-jose": "0.1.10", "zod": "^3.23.8" } }, "sha512-JZsavs6JiSmw5rgcjkGDwzr1aCJGdybZOjVfYH+m9sXRU1BrUCA30uwNfZY7eFyWXyRAnCFiYiGVZgypXyKotw=="], 60 + 61 + "@atproto/lexicon": ["@atproto/lexicon@0.5.1", "", { "dependencies": { "@atproto/common-web": "^0.4.3", "@atproto/syntax": "^0.4.1", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "zod": "^3.23.8" } }, "sha512-y8AEtYmfgVl4fqFxqXAeGvhesiGkxiy3CWoJIfsFDDdTlZUC8DFnZrYhcqkIop3OlCkkljvpSJi1hbeC1tbi8A=="], 62 + 63 + "@atproto/oauth-client": ["@atproto/oauth-client@0.5.6", "", { "dependencies": { "@atproto-labs/did-resolver": "0.2.1", "@atproto-labs/fetch": "0.2.3", "@atproto-labs/handle-resolver": "0.3.1", "@atproto-labs/identity-resolver": "0.3.1", "@atproto-labs/simple-store": "0.3.0", "@atproto-labs/simple-store-memory": "0.1.4", "@atproto/did": "0.2.0", "@atproto/jwk": "0.5.0", "@atproto/oauth-types": "0.4.1", "@atproto/xrpc": "0.7.5", "multiformats": "^9.9.0", "zod": "^3.23.8" } }, "sha512-O1S9lPptJxWPcNd2kODaLgWntz+A7PzskU2hP4IFa7hVLs4aEnEt9dKq5wJE97tDli8mgyh/ndPQhxUaCVQ5iQ=="], 64 + 65 + "@atproto/oauth-client-node": ["@atproto/oauth-client-node@0.3.8", "", { "dependencies": { "@atproto-labs/did-resolver": "0.2.1", "@atproto-labs/handle-resolver-node": "0.1.19", "@atproto-labs/simple-store": "0.3.0", "@atproto/did": "0.2.0", "@atproto/jwk": "0.5.0", "@atproto/jwk-jose": "0.1.10", "@atproto/jwk-webcrypto": "0.1.10", "@atproto/oauth-client": "0.5.6", "@atproto/oauth-types": "0.4.1" } }, "sha512-HIBiYQERj04Xa0l8cJkqcZC0BbHH5uqDEvhqHWnJ5umSq/ms0+HZi3JKJXGv1XfYOvxUxx6NKgXJ8VhhYoQa5A=="], 66 + 67 + "@atproto/oauth-types": ["@atproto/oauth-types@0.4.1", "", { "dependencies": { "@atproto/jwk": "0.5.0", "zod": "^3.23.8" } }, "sha512-c5ixf2ZOzcltOu1fDBnO/tok6Wj7JDDK66+Z0q/+bAr8LXgOnxP7zQfJ+DD4gTkB+saTqsqWtVv8qvx/IEtm1g=="], 68 + 69 + "@atproto/syntax": ["@atproto/syntax@0.4.1", "", {}, "sha512-CJdImtLAiFO+0z3BWTtxwk6aY5w4t8orHTMVJgkf++QRJWTxPbIFko/0hrkADB7n2EruDxDSeAgfUGehpH6ngw=="], 70 + 71 + "@atproto/xrpc": ["@atproto/xrpc@0.7.5", "", { "dependencies": { "@atproto/lexicon": "^0.5.1", "zod": "^3.23.8" } }, "sha512-MUYNn5d2hv8yVegRL0ccHvTHAVj5JSnW07bkbiaz96UH45lvYNRVwt44z+yYVnb0/mvBzyD3/ZQ55TRGt7fHkA=="], 72 + 73 + "@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="], 28 74 29 75 "@emnapi/core": ["@emnapi/core@0.45.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw=="], 30 76 31 77 "@emnapi/runtime": ["@emnapi/runtime@0.45.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w=="], 78 + 79 + "@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="], 80 + 81 + "@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="], 32 82 33 83 "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.10", "", { "os": "aix", "cpu": "ppc64" }, "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw=="], 34 84 ··· 81 131 "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.10", "", { "os": "win32", "cpu": "ia32" }, "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw=="], 82 132 83 133 "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.10", "", { "os": "win32", "cpu": "x64" }, "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw=="], 134 + 135 + "@iarna/toml": ["@iarna/toml@2.2.5", "", {}, "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg=="], 84 136 85 137 "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], 86 138 ··· 154 206 155 207 "@node-rs/bcrypt-win32-x64-msvc": ["@node-rs/bcrypt-win32-x64-msvc@1.9.0", "", { "os": "win32", "cpu": "x64" }, "sha512-2y0Tuo6ZAT2Cz8V7DHulSlv1Bip3zbzeXyeur+uR25IRNYXKvI/P99Zl85Fbuu/zzYAZRLLlGTRe6/9IHofe/w=="], 156 208 209 + "@oslojs/encoding": ["@oslojs/encoding@1.1.0", "", {}, "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ=="], 210 + 157 211 "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], 158 212 159 213 "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.3", "", { "os": "android", "cpu": "arm" }, "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw=="], ··· 204 258 205 259 "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.6", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ=="], 206 260 207 - "@sveltejs/kit": ["@sveltejs/kit@2.43.5", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.3.2", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["@opentelemetry/api"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-44Mm5csR4mesKx2Eyhtk8UVrLJ4c04BT2wMTfYGKJMOkUqpHP5KLL2DPV0hXUA4t4+T3ZYe0aBygd42lVYv2cA=="], 261 + "@sveltejs/adapter-netlify": ["@sveltejs/adapter-netlify@5.2.4", "", { "dependencies": { "@iarna/toml": "^2.2.5", "esbuild": "^0.25.4", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "@sveltejs/kit": "^2.4.0" } }, "sha512-UtPcZq1HUA43hM8uLi+nsm5Q+YjHNj7/SMFoyeLZeY/VTloVWABEZ0tJ5WodTUmy/8j5QJ7oLZjj28aQxi8y3g=="], 262 + 263 + "@sveltejs/kit": ["@sveltejs/kit@2.44.0", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.3.2", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["@opentelemetry/api"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-xU5qP7PiYmrSH70Whm/I+nf0j4xBnHyRQNkC1SEfaBOwCCkkeuL6WNxSb8q4Ib7+Z+sZ4JUTDYHfoyVm02EXVQ=="], 208 264 209 265 "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@6.2.1", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "debug": "^4.4.1", "deepmerge": "^4.3.1", "magic-string": "^0.30.17", "vitefu": "^1.1.1" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ=="], 210 266 211 267 "@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@5.0.1", "", { "dependencies": { "debug": "^4.4.1" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA=="], 212 268 213 - "@tailwindcss/node": ["@tailwindcss/node@4.1.13", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.5.1", "lightningcss": "1.30.1", "magic-string": "^0.30.18", "source-map-js": "^1.2.1", "tailwindcss": "4.1.13" } }, "sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw=="], 269 + "@tailwindcss/node": ["@tailwindcss/node@4.1.14", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.0", "lightningcss": "1.30.1", "magic-string": "^0.30.19", "source-map-js": "^1.2.1", "tailwindcss": "4.1.14" } }, "sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw=="], 214 270 215 - "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.13", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.13", "@tailwindcss/oxide-darwin-arm64": "4.1.13", "@tailwindcss/oxide-darwin-x64": "4.1.13", "@tailwindcss/oxide-freebsd-x64": "4.1.13", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.13", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.13", "@tailwindcss/oxide-linux-arm64-musl": "4.1.13", "@tailwindcss/oxide-linux-x64-gnu": "4.1.13", "@tailwindcss/oxide-linux-x64-musl": "4.1.13", "@tailwindcss/oxide-wasm32-wasi": "4.1.13", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.13", "@tailwindcss/oxide-win32-x64-msvc": "4.1.13" } }, "sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA=="], 271 + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.14", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.5.1" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.14", "@tailwindcss/oxide-darwin-arm64": "4.1.14", "@tailwindcss/oxide-darwin-x64": "4.1.14", "@tailwindcss/oxide-freebsd-x64": "4.1.14", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.14", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.14", "@tailwindcss/oxide-linux-arm64-musl": "4.1.14", "@tailwindcss/oxide-linux-x64-gnu": "4.1.14", "@tailwindcss/oxide-linux-x64-musl": "4.1.14", "@tailwindcss/oxide-wasm32-wasi": "4.1.14", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.14", "@tailwindcss/oxide-win32-x64-msvc": "4.1.14" } }, "sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw=="], 216 272 217 - "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.13", "", { "os": "android", "cpu": "arm64" }, "sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew=="], 273 + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.14", "", { "os": "android", "cpu": "arm64" }, "sha512-a94ifZrGwMvbdeAxWoSuGcIl6/DOP5cdxagid7xJv6bwFp3oebp7y2ImYsnZBMTwjn5Ev5xESvS3FFYUGgPODQ=="], 218 274 219 - "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.13", "", { "os": "darwin", "cpu": "arm64" }, "sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ=="], 275 + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.14", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HkFP/CqfSh09xCnrPJA7jud7hij5ahKyWomrC3oiO2U9i0UjP17o9pJbxUN0IJ471GTQQmzwhp0DEcpbp4MZTA=="], 220 276 221 - "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.13", "", { "os": "darwin", "cpu": "x64" }, "sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw=="], 277 + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.14", "", { "os": "darwin", "cpu": "x64" }, "sha512-eVNaWmCgdLf5iv6Qd3s7JI5SEFBFRtfm6W0mphJYXgvnDEAZ5sZzqmI06bK6xo0IErDHdTA5/t7d4eTfWbWOFw=="], 222 278 223 - "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.13", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ=="], 279 + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.14", "", { "os": "freebsd", "cpu": "x64" }, "sha512-QWLoRXNikEuqtNb0dhQN6wsSVVjX6dmUFzuuiL09ZeXju25dsei2uIPl71y2Ic6QbNBsB4scwBoFnlBfabHkEw=="], 224 280 225 - "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13", "", { "os": "linux", "cpu": "arm" }, "sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw=="], 281 + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14", "", { "os": "linux", "cpu": "arm" }, "sha512-VB4gjQni9+F0VCASU+L8zSIyjrLLsy03sjcR3bM0V2g4SNamo0FakZFKyUQ96ZVwGK4CaJsc9zd/obQy74o0Fw=="], 226 282 227 - "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ=="], 283 + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w=="], 228 284 229 - "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg=="], 285 + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ=="], 230 286 231 - "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.13", "", { "os": "linux", "cpu": "x64" }, "sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ=="], 287 + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.14", "", { "os": "linux", "cpu": "x64" }, "sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg=="], 232 288 233 - "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.13", "", { "os": "linux", "cpu": "x64" }, "sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ=="], 289 + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.14", "", { "os": "linux", "cpu": "x64" }, "sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q=="], 234 290 235 - "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.13", "", { "dependencies": { "@emnapi/core": "^1.4.5", "@emnapi/runtime": "^1.4.5", "@emnapi/wasi-threads": "^1.0.4", "@napi-rs/wasm-runtime": "^0.2.12", "@tybys/wasm-util": "^0.10.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA=="], 291 + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.14", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.0.5", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-uZYAsaW/jS/IYkd6EWPJKW/NlPNSkWkBlaeVBi/WsFQNP05/bzkebUL8FH1pdsqx4f2fH/bWFcUABOM9nfiJkQ=="], 236 292 237 - "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.13", "", { "os": "win32", "cpu": "arm64" }, "sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg=="], 293 + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.14", "", { "os": "win32", "cpu": "arm64" }, "sha512-Az0RnnkcvRqsuoLH2Z4n3JfAef0wElgzHD5Aky/e+0tBUxUhIeIqFBTMNQvmMRSP15fWwmvjBxZ3Q8RhsDnxAA=="], 238 294 239 - "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.13", "", { "os": "win32", "cpu": "x64" }, "sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw=="], 295 + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.14", "", { "os": "win32", "cpu": "x64" }, "sha512-ttblVGHgf68kEE4om1n/n44I0yGPkCPbLsqzjvybhpwa6mKKtgFfAzy6btc3HRmuW7nHe0OOrSeNP9sQmmH9XA=="], 240 296 241 - "@tailwindcss/vite": ["@tailwindcss/vite@4.1.13", "", { "dependencies": { "@tailwindcss/node": "4.1.13", "@tailwindcss/oxide": "4.1.13", "tailwindcss": "4.1.13" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ=="], 297 + "@tailwindcss/vite": ["@tailwindcss/vite@4.1.14", "", { "dependencies": { "@tailwindcss/node": "4.1.14", "@tailwindcss/oxide": "4.1.14", "tailwindcss": "4.1.14" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-BoFUoU0XqgCUS1UXWhmDJroKKhNXeDzD7/XwabjkDIAbMnc4ULn5e2FuEuBbhZ6ENZoSYzKlzvZ44Yr6EUDUSA=="], 242 298 243 299 "@tybys/wasm-util": ["@tybys/wasm-util@0.8.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Z96T/L6dUFFxgFJ+pQtkPpne9q7i6kIPYCFnQBHSgSPV9idTsKfIhCss0h5iM9irweZCatkrdeP8yi5uM1eX6Q=="], 244 300 ··· 250 306 251 307 "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], 252 308 309 + "await-lock": ["await-lock@2.2.2", "", {}, "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw=="], 310 + 253 311 "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], 312 + 313 + "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], 254 314 255 315 "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], 256 316 ··· 268 328 269 329 "devalue": ["devalue@5.3.2", "", {}, "sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw=="], 270 330 331 + "drizzle-kit": ["drizzle-kit@0.31.5", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-+CHgPFzuoTQTt7cOYCV6MOw2w8vqEn/ap1yv4bpZOWL03u7rlVRQhUY0WYT3rHsgVTXwYQDZaSUJSQrMBUKuWg=="], 332 + 333 + "drizzle-orm": ["drizzle-orm@0.44.6", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-uy6uarrrEOc9K1u5/uhBFJbdF5VJ5xQ/Yzbecw3eAYOunv5FDeYkR2m8iitocdHBOHbvorviKOW5GVw0U1j4LQ=="], 334 + 271 335 "enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="], 272 336 273 337 "esbuild": ["esbuild@0.25.10", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.10", "@esbuild/android-arm": "0.25.10", "@esbuild/android-arm64": "0.25.10", "@esbuild/android-x64": "0.25.10", "@esbuild/darwin-arm64": "0.25.10", "@esbuild/darwin-x64": "0.25.10", "@esbuild/freebsd-arm64": "0.25.10", "@esbuild/freebsd-x64": "0.25.10", "@esbuild/linux-arm": "0.25.10", "@esbuild/linux-arm64": "0.25.10", "@esbuild/linux-ia32": "0.25.10", "@esbuild/linux-loong64": "0.25.10", "@esbuild/linux-mips64el": "0.25.10", "@esbuild/linux-ppc64": "0.25.10", "@esbuild/linux-riscv64": "0.25.10", "@esbuild/linux-s390x": "0.25.10", "@esbuild/linux-x64": "0.25.10", "@esbuild/netbsd-arm64": "0.25.10", "@esbuild/netbsd-x64": "0.25.10", "@esbuild/openbsd-arm64": "0.25.10", "@esbuild/openbsd-x64": "0.25.10", "@esbuild/openharmony-arm64": "0.25.10", "@esbuild/sunos-x64": "0.25.10", "@esbuild/win32-arm64": "0.25.10", "@esbuild/win32-ia32": "0.25.10", "@esbuild/win32-x64": "0.25.10" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ=="], 338 + 339 + "esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="], 274 340 275 341 "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], 276 342 ··· 282 348 283 349 "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 284 350 351 + "get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="], 352 + 285 353 "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], 286 354 355 + "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], 356 + 357 + "ipaddr.js": ["ipaddr.js@2.2.0", "", {}, "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA=="], 358 + 287 359 "is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="], 288 360 361 + "iso-datestring-validator": ["iso-datestring-validator@2.2.2", "", {}, "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA=="], 362 + 289 363 "jiti": ["jiti@2.6.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-VXe6RjJkBPj0ohtqaO8vSWP3ZhAKo66fKrFNCll4BTcwljPLz03pCbaNKfzGP5MbrCYcbJ7v0nOYYwUzTEIdXQ=="], 364 + 365 + "jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="], 290 366 291 367 "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], 292 368 ··· 314 390 315 391 "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="], 316 392 393 + "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], 394 + 317 395 "magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="], 318 396 319 397 "memfs": ["memfs@3.5.3", "", { "dependencies": { "fs-monkey": "^1.0.4" } }, "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw=="], ··· 329 407 "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], 330 408 331 409 "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], 410 + 411 + "multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 332 412 333 413 "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], 334 414 ··· 340 420 341 421 "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], 342 422 423 + "postgres": ["postgres@3.4.7", "", {}, "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw=="], 424 + 343 425 "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], 426 + 427 + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], 344 428 345 429 "rollup": ["rollup@4.52.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.3", "@rollup/rollup-android-arm64": "4.52.3", "@rollup/rollup-darwin-arm64": "4.52.3", "@rollup/rollup-darwin-x64": "4.52.3", "@rollup/rollup-freebsd-arm64": "4.52.3", "@rollup/rollup-freebsd-x64": "4.52.3", "@rollup/rollup-linux-arm-gnueabihf": "4.52.3", "@rollup/rollup-linux-arm-musleabihf": "4.52.3", "@rollup/rollup-linux-arm64-gnu": "4.52.3", "@rollup/rollup-linux-arm64-musl": "4.52.3", "@rollup/rollup-linux-loong64-gnu": "4.52.3", "@rollup/rollup-linux-ppc64-gnu": "4.52.3", "@rollup/rollup-linux-riscv64-gnu": "4.52.3", "@rollup/rollup-linux-riscv64-musl": "4.52.3", "@rollup/rollup-linux-s390x-gnu": "4.52.3", "@rollup/rollup-linux-x64-gnu": "4.52.3", "@rollup/rollup-linux-x64-musl": "4.52.3", "@rollup/rollup-openharmony-arm64": "4.52.3", "@rollup/rollup-win32-arm64-msvc": "4.52.3", "@rollup/rollup-win32-ia32-msvc": "4.52.3", "@rollup/rollup-win32-x64-gnu": "4.52.3", "@rollup/rollup-win32-x64-msvc": "4.52.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A=="], 346 430 ··· 349 433 "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="], 350 434 351 435 "sirv": ["sirv@3.0.2", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g=="], 436 + 437 + "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], 352 438 353 439 "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], 354 440 355 - "svelte": ["svelte@5.39.6", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^2.1.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-bOJXmuwLNaoqPCTWO8mPu/fwxI5peGE5Efe7oo6Cakpz/G60vsnVF6mxbGODaxMUFUKEnjm6XOwHEqOht6cbvw=="], 441 + "source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="], 442 + 443 + "svelte": ["svelte@5.39.8", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^2.1.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-KfZ3hCITdxIXTOvrea4nFZX2o+47HPTChKeocgj9BwJQYqWrviVCcPj4boXHF5yf8+eBKqhHY8xii//XaakKXA=="], 356 444 357 445 "svelte-check": ["svelte-check@4.3.2", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-71udP5w2kaSTcX8iV0hn3o2FWlabQHhJTJLIQrCqMsrcOeDUO2VhCQKKCA8AMVHSPwdxLEWkUWh9OKxns5PD9w=="], 358 446 ··· 360 448 361 449 "svelte-writable-derived": ["svelte-writable-derived@3.1.1", "", { "peerDependencies": { "svelte": "^3.2.1 || ^4.0.0-next.1 || ^5.0.0-next.94" } }, "sha512-w4LR6/bYZEuCs7SGr+M54oipk/UQKtiMadyOhW0PTwAtJ/Ai12QS77sLngEcfBx2q4H8ZBQucc9ktSA5sUGZWw=="], 362 450 363 - "tailwindcss": ["tailwindcss@4.1.13", "", {}, "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w=="], 451 + "tailwindcss": ["tailwindcss@4.1.14", "", {}, "sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA=="], 364 452 365 453 "tapable": ["tapable@2.2.3", "", {}, "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg=="], 366 454 ··· 368 456 369 457 "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], 370 458 459 + "tlds": ["tlds@1.260.0", "", { "bin": { "tlds": "bin.js" } }, "sha512-78+28EWBhCEE7qlyaHA9OR3IPvbCLiDh3Ckla593TksfFc9vfTsgvH7eS+dr3o9qr31gwGbogcI16yN91PoRjQ=="], 460 + 371 461 "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], 372 462 373 463 "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 374 464 375 - "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="], 465 + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], 376 466 377 - "urlpattern-polyfill": ["urlpattern-polyfill@10.1.0", "", {}, "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw=="], 467 + "uint8arrays": ["uint8arrays@3.0.0", "", { "dependencies": { "multiformats": "^9.4.2" } }, "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA=="], 378 468 379 - "vite": ["vite@7.1.7", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA=="], 469 + "undici": ["undici@6.21.3", "", {}, "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw=="], 470 + 471 + "vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], 380 472 381 473 "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], 382 474 383 475 "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], 384 476 385 477 "zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="], 478 + 479 + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], 480 + 481 + "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], 386 482 387 483 "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="], 388 484 ··· 390 486 391 487 "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], 392 488 393 - "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" }, "bundled": true }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="], 489 + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.0.6", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-DXj75ewm11LIWUk198QSKUTxjyRjsBwk09MuMk5DGK+GDUtyPhhEHOGP/Xwwj3DjQXXkivoBirmOnKrLfc0+9g=="], 394 490 395 491 "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], 396 492 397 493 "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 494 + 495 + "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="], 496 + 497 + "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="], 498 + 499 + "@esbuild-kit/core-utils/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.18.20", "", { "os": "android", "cpu": "x64" }, "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="], 500 + 501 + "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.18.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="], 502 + 503 + "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.18.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="], 504 + 505 + "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.18.20", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="], 506 + 507 + "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.18.20", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="], 508 + 509 + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.18.20", "", { "os": "linux", "cpu": "arm" }, "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="], 510 + 511 + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.18.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="], 512 + 513 + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.18.20", "", { "os": "linux", "cpu": "ia32" }, "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="], 514 + 515 + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="], 516 + 517 + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="], 518 + 519 + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.18.20", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="], 520 + 521 + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="], 522 + 523 + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.18.20", "", { "os": "linux", "cpu": "s390x" }, "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="], 524 + 525 + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.18.20", "", { "os": "linux", "cpu": "x64" }, "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="], 526 + 527 + "@esbuild-kit/core-utils/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.18.20", "", { "os": "none", "cpu": "x64" }, "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="], 528 + 529 + "@esbuild-kit/core-utils/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.18.20", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="], 530 + 531 + "@esbuild-kit/core-utils/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.18.20", "", { "os": "sunos", "cpu": "x64" }, "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="], 532 + 533 + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.18.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="], 534 + 535 + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="], 536 + 537 + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="], 398 538 } 399 539 }
+11
drizzle/0000_first_praxagora.sql
··· 1 + CREATE TABLE "auth_session" ( 2 + "key" text PRIMARY KEY NOT NULL, 3 + "session" json NOT NULL, 4 + CONSTRAINT "auth_session_key_unique" UNIQUE("key") 5 + ); 6 + --> statement-breakpoint 7 + CREATE TABLE "auth_state" ( 8 + "key" text PRIMARY KEY NOT NULL, 9 + "state" json NOT NULL, 10 + CONSTRAINT "auth_state_key_unique" UNIQUE("key") 11 + );
+85
drizzle/meta/0000_snapshot.json
··· 1 + { 2 + "id": "6e06677f-9a96-41ee-82f8-e0f6e1ea34e3", 3 + "prevId": "00000000-0000-0000-0000-000000000000", 4 + "version": "7", 5 + "dialect": "postgresql", 6 + "tables": { 7 + "public.auth_session": { 8 + "name": "auth_session", 9 + "schema": "", 10 + "columns": { 11 + "key": { 12 + "name": "key", 13 + "type": "text", 14 + "primaryKey": true, 15 + "notNull": true 16 + }, 17 + "session": { 18 + "name": "session", 19 + "type": "json", 20 + "primaryKey": false, 21 + "notNull": true 22 + } 23 + }, 24 + "indexes": {}, 25 + "foreignKeys": {}, 26 + "compositePrimaryKeys": {}, 27 + "uniqueConstraints": { 28 + "auth_session_key_unique": { 29 + "name": "auth_session_key_unique", 30 + "nullsNotDistinct": false, 31 + "columns": [ 32 + "key" 33 + ] 34 + } 35 + }, 36 + "policies": {}, 37 + "checkConstraints": {}, 38 + "isRLSEnabled": false 39 + }, 40 + "public.auth_state": { 41 + "name": "auth_state", 42 + "schema": "", 43 + "columns": { 44 + "key": { 45 + "name": "key", 46 + "type": "text", 47 + "primaryKey": true, 48 + "notNull": true 49 + }, 50 + "state": { 51 + "name": "state", 52 + "type": "json", 53 + "primaryKey": false, 54 + "notNull": true 55 + } 56 + }, 57 + "indexes": {}, 58 + "foreignKeys": {}, 59 + "compositePrimaryKeys": {}, 60 + "uniqueConstraints": { 61 + "auth_state_key_unique": { 62 + "name": "auth_state_key_unique", 63 + "nullsNotDistinct": false, 64 + "columns": [ 65 + "key" 66 + ] 67 + } 68 + }, 69 + "policies": {}, 70 + "checkConstraints": {}, 71 + "isRLSEnabled": false 72 + } 73 + }, 74 + "enums": {}, 75 + "schemas": {}, 76 + "sequences": {}, 77 + "roles": {}, 78 + "policies": {}, 79 + "views": {}, 80 + "_meta": { 81 + "columns": {}, 82 + "schemas": {}, 83 + "tables": {} 84 + } 85 + }
+13
drizzle/meta/_journal.json
··· 1 + { 2 + "version": "7", 3 + "dialect": "postgresql", 4 + "entries": [ 5 + { 6 + "idx": 0, 7 + "version": "7", 8 + "when": 1759136833553, 9 + "tag": "0000_first_praxagora", 10 + "breakpoints": true 11 + } 12 + ] 13 + }
+12
drizzle.config.ts
··· 1 + import { defineConfig } from "drizzle-kit"; 2 + if (!process.env.DATABASE_URL) throw new Error("DATABASE_URL is not set"); 3 + 4 + export default defineConfig({ 5 + schema: "./src/lib/schema.ts", 6 + dbCredentials: { 7 + url: process.env.DATABASE_URL 8 + }, 9 + verbose: true, 10 + strict: true, 11 + dialect: "postgresql" 12 + });
+27
link/easytodo/tasks/list.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "link.easytodo.tasks.list", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "description": "A list of items to do.", 8 + "key": "tid", 9 + "record": { 10 + "type": "object", 11 + "required": ["id", "title", "createdAt"], 12 + "properties": { 13 + "id": { "type": "string" }, 14 + "title": { "type": "string" }, 15 + "createdAt": { "type": "string", "format": "datetime" }, 16 + "tasks": { 17 + "type": "array", 18 + "items": { 19 + "type": "ref", 20 + "ref": "link.easytodo.tasks.task" 21 + } 22 + } 23 + } 24 + } 25 + } 26 + } 27 + }
+21
link/easytodo/tasks/task.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "link.easytodo.tasks.task", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "description": "An item to do.", 8 + "key": "tid", 9 + "record": { 10 + "type": "object", 11 + "required": ["id", "description", "is_completed"], 12 + "properties": { 13 + "id": { "type": "string" }, 14 + "description": { "type": "string" }, 15 + "is_completed": { "type": "boolean", "default": false }, 16 + "duration": { "type": "integer" } 17 + } 18 + } 19 + } 20 + } 21 + }
+17 -7
package.json
··· 7 7 "build": "vite build", 8 8 "preview": "vite preview", 9 9 "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 10 - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" 10 + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 11 + "db:push": "drizzle-kit push", 12 + "db:migrate": "drizzle-kit migrate", 13 + "db:studio": "drizzle-kit studio" 11 14 }, 12 15 "devDependencies": { 13 - "@sveltejs/kit": "^2.43.5", 16 + "@sveltejs/adapter-netlify": "^5.2.4", 17 + "@sveltejs/kit": "^2.44.0", 14 18 "@sveltejs/vite-plugin-svelte": "^6.2.1", 15 - "svelte": "^5.39.6", 19 + "drizzle-kit": "^0.31.5", 20 + "svelte": "^5.39.8", 16 21 "svelte-check": "^4.3.2", 17 22 "tslib": "^2.8.1", 18 - "typescript": "^5.9.2", 19 - "vite": "^7.1.7" 23 + "typescript": "^5.9.3", 24 + "vite": "^7.1.9" 20 25 }, 21 26 "type": "module", 22 27 "dependencies": { 23 - "@tailwindcss/vite": "^4.1.13", 28 + "@atproto/api": "^0.17.0", 29 + "@atproto/oauth-client-node": "^0.3.8", 30 + "@oslojs/encoding": "^1.1.0", 31 + "@tailwindcss/vite": "^4.1.14", 32 + "drizzle-orm": "^0.44.6", 24 33 "oslo": "^1.2.1", 34 + "postgres": "^3.4.7", 25 35 "svelte-french-toast": "^1.2.0", 26 - "tailwindcss": "^4.1.13" 36 + "tailwindcss": "^4.1.14" 27 37 } 28 38 }
+12 -2
src/app.d.ts
··· 1 - // See https://kit.svelte.dev/docs/types#app 1 + // See https://svelte.dev/docs/kit/types#app.d.ts 2 + 3 + import type { Agent } from "@atproto/api"; 4 + import type { ProfileViewDetailed } from "@atproto/api/dist/client/types/app/bsky/actor/defs"; 5 + 2 6 // for information about these interfaces 3 7 declare global { 4 8 namespace App { 5 9 // interface Error {} 6 - // interface Locals {} 10 + 11 + // set on `hooks.server.ts`, available on server functions 12 + interface Locals { 13 + authedAgent: Agent | undefined; 14 + user: ProfileViewDetailed | undefined; 15 + } 16 + 7 17 // interface PageData {} 8 18 // interface PageState {} 9 19 // interface Platform {}
+41
src/hooks.server.ts
··· 1 + import { Agent } from "@atproto/api"; 2 + import { atclient } from "$lib/atproto"; 3 + 4 + import { decryptToString } from "$lib/server/encryption"; 5 + import { decodeBase64, decodeBase64urlIgnorePadding } from "@oslojs/encoding"; 6 + 7 + import type { Handle } from "@sveltejs/kit"; 8 + import { ENCRYPTION_PASSWORD } from "$env/static/private"; 9 + 10 + // runs everytime there's a new request 11 + export const handle: Handle = async ({ event, resolve }) => { 12 + const sid = event.cookies.get("sid"); 13 + 14 + // if there is a session cookie 15 + if (sid) { 16 + // if a user is already authed, skip reauthing 17 + if (event.locals.user) { return resolve(event); } 18 + 19 + // decrypt session cookie 20 + const decoded = decodeBase64urlIgnorePadding(sid); 21 + const key = decodeBase64(ENCRYPTION_PASSWORD); 22 + const decrypted = await decryptToString(key, decoded); 23 + 24 + // get oauth session from client using decrypted cookie 25 + const oauthSession = await atclient.restore(decrypted); 26 + 27 + // set the authed agent 28 + const authedAgent = new Agent(oauthSession); 29 + if (!event.locals.authedAgent) { 30 + event.locals.authedAgent = authedAgent; 31 + } 32 + 33 + // set the authed user with decrypted session DID 34 + const user = await authedAgent.getProfile({ actor: decrypted }); 35 + event.locals.user = user.data; 36 + } 37 + 38 + return resolve(event); 39 + } 40 + 41 +
+92
src/lib/atproto.ts
··· 1 + import { eq } from "drizzle-orm"; 2 + import * as schema from "./schema"; 3 + import { db as database } from "./server/db"; 4 + import { NodeOAuthClient } from "@atproto/oauth-client-node"; 5 + import type { NodeSavedSession, NodeSavedSessionStore, NodeSavedState, NodeSavedStateStore } from "@atproto/oauth-client-node"; 6 + import { db } from "./server/db"; 7 + import { dev } from "$app/environment"; 8 + 9 + // can be implemented with your preferred DB and ORM 10 + // both stores are the same, only different is 'state' and 'session' 11 + 12 + export class AuthStateStore implements NodeSavedStateStore { 13 + constructor(private db: typeof database) {} 14 + 15 + async get(key: string): Promise<NodeSavedState | undefined> { 16 + const result = await this.db.query.AuthState.findFirst({ 17 + where: eq(schema.AuthState.key, key) 18 + }); 19 + 20 + if (!result) return; 21 + 22 + return result.state as NodeSavedState; 23 + } 24 + 25 + async set(key: string, val: NodeSavedState) { 26 + await this.db.insert(schema.AuthState) 27 + .values({ key, state: val }) 28 + .onConflictDoUpdate({ 29 + target: schema.AuthState.key, 30 + set: { state: val } 31 + }); 32 + } 33 + 34 + async del(key: string) { 35 + await this.db.delete(schema.AuthState) 36 + .where(eq(schema.AuthState.key, key)); 37 + } 38 + } 39 + 40 + export class AuthSessionStore implements NodeSavedSessionStore { 41 + constructor(private db: typeof database) {} 42 + 43 + async get(key: string): Promise<NodeSavedSession | undefined> { 44 + const result = await this.db.query.AuthSession.findFirst({ 45 + where: eq(schema.AuthSession.key, key) 46 + }); 47 + 48 + if (!result) return; 49 + return result.session as NodeSavedSession; 50 + } 51 + 52 + async set(key: string, val: NodeSavedSession) { 53 + await this.db.insert(schema.AuthSession) 54 + .values({ key, session: val }) 55 + .onConflictDoUpdate({ 56 + target: schema.AuthSession.key, 57 + set: { session: val } 58 + }); 59 + } 60 + 61 + async del(key: string) { 62 + await this.db.delete(schema.AuthSession) 63 + .where(eq(schema.AuthSession.key, key)); 64 + } 65 + } 66 + 67 + const publicUrl = "https://easytodo.link" 68 + // localhost resolves to either 127.0.0.1 or [::1] (if ipv6) 69 + const url = dev ? "http://[::1]:5173" : publicUrl; 70 + 71 + export const atclient = new NodeOAuthClient({ 72 + stateStore: new AuthStateStore(db), 73 + sessionStore: new AuthSessionStore(db), 74 + clientMetadata: { 75 + client_name: "easytodo.link", 76 + client_id: !dev ? `${publicUrl}/client-metadata.json` 77 + : `http://localhost?redirect_uri=${ 78 + encodeURIComponent(`${url}/oauth/callback`) 79 + }&scope=${ 80 + encodeURIComponent(`atproto repo:link.easytodo.tasks.list repo:link.easytodo.tasks.task rpc:app.bsky.actor.getProfile?aud=did:web:api.bsky.app%23bsky_appview`) 81 + }`, 82 + client_uri: url, 83 + redirect_uris: [`${url}/oauth/callback`], 84 + scope: "atproto repo:link.easytodo.tasks.list repo:link.easytodo.tasks.task rpc:app.bsky.actor.getProfile?aud=did:web:api.bsky.app%23bsky_appview", 85 + grant_types: ["authorization_code", "refresh_token"], 86 + application_type: "web", 87 + token_endpoint_auth_method: "none", 88 + dpop_bound_access_tokens: true 89 + } 90 + }); 91 + 92 +
+11
src/lib/schema.ts
··· 1 + import { pgTable, text, json } from 'drizzle-orm/pg-core'; 2 + 3 + export const AuthState = pgTable('auth_state', { 4 + key: text('key').primaryKey().unique(), 5 + state: json('state').notNull() 6 + }); 7 + 8 + export const AuthSession = pgTable('auth_session', { 9 + key: text('key').primaryKey().unique(), 10 + session: json('session').notNull() 11 + });
+10
src/lib/server/db.ts
··· 1 + import { drizzle } from 'drizzle-orm/postgres-js'; 2 + import postgres from 'postgres'; 3 + import { env } from '$env/dynamic/private'; 4 + import * as schema from "../schema"; 5 + 6 + if (!env.DATABASE_URL) throw new Error('DATABASE_URL is not set'); 7 + const client = postgres(env.DATABASE_URL); 8 + 9 + // add schema 10 + export const db = drizzle(client, { schema });
+50
src/lib/server/encryption.ts
··· 1 + // Code by @pilcrowonpaper on GitHub: https://gist.github.com/pilcrowonpaper/353318556029221c8e25f451b91e5f76 2 + // AES128 with the Web Crypto API. 3 + async function encrypt(key: Uint8Array, data: Uint8Array): Promise<Uint8Array> { 4 + const iv = new Uint8Array(16); 5 + crypto.getRandomValues(iv); 6 + const cryptoKey = await crypto.subtle.importKey("raw", key, "AES-GCM", false, ["encrypt"]); 7 + const cipher = await crypto.subtle.encrypt( 8 + { 9 + name: "AES-GCM", 10 + iv, 11 + tagLength: 128 12 + }, 13 + cryptoKey, 14 + data 15 + ); 16 + const encrypted = new Uint8Array(iv.byteLength + cipher.byteLength); 17 + encrypted.set(iv); 18 + encrypted.set(new Uint8Array(cipher), iv.byteLength); 19 + return encrypted; 20 + } 21 + 22 + export async function encryptString(key: Uint8Array, data: string): Promise<Uint8Array> { 23 + const encoded = new TextEncoder().encode(data); 24 + const encrypted = await encrypt(key, encoded); 25 + return encrypted; 26 + } 27 + 28 + async function decrypt(key: Uint8Array, encrypted: Uint8Array): Promise<Uint8Array> { 29 + if (encrypted.length < 16) { 30 + throw new Error("Invalid data"); 31 + } 32 + const cryptoKey = await crypto.subtle.importKey("raw", key, "AES-GCM", false, ["decrypt"]); 33 + const decrypted = await crypto.subtle.decrypt( 34 + { 35 + name: "AES-GCM", 36 + iv: encrypted.slice(0, 16), 37 + tagLength: 128 38 + }, 39 + cryptoKey, 40 + encrypted.slice(16) 41 + ); 42 + return new Uint8Array(decrypted); 43 + } 44 + 45 + export async function decryptToString(key: Uint8Array, data: Uint8Array): Promise<string> { 46 + const decrypted = await decrypt(key, data); 47 + const decoded = new TextDecoder().decode(decrypted); 48 + return decoded; 49 + } 50 +
+2
src/lib/stores.svelte.ts
··· 38 38 // optional 39 39 duration?: number; 40 40 stopwatchInterval?: number; 41 + rkey?: string; 41 42 } 42 43 43 44 export type List = { 44 45 id: string; 45 46 title: string; 46 47 tasks: Task[]; 48 + rkey?: string; 47 49 } 48 50 49 51 export const local_lists = persisted<List[]>("local_lists", [
+10
src/lib/utils.ts
··· 4 4 return generateRandomString(10, alphabet("a-z", "0-9")); 5 5 } 6 6 7 + export function parseAtUri(uri: string) { 8 + const regex = /at:\/\/(?<did>did.*)\/(?<lexi>.*)\/(?<rkey>.*)/; 9 + const groups = regex.exec(uri)?.groups; 10 + return { 11 + did: groups?.did, 12 + lexi: groups?.lexi, 13 + rkey: groups?.rkey 14 + } 15 + } 16 + 7 17 export function formatSecondsToDuration(seconds: number = 0) { 8 18 let hours = Math.floor(seconds / 3600); 9 19 let minutes = Math.floor((seconds - (hours * 3600)) / 60);
+10
src/routes/+layout.server.ts
··· 1 + import type { LayoutServerLoadEvent } from "./$types"; 2 + 3 + export async function load({ locals }: LayoutServerLoadEvent) { 4 + // have user available throughout the app via LayoutData 5 + return !locals.user ? undefined : { user: { 6 + did: locals.user.did, 7 + handle: locals.user.handle, 8 + avatar: locals.user.avatar 9 + }}; 10 + }
+54 -16
src/routes/+layout.svelte
··· 1 1 <script lang="ts"> 2 2 import "../app.css"; 3 - import { onMount, type Snippet } from "svelte"; 3 + import { onMount } from "svelte"; 4 4 import { page } from "$app/state"; 5 5 import { goto } from "$app/navigation"; 6 6 import { fade } from "svelte/transition"; 7 + import type { LayoutProps } from "./$types"; 7 8 import toast, { Toaster } from "svelte-french-toast"; 8 9 import { persisted, pinned_list } from "$lib/stores.svelte"; 9 10 10 - interface Props { 11 - children: Snippet 12 - } 13 - 14 - let { children }: Props = $props(); 11 + let { data, children }: LayoutProps = $props(); 12 + let { user } = $derived(data); 15 13 16 14 let theme = persisted<string>("theme", "dark"); 17 15 let is_menu_open = $state(false); 16 + let loginDialog = $state<HTMLDialogElement>(); 17 + let accountDialog = $state<HTMLDialogElement>(); 18 18 let theme_style = $derived(theme.value === "dark" 19 19 ? "text-white absolute top-0 z-[-2] h-screen w-screen bg-[#000000] bg-[radial-gradient(#ffffff33_1px,#00091d_1px)] bg-size-[20px_20px]" 20 20 : "text-black absolute inset-0 -z-10 h-full w-full bg-white bg-[radial-gradient(#e5e7eb_1px,transparent_1px)] bg-size-[16px_16px]" ··· 36 36 {@render children()} 37 37 </section> 38 38 39 + <dialog bind:this={loginDialog} class="flex flex-col w-lg gap-4 bg-white top-1/2 left-1/2 -translate-1/2 p-4 rounded"> 40 + <span class="flex items-center gap-4 self-end w-full justify-between"> 41 + <h1 class="text-xl font-bold">Log into the Atmosphere</h1> 42 + <button onclick={() => loginDialog?.close()} class="bg-gray-100 px-3 py-2 rounded self-end">Close</button> 43 + </span> 44 + <form method="POST" action="/?/login" class="flex flex-col gap-4"> 45 + <input name="handle" type="text" placeholder="zeu.dev" class="border rounded px-4 py-2" /> 46 + <button type="submit" class="border px-3 py-2 rounded">Login</button> 47 + </form> 48 + <details class="border border-gray-300 px-3 py-2 rounded"> 49 + <summary class="marker:hidden font-semibold cursor-pointer"> 50 + <span class="text-blue-500">@</span> Enter your internet handle 51 + </summary> 52 + <span class="flex flex-col gap-2 text-sm pt-1"> 53 + <p> 54 + This would be a domain you control, most likely first created with Bluesky, Tangled, Gander, 55 + or other Atmosphere applications. 56 + </p> 57 + <a href="" class="text-blue-500 underline"> 58 + Learn more about ATproto and controlling your social media data 59 + </a> 60 + </span> 61 + </details> 62 + </dialog> 63 + 64 + <dialog bind:this={accountDialog} class="bg-white p-6 shadow top-1/2 left-1/2 -translate-1/2"> 65 + <h1>Account</h1> 66 + <button onclick={() => accountDialog?.close()}>Close</button> 67 + <form method="POST" action="/?/logout"> 68 + <button type="submit">Logout</button> 69 + </form> 70 + </dialog> 71 + 39 72 <aside class="z-50 fixed inset-x-0 bottom-0 text-black! flex w-full h-fit items-end justify-between p-8 pointer-events-none"> 40 73 <div class="flex flex-col justify-start gap-4 pointer-events-auto"> 41 74 {#if is_menu_open} ··· 83 116 </button> 84 117 85 118 <!-- TODO: change to <a href='/login'> --> 86 - <button 87 - onclick={comingSoon} 88 - class="items-center h-fit w-full hover:bg-slate-500/10 rounded-full" 89 - > 90 - <img src="/login-line.svg" alt="Login" class="w-12 h-12"/> 91 - </button> 119 + {#if !user} 120 + <button 121 + onclick={() => loginDialog?.showModal()} 122 + class="items-center h-fit w-full hover:bg-slate-500/10 rounded-full" 123 + > 124 + <img src="/login-line.svg" alt="Login" class="w-12 h-12"/> 125 + </button> 126 + {:else} 127 + <button 128 + onclick={() => accountDialog?.showModal()} 129 + class="items-center h-fit w-full rounded-full" 130 + > 131 + <img src={user.avatar || "/user-line.svg"} alt="Login" class="w-10 h-10 rounded-full" /> 132 + </button> 133 + {/if} 92 134 </nav> 93 135 </div> 94 136 ··· 106 148 </aside> 107 149 <Toaster /> 108 150 </div> 109 - 110 - <style lang="postcss"> 111 - @reference "tailwindcss"; 112 - </style>
+116
src/routes/+page.server.ts
··· 1 + import { atclient } from "$lib/atproto"; 2 + import type { Task } from "$lib/stores.svelte"; 3 + import { parseAtUri } from "$lib/utils"; 4 + import type { $Typed } from "@atproto/api"; 5 + import type { Create, CreateResult } from "@atproto/api/dist/client/types/com/atproto/repo/applyWrites"; 6 + import { isValidHandle } from "@atproto/syntax"; 7 + import { error, fail, redirect, type Actions } from "@sveltejs/kit"; 8 + 9 + export const actions: Actions = { 10 + login: async ({ request }) => { 11 + // get handle from form 12 + const formData = await request.formData(); 13 + const handle = formData.get("handle") as string; 14 + 15 + // validate handle using ATProto SDK 16 + if (!isValidHandle(handle)) { 17 + error(400, { message: "Invalid handle" }); 18 + } 19 + 20 + // get oauth authorizing url to redirect to 21 + const redirectUrl = await atclient.authorize(handle, { 22 + scope: "atproto repo:link.easytodo.tasks.list repo:link.easytodo.tasks.task rpc:app.bsky.actor.getProfile?aud=did:web:api.bsky.app%23bsky_appview" 23 + }); 24 + 25 + if (!redirectUrl) { 26 + error(500, { message: "Unable to authorize" }); 27 + } 28 + 29 + // redirect for user to authorize 30 + redirect(301, redirectUrl.toString()); 31 + }, 32 + logout: async ({ cookies }) => { 33 + cookies.delete("sid", { path: "/" }); 34 + redirect(301, "/"); 35 + }, 36 + 37 + 38 + // ATProto CRUD 39 + saveListRecord: async ({ request, locals }) => { 40 + const user = locals.user; 41 + const agent = locals.authedAgent; 42 + if (!user || !agent) { return fail(401); } 43 + 44 + const formData = await request.formData(); 45 + const id = formData.get("id") as string; 46 + const list_rkey = formData.get("rkey") as string; 47 + const title = formData.get("title") as string; 48 + const tasks = JSON.parse(formData.get("tasks") as string) as Task[]; 49 + 50 + const response = await agent.com.atproto.repo.applyWrites({ 51 + repo: user.did, 52 + writes: tasks.map((t) => { 53 + const { rkey: task_rkey, stopwatchInterval, ...rest } = t; 54 + if (task_rkey) { 55 + console.log("UPDATE TASK"); 56 + return { 57 + $type: 'com.atproto.repo.applyWrites#update', 58 + collection: "link.easytodo.tasks.task", 59 + rkey: task_rkey, 60 + value: { 61 + $type: "link.easytodo.tasks.task", 62 + ...rest 63 + } 64 + } 65 + } 66 + else { 67 + console.log("CREATE TASK"); 68 + return { 69 + $type: 'com.atproto.repo.applyWrites#create', 70 + collection: "link.easytodo.tasks.task", 71 + value: { 72 + $type: "link.easytodo.tasks.task", 73 + ...rest 74 + }, 75 + } 76 + } 77 + }) 78 + }); 79 + 80 + if (response.success) { 81 + console.log(response.data.results); 82 + const list_record = { 83 + $type: "link.easytodo.tasks.list", 84 + createdAt: new Date().toISOString(), 85 + id, 86 + title, 87 + tasks: response.data.results?.map((t) => { 88 + // @ts-ignore 89 + return { cid: t.cid, uri: t.uri } 90 + }) 91 + }; 92 + 93 + if (list_rkey) { 94 + const { success, data } = await agent.com.atproto.repo.putRecord({ 95 + rkey: list_rkey, 96 + repo: user.did, 97 + collection: "link.easytodo.tasks.list", 98 + record: list_record 99 + }); 100 + console.log("UPDATE LIST", { success, uri: data.uri }); 101 + return { saveListRecordResult: { success, rkey: list_rkey, uri: data.uri }}; 102 + } 103 + else { 104 + const { success, data } = await agent.com.atproto.repo.createRecord({ 105 + repo: user.did, 106 + collection: "link.easytodo.tasks.list", 107 + record: list_record 108 + }); 109 + const { rkey } = parseAtUri(data.uri); 110 + console.log("CREATE LIST", { success, rkey, uri: data.uri }); 111 + return { saveListRecordResult: { success, rkey, uri: data.uri }}; 112 + } 113 + 114 + } 115 + } 116 + };
+44 -7
src/routes/[id]/+page.svelte
··· 1 1 <script lang="ts"> 2 2 import { onMount } from "svelte"; 3 3 import { page } from "$app/state"; 4 + import { enhance } from "$app/forms"; 4 5 import { goto } from "$app/navigation"; 5 6 import toast from "svelte-french-toast"; 7 + import type { PageProps } from "../$types.js"; 6 8 import { formatSecondsToDuration, generateId } from "$lib/utils"; 7 9 import { local_lists, pinned_list, type List, type Task } from "$lib/stores.svelte"; 8 10 9 - let is_menu_open = $state(false); 11 + let { data, form }: PageProps = $props(); 12 + let { user } = $derived(data); 13 + let is_lists_menu_open = $state(false); 14 + let is_cloud_menu_open = $state(false); 10 15 let list : List | undefined = $state(local_lists.value!.find((l) => l.id === page.params.id)); 11 16 let task_input = $state(""); 12 17 let user_lists = $derived(local_lists.value) as List[]; ··· 14 19 // since list points to something inside local_lists, 15 20 // it will run when list state changes 16 21 $effect(() => local_lists.update()); 22 + 23 + $effect(() => { 24 + if (form?.saveListRecordResult.success) { 25 + toast.success("Successfully saved to PDS!"); 26 + } 27 + }); 17 28 18 29 function addTask() { 19 30 if (task_input.length === 0) { ··· 106 117 {#if list} 107 118 <section class="relative flex gap-4 w-full"> 108 119 <div class="flex gap-4 border-black border w-fit h-fit p-2 bg-white rounded-xl"> 109 - <button onclick={() => is_menu_open = !is_menu_open}> 120 + <button onclick={() => is_lists_menu_open = !is_lists_menu_open}> 110 121 <img 111 122 src="/list-box-line.svg" 112 - alt="Lists button" 123 + alt="Lists menu button" 113 124 class="w-12 h-12 hover:bg-slate-500/10 rounded-full" 114 125 /> 115 126 </button> ··· 120 131 class="w-12 h-12 hover:bg-slate-500/10 rounded-full" 121 132 /> 122 133 </button> 134 + {#if user} 135 + <button onclick={() => is_cloud_menu_open = !is_cloud_menu_open}> 136 + <img 137 + src="/cloud.svg" 138 + alt="Cloud menu button" 139 + class="w-12 h-12 p-2 hover:bg-slate-500/10 rounded-full" 140 + /> 141 + </button> 142 + {/if} 123 143 <button onclick={deleteList}> 124 144 <img 125 145 src="/trash-line.svg" ··· 129 149 </button> 130 150 </div> 131 151 132 - {#if is_menu_open} 152 + {#if is_lists_menu_open} 133 153 <menu class="absolute flex flex-col gap-2 w-fit h-fit top-20 p-2 bg-white border border-black rounded-lg text-black! text-lg!"> 134 154 {#each user_lists as user_list : List (user_list.id)} 135 155 <button 136 156 onclick={() => { 137 - switchToList(user_list.id) 138 - is_menu_open = false; 157 + switchToList(user_list.id); 158 + is_lists_menu_open = false; 139 159 }} 140 160 class="flex gap-2 justify-between text-start w-full h-full rounded-xl pl-2 pr-5 py-2 hover:bg-slate-500/10 transition-all duration-150 items-center" 141 161 > ··· 148 168 <button 149 169 onclick={() => { 150 170 createList(); 151 - is_menu_open = false; 171 + is_lists_menu_open = false; 152 172 }} 153 173 class="flex gap-2 justify-between text-start w-full h-full rounded-xl pl-2 pr-5 py-2 hover:bg-slate-500/10 transition-all duration-150 items-center" 154 174 > 155 175 Create new list 156 176 </button> 177 + </menu> 178 + {/if} 179 + 180 + {#if is_cloud_menu_open} 181 + <menu class="absolute flex flex-col gap-2 w-fit h-fit top-20 p-2 bg-white border border-black rounded-lg text-black! text-lg!"> 182 + <form method="POST" action="/?/saveListRecord" use:enhance> 183 + <input name="id" type="hidden" value={list.id} /> 184 + <input name="title" type="hidden" value={list.title} /> 185 + <input name="tasks" type="hidden" value={JSON.stringify(list.tasks)} /> 186 + <button 187 + type="submit" 188 + class="flex gap-2 justify-between text-start w-full h-full rounded-xl pl-2 pr-5 py-2 hover:bg-slate-500/10 transition-all duration-150 items-center" 189 + > 190 + <img src="/save.svg" alt="Save to PDS button" class="w-8 h-8 p-2"/> 191 + Save to PDS 192 + </button> 193 + </form> 157 194 </menu> 158 195 {/if} 159 196 </section>
+6
src/routes/client-metadata.json/+server.ts
··· 1 + import { atclient } from "$lib/atproto"; 2 + import { json } from "@sveltejs/kit"; 3 + 4 + export async function GET() { 5 + return json(atclient.clientMetadata); 6 + }
+34
src/routes/oauth/callback/+server.ts
··· 1 + import { atclient } from "$lib/atproto"; 2 + import { encryptString } from "$lib/server/encryption"; 3 + import { decodeBase64, encodeBase64urlNoPadding } from "@oslojs/encoding"; 4 + 5 + import { error, redirect } from "@sveltejs/kit"; 6 + import type { RequestEvent } from "@sveltejs/kit"; 7 + import { ENCRYPTION_PASSWORD } from "$env/static/private"; 8 + 9 + // called on after authorizing OAuth 10 + export async function GET({ request, cookies }: RequestEvent) { 11 + // get parameters set by the callback 12 + const params = new URLSearchParams(request.url.split("?")[1]); 13 + 14 + try { 15 + const { session } = await atclient.callback(params); 16 + const key = decodeBase64(ENCRYPTION_PASSWORD); 17 + 18 + // encrypt the user DID 19 + const encrypted = await encryptString(key, session.did); 20 + const encoded = encodeBase64urlNoPadding(encrypted); 21 + 22 + // set encoded session DID as cookies for auth 23 + cookies.set("sid", encoded, { 24 + path: "/", 25 + maxAge: 60 * 60, 26 + httpOnly: true, 27 + sameSite: "lax" 28 + }); 29 + } catch (err) { 30 + error(500, { message: (err as Error).message }); 31 + } 32 + 33 + redirect(301, "/"); 34 + }
+1
static/cloud.svg
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9"/></svg>
+1
static/save.svg
··· 1 + <svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z"/><path d="M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7M7 3v4a1 1 0 0 0 1 1h7"/></g></svg>
+1
static/user-line.svg
··· 1 + <svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><circle cx="12" cy="8" r="5"/><path d="M20 21a8 8 0 1 0-16 0m16 0a8 8 0 1 0-16 0"/></g></svg>