+176
-36
bun.lock
+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
+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
+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
+13
drizzle/meta/_journal.json
+12
drizzle.config.ts
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+6
src/routes/client-metadata.json/+server.ts
+34
src/routes/oauth/callback/+server.ts
+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
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
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
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>