diff --git a/frontend/deno.lock b/frontend/deno.lock
index a72040c..c3a9dc8 100644
--- a/frontend/deno.lock
+++ b/frontend/deno.lock
@@ -5,20 +5,20 @@
"npm:@atcute/crypto@^2.3.0": "2.3.0",
"npm:@atcute/did-plc@~0.3.1": "0.3.1",
"npm:@atcute/multibase@^1.1.6": "1.1.6",
- "npm:@noble/secp256k1@^2.1.0": "2.3.0",
- "npm:@sveltejs/vite-plugin-svelte@5": "5.1.1_svelte@5.45.10__acorn@8.15.0_vite@6.4.1__picomatch@4.0.3",
- "npm:@testing-library/jest-dom@^6.6.3": "6.9.1",
- "npm:@testing-library/svelte@^5.2.6": "5.2.9_svelte@5.45.10__acorn@8.15.0_vite@6.4.1__picomatch@4.0.3_vitest@2.1.9__jsdom@25.0.1__vite@5.4.21_jsdom@25.0.1",
- "npm:@testing-library/user-event@^14.5.2": "14.6.1_@testing-library+dom@10.4.1",
+ "npm:@noble/secp256k1@3": "3.0.0",
+ "npm:@sveltejs/vite-plugin-svelte@^6.2.1": "6.2.1_svelte@5.46.1__acorn@8.15.0_vite@7.3.0__picomatch@4.0.3",
+ "npm:@testing-library/jest-dom@^6.9.1": "6.9.1",
+ "npm:@testing-library/svelte@^5.3.1": "5.3.1_svelte@5.46.1__acorn@8.15.0_vite@7.3.0__picomatch@4.0.3_vitest@4.0.16__jsdom@25.0.1__vite@7.3.0___picomatch@4.0.3_jsdom@25.0.1",
+ "npm:@testing-library/user-event@^14.6.1": "14.6.1_@testing-library+dom@10.4.1",
"npm:jsdom@^25.0.1": "25.0.1",
- "npm:multiformats@^13.3.1": "13.4.2",
- "npm:svelte-check@*": "4.3.5_svelte@5.45.10__acorn@8.15.0_typescript@5.9.3",
- "npm:svelte-i18n@^4.0.1": "4.0.1_svelte@5.45.10__acorn@8.15.0",
- "npm:svelte@5": "5.45.10_acorn@8.15.0",
- "npm:vite@*": "6.4.1_picomatch@4.0.3",
- "npm:vite@6": "6.4.1_picomatch@4.0.3",
- "npm:vitest@*": "2.1.9_jsdom@25.0.1_vite@5.4.21",
- "npm:vitest@^2.1.8": "2.1.9_jsdom@25.0.1_vite@5.4.21"
+ "npm:multiformats@^13.4.2": "13.4.2",
+ "npm:svelte-i18n@^4.0.1": "4.0.1_svelte@5.46.1__acorn@8.15.0",
+ "npm:svelte@^5.46.1": "5.46.1_acorn@8.15.0",
+ "npm:vite@*": "7.3.0_picomatch@4.0.3",
+ "npm:vite@^7.3.0": "7.3.0_picomatch@4.0.3",
+ "npm:vitest@*": "4.0.16_jsdom@25.0.1_vite@7.3.0__picomatch@4.0.3",
+ "npm:vitest@^4.0.16": "4.0.16_jsdom@25.0.1_vite@7.3.0__picomatch@4.0.3",
+ "npm:zod@^4.3.5": "4.3.5"
},
"npm": {
"@adobe/css-tools@4.4.4": {
@@ -54,7 +54,7 @@
"dependencies": [
"@atcute/multibase",
"@atcute/uint8array",
- "@noble/secp256k1@3.0.0"
+ "@noble/secp256k1"
]
},
"@atcute/did-plc@0.3.1": {
@@ -96,8 +96,8 @@
"@atcute/uint8array@1.0.6": {
"integrity": "sha512-ucfRBQc7BFT8n9eCyGOzDHEMKF/nZwhS2pPao4Xtab1ML3HdFYcX2DM1tadCzas85QTGxHe5urnUAAcNKGRi9A=="
},
- "@atcute/util-fetch@1.0.4": {
- "integrity": "sha512-sIU9Qk0dE8PLEXSfhy+gIJV+HpiiknMytCI2SqLlqd0vgZUtEKI/EQfP+23LHWvP+CLCzVDOa6cpH045OlmNBg==",
+ "@atcute/util-fetch@1.0.5": {
+ "integrity": "sha512-qjHj01BGxjSjIFdPiAjSARnodJIIyKxnCMMEcXMESo9TAyND6XZQqrie5fia+LlYWVXdpsTds8uFQwc9jdKTig==",
"dependencies": [
"@badrap/valita"
]
@@ -158,13 +158,8 @@
"os": ["aix"],
"cpu": ["ppc64"]
},
- "@esbuild/aix-ppc64@0.21.5": {
- "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
- "os": ["aix"],
- "cpu": ["ppc64"]
- },
- "@esbuild/aix-ppc64@0.25.12": {
- "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+ "@esbuild/aix-ppc64@0.27.2": {
+ "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
"os": ["aix"],
"cpu": ["ppc64"]
},
@@ -173,13 +168,8 @@
"os": ["android"],
"cpu": ["arm64"]
},
- "@esbuild/android-arm64@0.21.5": {
- "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
- "os": ["android"],
- "cpu": ["arm64"]
- },
- "@esbuild/android-arm64@0.25.12": {
- "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+ "@esbuild/android-arm64@0.27.2": {
+ "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
"os": ["android"],
"cpu": ["arm64"]
},
@@ -188,13 +178,8 @@
"os": ["android"],
"cpu": ["arm"]
},
- "@esbuild/android-arm@0.21.5": {
- "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
- "os": ["android"],
- "cpu": ["arm"]
- },
- "@esbuild/android-arm@0.25.12": {
- "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "@esbuild/android-arm@0.27.2": {
+ "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
"os": ["android"],
"cpu": ["arm"]
},
@@ -203,13 +188,8 @@
"os": ["android"],
"cpu": ["x64"]
},
- "@esbuild/android-x64@0.21.5": {
- "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
- "os": ["android"],
- "cpu": ["x64"]
- },
- "@esbuild/android-x64@0.25.12": {
- "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "@esbuild/android-x64@0.27.2": {
+ "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
"os": ["android"],
"cpu": ["x64"]
},
@@ -218,13 +198,8 @@
"os": ["darwin"],
"cpu": ["arm64"]
},
- "@esbuild/darwin-arm64@0.21.5": {
- "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
- "os": ["darwin"],
- "cpu": ["arm64"]
- },
- "@esbuild/darwin-arm64@0.25.12": {
- "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+ "@esbuild/darwin-arm64@0.27.2": {
+ "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
"os": ["darwin"],
"cpu": ["arm64"]
},
@@ -233,13 +208,8 @@
"os": ["darwin"],
"cpu": ["x64"]
},
- "@esbuild/darwin-x64@0.21.5": {
- "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
- "os": ["darwin"],
- "cpu": ["x64"]
- },
- "@esbuild/darwin-x64@0.25.12": {
- "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "@esbuild/darwin-x64@0.27.2": {
+ "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
"os": ["darwin"],
"cpu": ["x64"]
},
@@ -248,13 +218,8 @@
"os": ["freebsd"],
"cpu": ["arm64"]
},
- "@esbuild/freebsd-arm64@0.21.5": {
- "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
- "os": ["freebsd"],
- "cpu": ["arm64"]
- },
- "@esbuild/freebsd-arm64@0.25.12": {
- "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+ "@esbuild/freebsd-arm64@0.27.2": {
+ "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
"os": ["freebsd"],
"cpu": ["arm64"]
},
@@ -263,13 +228,8 @@
"os": ["freebsd"],
"cpu": ["x64"]
},
- "@esbuild/freebsd-x64@0.21.5": {
- "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
- "os": ["freebsd"],
- "cpu": ["x64"]
- },
- "@esbuild/freebsd-x64@0.25.12": {
- "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "@esbuild/freebsd-x64@0.27.2": {
+ "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
"os": ["freebsd"],
"cpu": ["x64"]
},
@@ -278,13 +238,8 @@
"os": ["linux"],
"cpu": ["arm64"]
},
- "@esbuild/linux-arm64@0.21.5": {
- "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
- "os": ["linux"],
- "cpu": ["arm64"]
- },
- "@esbuild/linux-arm64@0.25.12": {
- "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+ "@esbuild/linux-arm64@0.27.2": {
+ "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
"os": ["linux"],
"cpu": ["arm64"]
},
@@ -293,13 +248,8 @@
"os": ["linux"],
"cpu": ["arm"]
},
- "@esbuild/linux-arm@0.21.5": {
- "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
- "os": ["linux"],
- "cpu": ["arm"]
- },
- "@esbuild/linux-arm@0.25.12": {
- "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "@esbuild/linux-arm@0.27.2": {
+ "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
"os": ["linux"],
"cpu": ["arm"]
},
@@ -308,13 +258,8 @@
"os": ["linux"],
"cpu": ["ia32"]
},
- "@esbuild/linux-ia32@0.21.5": {
- "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
- "os": ["linux"],
- "cpu": ["ia32"]
- },
- "@esbuild/linux-ia32@0.25.12": {
- "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "@esbuild/linux-ia32@0.27.2": {
+ "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
"os": ["linux"],
"cpu": ["ia32"]
},
@@ -323,13 +268,8 @@
"os": ["linux"],
"cpu": ["loong64"]
},
- "@esbuild/linux-loong64@0.21.5": {
- "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
- "os": ["linux"],
- "cpu": ["loong64"]
- },
- "@esbuild/linux-loong64@0.25.12": {
- "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "@esbuild/linux-loong64@0.27.2": {
+ "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
"os": ["linux"],
"cpu": ["loong64"]
},
@@ -338,13 +278,8 @@
"os": ["linux"],
"cpu": ["mips64el"]
},
- "@esbuild/linux-mips64el@0.21.5": {
- "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
- "os": ["linux"],
- "cpu": ["mips64el"]
- },
- "@esbuild/linux-mips64el@0.25.12": {
- "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "@esbuild/linux-mips64el@0.27.2": {
+ "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
"os": ["linux"],
"cpu": ["mips64el"]
},
@@ -353,13 +288,8 @@
"os": ["linux"],
"cpu": ["ppc64"]
},
- "@esbuild/linux-ppc64@0.21.5": {
- "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
- "os": ["linux"],
- "cpu": ["ppc64"]
- },
- "@esbuild/linux-ppc64@0.25.12": {
- "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "@esbuild/linux-ppc64@0.27.2": {
+ "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
"os": ["linux"],
"cpu": ["ppc64"]
},
@@ -368,13 +298,8 @@
"os": ["linux"],
"cpu": ["riscv64"]
},
- "@esbuild/linux-riscv64@0.21.5": {
- "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
- "os": ["linux"],
- "cpu": ["riscv64"]
- },
- "@esbuild/linux-riscv64@0.25.12": {
- "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "@esbuild/linux-riscv64@0.27.2": {
+ "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
"os": ["linux"],
"cpu": ["riscv64"]
},
@@ -383,13 +308,8 @@
"os": ["linux"],
"cpu": ["s390x"]
},
- "@esbuild/linux-s390x@0.21.5": {
- "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
- "os": ["linux"],
- "cpu": ["s390x"]
- },
- "@esbuild/linux-s390x@0.25.12": {
- "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "@esbuild/linux-s390x@0.27.2": {
+ "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
"os": ["linux"],
"cpu": ["s390x"]
},
@@ -398,18 +318,13 @@
"os": ["linux"],
"cpu": ["x64"]
},
- "@esbuild/linux-x64@0.21.5": {
- "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+ "@esbuild/linux-x64@0.27.2": {
+ "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
"os": ["linux"],
"cpu": ["x64"]
},
- "@esbuild/linux-x64@0.25.12": {
- "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
- "os": ["linux"],
- "cpu": ["x64"]
- },
- "@esbuild/netbsd-arm64@0.25.12": {
- "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "@esbuild/netbsd-arm64@0.27.2": {
+ "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
"os": ["netbsd"],
"cpu": ["arm64"]
},
@@ -418,18 +333,13 @@
"os": ["netbsd"],
"cpu": ["x64"]
},
- "@esbuild/netbsd-x64@0.21.5": {
- "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+ "@esbuild/netbsd-x64@0.27.2": {
+ "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
"os": ["netbsd"],
"cpu": ["x64"]
},
- "@esbuild/netbsd-x64@0.25.12": {
- "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
- "os": ["netbsd"],
- "cpu": ["x64"]
- },
- "@esbuild/openbsd-arm64@0.25.12": {
- "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "@esbuild/openbsd-arm64@0.27.2": {
+ "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
"os": ["openbsd"],
"cpu": ["arm64"]
},
@@ -438,18 +348,13 @@
"os": ["openbsd"],
"cpu": ["x64"]
},
- "@esbuild/openbsd-x64@0.21.5": {
- "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+ "@esbuild/openbsd-x64@0.27.2": {
+ "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
"os": ["openbsd"],
"cpu": ["x64"]
},
- "@esbuild/openbsd-x64@0.25.12": {
- "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
- "os": ["openbsd"],
- "cpu": ["x64"]
- },
- "@esbuild/openharmony-arm64@0.25.12": {
- "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "@esbuild/openharmony-arm64@0.27.2": {
+ "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
"os": ["openharmony"],
"cpu": ["arm64"]
},
@@ -458,13 +363,8 @@
"os": ["sunos"],
"cpu": ["x64"]
},
- "@esbuild/sunos-x64@0.21.5": {
- "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
- "os": ["sunos"],
- "cpu": ["x64"]
- },
- "@esbuild/sunos-x64@0.25.12": {
- "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+ "@esbuild/sunos-x64@0.27.2": {
+ "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
"os": ["sunos"],
"cpu": ["x64"]
},
@@ -473,13 +373,8 @@
"os": ["win32"],
"cpu": ["arm64"]
},
- "@esbuild/win32-arm64@0.21.5": {
- "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
- "os": ["win32"],
- "cpu": ["arm64"]
- },
- "@esbuild/win32-arm64@0.25.12": {
- "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+ "@esbuild/win32-arm64@0.27.2": {
+ "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
"os": ["win32"],
"cpu": ["arm64"]
},
@@ -488,13 +383,8 @@
"os": ["win32"],
"cpu": ["ia32"]
},
- "@esbuild/win32-ia32@0.21.5": {
- "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
- "os": ["win32"],
- "cpu": ["ia32"]
- },
- "@esbuild/win32-ia32@0.25.12": {
- "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "@esbuild/win32-ia32@0.27.2": {
+ "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
"os": ["win32"],
"cpu": ["ia32"]
},
@@ -503,13 +393,8 @@
"os": ["win32"],
"cpu": ["x64"]
},
- "@esbuild/win32-x64@0.21.5": {
- "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
- "os": ["win32"],
- "cpu": ["x64"]
- },
- "@esbuild/win32-x64@0.25.12": {
- "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "@esbuild/win32-x64@0.27.2": {
+ "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
"os": ["win32"],
"cpu": ["x64"]
},
@@ -576,119 +461,116 @@
"@jridgewell/sourcemap-codec"
]
},
- "@noble/secp256k1@2.3.0": {
- "integrity": "sha512-0TQed2gcBbIrh7Ccyw+y/uZQvbJwm7Ao4scBUxqpBCcsOlZG0O4KGfjtNAy/li4W8n1xt3dxrwJ0beZ2h2G6Kw=="
- },
"@noble/secp256k1@3.0.0": {
"integrity": "sha512-NJBaR352KyIvj3t6sgT/+7xrNyF9Xk9QlLSIqUGVUYlsnDTAUqY8LOmwpcgEx4AMJXRITQ5XEVHD+mMaPfr3mg=="
},
- "@rollup/rollup-android-arm-eabi@4.53.3": {
- "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==",
+ "@rollup/rollup-android-arm-eabi@4.54.0": {
+ "integrity": "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==",
"os": ["android"],
"cpu": ["arm"]
},
- "@rollup/rollup-android-arm64@4.53.3": {
- "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==",
+ "@rollup/rollup-android-arm64@4.54.0": {
+ "integrity": "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==",
"os": ["android"],
"cpu": ["arm64"]
},
- "@rollup/rollup-darwin-arm64@4.53.3": {
- "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==",
+ "@rollup/rollup-darwin-arm64@4.54.0": {
+ "integrity": "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==",
"os": ["darwin"],
"cpu": ["arm64"]
},
- "@rollup/rollup-darwin-x64@4.53.3": {
- "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==",
+ "@rollup/rollup-darwin-x64@4.54.0": {
+ "integrity": "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==",
"os": ["darwin"],
"cpu": ["x64"]
},
- "@rollup/rollup-freebsd-arm64@4.53.3": {
- "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==",
+ "@rollup/rollup-freebsd-arm64@4.54.0": {
+ "integrity": "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==",
"os": ["freebsd"],
"cpu": ["arm64"]
},
- "@rollup/rollup-freebsd-x64@4.53.3": {
- "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==",
+ "@rollup/rollup-freebsd-x64@4.54.0": {
+ "integrity": "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==",
"os": ["freebsd"],
"cpu": ["x64"]
},
- "@rollup/rollup-linux-arm-gnueabihf@4.53.3": {
- "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==",
+ "@rollup/rollup-linux-arm-gnueabihf@4.54.0": {
+ "integrity": "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==",
"os": ["linux"],
"cpu": ["arm"]
},
- "@rollup/rollup-linux-arm-musleabihf@4.53.3": {
- "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==",
+ "@rollup/rollup-linux-arm-musleabihf@4.54.0": {
+ "integrity": "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==",
"os": ["linux"],
"cpu": ["arm"]
},
- "@rollup/rollup-linux-arm64-gnu@4.53.3": {
- "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==",
+ "@rollup/rollup-linux-arm64-gnu@4.54.0": {
+ "integrity": "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==",
"os": ["linux"],
"cpu": ["arm64"]
},
- "@rollup/rollup-linux-arm64-musl@4.53.3": {
- "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==",
+ "@rollup/rollup-linux-arm64-musl@4.54.0": {
+ "integrity": "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==",
"os": ["linux"],
"cpu": ["arm64"]
},
- "@rollup/rollup-linux-loong64-gnu@4.53.3": {
- "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==",
+ "@rollup/rollup-linux-loong64-gnu@4.54.0": {
+ "integrity": "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==",
"os": ["linux"],
"cpu": ["loong64"]
},
- "@rollup/rollup-linux-ppc64-gnu@4.53.3": {
- "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==",
+ "@rollup/rollup-linux-ppc64-gnu@4.54.0": {
+ "integrity": "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==",
"os": ["linux"],
"cpu": ["ppc64"]
},
- "@rollup/rollup-linux-riscv64-gnu@4.53.3": {
- "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==",
+ "@rollup/rollup-linux-riscv64-gnu@4.54.0": {
+ "integrity": "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==",
"os": ["linux"],
"cpu": ["riscv64"]
},
- "@rollup/rollup-linux-riscv64-musl@4.53.3": {
- "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==",
+ "@rollup/rollup-linux-riscv64-musl@4.54.0": {
+ "integrity": "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==",
"os": ["linux"],
"cpu": ["riscv64"]
},
- "@rollup/rollup-linux-s390x-gnu@4.53.3": {
- "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==",
+ "@rollup/rollup-linux-s390x-gnu@4.54.0": {
+ "integrity": "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==",
"os": ["linux"],
"cpu": ["s390x"]
},
- "@rollup/rollup-linux-x64-gnu@4.53.3": {
- "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==",
+ "@rollup/rollup-linux-x64-gnu@4.54.0": {
+ "integrity": "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==",
"os": ["linux"],
"cpu": ["x64"]
},
- "@rollup/rollup-linux-x64-musl@4.53.3": {
- "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==",
+ "@rollup/rollup-linux-x64-musl@4.54.0": {
+ "integrity": "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==",
"os": ["linux"],
"cpu": ["x64"]
},
- "@rollup/rollup-openharmony-arm64@4.53.3": {
- "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==",
+ "@rollup/rollup-openharmony-arm64@4.54.0": {
+ "integrity": "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==",
"os": ["openharmony"],
"cpu": ["arm64"]
},
- "@rollup/rollup-win32-arm64-msvc@4.53.3": {
- "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==",
+ "@rollup/rollup-win32-arm64-msvc@4.54.0": {
+ "integrity": "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==",
"os": ["win32"],
"cpu": ["arm64"]
},
- "@rollup/rollup-win32-ia32-msvc@4.53.3": {
- "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==",
+ "@rollup/rollup-win32-ia32-msvc@4.54.0": {
+ "integrity": "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==",
"os": ["win32"],
"cpu": ["ia32"]
},
- "@rollup/rollup-win32-x64-gnu@4.53.3": {
- "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==",
+ "@rollup/rollup-win32-x64-gnu@4.54.0": {
+ "integrity": "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==",
"os": ["win32"],
"cpu": ["x64"]
},
- "@rollup/rollup-win32-x64-msvc@4.53.3": {
- "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==",
+ "@rollup/rollup-win32-x64-msvc@4.54.0": {
+ "integrity": "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==",
"os": ["win32"],
"cpu": ["x64"]
},
@@ -701,25 +583,24 @@
"acorn"
]
},
- "@sveltejs/vite-plugin-svelte-inspector@4.0.1_@sveltejs+vite-plugin-svelte@5.1.1__svelte@5.45.10___acorn@8.15.0__vite@6.4.1___picomatch@4.0.3_svelte@5.45.10__acorn@8.15.0_vite@6.4.1__picomatch@4.0.3": {
- "integrity": "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==",
+ "@sveltejs/vite-plugin-svelte-inspector@5.0.1_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.46.1___acorn@8.15.0__vite@7.3.0___picomatch@4.0.3_svelte@5.46.1__acorn@8.15.0_vite@7.3.0__picomatch@4.0.3": {
+ "integrity": "sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA==",
"dependencies": [
"@sveltejs/vite-plugin-svelte",
"debug",
"svelte",
- "vite@6.4.1_picomatch@4.0.3"
+ "vite"
]
},
- "@sveltejs/vite-plugin-svelte@5.1.1_svelte@5.45.10__acorn@8.15.0_vite@6.4.1__picomatch@4.0.3": {
- "integrity": "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ==",
+ "@sveltejs/vite-plugin-svelte@6.2.1_svelte@5.46.1__acorn@8.15.0_vite@7.3.0__picomatch@4.0.3": {
+ "integrity": "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==",
"dependencies": [
"@sveltejs/vite-plugin-svelte-inspector",
"debug",
"deepmerge",
- "kleur",
"magic-string",
"svelte",
- "vite@6.4.1_picomatch@4.0.3",
+ "vite",
"vitefu"
]
},
@@ -747,16 +628,23 @@
"redent"
]
},
- "@testing-library/svelte@5.2.9_svelte@5.45.10__acorn@8.15.0_vite@6.4.1__picomatch@4.0.3_vitest@2.1.9__jsdom@25.0.1__vite@5.4.21_jsdom@25.0.1": {
- "integrity": "sha512-p0Lg/vL1iEsEasXKSipvW9nBCtItQGhYvxL8OZ4w7/IDdC+LGoSJw4mMS5bndVFON/gWryitEhMr29AlO4FvBg==",
+ "@testing-library/svelte-core@1.0.0_svelte@5.46.1__acorn@8.15.0": {
+ "integrity": "sha512-VkUePoLV6oOYwSUvX6ShA8KLnJqZiYMIbP2JW2t0GLWLkJxKGvuH5qrrZBV/X7cXFnLGuFQEC7RheYiZOW68KQ==",
+ "dependencies": [
+ "svelte"
+ ]
+ },
+ "@testing-library/svelte@5.3.1_svelte@5.46.1__acorn@8.15.0_vite@7.3.0__picomatch@4.0.3_vitest@4.0.16__jsdom@25.0.1__vite@7.3.0___picomatch@4.0.3_jsdom@25.0.1": {
+ "integrity": "sha512-8Ez7ZOqW5geRf9PF5rkuopODe5RGy3I9XR+kc7zHh26gBiktLaxTfKmhlGaSHYUOTQE7wFsLMN9xCJVCszw47w==",
"dependencies": [
"@testing-library/dom",
+ "@testing-library/svelte-core",
"svelte",
- "vite@6.4.1_picomatch@4.0.3",
+ "vite",
"vitest"
],
"optionalPeers": [
- "vite@6.4.1_picomatch@4.0.3",
+ "vite",
"vitest"
]
},
@@ -769,62 +657,70 @@
"@types/aria-query@5.0.4": {
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="
},
+ "@types/chai@5.2.3": {
+ "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
+ "dependencies": [
+ "@types/deep-eql",
+ "assertion-error"
+ ]
+ },
+ "@types/deep-eql@4.0.2": {
+ "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="
+ },
"@types/estree@1.0.8": {
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="
},
- "@vitest/expect@2.1.9": {
- "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==",
+ "@vitest/expect@4.0.16": {
+ "integrity": "sha512-eshqULT2It7McaJkQGLkPjPjNph+uevROGuIMJdG3V+0BSR2w9u6J9Lwu+E8cK5TETlfou8GRijhafIMhXsimA==",
"dependencies": [
+ "@standard-schema/spec",
+ "@types/chai",
"@vitest/spy",
"@vitest/utils",
"chai",
"tinyrainbow"
]
},
- "@vitest/mocker@2.1.9_vite@5.4.21": {
- "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==",
+ "@vitest/mocker@4.0.16_vite@7.3.0__picomatch@4.0.3": {
+ "integrity": "sha512-yb6k4AZxJTB+q9ycAvsoxGn+j/po0UaPgajllBgt1PzoMAAmJGYFdDk0uCcRcxb3BrME34I6u8gHZTQlkqSZpg==",
"dependencies": [
"@vitest/spy",
"estree-walker@3.0.3",
"magic-string",
- "vite@5.4.21"
+ "vite"
],
"optionalPeers": [
- "vite@5.4.21"
+ "vite"
]
},
- "@vitest/pretty-format@2.1.9": {
- "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==",
+ "@vitest/pretty-format@4.0.16": {
+ "integrity": "sha512-eNCYNsSty9xJKi/UdVD8Ou16alu7AYiS2fCPRs0b1OdhJiV89buAXQLpTbe+X8V9L6qrs9CqyvU7OaAopJYPsA==",
"dependencies": [
"tinyrainbow"
]
},
- "@vitest/runner@2.1.9": {
- "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==",
+ "@vitest/runner@4.0.16": {
+ "integrity": "sha512-VWEDm5Wv9xEo80ctjORcTQRJ539EGPB3Pb9ApvVRAY1U/WkHXmmYISqU5E79uCwcW7xYUV38gwZD+RV755fu3Q==",
"dependencies": [
"@vitest/utils",
"pathe"
]
},
- "@vitest/snapshot@2.1.9": {
- "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==",
+ "@vitest/snapshot@4.0.16": {
+ "integrity": "sha512-sf6NcrYhYBsSYefxnry+DR8n3UV4xWZwWxYbCJUt2YdvtqzSPR7VfGrY0zsv090DAbjFZsi7ZaMi1KnSRyK1XA==",
"dependencies": [
"@vitest/pretty-format",
"magic-string",
"pathe"
]
},
- "@vitest/spy@2.1.9": {
- "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==",
- "dependencies": [
- "tinyspy"
- ]
+ "@vitest/spy@4.0.16": {
+ "integrity": "sha512-4jIOWjKP0ZUaEmJm00E0cOBLU+5WE0BpeNr3XN6TEF05ltro6NJqHWxXD0kA8/Zc8Nh23AT8WQxwNG+WeROupw=="
},
- "@vitest/utils@2.1.9": {
- "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==",
+ "@vitest/utils@4.0.16": {
+ "integrity": "sha512-h8z9yYhV3e1LEfaQ3zdypIrnAg/9hguReGZoS7Gl0aBG5xgA410zBqECqmaF/+RkTggRsfnzc1XaAHA6bmUufA==",
"dependencies": [
"@vitest/pretty-format",
- "loupe",
"tinyrainbow"
]
},
@@ -859,9 +755,6 @@
"axobject-query@4.1.0": {
"integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="
},
- "cac@6.7.14": {
- "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="
- },
"call-bind-apply-helpers@1.0.2": {
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"dependencies": [
@@ -869,24 +762,8 @@
"function-bind"
]
},
- "chai@5.3.3": {
- "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==",
- "dependencies": [
- "assertion-error",
- "check-error",
- "deep-eql",
- "loupe",
- "pathval"
- ]
- },
- "check-error@2.1.1": {
- "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="
- },
- "chokidar@4.0.3": {
- "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
- "dependencies": [
- "readdirp"
- ]
+ "chai@6.2.2": {
+ "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="
},
"cli-color@2.0.4": {
"integrity": "sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA==",
@@ -940,9 +817,6 @@
"decimal.js@10.6.0": {
"integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="
},
- "deep-eql@5.0.2": {
- "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="
- },
"deepmerge@4.3.1": {
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
},
@@ -1060,65 +934,35 @@
"scripts": true,
"bin": true
},
- "esbuild@0.21.5": {
- "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+ "esbuild@0.27.2": {
+ "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
"optionalDependencies": [
- "@esbuild/aix-ppc64@0.21.5",
- "@esbuild/android-arm@0.21.5",
- "@esbuild/android-arm64@0.21.5",
- "@esbuild/android-x64@0.21.5",
- "@esbuild/darwin-arm64@0.21.5",
- "@esbuild/darwin-x64@0.21.5",
- "@esbuild/freebsd-arm64@0.21.5",
- "@esbuild/freebsd-x64@0.21.5",
- "@esbuild/linux-arm@0.21.5",
- "@esbuild/linux-arm64@0.21.5",
- "@esbuild/linux-ia32@0.21.5",
- "@esbuild/linux-loong64@0.21.5",
- "@esbuild/linux-mips64el@0.21.5",
- "@esbuild/linux-ppc64@0.21.5",
- "@esbuild/linux-riscv64@0.21.5",
- "@esbuild/linux-s390x@0.21.5",
- "@esbuild/linux-x64@0.21.5",
- "@esbuild/netbsd-x64@0.21.5",
- "@esbuild/openbsd-x64@0.21.5",
- "@esbuild/sunos-x64@0.21.5",
- "@esbuild/win32-arm64@0.21.5",
- "@esbuild/win32-ia32@0.21.5",
- "@esbuild/win32-x64@0.21.5"
- ],
- "scripts": true,
- "bin": true
- },
- "esbuild@0.25.12": {
- "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
- "optionalDependencies": [
- "@esbuild/aix-ppc64@0.25.12",
- "@esbuild/android-arm@0.25.12",
- "@esbuild/android-arm64@0.25.12",
- "@esbuild/android-x64@0.25.12",
- "@esbuild/darwin-arm64@0.25.12",
- "@esbuild/darwin-x64@0.25.12",
- "@esbuild/freebsd-arm64@0.25.12",
- "@esbuild/freebsd-x64@0.25.12",
- "@esbuild/linux-arm@0.25.12",
- "@esbuild/linux-arm64@0.25.12",
- "@esbuild/linux-ia32@0.25.12",
- "@esbuild/linux-loong64@0.25.12",
- "@esbuild/linux-mips64el@0.25.12",
- "@esbuild/linux-ppc64@0.25.12",
- "@esbuild/linux-riscv64@0.25.12",
- "@esbuild/linux-s390x@0.25.12",
- "@esbuild/linux-x64@0.25.12",
+ "@esbuild/aix-ppc64@0.27.2",
+ "@esbuild/android-arm@0.27.2",
+ "@esbuild/android-arm64@0.27.2",
+ "@esbuild/android-x64@0.27.2",
+ "@esbuild/darwin-arm64@0.27.2",
+ "@esbuild/darwin-x64@0.27.2",
+ "@esbuild/freebsd-arm64@0.27.2",
+ "@esbuild/freebsd-x64@0.27.2",
+ "@esbuild/linux-arm@0.27.2",
+ "@esbuild/linux-arm64@0.27.2",
+ "@esbuild/linux-ia32@0.27.2",
+ "@esbuild/linux-loong64@0.27.2",
+ "@esbuild/linux-mips64el@0.27.2",
+ "@esbuild/linux-ppc64@0.27.2",
+ "@esbuild/linux-riscv64@0.27.2",
+ "@esbuild/linux-s390x@0.27.2",
+ "@esbuild/linux-x64@0.27.2",
"@esbuild/netbsd-arm64",
- "@esbuild/netbsd-x64@0.25.12",
+ "@esbuild/netbsd-x64@0.27.2",
"@esbuild/openbsd-arm64",
- "@esbuild/openbsd-x64@0.25.12",
+ "@esbuild/openbsd-x64@0.27.2",
"@esbuild/openharmony-arm64",
- "@esbuild/sunos-x64@0.25.12",
- "@esbuild/win32-arm64@0.25.12",
- "@esbuild/win32-ia32@0.25.12",
- "@esbuild/win32-x64@0.25.12"
+ "@esbuild/sunos-x64@0.27.2",
+ "@esbuild/win32-arm64@0.27.2",
+ "@esbuild/win32-ia32@0.27.2",
+ "@esbuild/win32-x64@0.27.2"
],
"scripts": true,
"bin": true
@@ -1318,15 +1162,9 @@
"xml-name-validator"
]
},
- "kleur@4.1.5": {
- "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="
- },
"locate-character@3.0.0": {
"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="
},
- "loupe@3.2.1": {
- "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="
- },
"lru-cache@10.4.3": {
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="
},
@@ -1393,17 +1231,17 @@
"nwsapi@2.2.23": {
"integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ=="
},
+ "obug@2.1.1": {
+ "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="
+ },
"parse5@7.3.0": {
"integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
"dependencies": [
"entities"
]
},
- "pathe@1.1.2": {
- "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="
- },
- "pathval@2.0.1": {
- "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="
+ "pathe@2.0.3": {
+ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="
},
"picocolors@1.1.1": {
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
@@ -1433,9 +1271,6 @@
"react-is@17.0.2": {
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
},
- "readdirp@4.1.2": {
- "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="
- },
"redent@3.0.0": {
"integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
"dependencies": [
@@ -1443,8 +1278,8 @@
"strip-indent"
]
},
- "rollup@4.53.3": {
- "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
+ "rollup@4.54.0": {
+ "integrity": "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==",
"dependencies": [
"@types/estree"
],
@@ -1514,20 +1349,7 @@
"min-indent"
]
},
- "svelte-check@4.3.5_svelte@5.45.10__acorn@8.15.0_typescript@5.9.3": {
- "integrity": "sha512-e4VWZETyXaKGhpkxOXP+B/d0Fp/zKViZoJmneZWe/05Y2aqSKj3YN2nLfYPJBQ87WEiY4BQCQ9hWGu9mPT1a1Q==",
- "dependencies": [
- "@jridgewell/trace-mapping",
- "chokidar",
- "fdir",
- "picocolors",
- "sade",
- "svelte",
- "typescript"
- ],
- "bin": true
- },
- "svelte-i18n@4.0.1_svelte@5.45.10__acorn@8.15.0": {
+ "svelte-i18n@4.0.1_svelte@5.46.1__acorn@8.15.0": {
"integrity": "sha512-jaykGlGT5PUaaq04JWbJREvivlCnALtT+m87Kbm0fxyYHynkQaxQMnIKHLm2WeIuBRoljzwgyvz0Z6/CMwfdmQ==",
"dependencies": [
"cli-color",
@@ -1541,8 +1363,8 @@
],
"bin": true
},
- "svelte@5.45.10_acorn@8.15.0": {
- "integrity": "sha512-GiWXq6akkEN3zVDMQ1BVlRolmks5JkEdzD/67mvXOz6drRfuddT5JwsGZjMGSnsTRv/PjAXX8fqBcOr2g2qc/Q==",
+ "svelte@5.46.1_acorn@8.15.0": {
+ "integrity": "sha512-ynjfCHD3nP2el70kN5Pmg37sSi0EjOm9FgHYQdC4giWG/hzO3AatzXXJJgP305uIhGQxSufJLuYWtkY8uK/8RA==",
"dependencies": [
"@jridgewell/remapping",
"@jridgewell/sourcemap-codec",
@@ -1581,8 +1403,8 @@
"tinybench@2.9.0": {
"integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="
},
- "tinyexec@0.3.2": {
- "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="
+ "tinyexec@1.0.2": {
+ "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="
},
"tinyglobby@0.2.15_picomatch@4.0.3": {
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
@@ -1591,14 +1413,8 @@
"picomatch"
]
},
- "tinypool@1.1.1": {
- "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="
- },
- "tinyrainbow@1.2.0": {
- "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ=="
- },
- "tinyspy@3.0.2": {
- "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q=="
+ "tinyrainbow@3.0.3": {
+ "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q=="
},
"tldts-core@6.1.86": {
"integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA=="
@@ -1628,40 +1444,13 @@
"type@2.7.3": {
"integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ=="
},
- "typescript@5.9.3": {
- "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
- "bin": true
+ "unicode-segmenter@0.14.5": {
+ "integrity": "sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g=="
},
- "unicode-segmenter@0.14.4": {
- "integrity": "sha512-pR5VCiCrLrKOL6FRW61jnk9+wyMtKKowq+jyFY9oc6uHbWKhDL4yVRiI4YZPksGMK72Pahh8m0cn/0JvbDDyJg=="
- },
- "vite-node@2.1.9": {
- "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==",
+ "vite@7.3.0_picomatch@4.0.3": {
+ "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
"dependencies": [
- "cac",
- "debug",
- "es-module-lexer",
- "pathe",
- "vite@5.4.21"
- ],
- "bin": true
- },
- "vite@5.4.21": {
- "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
- "dependencies": [
- "esbuild@0.21.5",
- "postcss",
- "rollup"
- ],
- "optionalDependencies": [
- "fsevents"
- ],
- "bin": true
- },
- "vite@6.4.1_picomatch@4.0.3": {
- "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
- "dependencies": [
- "esbuild@0.25.12",
+ "esbuild@0.27.2",
"fdir",
"picomatch",
"postcss",
@@ -1673,17 +1462,17 @@
],
"bin": true
},
- "vitefu@1.1.1_vite@6.4.1__picomatch@4.0.3": {
+ "vitefu@1.1.1_vite@7.3.0__picomatch@4.0.3": {
"integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==",
"dependencies": [
- "vite@6.4.1_picomatch@4.0.3"
+ "vite"
],
"optionalPeers": [
- "vite@6.4.1_picomatch@4.0.3"
+ "vite"
]
},
- "vitest@2.1.9_jsdom@25.0.1_vite@5.4.21": {
- "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==",
+ "vitest@4.0.16_jsdom@25.0.1_vite@7.3.0__picomatch@4.0.3": {
+ "integrity": "sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==",
"dependencies": [
"@vitest/expect",
"@vitest/mocker",
@@ -1692,19 +1481,19 @@
"@vitest/snapshot",
"@vitest/spy",
"@vitest/utils",
- "chai",
- "debug",
+ "es-module-lexer",
"expect-type",
"jsdom",
"magic-string",
+ "obug",
"pathe",
+ "picomatch",
"std-env",
"tinybench",
"tinyexec",
- "tinypool",
+ "tinyglobby",
"tinyrainbow",
- "vite@5.4.21",
- "vite-node",
+ "vite",
"why-is-node-running"
],
"optionalPeers": [
@@ -1725,7 +1514,8 @@
"integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
"dependencies": [
"iconv-lite"
- ]
+ ],
+ "deprecated": true
},
"whatwg-mimetype@4.0.0": {
"integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="
@@ -1756,6 +1546,9 @@
},
"zimmerframe@1.1.4": {
"integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="
+ },
+ "zod@4.3.5": {
+ "integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g=="
}
},
"workspace": {
@@ -1765,17 +1558,18 @@
"npm:@atcute/crypto@^2.3.0",
"npm:@atcute/did-plc@~0.3.1",
"npm:@atcute/multibase@^1.1.6",
- "npm:@noble/secp256k1@^2.1.0",
- "npm:@sveltejs/vite-plugin-svelte@5",
- "npm:@testing-library/jest-dom@^6.6.3",
- "npm:@testing-library/svelte@^5.2.6",
- "npm:@testing-library/user-event@^14.5.2",
+ "npm:@noble/secp256k1@3",
+ "npm:@sveltejs/vite-plugin-svelte@^6.2.1",
+ "npm:@testing-library/jest-dom@^6.9.1",
+ "npm:@testing-library/svelte@^5.3.1",
+ "npm:@testing-library/user-event@^14.6.1",
"npm:jsdom@^25.0.1",
- "npm:multiformats@^13.3.1",
+ "npm:multiformats@^13.4.2",
"npm:svelte-i18n@^4.0.1",
- "npm:svelte@5",
- "npm:vite@6",
- "npm:vitest@^2.1.8"
+ "npm:svelte@^5.46.1",
+ "npm:vite@^7.3.0",
+ "npm:vitest@^4.0.16",
+ "npm:zod@^4.3.5"
]
}
}
diff --git a/frontend/package.json b/frontend/package.json
index 875a16e..d0aa605 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -16,18 +16,19 @@
"@atcute/crypto": "^2.3.0",
"@atcute/did-plc": "^0.3.1",
"@atcute/multibase": "^1.1.6",
- "@noble/secp256k1": "^2.1.0",
- "multiformats": "^13.3.1",
- "svelte-i18n": "^4.0.1"
+ "@noble/secp256k1": "^3.0.0",
+ "multiformats": "^13.4.2",
+ "svelte-i18n": "^4.0.1",
+ "zod": "^4.3.5"
},
"devDependencies": {
- "@sveltejs/vite-plugin-svelte": "^5.0.0",
- "@testing-library/jest-dom": "^6.6.3",
- "@testing-library/svelte": "^5.2.6",
- "@testing-library/user-event": "^14.5.2",
+ "@sveltejs/vite-plugin-svelte": "^6.2.1",
+ "@testing-library/jest-dom": "^6.9.1",
+ "@testing-library/svelte": "^5.3.1",
+ "@testing-library/user-event": "^14.6.1",
"jsdom": "^25.0.1",
- "svelte": "^5.0.0",
- "vite": "^6.0.0",
- "vitest": "^2.1.8"
+ "svelte": "^5.46.1",
+ "vite": "^7.3.0",
+ "vitest": "^4.0.16"
}
}
diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte
index 5bac107..1adaa5a 100644
--- a/frontend/src/App.svelte
+++ b/frontend/src/App.svelte
@@ -4,6 +4,7 @@
import { initServerConfig } from './lib/serverConfig.svelte'
import { initI18n } from './lib/i18n'
import { isLoading as i18nLoading } from 'svelte-i18n'
+ import Toast from './components/Toast.svelte'
import Login from './routes/Login.svelte'
import Register from './routes/Register.svelte'
import RegisterPasskey from './routes/RegisterPasskey.svelte'
@@ -36,7 +37,7 @@
import DidDocumentEditor from './routes/DidDocumentEditor.svelte'
initI18n()
- const auth = getAuthState()
+ const auth = $derived(getAuthState())
let oauthCallbackPending = $state(hasOAuthCallback())
@@ -59,10 +60,10 @@
})
$effect(() => {
- if (auth.loading) return
+ if (auth.kind === 'loading') return
const path = getCurrentPath()
if (path === '/') {
- if (auth.session) {
+ if (auth.kind === 'authenticated') {
navigate('/dashboard', true)
} else {
navigate('/login', true)
@@ -142,14 +143,13 @@
- {#if auth.loading || $i18nLoading || oauthCallbackPending}
-
+ {#if auth.kind === 'loading' || $i18nLoading || oauthCallbackPending}
+
{:else}
{/if}
+
diff --git a/frontend/src/components/ReauthModal.svelte b/frontend/src/components/ReauthModal.svelte
index 56e91aa..e0a27ae 100644
--- a/frontend/src/components/ReauthModal.svelte
+++ b/frontend/src/components/ReauthModal.svelte
@@ -2,6 +2,12 @@
import { getAuthState, getValidToken } from '../lib/auth.svelte'
import { api, ApiError } from '../lib/api'
import { _ } from '../lib/i18n'
+ import type { Session } from '../lib/types/api'
+ import {
+ prepareRequestOptions,
+ serializeAssertionResponse,
+ type WebAuthnRequestOptionsResponse,
+ } from '../lib/webauthn'
interface Props {
show: boolean
@@ -12,7 +18,13 @@
let { show = $bindable(), availableMethods = ['password'], onSuccess, onCancel }: Props = $props()
- const auth = getAuthState()
+ const auth = $derived(getAuthState())
+
+ function getSession(): Session | null {
+ return auth.kind === 'authenticated' ? auth.session : null
+ }
+
+ const session = $derived(getSession())
let activeMethod = $state<'password' | 'totp' | 'passkey'>('password')
let password = $state('')
let totpCode = $state('')
@@ -37,40 +49,9 @@
}
})
- function arrayBufferToBase64Url(buffer: ArrayBuffer): string {
- const bytes = new Uint8Array(buffer)
- let binary = ''
- for (let i = 0; i < bytes.byteLength; i++) {
- binary += String.fromCharCode(bytes[i])
- }
- return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
- }
-
- function base64UrlToArrayBuffer(base64url: string): ArrayBuffer {
- const base64 = base64url.replace(/-/g, '+').replace(/_/g, '/')
- const padded = base64 + '='.repeat((4 - base64.length % 4) % 4)
- const binary = atob(padded)
- const bytes = new Uint8Array(binary.length)
- for (let i = 0; i < binary.length; i++) {
- bytes[i] = binary.charCodeAt(i)
- }
- return bytes.buffer
- }
-
- function prepareAuthOptions(options: any): PublicKeyCredentialRequestOptions {
- return {
- ...options.publicKey,
- challenge: base64UrlToArrayBuffer(options.publicKey.challenge),
- allowCredentials: options.publicKey.allowCredentials?.map((cred: any) => ({
- ...cred,
- id: base64UrlToArrayBuffer(cred.id)
- })) || []
- }
- }
-
async function handlePasswordSubmit(e: Event) {
e.preventDefault()
- if (!auth.session || !password) return
+ if (!session || !password) return
loading = true
error = ''
try {
@@ -91,7 +72,7 @@
async function handleTotpSubmit(e: Event) {
e.preventDefault()
- if (!auth.session || !totpCode) return
+ if (!session || !totpCode) return
loading = true
error = ''
try {
@@ -111,7 +92,7 @@
}
async function handlePasskeyAuth() {
- if (!auth.session) return
+ if (!session) return
if (!window.PublicKeyCredential) {
error = 'Passkeys are not supported in this browser'
return
@@ -125,7 +106,7 @@
return
}
const { options } = await api.reauthPasskeyStart(token)
- const publicKeyOptions = prepareAuthOptions(options)
+ const publicKeyOptions = prepareRequestOptions(options as WebAuthnRequestOptionsResponse)
const credential = await navigator.credentials.get({
publicKey: publicKeyOptions
})
@@ -133,19 +114,7 @@
error = 'Passkey authentication was cancelled'
return
}
- const pkCredential = credential as PublicKeyCredential
- const response = pkCredential.response as AuthenticatorAssertionResponse
- const credentialResponse = {
- id: pkCredential.id,
- type: pkCredential.type,
- rawId: arrayBufferToBase64Url(pkCredential.rawId),
- response: {
- clientDataJSON: arrayBufferToBase64Url(response.clientDataJSON),
- authenticatorData: arrayBufferToBase64Url(response.authenticatorData),
- signature: arrayBufferToBase64Url(response.signature),
- userHandle: response.userHandle ? arrayBufferToBase64Url(response.userHandle) : null,
- },
- }
+ const credentialResponse = serializeAssertionResponse(credential as PublicKeyCredential)
await api.reauthPasskeyFinish(token, credentialResponse)
show = false
onSuccess()
diff --git a/frontend/src/components/Skeleton.svelte b/frontend/src/components/Skeleton.svelte
new file mode 100644
index 0000000..4fbb16e
--- /dev/null
+++ b/frontend/src/components/Skeleton.svelte
@@ -0,0 +1,77 @@
+
+
+{#if variant === 'card'}
+
+
+ {#each Array(lines) as _}
+
+ {/each}
+
+
+{:else if variant === 'circle'}
+
+{:else}
+ {#each Array(lines) as _, i}
+
+ {/each}
+{/if}
+
+
diff --git a/frontend/src/components/Toast.svelte b/frontend/src/components/Toast.svelte
new file mode 100644
index 0000000..ab77c00
--- /dev/null
+++ b/frontend/src/components/Toast.svelte
@@ -0,0 +1,188 @@
+
+
+{#if toasts.length > 0}
+
+ {#each toasts as toast (toast.id)}
+
+ {getIcon(toast.type)}
+ {toast.message}
+
+
+ {/each}
+
+{/if}
+
+
diff --git a/frontend/src/lib/api-validated.ts b/frontend/src/lib/api-validated.ts
new file mode 100644
index 0000000..827eff8
--- /dev/null
+++ b/frontend/src/lib/api-validated.ts
@@ -0,0 +1,345 @@
+import { z } from 'zod'
+import { ok, err, type Result } from './types/result'
+import { ApiError } from './api'
+import type { AccessToken, RefreshToken, Did, Handle, Nsid, Rkey } from './types/branded'
+import {
+ sessionSchema,
+ serverDescriptionSchema,
+ appPasswordSchema,
+ createdAppPasswordSchema,
+ listSessionsResponseSchema,
+ totpStatusSchema,
+ totpSecretSchema,
+ enableTotpResponseSchema,
+ listPasskeysResponseSchema,
+ listTrustedDevicesResponseSchema,
+ reauthStatusSchema,
+ notificationPrefsSchema,
+ didDocumentSchema,
+ repoDescriptionSchema,
+ listRecordsResponseSchema,
+ recordResponseSchema,
+ createRecordResponseSchema,
+ serverStatsSchema,
+ serverConfigSchema,
+ passwordStatusSchema,
+ successResponseSchema,
+ legacyLoginPreferenceSchema,
+ accountInfoSchema,
+ searchAccountsResponseSchema,
+ listBackupsResponseSchema,
+ createBackupResponseSchema,
+ type ValidatedSession,
+ type ValidatedServerDescription,
+ type ValidatedListSessionsResponse,
+ type ValidatedTotpStatus,
+ type ValidatedTotpSecret,
+ type ValidatedEnableTotpResponse,
+ type ValidatedListPasskeysResponse,
+ type ValidatedListTrustedDevicesResponse,
+ type ValidatedReauthStatus,
+ type ValidatedNotificationPrefs,
+ type ValidatedDidDocument,
+ type ValidatedRepoDescription,
+ type ValidatedListRecordsResponse,
+ type ValidatedRecordResponse,
+ type ValidatedCreateRecordResponse,
+ type ValidatedServerStats,
+ type ValidatedServerConfig,
+ type ValidatedPasswordStatus,
+ type ValidatedSuccessResponse,
+ type ValidatedLegacyLoginPreference,
+ type ValidatedAccountInfo,
+ type ValidatedSearchAccountsResponse,
+ type ValidatedListBackupsResponse,
+ type ValidatedCreateBackupResponse,
+ type ValidatedCreatedAppPassword,
+ type ValidatedAppPassword,
+} from './types/schemas'
+
+const API_BASE = '/xrpc'
+
+interface XrpcOptions {
+ method?: 'GET' | 'POST'
+ params?: Record
+ body?: unknown
+ token?: string
+}
+
+class ValidationError extends Error {
+ constructor(
+ public issues: z.ZodIssue[],
+ message: string = 'API response validation failed'
+ ) {
+ super(message)
+ this.name = 'ValidationError'
+ }
+}
+
+async function xrpcValidated(
+ method: string,
+ schema: z.ZodType,
+ options?: XrpcOptions
+): Promise> {
+ const { method: httpMethod = 'GET', params, body, token } = options ?? {}
+ let url = `${API_BASE}/${method}`
+ if (params) {
+ const searchParams = new URLSearchParams(params)
+ url += `?${searchParams}`
+ }
+ const headers: Record = {}
+ if (token) {
+ headers['Authorization'] = `Bearer ${token}`
+ }
+ if (body) {
+ headers['Content-Type'] = 'application/json'
+ }
+
+ try {
+ const res = await fetch(url, {
+ method: httpMethod,
+ headers,
+ body: body ? JSON.stringify(body) : undefined,
+ })
+
+ if (!res.ok) {
+ const errData = await res.json().catch(() => ({
+ error: 'Unknown',
+ message: res.statusText,
+ }))
+ return err(new ApiError(res.status, errData.error, errData.message))
+ }
+
+ const data = await res.json()
+ const parsed = schema.safeParse(data)
+
+ if (!parsed.success) {
+ return err(new ValidationError(parsed.error.issues))
+ }
+
+ return ok(parsed.data)
+ } catch (e) {
+ if (e instanceof ApiError || e instanceof ValidationError) {
+ return err(e)
+ }
+ return err(new ApiError(0, 'Unknown', e instanceof Error ? e.message : String(e)))
+ }
+}
+
+export const validatedApi = {
+ getSession(token: AccessToken): Promise> {
+ return xrpcValidated('com.atproto.server.getSession', sessionSchema, { token })
+ },
+
+ refreshSession(refreshJwt: RefreshToken): Promise> {
+ return xrpcValidated('com.atproto.server.refreshSession', sessionSchema, {
+ method: 'POST',
+ token: refreshJwt,
+ })
+ },
+
+ createSession(
+ identifier: string,
+ password: string
+ ): Promise> {
+ return xrpcValidated('com.atproto.server.createSession', sessionSchema, {
+ method: 'POST',
+ body: { identifier, password },
+ })
+ },
+
+ describeServer(): Promise> {
+ return xrpcValidated('com.atproto.server.describeServer', serverDescriptionSchema)
+ },
+
+ listAppPasswords(
+ token: AccessToken
+ ): Promise> {
+ return xrpcValidated(
+ 'com.atproto.server.listAppPasswords',
+ z.object({ passwords: z.array(appPasswordSchema) }),
+ { token }
+ )
+ },
+
+ createAppPassword(
+ token: AccessToken,
+ name: string,
+ scopes?: string
+ ): Promise> {
+ return xrpcValidated('com.atproto.server.createAppPassword', createdAppPasswordSchema, {
+ method: 'POST',
+ token,
+ body: { name, scopes },
+ })
+ },
+
+ listSessions(token: AccessToken): Promise> {
+ return xrpcValidated('_account.listSessions', listSessionsResponseSchema, { token })
+ },
+
+ getTotpStatus(token: AccessToken): Promise> {
+ return xrpcValidated('com.atproto.server.getTotpStatus', totpStatusSchema, { token })
+ },
+
+ createTotpSecret(token: AccessToken): Promise> {
+ return xrpcValidated('com.atproto.server.createTotpSecret', totpSecretSchema, {
+ method: 'POST',
+ token,
+ })
+ },
+
+ enableTotp(
+ token: AccessToken,
+ code: string
+ ): Promise> {
+ return xrpcValidated('com.atproto.server.enableTotp', enableTotpResponseSchema, {
+ method: 'POST',
+ token,
+ body: { code },
+ })
+ },
+
+ listPasskeys(token: AccessToken): Promise> {
+ return xrpcValidated('com.atproto.server.listPasskeys', listPasskeysResponseSchema, { token })
+ },
+
+ listTrustedDevices(
+ token: AccessToken
+ ): Promise> {
+ return xrpcValidated('_account.listTrustedDevices', listTrustedDevicesResponseSchema, { token })
+ },
+
+ getReauthStatus(token: AccessToken): Promise> {
+ return xrpcValidated('_account.getReauthStatus', reauthStatusSchema, { token })
+ },
+
+ getNotificationPrefs(
+ token: AccessToken
+ ): Promise> {
+ return xrpcValidated('_account.getNotificationPrefs', notificationPrefsSchema, { token })
+ },
+
+ getDidDocument(token: AccessToken): Promise> {
+ return xrpcValidated('_account.getDidDocument', didDocumentSchema, { token })
+ },
+
+ describeRepo(
+ token: AccessToken,
+ repo: Did
+ ): Promise> {
+ return xrpcValidated('com.atproto.repo.describeRepo', repoDescriptionSchema, {
+ token,
+ params: { repo },
+ })
+ },
+
+ listRecords(
+ token: AccessToken,
+ repo: Did,
+ collection: Nsid,
+ options?: { limit?: number; cursor?: string; reverse?: boolean }
+ ): Promise> {
+ const params: Record = { repo, collection }
+ if (options?.limit) params.limit = String(options.limit)
+ if (options?.cursor) params.cursor = options.cursor
+ if (options?.reverse) params.reverse = 'true'
+ return xrpcValidated('com.atproto.repo.listRecords', listRecordsResponseSchema, {
+ token,
+ params,
+ })
+ },
+
+ getRecord(
+ token: AccessToken,
+ repo: Did,
+ collection: Nsid,
+ rkey: Rkey
+ ): Promise> {
+ return xrpcValidated('com.atproto.repo.getRecord', recordResponseSchema, {
+ token,
+ params: { repo, collection, rkey },
+ })
+ },
+
+ createRecord(
+ token: AccessToken,
+ repo: Did,
+ collection: Nsid,
+ record: unknown,
+ rkey?: Rkey
+ ): Promise> {
+ return xrpcValidated('com.atproto.repo.createRecord', createRecordResponseSchema, {
+ method: 'POST',
+ token,
+ body: { repo, collection, record, rkey },
+ })
+ },
+
+ getServerStats(token: AccessToken): Promise> {
+ return xrpcValidated('_admin.getServerStats', serverStatsSchema, { token })
+ },
+
+ getServerConfig(): Promise> {
+ return xrpcValidated('_server.getConfig', serverConfigSchema)
+ },
+
+ getPasswordStatus(token: AccessToken): Promise> {
+ return xrpcValidated('_account.getPasswordStatus', passwordStatusSchema, { token })
+ },
+
+ changePassword(
+ token: AccessToken,
+ currentPassword: string,
+ newPassword: string
+ ): Promise> {
+ return xrpcValidated('_account.changePassword', successResponseSchema, {
+ method: 'POST',
+ token,
+ body: { currentPassword, newPassword },
+ })
+ },
+
+ getLegacyLoginPreference(
+ token: AccessToken
+ ): Promise> {
+ return xrpcValidated('_account.getLegacyLoginPreference', legacyLoginPreferenceSchema, { token })
+ },
+
+ getAccountInfo(
+ token: AccessToken,
+ did: Did
+ ): Promise> {
+ return xrpcValidated('com.atproto.admin.getAccountInfo', accountInfoSchema, {
+ token,
+ params: { did },
+ })
+ },
+
+ searchAccounts(
+ token: AccessToken,
+ options?: { handle?: string; cursor?: string; limit?: number }
+ ): Promise> {
+ const params: Record = {}
+ if (options?.handle) params.handle = options.handle
+ if (options?.cursor) params.cursor = options.cursor
+ if (options?.limit) params.limit = String(options.limit)
+ return xrpcValidated('com.atproto.admin.searchAccounts', searchAccountsResponseSchema, {
+ token,
+ params,
+ })
+ },
+
+ listBackups(token: AccessToken): Promise> {
+ return xrpcValidated('_backup.listBackups', listBackupsResponseSchema, { token })
+ },
+
+ createBackup(token: AccessToken): Promise> {
+ return xrpcValidated('_backup.createBackup', createBackupResponseSchema, {
+ method: 'POST',
+ token,
+ })
+ },
+}
+
+export { ValidationError }
diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts
index 3cf10c8..339ba8f 100644
--- a/frontend/src/lib/api.ts
+++ b/frontend/src/lib/api.ts
@@ -1,172 +1,207 @@
-const API_BASE = "/xrpc";
+import { ok, err, type Result } from './types/result'
+import type {
+ Did,
+ Handle,
+ AccessToken,
+ RefreshToken,
+ Cid,
+ Rkey,
+ AtUri,
+ Nsid,
+ ISODateString,
+ EmailAddress,
+ InviteCode as InviteCodeBrand,
+} from './types/branded'
+import {
+ unsafeAsDid,
+ unsafeAsHandle,
+ unsafeAsAccessToken,
+ unsafeAsRefreshToken,
+ unsafeAsCid,
+ unsafeAsISODate,
+ unsafeAsEmail,
+ unsafeAsInviteCode,
+} from './types/branded'
+import type {
+ Session,
+ DidDocument,
+ AppPassword,
+ CreatedAppPassword,
+ InviteCodeInfo,
+ ServerDescription,
+ NotificationPrefs,
+ NotificationHistoryResponse,
+ ServerStats,
+ ServerConfig,
+ UploadBlobResponse,
+ ListSessionsResponse,
+ SearchAccountsResponse,
+ GetInviteCodesResponse,
+ AccountInfo,
+ RepoDescription,
+ ListRecordsResponse,
+ RecordResponse,
+ CreateRecordResponse,
+ TotpStatus,
+ TotpSecret,
+ EnableTotpResponse,
+ RegenerateBackupCodesResponse,
+ ListPasskeysResponse,
+ StartPasskeyRegistrationResponse,
+ FinishPasskeyRegistrationResponse,
+ ListTrustedDevicesResponse,
+ ReauthStatus,
+ ReauthResponse,
+ ReauthPasskeyStartResponse,
+ ReserveSigningKeyResponse,
+ RecommendedDidCredentials,
+ PasskeyAccountCreateResponse,
+ CompletePasskeySetupResponse,
+ VerifyTokenResponse,
+ ListBackupsResponse,
+ CreateBackupResponse,
+ SetBackupEnabledResponse,
+ EmailUpdateResponse,
+ LegacyLoginPreference,
+ UpdateLegacyLoginResponse,
+ UpdateLocaleResponse,
+ PasswordStatus,
+ SuccessResponse,
+ CheckEmailVerifiedResponse,
+ VerifyMigrationEmailResponse,
+ ResendMigrationVerificationResponse,
+ ListReposResponse,
+ VerificationChannel,
+ DidType,
+ ApiErrorCode,
+ VerificationMethod as VerificationMethodType,
+ CreateAccountParams,
+ CreateAccountResult,
+ ConfirmSignupResult,
+} from './types/api'
+
+const API_BASE = '/xrpc'
export class ApiError extends Error {
- public did?: string;
- public reauthMethods?: string[];
+ public did?: Did
+ public reauthMethods?: string[]
constructor(
public status: number,
- public error: string,
+ public error: ApiErrorCode,
message: string,
did?: string,
reauthMethods?: string[],
) {
- super(message);
- this.name = "ApiError";
- this.did = did;
- this.reauthMethods = reauthMethods;
+ super(message)
+ this.name = 'ApiError'
+ this.did = did ? unsafeAsDid(did) : undefined
+ this.reauthMethods = reauthMethods
}
}
-let tokenRefreshCallback: (() => Promise) | null = null;
+let tokenRefreshCallback: (() => Promise) | null = null
export function setTokenRefreshCallback(
callback: () => Promise,
) {
- tokenRefreshCallback = callback;
+ tokenRefreshCallback = callback
}
-async function xrpc(method: string, options?: {
- method?: "GET" | "POST";
- params?: Record;
- body?: unknown;
- token?: string;
- skipRetry?: boolean;
-}): Promise {
- const { method: httpMethod = "GET", params, body, token, skipRetry } =
- options ?? {};
- let url = `${API_BASE}/${method}`;
+interface XrpcOptions {
+ method?: 'GET' | 'POST'
+ params?: Record
+ body?: unknown
+ token?: string
+ skipRetry?: boolean
+}
+
+async function xrpc(method: string, options?: XrpcOptions): Promise {
+ const { method: httpMethod = 'GET', params, body, token, skipRetry } =
+ options ?? {}
+ let url = `${API_BASE}/${method}`
if (params) {
- const searchParams = new URLSearchParams(params);
- url += `?${searchParams}`;
+ const searchParams = new URLSearchParams(params)
+ url += `?${searchParams}`
}
- const headers: Record = {};
+ const headers: Record = {}
if (token) {
- headers["Authorization"] = `Bearer ${token}`;
+ headers['Authorization'] = `Bearer ${token}`
}
if (body) {
- headers["Content-Type"] = "application/json";
+ headers['Content-Type'] = 'application/json'
}
const res = await fetch(url, {
method: httpMethod,
headers,
body: body ? JSON.stringify(body) : undefined,
- });
+ })
if (!res.ok) {
- const err = await res.json().catch(() => ({
- error: "Unknown",
+ const errData = await res.json().catch(() => ({
+ error: 'Unknown',
message: res.statusText,
- }));
+ }))
if (
res.status === 401 &&
- (err.error === "AuthenticationFailed" || err.error === "ExpiredToken") &&
+ (errData.error === 'AuthenticationFailed' || errData.error === 'ExpiredToken') &&
token && tokenRefreshCallback && !skipRetry
) {
- const newToken = await tokenRefreshCallback();
+ const newToken = await tokenRefreshCallback()
if (newToken && newToken !== token) {
- return xrpc(method, { ...options, token: newToken, skipRetry: true });
+ return xrpc(method, { ...options, token: newToken, skipRetry: true })
}
}
throw new ApiError(
res.status,
- err.error,
- err.message,
- err.did,
- err.reauthMethods,
- );
+ errData.error as ApiErrorCode,
+ errData.message,
+ errData.did,
+ errData.reauthMethods,
+ )
}
- return res.json();
+ return res.json()
}
-export interface Session {
- did: string;
- handle: string;
- email?: string;
- emailConfirmed?: boolean;
- preferredChannel?: string;
- preferredChannelVerified?: boolean;
- isAdmin?: boolean;
- active?: boolean;
- status?: "active" | "deactivated" | "migrated";
- migratedToPds?: string;
- migratedAt?: string;
- accessJwt: string;
- refreshJwt: string;
+async function xrpcResult(
+ method: string,
+ options?: XrpcOptions
+): Promise> {
+ try {
+ const value = await xrpc(method, options)
+ return ok(value)
+ } catch (e) {
+ if (e instanceof ApiError) {
+ return err(e)
+ }
+ return err(new ApiError(0, 'Unknown', e instanceof Error ? e.message : String(e)))
+ }
}
export interface VerificationMethod {
- id: string;
- type: string;
- publicKeyMultibase: string;
+ id: string
+ type: string
+ publicKeyMultibase: string
}
-export interface DidDocument {
- "@context": string[];
- id: string;
- alsoKnownAs: string[];
- verificationMethod: Array<{
- id: string;
- type: string;
- controller: string;
- publicKeyMultibase: string;
- }>;
- service: Array<{
- id: string;
- type: string;
- serviceEndpoint: string;
- }>;
-}
+export type { Session, DidDocument, AppPassword, InviteCodeInfo as InviteCode }
+export type { VerificationChannel, DidType, CreateAccountParams, CreateAccountResult, ConfirmSignupResult }
-export interface AppPassword {
- name: string;
- createdAt: string;
- scopes?: string;
- createdByController?: string;
-}
-
-export interface InviteCode {
- code: string;
- available: number;
- disabled: boolean;
- forAccount: string;
- createdBy: string;
- createdAt: string;
- uses: { usedBy: string; usedByHandle?: string; usedAt: string }[];
-}
-
-export type VerificationChannel = "email" | "discord" | "telegram" | "signal";
-
-export type DidType = "plc" | "web" | "web-external";
-
-export interface CreateAccountParams {
- handle: string;
- email: string;
- password: string;
- inviteCode?: string;
- didType?: DidType;
- did?: string;
- signingKey?: string;
- verificationChannel?: VerificationChannel;
- discordId?: string;
- telegramUsername?: string;
- signalNumber?: string;
-}
-
-export interface CreateAccountResult {
- handle: string;
- did: string;
- verificationRequired: boolean;
- verificationChannel: string;
-}
-
-export interface ConfirmSignupResult {
- accessJwt: string;
- refreshJwt: string;
- handle: string;
- did: string;
- email?: string;
- emailConfirmed?: boolean;
- preferredChannel?: string;
- preferredChannelVerified?: boolean;
+function castSession(raw: unknown): Session {
+ const s = raw as Record
+ return {
+ did: unsafeAsDid(s.did as string),
+ handle: unsafeAsHandle(s.handle as string),
+ email: s.email ? unsafeAsEmail(s.email as string) : undefined,
+ emailConfirmed: s.emailConfirmed as boolean | undefined,
+ preferredChannel: s.preferredChannel as VerificationChannel | undefined,
+ preferredChannelVerified: s.preferredChannelVerified as boolean | undefined,
+ isAdmin: s.isAdmin as boolean | undefined,
+ active: s.active as boolean | undefined,
+ status: s.status as Session['status'],
+ migratedToPds: s.migratedToPds as string | undefined,
+ migratedAt: s.migratedAt ? unsafeAsISODate(s.migratedAt as string) : undefined,
+ accessJwt: unsafeAsAccessToken(s.accessJwt as string),
+ refreshJwt: unsafeAsRefreshToken(s.refreshJwt as string),
+ }
}
export const api = {
@@ -174,15 +209,15 @@ export const api = {
params: CreateAccountParams,
byodToken?: string,
): Promise {
- const url = `${API_BASE}/com.atproto.server.createAccount`;
+ const url = `${API_BASE}/com.atproto.server.createAccount`
const headers: Record = {
- "Content-Type": "application/json",
- };
+ 'Content-Type': 'application/json',
+ }
if (byodToken) {
- headers["Authorization"] = `Bearer ${byodToken}`;
+ headers['Authorization'] = `Bearer ${byodToken}`
}
const response = await fetch(url, {
- method: "POST",
+ method: 'POST',
headers,
body: JSON.stringify({
handle: params.handle,
@@ -197,30 +232,30 @@ export const api = {
telegramUsername: params.telegramUsername,
signalNumber: params.signalNumber,
}),
- });
- const data = await response.json();
+ })
+ const data = await response.json()
if (!response.ok) {
- throw new ApiError(response.status, data.error, data.message);
+ throw new ApiError(response.status, data.error, data.message)
}
- return data;
+ return data
},
async createAccountWithServiceAuth(
serviceAuthToken: string,
params: {
- did: string;
- handle: string;
- email: string;
- password: string;
- inviteCode?: string;
+ did: Did
+ handle: Handle
+ email: EmailAddress
+ password: string
+ inviteCode?: string
},
): Promise {
- const url = `${API_BASE}/com.atproto.server.createAccount`;
+ const url = `${API_BASE}/com.atproto.server.createAccount`
const response = await fetch(url, {
- method: "POST",
+ method: 'POST',
headers: {
- "Content-Type": "application/json",
- "Authorization": `Bearer ${serviceAuthToken}`,
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${serviceAuthToken}`,
},
body: JSON.stringify({
did: params.did,
@@ -229,1023 +264,1387 @@ export const api = {
password: params.password,
inviteCode: params.inviteCode,
}),
- });
- const data = await response.json();
+ })
+ const data = await response.json()
if (!response.ok) {
- throw new ApiError(response.status, data.error, data.message);
+ throw new ApiError(response.status, data.error, data.message)
}
- return data;
+ return castSession(data)
},
confirmSignup(
- did: string,
+ did: Did,
verificationCode: string,
): Promise {
- return xrpc("com.atproto.server.confirmSignup", {
- method: "POST",
+ return xrpc('com.atproto.server.confirmSignup', {
+ method: 'POST',
body: { did, verificationCode },
- });
+ })
},
- resendVerification(did: string): Promise<{ success: boolean }> {
- return xrpc("com.atproto.server.resendVerification", {
- method: "POST",
+ resendVerification(did: Did): Promise<{ success: boolean }> {
+ return xrpc('com.atproto.server.resendVerification', {
+ method: 'POST',
body: { did },
- });
+ })
},
- createSession(identifier: string, password: string): Promise {
- return xrpc("com.atproto.server.createSession", {
- method: "POST",
+ async createSession(identifier: string, password: string): Promise {
+ const raw = await xrpc('com.atproto.server.createSession', {
+ method: 'POST',
body: { identifier, password },
- });
+ })
+ return castSession(raw)
},
checkEmailVerified(identifier: string): Promise<{ verified: boolean }> {
- return xrpc("_checkEmailVerified", {
- method: "POST",
+ return xrpc('_checkEmailVerified', {
+ method: 'POST',
body: { identifier },
- });
+ })
},
- getSession(token: string): Promise {
- return xrpc("com.atproto.server.getSession", { token });
+ async getSession(token: AccessToken): Promise {
+ const raw = await xrpc('com.atproto.server.getSession', { token })
+ return castSession(raw)
},
- refreshSession(refreshJwt: string): Promise {
- return xrpc("com.atproto.server.refreshSession", {
- method: "POST",
+ async refreshSession(refreshJwt: RefreshToken): Promise {
+ const raw = await xrpc('com.atproto.server.refreshSession', {
+ method: 'POST',
token: refreshJwt,
- });
+ })
+ return castSession(raw)
},
- async deleteSession(token: string): Promise {
- await xrpc("com.atproto.server.deleteSession", {
- method: "POST",
+ async deleteSession(token: AccessToken): Promise {
+ await xrpc('com.atproto.server.deleteSession', {
+ method: 'POST',
token,
- });
+ })
},
- listAppPasswords(token: string): Promise<{ passwords: AppPassword[] }> {
- return xrpc("com.atproto.server.listAppPasswords", { token });
+ listAppPasswords(token: AccessToken): Promise<{ passwords: AppPassword[] }> {
+ return xrpc('com.atproto.server.listAppPasswords', { token })
},
createAppPassword(
- token: string,
+ token: AccessToken,
name: string,
scopes?: string,
- ): Promise<
- { name: string; password: string; createdAt: string; scopes?: string }
- > {
- return xrpc("com.atproto.server.createAppPassword", {
- method: "POST",
+ ): Promise {
+ return xrpc('com.atproto.server.createAppPassword', {
+ method: 'POST',
token,
body: { name, scopes },
- });
+ })
},
- async revokeAppPassword(token: string, name: string): Promise {
- await xrpc("com.atproto.server.revokeAppPassword", {
- method: "POST",
+ async revokeAppPassword(token: AccessToken, name: string): Promise {
+ await xrpc('com.atproto.server.revokeAppPassword', {
+ method: 'POST',
token,
body: { name },
- });
+ })
},
- getAccountInviteCodes(token: string): Promise<{ codes: InviteCode[] }> {
- return xrpc("com.atproto.server.getAccountInviteCodes", { token });
+ getAccountInviteCodes(token: AccessToken): Promise<{ codes: InviteCodeInfo[] }> {
+ return xrpc('com.atproto.server.getAccountInviteCodes', { token })
},
createInviteCode(
- token: string,
+ token: AccessToken,
useCount: number = 1,
): Promise<{ code: string }> {
- return xrpc("com.atproto.server.createInviteCode", {
- method: "POST",
+ return xrpc('com.atproto.server.createInviteCode', {
+ method: 'POST',
token,
body: { useCount },
- });
+ })
},
- async requestPasswordReset(email: string): Promise {
- await xrpc("com.atproto.server.requestPasswordReset", {
- method: "POST",
+ async requestPasswordReset(email: EmailAddress): Promise {
+ await xrpc('com.atproto.server.requestPasswordReset', {
+ method: 'POST',
body: { email },
- });
+ })
},
async resetPassword(token: string, password: string): Promise {
- await xrpc("com.atproto.server.resetPassword", {
- method: "POST",
+ await xrpc('com.atproto.server.resetPassword', {
+ method: 'POST',
body: { token, password },
- });
+ })
},
- requestEmailUpdate(
- token: string,
- ): Promise<{ tokenRequired: boolean }> {
- return xrpc("com.atproto.server.requestEmailUpdate", {
- method: "POST",
+ requestEmailUpdate(token: AccessToken): Promise {
+ return xrpc('com.atproto.server.requestEmailUpdate', {
+ method: 'POST',
token,
- });
+ })
},
async updateEmail(
- token: string,
+ token: AccessToken,
email: string,
emailToken?: string,
): Promise {
- await xrpc("com.atproto.server.updateEmail", {
- method: "POST",
+ await xrpc('com.atproto.server.updateEmail', {
+ method: 'POST',
token,
body: { email, token: emailToken },
- });
+ })
},
- async updateHandle(token: string, handle: string): Promise {
- await xrpc("com.atproto.identity.updateHandle", {
- method: "POST",
+ async updateHandle(token: AccessToken, handle: Handle): Promise {
+ await xrpc('com.atproto.identity.updateHandle', {
+ method: 'POST',
token,
body: { handle },
- });
+ })
},
- async requestAccountDelete(token: string): Promise {
- await xrpc("com.atproto.server.requestAccountDelete", {
- method: "POST",
+ async requestAccountDelete(token: AccessToken): Promise {
+ await xrpc('com.atproto.server.requestAccountDelete', {
+ method: 'POST',
token,
- });
+ })
},
async deleteAccount(
- did: string,
+ did: Did,
password: string,
deleteToken: string,
): Promise {
- await xrpc("com.atproto.server.deleteAccount", {
- method: "POST",
+ await xrpc('com.atproto.server.deleteAccount', {
+ method: 'POST',
body: { did, password, token: deleteToken },
- });
+ })
},
- describeServer(): Promise<{
- availableUserDomains: string[];
- inviteCodeRequired: boolean;
- links?: { privacyPolicy?: string; termsOfService?: string };
- version?: string;
- availableCommsChannels?: string[];
- selfHostedDidWebEnabled?: boolean;
- }> {
- return xrpc("com.atproto.server.describeServer");
+ describeServer(): Promise {
+ return xrpc('com.atproto.server.describeServer')
},
- listRepos(limit?: number): Promise<{
- repos: Array<{ did: string; head: string; rev: string }>;
- cursor?: string;
- }> {
- const params: Record = {};
- if (limit) params.limit = String(limit);
- return xrpc("com.atproto.sync.listRepos", { params });
+ listRepos(limit?: number): Promise {
+ const params: Record = {}
+ if (limit) params.limit = String(limit)
+ return xrpc('com.atproto.sync.listRepos', { params })
},
- getNotificationPrefs(token: string): Promise<{
- preferredChannel: string;
- email: string;
- discordId: string | null;
- discordVerified: boolean;
- telegramUsername: string | null;
- telegramVerified: boolean;
- signalNumber: string | null;
- signalVerified: boolean;
- }> {
- return xrpc("_account.getNotificationPrefs", { token });
+ getNotificationPrefs(token: AccessToken): Promise {
+ return xrpc('_account.getNotificationPrefs', { token })
},
- updateNotificationPrefs(token: string, prefs: {
- preferredChannel?: string;
- discordId?: string;
- telegramUsername?: string;
- signalNumber?: string;
- }): Promise<{ success: boolean }> {
- return xrpc("_account.updateNotificationPrefs", {
- method: "POST",
+ updateNotificationPrefs(token: AccessToken, prefs: {
+ preferredChannel?: string
+ discordId?: string
+ telegramUsername?: string
+ signalNumber?: string
+ }): Promise {
+ return xrpc('_account.updateNotificationPrefs', {
+ method: 'POST',
token,
body: prefs,
- });
+ })
},
confirmChannelVerification(
- token: string,
+ token: AccessToken,
channel: string,
identifier: string,
code: string,
- ): Promise<{ success: boolean }> {
- return xrpc("_account.confirmChannelVerification", {
- method: "POST",
+ ): Promise {
+ return xrpc('_account.confirmChannelVerification', {
+ method: 'POST',
token,
body: { channel, identifier, code },
- });
+ })
},
- getNotificationHistory(token: string): Promise<{
- notifications: Array<{
- createdAt: string;
- channel: string;
- notificationType: string;
- status: string;
- subject: string | null;
- body: string;
- }>;
- }> {
- return xrpc("_account.getNotificationHistory", { token });
+ getNotificationHistory(token: AccessToken): Promise {
+ return xrpc('_account.getNotificationHistory', { token })
},
- getServerStats(token: string): Promise<{
- userCount: number;
- repoCount: number;
- recordCount: number;
- blobStorageBytes: number;
- }> {
- return xrpc("_admin.getServerStats", { token });
+ getServerStats(token: AccessToken): Promise {
+ return xrpc('_admin.getServerStats', { token })
},
- getServerConfig(): Promise<{
- serverName: string;
- primaryColor: string | null;
- primaryColorDark: string | null;
- secondaryColor: string | null;
- secondaryColorDark: string | null;
- logoCid: string | null;
- }> {
- return xrpc("_server.getConfig");
+ getServerConfig(): Promise {
+ return xrpc('_server.getConfig')
},
updateServerConfig(
- token: string,
+ token: AccessToken,
config: {
- serverName?: string;
- primaryColor?: string;
- primaryColorDark?: string;
- secondaryColor?: string;
- secondaryColorDark?: string;
- logoCid?: string;
+ serverName?: string
+ primaryColor?: string
+ primaryColorDark?: string
+ secondaryColor?: string
+ secondaryColorDark?: string
+ logoCid?: string
},
- ): Promise<{ success: boolean }> {
- return xrpc("_admin.updateServerConfig", {
- method: "POST",
+ ): Promise {
+ return xrpc('_admin.updateServerConfig', {
+ method: 'POST',
token,
body: config,
- });
+ })
},
- async uploadBlob(
- token: string,
- file: File,
- ): Promise<
- {
- blob: {
- $type: string;
- ref: { $link: string };
- mimeType: string;
- size: number;
- };
- }
- > {
- const res = await fetch("/xrpc/com.atproto.repo.uploadBlob", {
- method: "POST",
+ async uploadBlob(token: AccessToken, file: File): Promise {
+ const res = await fetch('/xrpc/com.atproto.repo.uploadBlob', {
+ method: 'POST',
headers: {
- "Authorization": `Bearer ${token}`,
- "Content-Type": file.type,
+ 'Authorization': `Bearer ${token}`,
+ 'Content-Type': file.type,
},
body: file,
- });
+ })
if (!res.ok) {
- const err = await res.json().catch(() => ({
- error: "Unknown",
+ const errData = await res.json().catch(() => ({
+ error: 'Unknown',
message: res.statusText,
- }));
- throw new ApiError(res.status, err.error, err.message);
+ }))
+ throw new ApiError(res.status, errData.error, errData.message)
}
- return res.json();
+ return res.json()
},
async changePassword(
- token: string,
+ token: AccessToken,
currentPassword: string,
newPassword: string,
): Promise {
- await xrpc("_account.changePassword", {
- method: "POST",
+ await xrpc('_account.changePassword', {
+ method: 'POST',
token,
body: { currentPassword, newPassword },
- });
+ })
},
- removePassword(token: string): Promise<{ success: boolean }> {
- return xrpc("_account.removePassword", {
- method: "POST",
+ removePassword(token: AccessToken): Promise {
+ return xrpc('_account.removePassword', {
+ method: 'POST',
token,
- });
+ })
},
- getPasswordStatus(token: string): Promise<{ hasPassword: boolean }> {
- return xrpc("_account.getPasswordStatus", { token });
+ getPasswordStatus(token: AccessToken): Promise {
+ return xrpc('_account.getPasswordStatus', { token })
},
- getLegacyLoginPreference(
- token: string,
- ): Promise<{ allowLegacyLogin: boolean; hasMfa: boolean }> {
- return xrpc("_account.getLegacyLoginPreference", { token });
+ getLegacyLoginPreference(token: AccessToken): Promise {
+ return xrpc('_account.getLegacyLoginPreference', { token })
},
updateLegacyLoginPreference(
- token: string,
+ token: AccessToken,
allowLegacyLogin: boolean,
- ): Promise<{ allowLegacyLogin: boolean }> {
- return xrpc("_account.updateLegacyLoginPreference", {
- method: "POST",
+ ): Promise {
+ return xrpc('_account.updateLegacyLoginPreference', {
+ method: 'POST',
token,
body: { allowLegacyLogin },
- });
+ })
},
- updateLocale(
- token: string,
- preferredLocale: string,
- ): Promise<{ preferredLocale: string }> {
- return xrpc("_account.updateLocale", {
- method: "POST",
+ updateLocale(token: AccessToken, preferredLocale: string): Promise {
+ return xrpc('_account.updateLocale', {
+ method: 'POST',
token,
body: { preferredLocale },
- });
+ })
},
- listSessions(token: string): Promise<{
- sessions: Array<{
- id: string;
- sessionType: string;
- clientName: string | null;
- createdAt: string;
- expiresAt: string;
- isCurrent: boolean;
- }>;
- }> {
- return xrpc("_account.listSessions", { token });
+ listSessions(token: AccessToken): Promise {
+ return xrpc('_account.listSessions', { token })
},
- async revokeSession(token: string, sessionId: string): Promise {
- await xrpc("_account.revokeSession", {
- method: "POST",
+ async revokeSession(token: AccessToken, sessionId: string): Promise {
+ await xrpc('_account.revokeSession', {
+ method: 'POST',
token,
body: { sessionId },
- });
+ })
},
- revokeAllSessions(token: string): Promise<{ revokedCount: number }> {
- return xrpc("_account.revokeAllSessions", {
- method: "POST",
+ revokeAllSessions(token: AccessToken): Promise<{ revokedCount: number }> {
+ return xrpc('_account.revokeAllSessions', {
+ method: 'POST',
token,
- });
+ })
},
- searchAccounts(token: string, options?: {
- handle?: string;
- cursor?: string;
- limit?: number;
- }): Promise<{
- cursor?: string;
- accounts: Array<{
- did: string;
- handle: string;
- email?: string;
- indexedAt: string;
- emailConfirmedAt?: string;
- deactivatedAt?: string;
- }>;
- }> {
- const params: Record = {};
- if (options?.handle) params.handle = options.handle;
- if (options?.cursor) params.cursor = options.cursor;
- if (options?.limit) params.limit = String(options.limit);
- return xrpc("com.atproto.admin.searchAccounts", { token, params });
+ searchAccounts(token: AccessToken, options?: {
+ handle?: string
+ cursor?: string
+ limit?: number
+ }): Promise {
+ const params: Record = {}
+ if (options?.handle) params.handle = options.handle
+ if (options?.cursor) params.cursor = options.cursor
+ if (options?.limit) params.limit = String(options.limit)
+ return xrpc('com.atproto.admin.searchAccounts', { token, params })
},
- getInviteCodes(token: string, options?: {
- sort?: "recent" | "usage";
- cursor?: string;
- limit?: number;
- }): Promise<{
- cursor?: string;
- codes: Array<{
- code: string;
- available: number;
- disabled: boolean;
- forAccount: string;
- createdBy: string;
- createdAt: string;
- uses: Array<{ usedBy: string; usedAt: string }>;
- }>;
- }> {
- const params: Record = {};
- if (options?.sort) params.sort = options.sort;
- if (options?.cursor) params.cursor = options.cursor;
- if (options?.limit) params.limit = String(options.limit);
- return xrpc("com.atproto.admin.getInviteCodes", { token, params });
+ getInviteCodes(token: AccessToken, options?: {
+ sort?: 'recent' | 'usage'
+ cursor?: string
+ limit?: number
+ }): Promise {
+ const params: Record = {}
+ if (options?.sort) params.sort = options.sort
+ if (options?.cursor) params.cursor = options.cursor
+ if (options?.limit) params.limit = String(options.limit)
+ return xrpc('com.atproto.admin.getInviteCodes', { token, params })
},
async disableInviteCodes(
- token: string,
+ token: AccessToken,
codes?: string[],
accounts?: string[],
): Promise {
- await xrpc("com.atproto.admin.disableInviteCodes", {
- method: "POST",
+ await xrpc('com.atproto.admin.disableInviteCodes', {
+ method: 'POST',
token,
body: { codes, accounts },
- });
+ })
},
- getAccountInfo(token: string, did: string): Promise<{
- did: string;
- handle: string;
- email?: string;
- indexedAt: string;
- emailConfirmedAt?: string;
- invitesDisabled?: boolean;
- deactivatedAt?: string;
- }> {
- return xrpc("com.atproto.admin.getAccountInfo", { token, params: { did } });
+ getAccountInfo(token: AccessToken, did: Did): Promise {
+ return xrpc('com.atproto.admin.getAccountInfo', { token, params: { did } })
},
- async disableAccountInvites(token: string, account: string): Promise {
- await xrpc("com.atproto.admin.disableAccountInvites", {
- method: "POST",
+ async disableAccountInvites(token: AccessToken, account: Did): Promise {
+ await xrpc('com.atproto.admin.disableAccountInvites', {
+ method: 'POST',
token,
body: { account },
- });
+ })
},
- async enableAccountInvites(token: string, account: string): Promise {
- await xrpc("com.atproto.admin.enableAccountInvites", {
- method: "POST",
+ async enableAccountInvites(token: AccessToken, account: Did): Promise {
+ await xrpc('com.atproto.admin.enableAccountInvites', {
+ method: 'POST',
token,
body: { account },
- });
+ })
},
- async adminDeleteAccount(token: string, did: string): Promise {
- await xrpc("com.atproto.admin.deleteAccount", {
- method: "POST",
+ async adminDeleteAccount(token: AccessToken, did: Did): Promise {
+ await xrpc('com.atproto.admin.deleteAccount', {
+ method: 'POST',
token,
body: { did },
- });
+ })
},
- describeRepo(token: string, repo: string): Promise<{
- handle: string;
- did: string;
- didDoc: unknown;
- collections: string[];
- handleIsCorrect: boolean;
- }> {
- return xrpc("com.atproto.repo.describeRepo", {
+ describeRepo(token: AccessToken, repo: Did): Promise {
+ return xrpc('com.atproto.repo.describeRepo', {
token,
params: { repo },
- });
+ })
},
- listRecords(token: string, repo: string, collection: string, options?: {
- limit?: number;
- cursor?: string;
- reverse?: boolean;
- }): Promise<{
- records: Array<{ uri: string; cid: string; value: unknown }>;
- cursor?: string;
- }> {
- const params: Record = { repo, collection };
- if (options?.limit) params.limit = String(options.limit);
- if (options?.cursor) params.cursor = options.cursor;
- if (options?.reverse) params.reverse = "true";
- return xrpc("com.atproto.repo.listRecords", { token, params });
+ listRecords(token: AccessToken, repo: Did, collection: Nsid, options?: {
+ limit?: number
+ cursor?: string
+ reverse?: boolean
+ }): Promise {
+ const params: Record = { repo, collection }
+ if (options?.limit) params.limit = String(options.limit)
+ if (options?.cursor) params.cursor = options.cursor
+ if (options?.reverse) params.reverse = 'true'
+ return xrpc('com.atproto.repo.listRecords', { token, params })
},
getRecord(
- token: string,
- repo: string,
- collection: string,
- rkey: string,
- ): Promise<{
- uri: string;
- cid: string;
- value: unknown;
- }> {
- return xrpc("com.atproto.repo.getRecord", {
+ token: AccessToken,
+ repo: Did,
+ collection: Nsid,
+ rkey: Rkey,
+ ): Promise {
+ return xrpc('com.atproto.repo.getRecord', {
token,
params: { repo, collection, rkey },
- });
+ })
},
createRecord(
- token: string,
- repo: string,
- collection: string,
+ token: AccessToken,
+ repo: Did,
+ collection: Nsid,
record: unknown,
- rkey?: string,
- ): Promise<{
- uri: string;
- cid: string;
- }> {
- return xrpc("com.atproto.repo.createRecord", {
- method: "POST",
+ rkey?: Rkey,
+ ): Promise {
+ return xrpc('com.atproto.repo.createRecord', {
+ method: 'POST',
token,
body: { repo, collection, record, rkey },
- });
+ })
},
putRecord(
- token: string,
- repo: string,
- collection: string,
- rkey: string,
+ token: AccessToken,
+ repo: Did,
+ collection: Nsid,
+ rkey: Rkey,
record: unknown,
- ): Promise<{
- uri: string;
- cid: string;
- }> {
- return xrpc("com.atproto.repo.putRecord", {
- method: "POST",
+ ): Promise {
+ return xrpc('com.atproto.repo.putRecord', {
+ method: 'POST',
token,
body: { repo, collection, rkey, record },
- });
+ })
},
async deleteRecord(
- token: string,
- repo: string,
- collection: string,
- rkey: string,
+ token: AccessToken,
+ repo: Did,
+ collection: Nsid,
+ rkey: Rkey,
): Promise {
- await xrpc("com.atproto.repo.deleteRecord", {
- method: "POST",
+ await xrpc('com.atproto.repo.deleteRecord', {
+ method: 'POST',
token,
body: { repo, collection, rkey },
- });
+ })
},
- getTotpStatus(
- token: string,
- ): Promise<{ enabled: boolean; hasBackupCodes: boolean }> {
- return xrpc("com.atproto.server.getTotpStatus", { token });
+ getTotpStatus(token: AccessToken): Promise {
+ return xrpc('com.atproto.server.getTotpStatus', { token })
},
- createTotpSecret(
- token: string,
- ): Promise<{ uri: string; qrBase64: string }> {
- return xrpc("com.atproto.server.createTotpSecret", {
- method: "POST",
+ createTotpSecret(token: AccessToken): Promise {
+ return xrpc('com.atproto.server.createTotpSecret', {
+ method: 'POST',
token,
- });
+ })
},
- enableTotp(
- token: string,
- code: string,
- ): Promise<{ success: boolean; backupCodes: string[] }> {
- return xrpc("com.atproto.server.enableTotp", {
- method: "POST",
+ enableTotp(token: AccessToken, code: string): Promise {
+ return xrpc('com.atproto.server.enableTotp', {
+ method: 'POST',
token,
body: { code },
- });
+ })
},
disableTotp(
- token: string,
+ token: AccessToken,
password: string,
code: string,
- ): Promise<{ success: boolean }> {
- return xrpc("com.atproto.server.disableTotp", {
- method: "POST",
+ ): Promise {
+ return xrpc('com.atproto.server.disableTotp', {
+ method: 'POST',
token,
body: { password, code },
- });
+ })
},
regenerateBackupCodes(
- token: string,
+ token: AccessToken,
password: string,
code: string,
- ): Promise<{ backupCodes: string[] }> {
- return xrpc("com.atproto.server.regenerateBackupCodes", {
- method: "POST",
+ ): Promise {
+ return xrpc('com.atproto.server.regenerateBackupCodes', {
+ method: 'POST',
token,
body: { password, code },
- });
+ })
},
startPasskeyRegistration(
- token: string,
+ token: AccessToken,
friendlyName?: string,
- ): Promise<{ options: unknown }> {
- return xrpc("com.atproto.server.startPasskeyRegistration", {
- method: "POST",
+ ): Promise {
+ return xrpc('com.atproto.server.startPasskeyRegistration', {
+ method: 'POST',
token,
body: { friendlyName },
- });
+ })
},
finishPasskeyRegistration(
- token: string,
+ token: AccessToken,
credential: unknown,
friendlyName?: string,
- ): Promise<{ id: string; credentialId: string }> {
- return xrpc("com.atproto.server.finishPasskeyRegistration", {
- method: "POST",
+ ): Promise {
+ return xrpc('com.atproto.server.finishPasskeyRegistration', {
+ method: 'POST',
token,
body: { credential, friendlyName },
- });
+ })
},
- listPasskeys(token: string): Promise<{
- passkeys: Array<{
- id: string;
- credentialId: string;
- friendlyName: string | null;
- createdAt: string;
- lastUsed: string | null;
- }>;
- }> {
- return xrpc("com.atproto.server.listPasskeys", { token });
+ listPasskeys(token: AccessToken): Promise {
+ return xrpc('com.atproto.server.listPasskeys', { token })
},
- async deletePasskey(token: string, id: string): Promise {
- await xrpc("com.atproto.server.deletePasskey", {
- method: "POST",
+ async deletePasskey(token: AccessToken, id: string): Promise {
+ await xrpc('com.atproto.server.deletePasskey', {
+ method: 'POST',
token,
body: { id },
- });
+ })
},
async updatePasskey(
- token: string,
+ token: AccessToken,
id: string,
friendlyName: string,
): Promise {
- await xrpc("com.atproto.server.updatePasskey", {
- method: "POST",
+ await xrpc('com.atproto.server.updatePasskey', {
+ method: 'POST',
token,
body: { id, friendlyName },
- });
+ })
},
- listTrustedDevices(token: string): Promise<{
- devices: Array<{
- id: string;
- userAgent: string | null;
- friendlyName: string | null;
- trustedAt: string | null;
- trustedUntil: string | null;
- lastSeenAt: string;
- }>;
- }> {
- return xrpc("_account.listTrustedDevices", { token });
+ listTrustedDevices(token: AccessToken): Promise {
+ return xrpc('_account.listTrustedDevices', { token })
},
- revokeTrustedDevice(
- token: string,
- deviceId: string,
- ): Promise<{ success: boolean }> {
- return xrpc("_account.revokeTrustedDevice", {
- method: "POST",
+ revokeTrustedDevice(token: AccessToken, deviceId: string): Promise {
+ return xrpc('_account.revokeTrustedDevice', {
+ method: 'POST',
token,
body: { deviceId },
- });
+ })
},
updateTrustedDevice(
- token: string,
+ token: AccessToken,
deviceId: string,
friendlyName: string,
- ): Promise<{ success: boolean }> {
- return xrpc("_account.updateTrustedDevice", {
- method: "POST",
+ ): Promise {
+ return xrpc('_account.updateTrustedDevice', {
+ method: 'POST',
token,
body: { deviceId, friendlyName },
- });
+ })
},
- getReauthStatus(token: string): Promise<{
- requiresReauth: boolean;
- lastReauthAt: string | null;
- availableMethods: string[];
- }> {
- return xrpc("_account.getReauthStatus", { token });
+ getReauthStatus(token: AccessToken): Promise {
+ return xrpc('_account.getReauthStatus', { token })
},
- reauthPassword(
- token: string,
- password: string,
- ): Promise<{ success: boolean; reauthAt: string }> {
- return xrpc("_account.reauthPassword", {
- method: "POST",
+ reauthPassword(token: AccessToken, password: string): Promise {
+ return xrpc('_account.reauthPassword', {
+ method: 'POST',
token,
body: { password },
- });
+ })
},
- reauthTotp(
- token: string,
- code: string,
- ): Promise<{ success: boolean; reauthAt: string }> {
- return xrpc("_account.reauthTotp", {
- method: "POST",
+ reauthTotp(token: AccessToken, code: string): Promise {
+ return xrpc('_account.reauthTotp', {
+ method: 'POST',
token,
body: { code },
- });
+ })
},
- reauthPasskeyStart(token: string): Promise<{ options: unknown }> {
- return xrpc("_account.reauthPasskeyStart", {
- method: "POST",
+ reauthPasskeyStart(token: AccessToken): Promise {
+ return xrpc('_account.reauthPasskeyStart', {
+ method: 'POST',
token,
- });
+ })
},
- reauthPasskeyFinish(
- token: string,
- credential: unknown,
- ): Promise<{ success: boolean; reauthAt: string }> {
- return xrpc("_account.reauthPasskeyFinish", {
- method: "POST",
+ reauthPasskeyFinish(token: AccessToken, credential: unknown): Promise {
+ return xrpc('_account.reauthPasskeyFinish', {
+ method: 'POST',
token,
body: { credential },
- });
+ })
},
- reserveSigningKey(did?: string): Promise<{ signingKey: string }> {
- return xrpc("com.atproto.server.reserveSigningKey", {
- method: "POST",
+ reserveSigningKey(did?: Did): Promise {
+ return xrpc('com.atproto.server.reserveSigningKey', {
+ method: 'POST',
body: { did },
- });
+ })
},
- getRecommendedDidCredentials(token: string): Promise<{
- rotationKeys?: string[];
- alsoKnownAs?: string[];
- verificationMethods?: { atproto?: string };
- services?: { atproto_pds?: { type: string; endpoint: string } };
- }> {
- return xrpc("com.atproto.identity.getRecommendedDidCredentials", { token });
+ getRecommendedDidCredentials(token: AccessToken): Promise {
+ return xrpc('com.atproto.identity.getRecommendedDidCredentials', { token })
},
- async activateAccount(token: string): Promise {
- await xrpc("com.atproto.server.activateAccount", {
- method: "POST",
+ async activateAccount(token: AccessToken): Promise {
+ await xrpc('com.atproto.server.activateAccount', {
+ method: 'POST',
token,
- });
+ })
},
async createPasskeyAccount(params: {
- handle: string;
- email?: string;
- inviteCode?: string;
- didType?: DidType;
- did?: string;
- signingKey?: string;
- verificationChannel?: VerificationChannel;
- discordId?: string;
- telegramUsername?: string;
- signalNumber?: string;
- }, byodToken?: string): Promise<{
- did: string;
- handle: string;
- setupToken: string;
- setupExpiresAt: string;
- }> {
- const url = `${API_BASE}/_account.createPasskeyAccount`;
+ handle: Handle
+ email?: EmailAddress
+ inviteCode?: string
+ didType?: DidType
+ did?: Did
+ signingKey?: string
+ verificationChannel?: VerificationChannel
+ discordId?: string
+ telegramUsername?: string
+ signalNumber?: string
+ }, byodToken?: string): Promise {
+ const url = `${API_BASE}/_account.createPasskeyAccount`
const headers: Record = {
- "Content-Type": "application/json",
- };
+ 'Content-Type': 'application/json',
+ }
if (byodToken) {
- headers["Authorization"] = `Bearer ${byodToken}`;
+ headers['Authorization'] = `Bearer ${byodToken}`
}
const res = await fetch(url, {
- method: "POST",
+ method: 'POST',
headers,
body: JSON.stringify(params),
- });
+ })
if (!res.ok) {
- const err = await res.json().catch(() => ({
- error: "Unknown",
+ const errData = await res.json().catch(() => ({
+ error: 'Unknown',
message: res.statusText,
- }));
- throw new ApiError(res.status, err.error, err.message);
+ }))
+ throw new ApiError(res.status, errData.error, errData.message)
}
- return res.json();
+ return res.json()
},
startPasskeyRegistrationForSetup(
- did: string,
+ did: Did,
setupToken: string,
friendlyName?: string,
- ): Promise<{ options: unknown }> {
- return xrpc("_account.startPasskeyRegistrationForSetup", {
- method: "POST",
+ ): Promise {
+ return xrpc('_account.startPasskeyRegistrationForSetup', {
+ method: 'POST',
body: { did, setupToken, friendlyName },
- });
+ })
},
completePasskeySetup(
- did: string,
+ did: Did,
setupToken: string,
passkeyCredential: unknown,
passkeyFriendlyName?: string,
- ): Promise<{
- did: string;
- handle: string;
- appPassword: string;
- appPasswordName: string;
- }> {
- return xrpc("_account.completePasskeySetup", {
- method: "POST",
+ ): Promise {
+ return xrpc('_account.completePasskeySetup', {
+ method: 'POST',
body: { did, setupToken, passkeyCredential, passkeyFriendlyName },
- });
+ })
},
- requestPasskeyRecovery(email: string): Promise<{ success: boolean }> {
- return xrpc("_account.requestPasskeyRecovery", {
- method: "POST",
+ requestPasskeyRecovery(email: EmailAddress): Promise {
+ return xrpc('_account.requestPasskeyRecovery', {
+ method: 'POST',
body: { email },
- });
+ })
},
recoverPasskeyAccount(
- did: string,
+ did: Did,
recoveryToken: string,
newPassword: string,
- ): Promise<{ success: boolean }> {
- return xrpc("_account.recoverPasskeyAccount", {
- method: "POST",
+ ): Promise {
+ return xrpc('_account.recoverPasskeyAccount', {
+ method: 'POST',
body: { did, recoveryToken, newPassword },
- });
+ })
},
- verifyMigrationEmail(
- token: string,
- email: string,
- ): Promise<{ success: boolean; did: string }> {
- return xrpc("com.atproto.server.verifyMigrationEmail", {
- method: "POST",
+ verifyMigrationEmail(token: string, email: EmailAddress): Promise {
+ return xrpc('com.atproto.server.verifyMigrationEmail', {
+ method: 'POST',
body: { token, email },
- });
+ })
},
- resendMigrationVerification(email: string): Promise<{ sent: boolean }> {
- return xrpc("com.atproto.server.resendMigrationVerification", {
- method: "POST",
+ resendMigrationVerification(email: EmailAddress): Promise {
+ return xrpc('com.atproto.server.resendMigrationVerification', {
+ method: 'POST',
body: { email },
- });
+ })
},
verifyToken(
token: string,
identifier: string,
- accessToken?: string,
- ): Promise<{
- success: boolean;
- did: string;
- purpose: string;
- channel: string;
- }> {
- return xrpc("_account.verifyToken", {
- method: "POST",
+ accessToken?: AccessToken,
+ ): Promise {
+ return xrpc('_account.verifyToken', {
+ method: 'POST',
body: { token, identifier },
token: accessToken,
- });
+ })
},
- getDidDocument(token: string): Promise {
- return xrpc("_account.getDidDocument", { token });
+ getDidDocument(token: AccessToken): Promise {
+ return xrpc('_account.getDidDocument', { token })
},
updateDidDocument(
- token: string,
+ token: AccessToken,
params: {
- verificationMethods?: VerificationMethod[];
- alsoKnownAs?: string[];
- serviceEndpoint?: string;
+ verificationMethods?: VerificationMethod[]
+ alsoKnownAs?: string[]
+ serviceEndpoint?: string
},
- ): Promise<{ success: boolean }> {
- return xrpc("_account.updateDidDocument", {
- method: "POST",
+ ): Promise {
+ return xrpc('_account.updateDidDocument', {
+ method: 'POST',
token,
body: params,
- });
+ })
},
- async deactivateAccount(
- token: string,
- deleteAfter?: string,
- ): Promise {
- await xrpc("com.atproto.server.deactivateAccount", {
- method: "POST",
+ async deactivateAccount(token: AccessToken, deleteAfter?: string): Promise {
+ await xrpc('com.atproto.server.deactivateAccount', {
+ method: 'POST',
token,
body: { deleteAfter },
- });
+ })
},
- async getRepo(token: string, did: string): Promise {
- const url = `${API_BASE}/com.atproto.sync.getRepo?did=${
- encodeURIComponent(did)
- }`;
+ async getRepo(token: AccessToken, did: Did): Promise {
+ const url = `${API_BASE}/com.atproto.sync.getRepo?did=${encodeURIComponent(did)}`
const res = await fetch(url, {
headers: { Authorization: `Bearer ${token}` },
- });
+ })
if (!res.ok) {
- const err = await res.json().catch(() => ({
- error: "Unknown",
+ const errData = await res.json().catch(() => ({
+ error: 'Unknown',
message: res.statusText,
- }));
- throw new ApiError(res.status, err.error, err.message);
+ }))
+ throw new ApiError(res.status, errData.error, errData.message)
}
- return res.arrayBuffer();
+ return res.arrayBuffer()
},
- listBackups(token: string): Promise<{
- backups: Array<{
- id: string;
- repoRev: string;
- repoRootCid: string;
- blockCount: number;
- sizeBytes: number;
- createdAt: string;
- }>;
- backupEnabled: boolean;
- }> {
- return xrpc("_backup.listBackups", { token });
+ listBackups(token: AccessToken): Promise {
+ return xrpc('_backup.listBackups', { token })
},
- async getBackup(token: string, id: string): Promise {
- const url = `${API_BASE}/_backup.getBackup?id=${encodeURIComponent(id)}`;
+ async getBackup(token: AccessToken, id: string): Promise {
+ const url = `${API_BASE}/_backup.getBackup?id=${encodeURIComponent(id)}`
const res = await fetch(url, {
headers: { Authorization: `Bearer ${token}` },
- });
+ })
if (!res.ok) {
- const err = await res.json().catch(() => ({
- error: "Unknown",
+ const errData = await res.json().catch(() => ({
+ error: 'Unknown',
message: res.statusText,
- }));
- throw new ApiError(res.status, err.error, err.message);
+ }))
+ throw new ApiError(res.status, errData.error, errData.message)
}
- return res.blob();
+ return res.blob()
},
- createBackup(token: string): Promise<{
- id: string;
- repoRev: string;
- sizeBytes: number;
- blockCount: number;
- }> {
- return xrpc("_backup.createBackup", {
- method: "POST",
+ createBackup(token: AccessToken): Promise {
+ return xrpc('_backup.createBackup', {
+ method: 'POST',
token,
- });
+ })
},
- async deleteBackup(token: string, id: string): Promise {
- await xrpc("_backup.deleteBackup", {
- method: "POST",
+ async deleteBackup(token: AccessToken, id: string): Promise {
+ await xrpc('_backup.deleteBackup', {
+ method: 'POST',
token,
params: { id },
- });
+ })
},
- setBackupEnabled(
- token: string,
- enabled: boolean,
- ): Promise<{ enabled: boolean }> {
- return xrpc("_backup.setEnabled", {
- method: "POST",
+ setBackupEnabled(token: AccessToken, enabled: boolean): Promise {
+ return xrpc('_backup.setEnabled', {
+ method: 'POST',
token,
body: { enabled },
- });
+ })
},
- async importRepo(token: string, car: Uint8Array): Promise {
- const url = `${API_BASE}/com.atproto.repo.importRepo`;
+ async importRepo(token: AccessToken, car: Uint8Array): Promise {
+ const url = `${API_BASE}/com.atproto.repo.importRepo`
const res = await fetch(url, {
- method: "POST",
+ method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
- "Content-Type": "application/vnd.ipld.car",
+ 'Content-Type': 'application/vnd.ipld.car',
},
body: car,
- });
+ })
if (!res.ok) {
- const err = await res.json().catch(() => ({
- error: "Unknown",
+ const errData = await res.json().catch(() => ({
+ error: 'Unknown',
message: res.statusText,
- }));
- throw new ApiError(res.status, err.error, err.message);
+ }))
+ throw new ApiError(res.status, errData.error, errData.message)
}
},
-};
+}
+
+export const typedApi = {
+ createSession(
+ identifier: string,
+ password: string
+ ): Promise> {
+ return xrpcResult('com.atproto.server.createSession', {
+ method: 'POST',
+ body: { identifier, password },
+ }).then(r => r.ok ? ok(castSession(r.value)) : r)
+ },
+
+ getSession(token: AccessToken): Promise> {
+ return xrpcResult('com.atproto.server.getSession', { token })
+ .then(r => r.ok ? ok(castSession(r.value)) : r)
+ },
+
+ refreshSession(refreshJwt: RefreshToken): Promise> {
+ return xrpcResult('com.atproto.server.refreshSession', {
+ method: 'POST',
+ token: refreshJwt,
+ }).then(r => r.ok ? ok(castSession(r.value)) : r)
+ },
+
+ describeServer(): Promise> {
+ return xrpcResult('com.atproto.server.describeServer')
+ },
+
+ listAppPasswords(token: AccessToken): Promise> {
+ return xrpcResult('com.atproto.server.listAppPasswords', { token })
+ },
+
+ createAppPassword(
+ token: AccessToken,
+ name: string,
+ scopes?: string
+ ): Promise> {
+ return xrpcResult('com.atproto.server.createAppPassword', {
+ method: 'POST',
+ token,
+ body: { name, scopes },
+ })
+ },
+
+ revokeAppPassword(token: AccessToken, name: string): Promise> {
+ return xrpcResult('com.atproto.server.revokeAppPassword', {
+ method: 'POST',
+ token,
+ body: { name },
+ })
+ },
+
+ listSessions(token: AccessToken): Promise> {
+ return xrpcResult('_account.listSessions', { token })
+ },
+
+ revokeSession(token: AccessToken, sessionId: string): Promise> {
+ return xrpcResult('_account.revokeSession', {
+ method: 'POST',
+ token,
+ body: { sessionId },
+ })
+ },
+
+ getTotpStatus(token: AccessToken): Promise> {
+ return xrpcResult('com.atproto.server.getTotpStatus', { token })
+ },
+
+ createTotpSecret(token: AccessToken): Promise> {
+ return xrpcResult('com.atproto.server.createTotpSecret', {
+ method: 'POST',
+ token,
+ })
+ },
+
+ enableTotp(token: AccessToken, code: string): Promise> {
+ return xrpcResult('com.atproto.server.enableTotp', {
+ method: 'POST',
+ token,
+ body: { code },
+ })
+ },
+
+ disableTotp(
+ token: AccessToken,
+ password: string,
+ code: string
+ ): Promise> {
+ return xrpcResult('com.atproto.server.disableTotp', {
+ method: 'POST',
+ token,
+ body: { password, code },
+ })
+ },
+
+ listPasskeys(token: AccessToken): Promise> {
+ return xrpcResult('com.atproto.server.listPasskeys', { token })
+ },
+
+ deletePasskey(token: AccessToken, id: string): Promise> {
+ return xrpcResult('com.atproto.server.deletePasskey', {
+ method: 'POST',
+ token,
+ body: { id },
+ })
+ },
+
+ listTrustedDevices(token: AccessToken): Promise> {
+ return xrpcResult('_account.listTrustedDevices', { token })
+ },
+
+ getReauthStatus(token: AccessToken): Promise> {
+ return xrpcResult('_account.getReauthStatus', { token })
+ },
+
+ getNotificationPrefs(token: AccessToken): Promise> {
+ return xrpcResult('_account.getNotificationPrefs', { token })
+ },
+
+ updateHandle(token: AccessToken, handle: Handle): Promise> {
+ return xrpcResult('com.atproto.identity.updateHandle', {
+ method: 'POST',
+ token,
+ body: { handle },
+ })
+ },
+
+ describeRepo(token: AccessToken, repo: Did): Promise> {
+ return xrpcResult('com.atproto.repo.describeRepo', {
+ token,
+ params: { repo },
+ })
+ },
+
+ listRecords(
+ token: AccessToken,
+ repo: Did,
+ collection: Nsid,
+ options?: { limit?: number; cursor?: string; reverse?: boolean }
+ ): Promise> {
+ const params: Record = { repo, collection }
+ if (options?.limit) params.limit = String(options.limit)
+ if (options?.cursor) params.cursor = options.cursor
+ if (options?.reverse) params.reverse = 'true'
+ return xrpcResult('com.atproto.repo.listRecords', { token, params })
+ },
+
+ getRecord(
+ token: AccessToken,
+ repo: Did,
+ collection: Nsid,
+ rkey: Rkey
+ ): Promise> {
+ return xrpcResult('com.atproto.repo.getRecord', {
+ token,
+ params: { repo, collection, rkey },
+ })
+ },
+
+ deleteRecord(
+ token: AccessToken,
+ repo: Did,
+ collection: Nsid,
+ rkey: Rkey
+ ): Promise> {
+ return xrpcResult('com.atproto.repo.deleteRecord', {
+ method: 'POST',
+ token,
+ body: { repo, collection, rkey },
+ })
+ },
+
+ searchAccounts(
+ token: AccessToken,
+ options?: { handle?: string; cursor?: string; limit?: number }
+ ): Promise> {
+ const params: Record = {}
+ if (options?.handle) params.handle = options.handle
+ if (options?.cursor) params.cursor = options.cursor
+ if (options?.limit) params.limit = String(options.limit)
+ return xrpcResult('com.atproto.admin.searchAccounts', { token, params })
+ },
+
+ getAccountInfo(token: AccessToken, did: Did): Promise> {
+ return xrpcResult('com.atproto.admin.getAccountInfo', { token, params: { did } })
+ },
+
+ getServerStats(token: AccessToken): Promise> {
+ return xrpcResult('_admin.getServerStats', { token })
+ },
+
+ listBackups(token: AccessToken): Promise> {
+ return xrpcResult('_backup.listBackups', { token })
+ },
+
+ createBackup(token: AccessToken): Promise> {
+ return xrpcResult('_backup.createBackup', {
+ method: 'POST',
+ token,
+ })
+ },
+
+ getDidDocument(token: AccessToken): Promise> {
+ return xrpcResult('_account.getDidDocument', { token })
+ },
+
+ deleteSession(token: AccessToken): Promise> {
+ return xrpcResult('com.atproto.server.deleteSession', {
+ method: 'POST',
+ token,
+ })
+ },
+
+ revokeAllSessions(token: AccessToken): Promise> {
+ return xrpcResult('_account.revokeAllSessions', {
+ method: 'POST',
+ token,
+ })
+ },
+
+ getAccountInviteCodes(token: AccessToken): Promise> {
+ return xrpcResult('com.atproto.server.getAccountInviteCodes', { token })
+ },
+
+ createInviteCode(token: AccessToken, useCount: number = 1): Promise> {
+ return xrpcResult('com.atproto.server.createInviteCode', {
+ method: 'POST',
+ token,
+ body: { useCount },
+ })
+ },
+
+ changePassword(
+ token: AccessToken,
+ currentPassword: string,
+ newPassword: string
+ ): Promise> {
+ return xrpcResult('_account.changePassword', {
+ method: 'POST',
+ token,
+ body: { currentPassword, newPassword },
+ })
+ },
+
+ getPasswordStatus(token: AccessToken): Promise> {
+ return xrpcResult('_account.getPasswordStatus', { token })
+ },
+
+ getServerConfig(): Promise> {
+ return xrpcResult('_server.getConfig')
+ },
+
+ getLegacyLoginPreference(token: AccessToken): Promise> {
+ return xrpcResult('_account.getLegacyLoginPreference', { token })
+ },
+
+ updateLegacyLoginPreference(
+ token: AccessToken,
+ allowLegacyLogin: boolean
+ ): Promise> {
+ return xrpcResult('_account.updateLegacyLoginPreference', {
+ method: 'POST',
+ token,
+ body: { allowLegacyLogin },
+ })
+ },
+
+ getNotificationHistory(token: AccessToken): Promise> {
+ return xrpcResult('_account.getNotificationHistory', { token })
+ },
+
+ updateNotificationPrefs(
+ token: AccessToken,
+ prefs: {
+ preferredChannel?: string
+ discordId?: string
+ telegramUsername?: string
+ signalNumber?: string
+ }
+ ): Promise> {
+ return xrpcResult('_account.updateNotificationPrefs', {
+ method: 'POST',
+ token,
+ body: prefs,
+ })
+ },
+
+ revokeTrustedDevice(token: AccessToken, deviceId: string): Promise> {
+ return xrpcResult('_account.revokeTrustedDevice', {
+ method: 'POST',
+ token,
+ body: { deviceId },
+ })
+ },
+
+ updateTrustedDevice(
+ token: AccessToken,
+ deviceId: string,
+ friendlyName: string
+ ): Promise> {
+ return xrpcResult('_account.updateTrustedDevice', {
+ method: 'POST',
+ token,
+ body: { deviceId, friendlyName },
+ })
+ },
+
+ reauthPassword(token: AccessToken, password: string): Promise> {
+ return xrpcResult('_account.reauthPassword', {
+ method: 'POST',
+ token,
+ body: { password },
+ })
+ },
+
+ reauthTotp(token: AccessToken, code: string): Promise> {
+ return xrpcResult('_account.reauthTotp', {
+ method: 'POST',
+ token,
+ body: { code },
+ })
+ },
+
+ reauthPasskeyStart(token: AccessToken): Promise> {
+ return xrpcResult('_account.reauthPasskeyStart', {
+ method: 'POST',
+ token,
+ })
+ },
+
+ reauthPasskeyFinish(token: AccessToken, credential: unknown): Promise> {
+ return xrpcResult('_account.reauthPasskeyFinish', {
+ method: 'POST',
+ token,
+ body: { credential },
+ })
+ },
+
+ confirmSignup(did: Did, verificationCode: string): Promise> {
+ return xrpcResult('com.atproto.server.confirmSignup', {
+ method: 'POST',
+ body: { did, verificationCode },
+ })
+ },
+
+ resendVerification(did: Did): Promise> {
+ return xrpcResult('com.atproto.server.resendVerification', {
+ method: 'POST',
+ body: { did },
+ })
+ },
+
+ requestEmailUpdate(token: AccessToken): Promise> {
+ return xrpcResult('com.atproto.server.requestEmailUpdate', {
+ method: 'POST',
+ token,
+ })
+ },
+
+ updateEmail(token: AccessToken, email: string, emailToken?: string): Promise> {
+ return xrpcResult('com.atproto.server.updateEmail', {
+ method: 'POST',
+ token,
+ body: { email, token: emailToken },
+ })
+ },
+
+ requestAccountDelete(token: AccessToken): Promise> {
+ return xrpcResult('com.atproto.server.requestAccountDelete', {
+ method: 'POST',
+ token,
+ })
+ },
+
+ deleteAccount(did: Did, password: string, deleteToken: string): Promise> {
+ return xrpcResult('com.atproto.server.deleteAccount', {
+ method: 'POST',
+ body: { did, password, token: deleteToken },
+ })
+ },
+
+ updateDidDocument(
+ token: AccessToken,
+ params: {
+ verificationMethods?: VerificationMethod[]
+ alsoKnownAs?: string[]
+ serviceEndpoint?: string
+ }
+ ): Promise> {
+ return xrpcResult('_account.updateDidDocument', {
+ method: 'POST',
+ token,
+ body: params,
+ })
+ },
+
+ deactivateAccount(token: AccessToken, deleteAfter?: string): Promise> {
+ return xrpcResult('com.atproto.server.deactivateAccount', {
+ method: 'POST',
+ token,
+ body: { deleteAfter },
+ })
+ },
+
+ activateAccount(token: AccessToken): Promise> {
+ return xrpcResult('com.atproto.server.activateAccount', {
+ method: 'POST',
+ token,
+ })
+ },
+
+ setBackupEnabled(token: AccessToken, enabled: boolean): Promise> {
+ return xrpcResult('_backup.setEnabled', {
+ method: 'POST',
+ token,
+ body: { enabled },
+ })
+ },
+
+ deleteBackup(token: AccessToken, id: string): Promise> {
+ return xrpcResult('_backup.deleteBackup', {
+ method: 'POST',
+ token,
+ params: { id },
+ })
+ },
+
+ createRecord(
+ token: AccessToken,
+ repo: Did,
+ collection: Nsid,
+ record: unknown,
+ rkey?: Rkey
+ ): Promise> {
+ return xrpcResult('com.atproto.repo.createRecord', {
+ method: 'POST',
+ token,
+ body: { repo, collection, record, rkey },
+ })
+ },
+
+ putRecord(
+ token: AccessToken,
+ repo: Did,
+ collection: Nsid,
+ rkey: Rkey,
+ record: unknown
+ ): Promise> {
+ return xrpcResult('com.atproto.repo.putRecord', {
+ method: 'POST',
+ token,
+ body: { repo, collection, rkey, record },
+ })
+ },
+
+ getInviteCodes(
+ token: AccessToken,
+ options?: { sort?: 'recent' | 'usage'; cursor?: string; limit?: number }
+ ): Promise> {
+ const params: Record = {}
+ if (options?.sort) params.sort = options.sort
+ if (options?.cursor) params.cursor = options.cursor
+ if (options?.limit) params.limit = String(options.limit)
+ return xrpcResult('com.atproto.admin.getInviteCodes', { token, params })
+ },
+
+ disableAccountInvites(token: AccessToken, account: Did): Promise> {
+ return xrpcResult('com.atproto.admin.disableAccountInvites', {
+ method: 'POST',
+ token,
+ body: { account },
+ })
+ },
+
+ enableAccountInvites(token: AccessToken, account: Did): Promise> {
+ return xrpcResult('com.atproto.admin.enableAccountInvites', {
+ method: 'POST',
+ token,
+ body: { account },
+ })
+ },
+
+ adminDeleteAccount(token: AccessToken, did: Did): Promise> {
+ return xrpcResult