handy online tools for AT Protocol boat.kelinci.net
atproto bluesky atcute typescript solidjs

Compare changes

Choose any two refs to compare.

+3
.gitignore
··· 1 1 node_modules/ 2 2 dist/ 3 3 4 + /.wrangler/ 5 + /.research/ 6 + 4 7 *.local 5 8 *.local.ts 6 9
-2
.npmrc
··· 1 1 auto-install-peers=false 2 2 public-hoist-pattern[]=workbox-window 3 - 4 - @jsr:registry=https://npm.jsr.io
+1 -1
.vscode/settings.json
··· 1 1 { 2 - "editor.defaultFormatter": "esbenp.prettier-vscode", 2 + "editor.defaultFormatter": "prettier.prettier-vscode", 3 3 "typescript.tsdk": "node_modules/typescript/lib", 4 4 "tailwindCSS.experimental.classRegex": ["tw`([^`]*)"] 5 5 }
+14
LICENSE
··· 1 + BSD Zero Clause License 2 + 3 + Copyright (c) 2025 Mary 4 + 5 + Permission to use, copy, modify, and/or distribute this software for any 6 + purpose with or without fee is hereby granted. 7 + 8 + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 + AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 13 + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 + PERFORMANCE OF THIS SOFTWARE.
+32 -29
package.json
··· 4 4 "dev": "vite", 5 5 "build": "tsc -b && vite build", 6 6 "preview": "vite preview", 7 - "fmt": "prettier --cache --write ." 7 + "fmt": "PRETTIER_EXPERIMENTAL_CLI=1 prettier --cache --write ." 8 8 }, 9 9 "dependencies": { 10 - "@atcute/bluesky": "^2.1.1", 11 - "@atcute/car": "^3.0.4", 12 - "@atcute/cbor": "^2.2.3", 13 - "@atcute/cid": "^2.2.2", 14 - "@atcute/client": "^3.1.0", 15 - "@atcute/crypto": "^2.2.1", 16 - "@atcute/did-plc": "^0.1.4", 17 - "@atcute/identity": "^0.1.3", 18 - "@atcute/identity-resolver": "^0.1.2", 19 - "@atcute/multibase": "^1.1.3", 20 - "@atcute/tid": "^1.0.2", 21 - "@badrap/valita": "^0.4.4", 22 - "@mary/array-fns": "npm:@jsr/mary__array-fns@^0.1.4", 23 - "@mary/ds-queue": "npm:@jsr/mary__ds-queue@^0.1.2", 24 - "@mary/events": "npm:@jsr/mary__events@^0.2.0", 10 + "@atcute/atproto": "^3.1.9", 11 + "@atcute/bluesky": "^3.2.13", 12 + "@atcute/car": "^5.0.0", 13 + "@atcute/cbor": "^2.2.8", 14 + "@atcute/cid": "^2.2.6", 15 + "@atcute/client": "^4.1.1", 16 + "@atcute/crypto": "^2.3.0", 17 + "@atcute/did-plc": "^0.2.0", 18 + "@atcute/identity": "^1.1.3", 19 + "@atcute/identity-resolver": "^1.2.0", 20 + "@atcute/lexicons": "^1.2.5", 21 + "@atcute/multibase": "^1.1.6", 22 + "@atcute/repo": "^0.1.0", 23 + "@atcute/tid": "^1.0.3", 24 + "@badrap/valita": "^0.4.6", 25 + "@mary/array-fns": "jsr:^0.1.5", 26 + "@mary/ds-queue": "jsr:^0.1.3", 27 + "@mary/events": "jsr:^0.2.0", 25 28 "@mary/solid-freeze": "npm:@externdefs/solid-freeze@^0.1.1", 26 - "@mary/tar": "npm:@jsr/mary__tar@^0.2.4", 27 - "nanoid": "^5.1.5", 29 + "@mary/tar": "jsr:^0.3.1", 30 + "nanoid": "^5.1.6", 28 31 "native-file-system-adapter": "^3.0.1", 29 - "solid-js": "^1.9.6" 32 + "solid-js": "^1.9.10" 30 33 }, 31 34 "devDependencies": { 32 35 "@tailwindcss/forms": "^0.5.10", 33 - "@types/node": "^22.15.12", 34 - "autoprefixer": "^10.4.21", 35 - "prettier": "^3.5.3", 36 - "prettier-plugin-tailwindcss": "^0.6.11", 37 - "tailwindcss": "^3.4.17", 38 - "terser": "^5.39.0", 39 - "typescript": "~5.8.3", 40 - "vite": "^6.3.5", 41 - "vite-plugin-solid": "^2.11.6", 42 - "wrangler": "^4.14.1" 36 + "@types/node": "^22.19.2", 37 + "autoprefixer": "^10.4.22", 38 + "prettier": "^3.7.4", 39 + "prettier-plugin-tailwindcss": "^0.6.14", 40 + "tailwindcss": "^3.4.18", 41 + "terser": "^5.44.1", 42 + "typescript": "~5.9.3", 43 + "vite": "^7.2.7", 44 + "vite-plugin-solid": "^2.11.10", 45 + "wrangler": "^4.53.0" 43 46 } 44 47 }
+987 -1150
pnpm-lock.yaml
··· 8 8 9 9 .: 10 10 dependencies: 11 + '@atcute/atproto': 12 + specifier: ^3.1.9 13 + version: 3.1.9 11 14 '@atcute/bluesky': 12 - specifier: ^2.1.1 13 - version: 2.1.1(@atcute/client@3.1.0) 15 + specifier: ^3.2.13 16 + version: 3.2.13 14 17 '@atcute/car': 15 - specifier: ^3.0.4 16 - version: 3.0.4 18 + specifier: ^5.0.0 19 + version: 5.0.0 17 20 '@atcute/cbor': 18 - specifier: ^2.2.3 19 - version: 2.2.3 21 + specifier: ^2.2.8 22 + version: 2.2.8 20 23 '@atcute/cid': 21 - specifier: ^2.2.2 22 - version: 2.2.2 24 + specifier: ^2.2.6 25 + version: 2.2.6 23 26 '@atcute/client': 24 - specifier: ^3.1.0 25 - version: 3.1.0 27 + specifier: ^4.1.1 28 + version: 4.1.1 26 29 '@atcute/crypto': 27 - specifier: ^2.2.1 28 - version: 2.2.1 30 + specifier: ^2.3.0 31 + version: 2.3.0 29 32 '@atcute/did-plc': 30 - specifier: ^0.1.4 31 - version: 0.1.4 33 + specifier: ^0.2.0 34 + version: 0.2.0 32 35 '@atcute/identity': 33 - specifier: ^0.1.3 34 - version: 0.1.3 36 + specifier: ^1.1.3 37 + version: 1.1.3 35 38 '@atcute/identity-resolver': 36 - specifier: ^0.1.2 37 - version: 0.1.2(@atcute/identity@0.1.3) 39 + specifier: ^1.2.0 40 + version: 1.2.0(@atcute/identity@1.1.3) 41 + '@atcute/lexicons': 42 + specifier: ^1.2.5 43 + version: 1.2.5 38 44 '@atcute/multibase': 39 - specifier: ^1.1.3 40 - version: 1.1.3 45 + specifier: ^1.1.6 46 + version: 1.1.6 47 + '@atcute/repo': 48 + specifier: ^0.1.0 49 + version: 0.1.0 41 50 '@atcute/tid': 42 - specifier: ^1.0.2 43 - version: 1.0.2 51 + specifier: ^1.0.3 52 + version: 1.0.3 44 53 '@badrap/valita': 45 - specifier: ^0.4.4 46 - version: 0.4.4 54 + specifier: ^0.4.6 55 + version: 0.4.6 47 56 '@mary/array-fns': 48 - specifier: npm:@jsr/mary__array-fns@^0.1.4 49 - version: '@jsr/mary__array-fns@0.1.4' 57 + specifier: jsr:^0.1.5 58 + version: '@jsr/mary__array-fns@0.1.5' 50 59 '@mary/ds-queue': 51 - specifier: npm:@jsr/mary__ds-queue@^0.1.2 52 - version: '@jsr/mary__ds-queue@0.1.2' 60 + specifier: jsr:^0.1.3 61 + version: '@jsr/mary__ds-queue@0.1.3' 53 62 '@mary/events': 54 - specifier: npm:@jsr/mary__events@^0.2.0 63 + specifier: jsr:^0.2.0 55 64 version: '@jsr/mary__events@0.2.0' 56 65 '@mary/solid-freeze': 57 66 specifier: npm:@externdefs/solid-freeze@^0.1.1 58 - version: '@externdefs/solid-freeze@0.1.1(solid-js@1.9.6)' 67 + version: '@externdefs/solid-freeze@0.1.1(solid-js@1.9.10)' 59 68 '@mary/tar': 60 - specifier: npm:@jsr/mary__tar@^0.2.4 61 - version: '@jsr/mary__tar@0.2.4' 69 + specifier: jsr:^0.3.1 70 + version: '@jsr/mary__tar@0.3.1' 62 71 nanoid: 63 - specifier: ^5.1.5 64 - version: 5.1.5 72 + specifier: ^5.1.6 73 + version: 5.1.6 65 74 native-file-system-adapter: 66 75 specifier: ^3.0.1 67 76 version: 3.0.1 68 77 solid-js: 69 - specifier: ^1.9.6 70 - version: 1.9.6 78 + specifier: ^1.9.10 79 + version: 1.9.10 71 80 devDependencies: 72 81 '@tailwindcss/forms': 73 82 specifier: ^0.5.10 74 - version: 0.5.10(tailwindcss@3.4.17) 83 + version: 0.5.10(tailwindcss@3.4.18) 75 84 '@types/node': 76 - specifier: ^22.15.12 77 - version: 22.15.12 85 + specifier: ^22.19.2 86 + version: 22.19.2 78 87 autoprefixer: 79 - specifier: ^10.4.21 80 - version: 10.4.21(postcss@8.5.3) 88 + specifier: ^10.4.22 89 + version: 10.4.22(postcss@8.5.6) 81 90 prettier: 82 - specifier: ^3.5.3 83 - version: 3.5.3 91 + specifier: ^3.7.4 92 + version: 3.7.4 84 93 prettier-plugin-tailwindcss: 85 - specifier: ^0.6.11 86 - version: 0.6.11(prettier@3.5.3) 94 + specifier: ^0.6.14 95 + version: 0.6.14(prettier@3.7.4) 87 96 tailwindcss: 88 - specifier: ^3.4.17 89 - version: 3.4.17 97 + specifier: ^3.4.18 98 + version: 3.4.18 90 99 terser: 91 - specifier: ^5.39.0 92 - version: 5.39.0 100 + specifier: ^5.44.1 101 + version: 5.44.1 93 102 typescript: 94 - specifier: ~5.8.3 95 - version: 5.8.3 103 + specifier: ~5.9.3 104 + version: 5.9.3 96 105 vite: 97 - specifier: ^6.3.5 98 - version: 6.3.5(@types/node@22.15.12)(jiti@1.21.7)(terser@5.39.0)(yaml@2.7.1) 106 + specifier: ^7.2.7 107 + version: 7.2.7(@types/node@22.19.2)(jiti@1.21.7)(terser@5.44.1) 99 108 vite-plugin-solid: 100 - specifier: ^2.11.6 101 - version: 2.11.6(solid-js@1.9.6)(vite@6.3.5(@types/node@22.15.12)(jiti@1.21.7)(terser@5.39.0)(yaml@2.7.1)) 109 + specifier: ^2.11.10 110 + version: 2.11.10(solid-js@1.9.10)(vite@7.2.7(@types/node@22.19.2)(jiti@1.21.7)(terser@5.44.1)) 102 111 wrangler: 103 - specifier: ^4.14.1 104 - version: 4.14.1 112 + specifier: ^4.53.0 113 + version: 4.53.0 105 114 106 115 packages: 107 116 ··· 109 118 resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} 110 119 engines: {node: '>=10'} 111 120 112 - '@ampproject/remapping@2.3.0': 113 - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} 114 - engines: {node: '>=6.0.0'} 121 + '@atcute/atproto@3.1.9': 122 + resolution: {integrity: sha512-DyWwHCTdR4hY2BPNbLXgVmm7lI+fceOwWbE4LXbGvbvVtSn+ejSVFaAv01Ra3kWDha0whsOmbJL8JP0QPpf1+w==} 115 123 116 - '@atcute/bluesky@2.1.1': 117 - resolution: {integrity: sha512-wEZfFW58J6yC1SqHcVJOn4qbHENTTzjeCEWthRT5HvKovADLqk54HSMSAuXDMBUbintSTBr0khQNZQ3ZdgzDdQ==} 118 - peerDependencies: 119 - '@atcute/client': ^3.0.0 124 + '@atcute/bluesky@3.2.13': 125 + resolution: {integrity: sha512-ZG/mqsCjVU6zvH6XsRw+oQglrsdu5R7mnncMO+Ux0KWbX2xJw4ZMFHfs7ZTC69dVPK9r/yle7YbpygZTOWDM9A==} 120 126 121 - '@atcute/car@3.0.4': 122 - resolution: {integrity: sha512-T38qVn09cgaMCWka3tLWbuc/nPI5Sfjac/PwJIei2GnMldKSrZrAjeCvJAg8TWvsNab1kRZEy5uYqcBUJtzsWA==} 127 + '@atcute/car@5.0.0': 128 + resolution: {integrity: sha512-OIY2xTXv8lSpZsDSn/UYQtJSMvDw5Hi4Q+uyvmiqSM+fht08QRAEq/nxa5YFciPZ3nfDFnZ3//EgJw7QhkSXLQ==} 123 129 124 - '@atcute/cbor@2.2.3': 125 - resolution: {integrity: sha512-yvzaPI6ChNlqYN41qY8cVGCubgb6N/Y2kpNQnjC5waAZ4Xy2WH7ohuyiv0BQDvgzPT4ww1+9G+zBFxcfM2PH/Q==} 130 + '@atcute/cbor@2.2.8': 131 + resolution: {integrity: sha512-UzOAN9BuN6JCXgn0ryV8qZuRJUDrNqrbLd6EFM8jc6RYssjRyGRxNy6RZ1NU/07Hd8Tq/0pz8+nQiMu5Zai5uw==} 126 132 127 - '@atcute/cid@2.2.2': 128 - resolution: {integrity: sha512-deAGMqLAyplt7eIukhkjlsGubvrcMrtXkDKlUYZDo4WUdL7hSjBywtPXf6SbMK+Mjvst7l2+83OqTcY5AuuxtA==} 133 + '@atcute/cid@2.2.6': 134 + resolution: {integrity: sha512-bTAHHbJ24p+E//V4KCS4xdmd39o211jJswvqQOevj7vk+5IYcgDLx1ryZWZ1sEPOo9x875li/kj5gpKL14RDwQ==} 129 135 130 - '@atcute/client@3.1.0': 131 - resolution: {integrity: sha512-+rQPsHXSf0DUm8XoHoaH7Y2E8tIpbsW84djyPj7dqAyrFIjvGuJ1X1DvMufwbTIcmLerdy+dzl34iZcz/h3Vhg==} 136 + '@atcute/client@4.1.1': 137 + resolution: {integrity: sha512-FROCbTTCeL5u4tO/n72jDEKyKqjdlXMB56Ehve3W/gnnLGCYWvN42sS7tvL1Mgu6sbO3yZwsXKDrmM2No4XpjA==} 132 138 133 - '@atcute/crypto@2.2.1': 134 - resolution: {integrity: sha512-ILnwxEkH0PujlDqn3PWzvpTOqSOzy7XAsLFZgJ4PN1cnNg9hbtSoKiGdBHKJqvdljSDgA8aOFvvxBfl30EOu8A==} 139 + '@atcute/crypto@2.3.0': 140 + resolution: {integrity: sha512-w5pkJKCjbNMQu+F4JRHbR3ROQyhi1wbn+GSC6WDQamcYHkZmEZk1/eoI354bIQOOfkEM6aFLv718iskrkon4GQ==} 135 141 136 - '@atcute/did-plc@0.1.4': 137 - resolution: {integrity: sha512-VymkNnaEujOVoiBIPGE1M4Kjr1SB7zz+qJhxtHT6SrBWuXEFOOzjsoxkuBlcV/mm4ZTtjAxBpX3MIRRDKq19LQ==} 142 + '@atcute/did-plc@0.2.0': 143 + resolution: {integrity: sha512-1sGek8GRM/Ph7nLVRREm8FqM7g4shGckItvdVwJcRbUa8Rh0zOsXQa0QyYWAC0k40BhkqO9FwKXhJEaXCmF5oQ==} 138 144 139 - '@atcute/identity-resolver@0.1.2': 140 - resolution: {integrity: sha512-fP2VbHD04kVcCdNi/Kszo6jFzqM7Pg3p33oGhfp2zVkwFKaVBlwCaFRWEga/Xvu/IDLwNdASGWnLqoA34SFeSg==} 145 + '@atcute/identity-resolver@1.2.0': 146 + resolution: {integrity: sha512-5UbSJfdV3JIkF8ksXz7g4nKBWasf2wROvzM66cfvTIWydWFO6/oS1KZd+zo9Eokje5Scf5+jsY9ZfgVARLepXg==} 141 147 peerDependencies: 142 - '@atcute/identity': ^0.1.0 148 + '@atcute/identity': ^1.0.0 149 + 150 + '@atcute/identity@1.1.3': 151 + resolution: {integrity: sha512-oIqPoI8TwWeQxvcLmFEZLdN2XdWcaLVtlm8pNk0E72As9HNzzD9pwKPrLr3rmTLRIoULPPFmq9iFNsTeCIU9ng==} 152 + 153 + '@atcute/lexicons@1.2.5': 154 + resolution: {integrity: sha512-9yO9WdgxW8jZ7SbzUycH710z+JmsQ9W9n5S6i6eghYju32kkluFmgBeS47r8e8p2+Dv4DemS7o/3SUGsX9FR5Q==} 155 + 156 + '@atcute/mst@0.1.0': 157 + resolution: {integrity: sha512-h+iDToKEnBpigk2DOHjSqY63vJtjYKUIztqu1CZ0P+I54wV2SrgoqAXAT1xrW6A1Iup8cjTv+U2H5WVG4KxPLw==} 143 158 144 - '@atcute/identity@0.1.3': 145 - resolution: {integrity: sha512-ndlD8nypHt8G00wixbozKdSNL0O8HTzBjFGEXeAcBUCXSZPBjRWbqtgyJxhgUWnr7swgxgw1mSbZwRB5b7xCiQ==} 159 + '@atcute/multibase@1.1.6': 160 + resolution: {integrity: sha512-HBxuCgYLKPPxETV0Rot4VP9e24vKl8JdzGCZOVsDaOXJgbRZoRIF67Lp0H/OgnJeH/Xpva8Z5ReoTNJE5dn3kg==} 146 161 147 - '@atcute/multibase@1.1.3': 148 - resolution: {integrity: sha512-vQQO0tDuQPguBvHdgV3ryn7R8U6beQ50KA/juYm+dCeT/3hOK2stMbX+IaW8JEuwkT5lJsU8wDIOicQT4mB7Ag==} 162 + '@atcute/repo@0.1.0': 163 + resolution: {integrity: sha512-INiYAuma8dydBu7cqd2WVpcXh3mzhIepYBUqFWAK5MqMulPRLTRCc/9GW3G9pxYrOdlvLCVamG2Jf8XK0nuFEw==} 149 164 150 - '@atcute/tid@1.0.2': 151 - resolution: {integrity: sha512-ahmjroNyeDPJhtuf3+HTJropaH04HmJ8fhntDu73Gpz/RkAF7+nkz6kcP2QTgfvMCgMPAJUdskAAP82GPDTY9w==} 165 + '@atcute/tid@1.0.3': 166 + resolution: {integrity: sha512-wfMJx1IMdnu0CZgWl0uR4JO2s6PGT1YPhpytD4ZHzEYKKQVuqV6Eb/7vieaVo1eYNMp2FrY67FZObeR7utRl2w==} 152 167 153 - '@atcute/uint8array@1.0.1': 154 - resolution: {integrity: sha512-AAnlFKyfDRgb9GNZJbhQ6OuMhbmNPirQyapb8KnmcEhxQZ3+tt+4NcwqekEegY4MpNqSTYeeTdyxq0wGZv1JHg==} 168 + '@atcute/uint8array@1.0.6': 169 + resolution: {integrity: sha512-ucfRBQc7BFT8n9eCyGOzDHEMKF/nZwhS2pPao4Xtab1ML3HdFYcX2DM1tadCzas85QTGxHe5urnUAAcNKGRi9A==} 155 170 156 - '@atcute/util-fetch@1.0.1': 157 - resolution: {integrity: sha512-Clc0E/5ufyGBVfYBUwWNlHONlZCoblSr4Ho50l1LhmRPGB1Wu/AQ9Sz+rsBg7fdaW/auve8ulmwhRhnX2cGRow==} 171 + '@atcute/util-fetch@1.0.4': 172 + resolution: {integrity: sha512-sIU9Qk0dE8PLEXSfhy+gIJV+HpiiknMytCI2SqLlqd0vgZUtEKI/EQfP+23LHWvP+CLCzVDOa6cpH045OlmNBg==} 158 173 159 - '@atcute/varint@1.0.2': 160 - resolution: {integrity: sha512-0O31hePzzr4O3NGWHUKKOyta6CGSL+AtN8iir8grGxu9jXyI7DBARlw6PbgKA6uTAvsXdpmRmF8MX+p0TsLnNg==} 174 + '@atcute/varint@1.0.3': 175 + resolution: {integrity: sha512-fdvMPyBB+McDT+Ai5e9RwEbwYV4yjZ60S2Dn5PTjGqUyxvoCH1z42viuheDZRUDkmfQehXJTZ5az7dSozVNtog==} 161 176 162 177 '@babel/code-frame@7.27.1': 163 178 resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} 164 179 engines: {node: '>=6.9.0'} 165 180 166 - '@babel/compat-data@7.27.1': 167 - resolution: {integrity: sha512-Q+E+rd/yBzNQhXkG+zQnF58e4zoZfBedaxwzPmicKsiK3nt8iJYrSrDbjwFFDGC4f+rPafqRaPH6TsDoSvMf7A==} 181 + '@babel/compat-data@7.28.5': 182 + resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} 168 183 engines: {node: '>=6.9.0'} 169 184 170 - '@babel/core@7.27.1': 171 - resolution: {integrity: sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==} 185 + '@babel/core@7.28.5': 186 + resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} 187 + engines: {node: '>=6.9.0'} 188 + 189 + '@babel/generator@7.28.5': 190 + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} 172 191 engines: {node: '>=6.9.0'} 173 192 174 - '@babel/generator@7.27.1': 175 - resolution: {integrity: sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==} 193 + '@babel/helper-compilation-targets@7.27.2': 194 + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} 176 195 engines: {node: '>=6.9.0'} 177 196 178 - '@babel/helper-compilation-targets@7.27.1': 179 - resolution: {integrity: sha512-2YaDd/Rd9E598B5+WIc8wJPmWETiiJXFYVE60oX8FDohv7rAUU3CQj+A1MgeEmcsk2+dQuEjIe/GDvig0SqL4g==} 197 + '@babel/helper-globals@7.28.0': 198 + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} 180 199 engines: {node: '>=6.9.0'} 181 200 182 201 '@babel/helper-module-imports@7.18.6': ··· 187 206 resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} 188 207 engines: {node: '>=6.9.0'} 189 208 190 - '@babel/helper-module-transforms@7.27.1': 191 - resolution: {integrity: sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==} 209 + '@babel/helper-module-transforms@7.28.3': 210 + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} 192 211 engines: {node: '>=6.9.0'} 193 212 peerDependencies: 194 213 '@babel/core': ^7.0.0 ··· 201 220 resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} 202 221 engines: {node: '>=6.9.0'} 203 222 204 - '@babel/helper-validator-identifier@7.27.1': 205 - resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} 223 + '@babel/helper-validator-identifier@7.28.5': 224 + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} 206 225 engines: {node: '>=6.9.0'} 207 226 208 227 '@babel/helper-validator-option@7.27.1': 209 228 resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} 210 229 engines: {node: '>=6.9.0'} 211 230 212 - '@babel/helpers@7.27.1': 213 - resolution: {integrity: sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==} 231 + '@babel/helpers@7.28.4': 232 + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} 214 233 engines: {node: '>=6.9.0'} 215 234 216 - '@babel/parser@7.27.1': 217 - resolution: {integrity: sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ==} 235 + '@babel/parser@7.28.5': 236 + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} 218 237 engines: {node: '>=6.0.0'} 219 238 hasBin: true 220 239 ··· 224 243 peerDependencies: 225 244 '@babel/core': ^7.0.0-0 226 245 227 - '@babel/template@7.27.1': 228 - resolution: {integrity: sha512-Fyo3ghWMqkHHpHQCoBs2VnYjR4iWFFjguTDEqA5WgZDOrFesVjMhMM2FSqTKSoUSDO1VQtavj8NFpdRBEvJTtg==} 246 + '@babel/template@7.27.2': 247 + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} 229 248 engines: {node: '>=6.9.0'} 230 249 231 - '@babel/traverse@7.27.1': 232 - resolution: {integrity: sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==} 250 + '@babel/traverse@7.28.5': 251 + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} 233 252 engines: {node: '>=6.9.0'} 234 253 235 - '@babel/types@7.27.1': 236 - resolution: {integrity: sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==} 254 + '@babel/types@7.28.5': 255 + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} 237 256 engines: {node: '>=6.9.0'} 238 257 239 - '@badrap/valita@0.4.4': 240 - resolution: {integrity: sha512-GEhUCk9c4XbNxi+0YZHZsV4fYNd6HejfWuN4Ti4c02DauX+LyX5WY1Y3WfyZ8Pxxl0zqhs+MLtW98cMh86vv6g==} 258 + '@badrap/valita@0.4.6': 259 + resolution: {integrity: sha512-4kdqcjyxo/8RQ8ayjms47HCWZIF5981oE5nIenbfThKDxWXtEHKipAOWlflpPJzZx9y/JWYQkp18Awr7VuepFg==} 241 260 engines: {node: '>= 18'} 242 261 243 - '@cloudflare/kv-asset-handler@0.4.0': 244 - resolution: {integrity: sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA==} 262 + '@cloudflare/kv-asset-handler@0.4.1': 263 + resolution: {integrity: sha512-Nu8ahitGFFJztxUml9oD/DLb7Z28C8cd8F46IVQ7y5Btz575pvMY8AqZsXkX7Gds29eCKdMgIHjIvzskHgPSFg==} 245 264 engines: {node: '>=18.0.0'} 246 265 247 - '@cloudflare/unenv-preset@2.3.1': 248 - resolution: {integrity: sha512-Xq57Qd+ADpt6hibcVBO0uLG9zzRgyRhfCUgBT9s+g3+3Ivg5zDyVgLFy40ES1VdNcu8rPNSivm9A+kGP5IVaPg==} 266 + '@cloudflare/unenv-preset@2.7.13': 267 + resolution: {integrity: sha512-NulO1H8R/DzsJguLC0ndMuk4Ufv0KSlN+E54ay9rn9ZCQo0kpAPwwh3LhgpZ96a3Dr6L9LqW57M4CqC34iLOvw==} 249 268 peerDependencies: 250 - unenv: 2.0.0-rc.15 251 - workerd: ^1.20250320.0 269 + unenv: 2.0.0-rc.24 270 + workerd: ^1.20251202.0 252 271 peerDependenciesMeta: 253 272 workerd: 254 273 optional: true 255 274 256 - '@cloudflare/workerd-darwin-64@1.20250428.0': 257 - resolution: {integrity: sha512-6nVe9oV4Hdec6ctzMtW80TiDvNTd2oFPi3VsKqSDVaJSJbL+4b6seyJ7G/UEPI+si6JhHBSLV2/9lNXNGLjClA==} 275 + '@cloudflare/workerd-darwin-64@1.20251202.0': 276 + resolution: {integrity: sha512-/uvEAWEukTWb1geHhbjGUeZqcSSSyYzp0mvoPUBl+l0ont4NVGao3fgwM0q8wtKvgoKCHSG6zcG23wj9Opj3Nw==} 258 277 engines: {node: '>=16'} 259 278 cpu: [x64] 260 279 os: [darwin] 261 280 262 - '@cloudflare/workerd-darwin-arm64@1.20250428.0': 263 - resolution: {integrity: sha512-/TB7bh7SIJ5f+6r4PHsAz7+9Qal/TK1cJuKFkUno1kqGlZbdrMwH0ATYwlWC/nBFeu2FB3NUolsTntEuy23hnQ==} 281 + '@cloudflare/workerd-darwin-arm64@1.20251202.0': 282 + resolution: {integrity: sha512-f52xRvcI9cWRd6400EZStRtXiRC5XKEud7K5aFIbbUv0VeINltujFQQ9nHWtsF6g1quIXWkjhh5u01gPAYNNXA==} 264 283 engines: {node: '>=16'} 265 284 cpu: [arm64] 266 285 os: [darwin] 267 286 268 - '@cloudflare/workerd-linux-64@1.20250428.0': 269 - resolution: {integrity: sha512-9eCbj+R3CKqpiXP6DfAA20DxKge+OTj7Hyw3ZewiEhWH9INIHiJwJQYybu4iq9kJEGjnGvxgguLFjSCWm26hgg==} 287 + '@cloudflare/workerd-linux-64@1.20251202.0': 288 + resolution: {integrity: sha512-HYXinF5RBH7oXbsFUMmwKCj+WltpYbf5mRKUBG5v3EuPhUjSIFB84U+58pDyfBJjcynHdy3EtvTWcvh/+lcgow==} 270 289 engines: {node: '>=16'} 271 290 cpu: [x64] 272 291 os: [linux] 273 292 274 - '@cloudflare/workerd-linux-arm64@1.20250428.0': 275 - resolution: {integrity: sha512-D9NRBnW46nl1EQsP13qfkYb5lbt4C6nxl38SBKY/NOcZAUoHzNB5K0GaK8LxvpkM7X/97ySojlMfR5jh5DNXYQ==} 293 + '@cloudflare/workerd-linux-arm64@1.20251202.0': 294 + resolution: {integrity: sha512-++L02Jdoxz7hEA9qDaQjbVU1RzQS+S+eqIi22DkPe2Tgiq2M3UfNpeu+75k5L9DGRIkZPYvwMBMbcmKvQqdIIg==} 276 295 engines: {node: '>=16'} 277 296 cpu: [arm64] 278 297 os: [linux] 279 298 280 - '@cloudflare/workerd-windows-64@1.20250428.0': 281 - resolution: {integrity: sha512-RQCRj28eitjKD0tmei6iFOuWqMuHMHdNGEigRmbkmuTlpbWHNAoHikgCzZQ/dkKDdatA76TmcpbyECNf31oaTA==} 299 + '@cloudflare/workerd-windows-64@1.20251202.0': 300 + resolution: {integrity: sha512-gzeU6eDydTi7ib+Q9DD/c0hpXtqPucnHk2tfGU03mljPObYxzMkkPGgB5qxpksFvub3y4K0ChjqYxGJB4F+j3g==} 282 301 engines: {node: '>=16'} 283 302 cpu: [x64] 284 303 os: [win32] ··· 287 306 resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} 288 307 engines: {node: '>=12'} 289 308 290 - '@emnapi/runtime@1.4.3': 291 - resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} 309 + '@emnapi/runtime@1.7.1': 310 + resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} 292 311 293 - '@esbuild/aix-ppc64@0.25.2': 294 - resolution: {integrity: sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==} 312 + '@esbuild/aix-ppc64@0.25.12': 313 + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} 295 314 engines: {node: '>=18'} 296 315 cpu: [ppc64] 297 316 os: [aix] 298 317 299 - '@esbuild/aix-ppc64@0.25.4': 300 - resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==} 318 + '@esbuild/aix-ppc64@0.27.0': 319 + resolution: {integrity: sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==} 301 320 engines: {node: '>=18'} 302 321 cpu: [ppc64] 303 322 os: [aix] 304 323 305 - '@esbuild/android-arm64@0.25.2': 306 - resolution: {integrity: sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==} 324 + '@esbuild/android-arm64@0.25.12': 325 + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} 307 326 engines: {node: '>=18'} 308 327 cpu: [arm64] 309 328 os: [android] 310 329 311 - '@esbuild/android-arm64@0.25.4': 312 - resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==} 330 + '@esbuild/android-arm64@0.27.0': 331 + resolution: {integrity: sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==} 313 332 engines: {node: '>=18'} 314 333 cpu: [arm64] 315 334 os: [android] 316 335 317 - '@esbuild/android-arm@0.25.2': 318 - resolution: {integrity: sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==} 336 + '@esbuild/android-arm@0.25.12': 337 + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} 319 338 engines: {node: '>=18'} 320 339 cpu: [arm] 321 340 os: [android] 322 341 323 - '@esbuild/android-arm@0.25.4': 324 - resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==} 342 + '@esbuild/android-arm@0.27.0': 343 + resolution: {integrity: sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==} 325 344 engines: {node: '>=18'} 326 345 cpu: [arm] 327 346 os: [android] 328 347 329 - '@esbuild/android-x64@0.25.2': 330 - resolution: {integrity: sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==} 348 + '@esbuild/android-x64@0.25.12': 349 + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} 331 350 engines: {node: '>=18'} 332 351 cpu: [x64] 333 352 os: [android] 334 353 335 - '@esbuild/android-x64@0.25.4': 336 - resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==} 354 + '@esbuild/android-x64@0.27.0': 355 + resolution: {integrity: sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==} 337 356 engines: {node: '>=18'} 338 357 cpu: [x64] 339 358 os: [android] 340 359 341 - '@esbuild/darwin-arm64@0.25.2': 342 - resolution: {integrity: sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==} 360 + '@esbuild/darwin-arm64@0.25.12': 361 + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} 343 362 engines: {node: '>=18'} 344 363 cpu: [arm64] 345 364 os: [darwin] 346 365 347 - '@esbuild/darwin-arm64@0.25.4': 348 - resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==} 366 + '@esbuild/darwin-arm64@0.27.0': 367 + resolution: {integrity: sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==} 349 368 engines: {node: '>=18'} 350 369 cpu: [arm64] 351 370 os: [darwin] 352 371 353 - '@esbuild/darwin-x64@0.25.2': 354 - resolution: {integrity: sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==} 372 + '@esbuild/darwin-x64@0.25.12': 373 + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} 355 374 engines: {node: '>=18'} 356 375 cpu: [x64] 357 376 os: [darwin] 358 377 359 - '@esbuild/darwin-x64@0.25.4': 360 - resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==} 378 + '@esbuild/darwin-x64@0.27.0': 379 + resolution: {integrity: sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==} 361 380 engines: {node: '>=18'} 362 381 cpu: [x64] 363 382 os: [darwin] 364 383 365 - '@esbuild/freebsd-arm64@0.25.2': 366 - resolution: {integrity: sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==} 384 + '@esbuild/freebsd-arm64@0.25.12': 385 + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} 367 386 engines: {node: '>=18'} 368 387 cpu: [arm64] 369 388 os: [freebsd] 370 389 371 - '@esbuild/freebsd-arm64@0.25.4': 372 - resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==} 390 + '@esbuild/freebsd-arm64@0.27.0': 391 + resolution: {integrity: sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==} 373 392 engines: {node: '>=18'} 374 393 cpu: [arm64] 375 394 os: [freebsd] 376 395 377 - '@esbuild/freebsd-x64@0.25.2': 378 - resolution: {integrity: sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==} 396 + '@esbuild/freebsd-x64@0.25.12': 397 + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} 379 398 engines: {node: '>=18'} 380 399 cpu: [x64] 381 400 os: [freebsd] 382 401 383 - '@esbuild/freebsd-x64@0.25.4': 384 - resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==} 402 + '@esbuild/freebsd-x64@0.27.0': 403 + resolution: {integrity: sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==} 385 404 engines: {node: '>=18'} 386 405 cpu: [x64] 387 406 os: [freebsd] 388 407 389 - '@esbuild/linux-arm64@0.25.2': 390 - resolution: {integrity: sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==} 408 + '@esbuild/linux-arm64@0.25.12': 409 + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} 391 410 engines: {node: '>=18'} 392 411 cpu: [arm64] 393 412 os: [linux] 394 413 395 - '@esbuild/linux-arm64@0.25.4': 396 - resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==} 414 + '@esbuild/linux-arm64@0.27.0': 415 + resolution: {integrity: sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==} 397 416 engines: {node: '>=18'} 398 417 cpu: [arm64] 399 418 os: [linux] 400 419 401 - '@esbuild/linux-arm@0.25.2': 402 - resolution: {integrity: sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==} 420 + '@esbuild/linux-arm@0.25.12': 421 + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} 403 422 engines: {node: '>=18'} 404 423 cpu: [arm] 405 424 os: [linux] 406 425 407 - '@esbuild/linux-arm@0.25.4': 408 - resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==} 426 + '@esbuild/linux-arm@0.27.0': 427 + resolution: {integrity: sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==} 409 428 engines: {node: '>=18'} 410 429 cpu: [arm] 411 430 os: [linux] 412 431 413 - '@esbuild/linux-ia32@0.25.2': 414 - resolution: {integrity: sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==} 432 + '@esbuild/linux-ia32@0.25.12': 433 + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} 415 434 engines: {node: '>=18'} 416 435 cpu: [ia32] 417 436 os: [linux] 418 437 419 - '@esbuild/linux-ia32@0.25.4': 420 - resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==} 438 + '@esbuild/linux-ia32@0.27.0': 439 + resolution: {integrity: sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==} 421 440 engines: {node: '>=18'} 422 441 cpu: [ia32] 423 442 os: [linux] 424 443 425 - '@esbuild/linux-loong64@0.25.2': 426 - resolution: {integrity: sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==} 444 + '@esbuild/linux-loong64@0.25.12': 445 + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} 427 446 engines: {node: '>=18'} 428 447 cpu: [loong64] 429 448 os: [linux] 430 449 431 - '@esbuild/linux-loong64@0.25.4': 432 - resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==} 450 + '@esbuild/linux-loong64@0.27.0': 451 + resolution: {integrity: sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==} 433 452 engines: {node: '>=18'} 434 453 cpu: [loong64] 435 454 os: [linux] 436 455 437 - '@esbuild/linux-mips64el@0.25.2': 438 - resolution: {integrity: sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==} 456 + '@esbuild/linux-mips64el@0.25.12': 457 + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} 439 458 engines: {node: '>=18'} 440 459 cpu: [mips64el] 441 460 os: [linux] 442 461 443 - '@esbuild/linux-mips64el@0.25.4': 444 - resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==} 462 + '@esbuild/linux-mips64el@0.27.0': 463 + resolution: {integrity: sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==} 445 464 engines: {node: '>=18'} 446 465 cpu: [mips64el] 447 466 os: [linux] 448 467 449 - '@esbuild/linux-ppc64@0.25.2': 450 - resolution: {integrity: sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==} 468 + '@esbuild/linux-ppc64@0.25.12': 469 + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} 451 470 engines: {node: '>=18'} 452 471 cpu: [ppc64] 453 472 os: [linux] 454 473 455 - '@esbuild/linux-ppc64@0.25.4': 456 - resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==} 474 + '@esbuild/linux-ppc64@0.27.0': 475 + resolution: {integrity: sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==} 457 476 engines: {node: '>=18'} 458 477 cpu: [ppc64] 459 478 os: [linux] 460 479 461 - '@esbuild/linux-riscv64@0.25.2': 462 - resolution: {integrity: sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==} 480 + '@esbuild/linux-riscv64@0.25.12': 481 + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} 463 482 engines: {node: '>=18'} 464 483 cpu: [riscv64] 465 484 os: [linux] 466 485 467 - '@esbuild/linux-riscv64@0.25.4': 468 - resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==} 486 + '@esbuild/linux-riscv64@0.27.0': 487 + resolution: {integrity: sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==} 469 488 engines: {node: '>=18'} 470 489 cpu: [riscv64] 471 490 os: [linux] 472 491 473 - '@esbuild/linux-s390x@0.25.2': 474 - resolution: {integrity: sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==} 492 + '@esbuild/linux-s390x@0.25.12': 493 + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} 475 494 engines: {node: '>=18'} 476 495 cpu: [s390x] 477 496 os: [linux] 478 497 479 - '@esbuild/linux-s390x@0.25.4': 480 - resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==} 498 + '@esbuild/linux-s390x@0.27.0': 499 + resolution: {integrity: sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==} 481 500 engines: {node: '>=18'} 482 501 cpu: [s390x] 483 502 os: [linux] 484 503 485 - '@esbuild/linux-x64@0.25.2': 486 - resolution: {integrity: sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==} 504 + '@esbuild/linux-x64@0.25.12': 505 + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} 487 506 engines: {node: '>=18'} 488 507 cpu: [x64] 489 508 os: [linux] 490 509 491 - '@esbuild/linux-x64@0.25.4': 492 - resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==} 510 + '@esbuild/linux-x64@0.27.0': 511 + resolution: {integrity: sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==} 493 512 engines: {node: '>=18'} 494 513 cpu: [x64] 495 514 os: [linux] 496 515 497 - '@esbuild/netbsd-arm64@0.25.2': 498 - resolution: {integrity: sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==} 516 + '@esbuild/netbsd-arm64@0.25.12': 517 + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} 499 518 engines: {node: '>=18'} 500 519 cpu: [arm64] 501 520 os: [netbsd] 502 521 503 - '@esbuild/netbsd-arm64@0.25.4': 504 - resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==} 522 + '@esbuild/netbsd-arm64@0.27.0': 523 + resolution: {integrity: sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==} 505 524 engines: {node: '>=18'} 506 525 cpu: [arm64] 507 526 os: [netbsd] 508 527 509 - '@esbuild/netbsd-x64@0.25.2': 510 - resolution: {integrity: sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==} 528 + '@esbuild/netbsd-x64@0.25.12': 529 + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} 511 530 engines: {node: '>=18'} 512 531 cpu: [x64] 513 532 os: [netbsd] 514 533 515 - '@esbuild/netbsd-x64@0.25.4': 516 - resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==} 534 + '@esbuild/netbsd-x64@0.27.0': 535 + resolution: {integrity: sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==} 517 536 engines: {node: '>=18'} 518 537 cpu: [x64] 519 538 os: [netbsd] 520 539 521 - '@esbuild/openbsd-arm64@0.25.2': 522 - resolution: {integrity: sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==} 540 + '@esbuild/openbsd-arm64@0.25.12': 541 + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} 523 542 engines: {node: '>=18'} 524 543 cpu: [arm64] 525 544 os: [openbsd] 526 545 527 - '@esbuild/openbsd-arm64@0.25.4': 528 - resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==} 546 + '@esbuild/openbsd-arm64@0.27.0': 547 + resolution: {integrity: sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==} 529 548 engines: {node: '>=18'} 530 549 cpu: [arm64] 531 550 os: [openbsd] 532 551 533 - '@esbuild/openbsd-x64@0.25.2': 534 - resolution: {integrity: sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==} 552 + '@esbuild/openbsd-x64@0.25.12': 553 + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} 535 554 engines: {node: '>=18'} 536 555 cpu: [x64] 537 556 os: [openbsd] 538 557 539 - '@esbuild/openbsd-x64@0.25.4': 540 - resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==} 558 + '@esbuild/openbsd-x64@0.27.0': 559 + resolution: {integrity: sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==} 541 560 engines: {node: '>=18'} 542 561 cpu: [x64] 543 562 os: [openbsd] 544 563 545 - '@esbuild/sunos-x64@0.25.2': 546 - resolution: {integrity: sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==} 564 + '@esbuild/openharmony-arm64@0.25.12': 565 + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} 566 + engines: {node: '>=18'} 567 + cpu: [arm64] 568 + os: [openharmony] 569 + 570 + '@esbuild/openharmony-arm64@0.27.0': 571 + resolution: {integrity: sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==} 572 + engines: {node: '>=18'} 573 + cpu: [arm64] 574 + os: [openharmony] 575 + 576 + '@esbuild/sunos-x64@0.25.12': 577 + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} 547 578 engines: {node: '>=18'} 548 579 cpu: [x64] 549 580 os: [sunos] 550 581 551 - '@esbuild/sunos-x64@0.25.4': 552 - resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==} 582 + '@esbuild/sunos-x64@0.27.0': 583 + resolution: {integrity: sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==} 553 584 engines: {node: '>=18'} 554 585 cpu: [x64] 555 586 os: [sunos] 556 587 557 - '@esbuild/win32-arm64@0.25.2': 558 - resolution: {integrity: sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==} 588 + '@esbuild/win32-arm64@0.25.12': 589 + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} 559 590 engines: {node: '>=18'} 560 591 cpu: [arm64] 561 592 os: [win32] 562 593 563 - '@esbuild/win32-arm64@0.25.4': 564 - resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==} 594 + '@esbuild/win32-arm64@0.27.0': 595 + resolution: {integrity: sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==} 565 596 engines: {node: '>=18'} 566 597 cpu: [arm64] 567 598 os: [win32] 568 599 569 - '@esbuild/win32-ia32@0.25.2': 570 - resolution: {integrity: sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==} 600 + '@esbuild/win32-ia32@0.25.12': 601 + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} 571 602 engines: {node: '>=18'} 572 603 cpu: [ia32] 573 604 os: [win32] 574 605 575 - '@esbuild/win32-ia32@0.25.4': 576 - resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==} 606 + '@esbuild/win32-ia32@0.27.0': 607 + resolution: {integrity: sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==} 577 608 engines: {node: '>=18'} 578 609 cpu: [ia32] 579 610 os: [win32] 580 611 581 - '@esbuild/win32-x64@0.25.2': 582 - resolution: {integrity: sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==} 612 + '@esbuild/win32-x64@0.25.12': 613 + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} 583 614 engines: {node: '>=18'} 584 615 cpu: [x64] 585 616 os: [win32] 586 617 587 - '@esbuild/win32-x64@0.25.4': 588 - resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==} 618 + '@esbuild/win32-x64@0.27.0': 619 + resolution: {integrity: sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==} 589 620 engines: {node: '>=18'} 590 621 cpu: [x64] 591 622 os: [win32] ··· 594 625 resolution: {integrity: sha512-duvZBfJB9oOLphx04ckKF534hP186xIBFaw4GHJ5fGeZY5syZs59UeumV5NC6aiEU9hVhAFMOnDDGkQrFqHrnQ==} 595 626 peerDependencies: 596 627 solid-js: ^1.8.5 597 - 598 - '@fastify/busboy@2.1.1': 599 - resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} 600 - engines: {node: '>=14'} 601 628 602 629 '@img/sharp-darwin-arm64@0.33.5': 603 630 resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} ··· 704 731 cpu: [x64] 705 732 os: [win32] 706 733 707 - '@isaacs/cliui@8.0.2': 708 - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} 709 - engines: {node: '>=12'} 734 + '@jridgewell/gen-mapping@0.3.13': 735 + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} 710 736 711 - '@jridgewell/gen-mapping@0.3.8': 712 - resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} 713 - engines: {node: '>=6.0.0'} 737 + '@jridgewell/remapping@2.3.5': 738 + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} 714 739 715 740 '@jridgewell/resolve-uri@3.1.2': 716 741 resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 717 742 engines: {node: '>=6.0.0'} 718 743 719 - '@jridgewell/set-array@1.2.1': 720 - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} 721 - engines: {node: '>=6.0.0'} 722 - 723 - '@jridgewell/source-map@0.3.6': 724 - resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} 744 + '@jridgewell/source-map@0.3.11': 745 + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} 725 746 726 - '@jridgewell/sourcemap-codec@1.5.0': 727 - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} 747 + '@jridgewell/sourcemap-codec@1.5.5': 748 + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} 728 749 729 - '@jridgewell/trace-mapping@0.3.25': 730 - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} 750 + '@jridgewell/trace-mapping@0.3.31': 751 + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} 731 752 732 753 '@jridgewell/trace-mapping@0.3.9': 733 754 resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} 734 755 735 - '@jsr/mary__array-fns@0.1.4': 736 - resolution: {integrity: sha512-+HbGYR9Ll5blEmAvVAoPejyGj01YeBbVmJ59qxaMDKt5i3F90ohYLA5a78y6AULDlet1IxYB+a/cMN+A0vGnDg==, tarball: https://npm.jsr.io/~/11/@jsr/mary__array-fns/0.1.4.tgz} 756 + '@jsr/mary__array-fns@0.1.5': 757 + resolution: {integrity: sha512-gI4scq/Hh9GtFUJfS8cvZf5nr+cs7udvrEpMv75grws5/0LIwBycKeeJcNi4+xNl6x4CGW6Fp46puhtJiQOpMg==, tarball: https://npm.jsr.io/~/11/@jsr/mary__array-fns/0.1.5.tgz} 737 758 738 - '@jsr/mary__ds-queue@0.1.2': 739 - resolution: {integrity: sha512-AOZ/FXYHVWI05bNHgi/Ln0RCiWvOQuZ/fZ1AxkT27Ytbon8VNtLAYs7uOgVITJcbMrsdE6zJXh/bP9LF16sk/A==, tarball: https://npm.jsr.io/~/11/@jsr/mary__ds-queue/0.1.2.tgz} 759 + '@jsr/mary__ds-queue@0.1.3': 760 + resolution: {integrity: sha512-gGqIHXiAmhUUtonNI6YVvL7VlXjEHUpGdc7RGU8BLP4XnFvqovDTH5y9VlBZmvozTWgTIMoZF6/1//sMrvYKtQ==, tarball: https://npm.jsr.io/~/11/@jsr/mary__ds-queue/0.1.3.tgz} 740 761 741 762 '@jsr/mary__events@0.2.0': 742 763 resolution: {integrity: sha512-WcBRbtuTno3zcfXKd7SEeKr1lAJF+CQ8BCv+PEEMmNKNqFurkEksGxRB3UDPZxIxjJ7sAqMVTL26wRuMpAcIeA==, tarball: https://npm.jsr.io/~/11/@jsr/mary__events/0.2.0.tgz} 743 764 744 - '@jsr/mary__tar@0.2.4': 745 - resolution: {integrity: sha512-jFjPcZj8DRSukPLZOt6+h74cVFdfdTMG9gzbW67YByCJTD52PEpe2sNcfCSw4mQ8hZBNgwiufCPyYL8hR9yicA==, tarball: https://npm.jsr.io/~/11/@jsr/mary__tar/0.2.4.tgz} 765 + '@jsr/mary__tar@0.3.1': 766 + resolution: {integrity: sha512-T803kucwCLVOXFJGzVbpkT5vRK6fARy5HL6xMiLK5hJFck72bsAeluENlRnvD0kFPSlFNp/5EJWfTHnpDK0qYA==, tarball: https://npm.jsr.io/~/11/@jsr/mary__tar/0.3.1.tgz} 746 767 747 - '@noble/secp256k1@2.2.3': 748 - resolution: {integrity: sha512-l7r5oEQym9Us7EAigzg30/PQAvynhMt2uoYtT3t26eGDVm9Yii5mZ5jWSWmZ/oSIR2Et0xfc6DXrG0bZ787V3w==} 768 + '@noble/secp256k1@3.0.0': 769 + resolution: {integrity: sha512-NJBaR352KyIvj3t6sgT/+7xrNyF9Xk9QlLSIqUGVUYlsnDTAUqY8LOmwpcgEx4AMJXRITQ5XEVHD+mMaPfr3mg==} 749 770 750 771 '@nodelib/fs.scandir@2.1.5': 751 772 resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} ··· 759 780 resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 760 781 engines: {node: '>= 8'} 761 782 762 - '@pkgjs/parseargs@0.11.0': 763 - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} 764 - engines: {node: '>=14'} 783 + '@poppinss/colors@4.1.5': 784 + resolution: {integrity: sha512-FvdDqtcRCtz6hThExcFOgW0cWX+xwSMWcRuQe5ZEb2m7cVQOAVZOIMt+/v9RxGiD9/OY16qJBXK4CVKWAPalBw==} 765 785 766 - '@rollup/rollup-android-arm-eabi@4.40.2': 767 - resolution: {integrity: sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==} 786 + '@poppinss/dumper@0.6.5': 787 + resolution: {integrity: sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==} 788 + 789 + '@poppinss/exception@1.2.2': 790 + resolution: {integrity: sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg==} 791 + 792 + '@rollup/rollup-android-arm-eabi@4.53.3': 793 + resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} 768 794 cpu: [arm] 769 795 os: [android] 770 796 771 - '@rollup/rollup-android-arm64@4.40.2': 772 - resolution: {integrity: sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==} 797 + '@rollup/rollup-android-arm64@4.53.3': 798 + resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} 773 799 cpu: [arm64] 774 800 os: [android] 775 801 776 - '@rollup/rollup-darwin-arm64@4.40.2': 777 - resolution: {integrity: sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==} 802 + '@rollup/rollup-darwin-arm64@4.53.3': 803 + resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} 778 804 cpu: [arm64] 779 805 os: [darwin] 780 806 781 - '@rollup/rollup-darwin-x64@4.40.2': 782 - resolution: {integrity: sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==} 807 + '@rollup/rollup-darwin-x64@4.53.3': 808 + resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} 783 809 cpu: [x64] 784 810 os: [darwin] 785 811 786 - '@rollup/rollup-freebsd-arm64@4.40.2': 787 - resolution: {integrity: sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==} 812 + '@rollup/rollup-freebsd-arm64@4.53.3': 813 + resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} 788 814 cpu: [arm64] 789 815 os: [freebsd] 790 816 791 - '@rollup/rollup-freebsd-x64@4.40.2': 792 - resolution: {integrity: sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==} 817 + '@rollup/rollup-freebsd-x64@4.53.3': 818 + resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} 793 819 cpu: [x64] 794 820 os: [freebsd] 795 821 796 - '@rollup/rollup-linux-arm-gnueabihf@4.40.2': 797 - resolution: {integrity: sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==} 822 + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': 823 + resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} 798 824 cpu: [arm] 799 825 os: [linux] 800 826 801 - '@rollup/rollup-linux-arm-musleabihf@4.40.2': 802 - resolution: {integrity: sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==} 827 + '@rollup/rollup-linux-arm-musleabihf@4.53.3': 828 + resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} 803 829 cpu: [arm] 804 830 os: [linux] 805 831 806 - '@rollup/rollup-linux-arm64-gnu@4.40.2': 807 - resolution: {integrity: sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==} 832 + '@rollup/rollup-linux-arm64-gnu@4.53.3': 833 + resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} 808 834 cpu: [arm64] 809 835 os: [linux] 810 836 811 - '@rollup/rollup-linux-arm64-musl@4.40.2': 812 - resolution: {integrity: sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==} 837 + '@rollup/rollup-linux-arm64-musl@4.53.3': 838 + resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} 813 839 cpu: [arm64] 814 840 os: [linux] 815 841 816 - '@rollup/rollup-linux-loongarch64-gnu@4.40.2': 817 - resolution: {integrity: sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==} 842 + '@rollup/rollup-linux-loong64-gnu@4.53.3': 843 + resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} 818 844 cpu: [loong64] 819 845 os: [linux] 820 846 821 - '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': 822 - resolution: {integrity: sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==} 847 + '@rollup/rollup-linux-ppc64-gnu@4.53.3': 848 + resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} 823 849 cpu: [ppc64] 824 850 os: [linux] 825 851 826 - '@rollup/rollup-linux-riscv64-gnu@4.40.2': 827 - resolution: {integrity: sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==} 852 + '@rollup/rollup-linux-riscv64-gnu@4.53.3': 853 + resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} 828 854 cpu: [riscv64] 829 855 os: [linux] 830 856 831 - '@rollup/rollup-linux-riscv64-musl@4.40.2': 832 - resolution: {integrity: sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==} 857 + '@rollup/rollup-linux-riscv64-musl@4.53.3': 858 + resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} 833 859 cpu: [riscv64] 834 860 os: [linux] 835 861 836 - '@rollup/rollup-linux-s390x-gnu@4.40.2': 837 - resolution: {integrity: sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==} 862 + '@rollup/rollup-linux-s390x-gnu@4.53.3': 863 + resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} 838 864 cpu: [s390x] 839 865 os: [linux] 840 866 841 - '@rollup/rollup-linux-x64-gnu@4.40.2': 842 - resolution: {integrity: sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==} 867 + '@rollup/rollup-linux-x64-gnu@4.53.3': 868 + resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} 843 869 cpu: [x64] 844 870 os: [linux] 845 871 846 - '@rollup/rollup-linux-x64-musl@4.40.2': 847 - resolution: {integrity: sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==} 872 + '@rollup/rollup-linux-x64-musl@4.53.3': 873 + resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} 848 874 cpu: [x64] 849 875 os: [linux] 850 876 851 - '@rollup/rollup-win32-arm64-msvc@4.40.2': 852 - resolution: {integrity: sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==} 877 + '@rollup/rollup-openharmony-arm64@4.53.3': 878 + resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} 879 + cpu: [arm64] 880 + os: [openharmony] 881 + 882 + '@rollup/rollup-win32-arm64-msvc@4.53.3': 883 + resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} 853 884 cpu: [arm64] 854 885 os: [win32] 855 886 856 - '@rollup/rollup-win32-ia32-msvc@4.40.2': 857 - resolution: {integrity: sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==} 887 + '@rollup/rollup-win32-ia32-msvc@4.53.3': 888 + resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} 858 889 cpu: [ia32] 859 890 os: [win32] 860 891 861 - '@rollup/rollup-win32-x64-msvc@4.40.2': 862 - resolution: {integrity: sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==} 892 + '@rollup/rollup-win32-x64-gnu@4.53.3': 893 + resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} 894 + cpu: [x64] 895 + os: [win32] 896 + 897 + '@rollup/rollup-win32-x64-msvc@4.53.3': 898 + resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} 863 899 cpu: [x64] 864 900 os: [win32] 865 901 902 + '@sindresorhus/is@7.1.1': 903 + resolution: {integrity: sha512-rO92VvpgMc3kfiTjGT52LEtJ8Yc5kCWhZjLQ3LwlA4pSgPpQO7bVpYXParOD8Jwf+cVQECJo3yP/4I8aZtUQTQ==} 904 + engines: {node: '>=18'} 905 + 906 + '@speed-highlight/core@1.2.12': 907 + resolution: {integrity: sha512-uilwrK0Ygyri5dToHYdZSjcvpS2ZwX0w5aSt3GCEN9hrjxWCoeV4Z2DTXuxjwbntaLQIEEAlCeNQss5SoHvAEA==} 908 + 909 + '@standard-schema/spec@1.0.0': 910 + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} 911 + 866 912 '@tailwindcss/forms@0.5.10': 867 913 resolution: {integrity: sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==} 868 914 peerDependencies: ··· 877 923 '@types/babel__template@7.4.4': 878 924 resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} 879 925 880 - '@types/babel__traverse@7.20.7': 881 - resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} 926 + '@types/babel__traverse@7.28.0': 927 + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} 882 928 883 - '@types/estree@1.0.7': 884 - resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} 929 + '@types/estree@1.0.8': 930 + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} 885 931 886 - '@types/node@22.15.12': 887 - resolution: {integrity: sha512-K0fpC/ZVeb8G9rm7bH7vI0KAec4XHEhBam616nVJCV51bKzJ6oA3luG4WdKoaztxe70QaNjS/xBmcDLmr4PiGw==} 932 + '@types/node@22.19.2': 933 + resolution: {integrity: sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw==} 888 934 889 935 acorn-walk@8.3.2: 890 936 resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} ··· 895 941 engines: {node: '>=0.4.0'} 896 942 hasBin: true 897 943 898 - acorn@8.14.1: 899 - resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} 944 + acorn@8.15.0: 945 + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} 900 946 engines: {node: '>=0.4.0'} 901 947 hasBin: true 902 - 903 - ansi-regex@5.0.1: 904 - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 905 - engines: {node: '>=8'} 906 - 907 - ansi-regex@6.1.0: 908 - resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} 909 - engines: {node: '>=12'} 910 - 911 - ansi-styles@4.3.0: 912 - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 913 - engines: {node: '>=8'} 914 - 915 - ansi-styles@6.2.1: 916 - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} 917 - engines: {node: '>=12'} 918 948 919 949 any-promise@1.3.0: 920 950 resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} ··· 926 956 arg@5.0.2: 927 957 resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} 928 958 929 - as-table@1.0.55: 930 - resolution: {integrity: sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==} 931 - 932 - autoprefixer@10.4.21: 933 - resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} 959 + autoprefixer@10.4.22: 960 + resolution: {integrity: sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==} 934 961 engines: {node: ^10 || ^12 || >=14} 935 962 hasBin: true 936 963 peerDependencies: 937 964 postcss: ^8.1.0 938 965 939 - babel-plugin-jsx-dom-expressions@0.39.8: 940 - resolution: {integrity: sha512-/MVOIIjonylDXnrWmG23ZX82m9mtKATsVHB7zYlPfDR9Vdd/NBE48if+wv27bSkBtyO7EPMUlcUc4J63QwuACQ==} 966 + babel-plugin-jsx-dom-expressions@0.40.3: 967 + resolution: {integrity: sha512-5HOwwt0BYiv/zxl7j8Pf2bGL6rDXfV6nUhLs8ygBX+EFJXzBPHM/euj9j/6deMZ6wa52Wb2PBaAV5U/jKwIY1w==} 941 968 peerDependencies: 942 969 '@babel/core': ^7.20.12 943 970 944 - babel-preset-solid@1.9.6: 945 - resolution: {integrity: sha512-HXTK9f93QxoH8dYn1M2mJdOlWgMsR88Lg/ul6QCZGkNTktjTE5HAf93YxQumHoCudLEtZrU1cFCMFOVho6GqFg==} 971 + babel-preset-solid@1.9.10: 972 + resolution: {integrity: sha512-HCelrgua/Y+kqO8RyL04JBWS/cVdrtUv/h45GntgQY+cJl4eBcKkCDV3TdMjtKx1nXwRaR9QXslM/Npm1dxdZQ==} 946 973 peerDependencies: 947 974 '@babel/core': ^7.0.0 975 + solid-js: ^1.9.10 976 + peerDependenciesMeta: 977 + solid-js: 978 + optional: true 948 979 949 - balanced-match@1.0.2: 950 - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 980 + baseline-browser-mapping@2.9.5: 981 + resolution: {integrity: sha512-D5vIoztZOq1XM54LUdttJVc96ggEsIfju2JBvht06pSzpckp3C7HReun67Bghzrtdsq9XdMGbSSB3v3GhMNmAA==} 982 + hasBin: true 951 983 952 984 binary-extensions@2.3.0: 953 985 resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} ··· 956 988 blake3-wasm@2.1.5: 957 989 resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} 958 990 959 - brace-expansion@2.0.1: 960 - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 961 - 962 991 braces@3.0.3: 963 992 resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} 964 993 engines: {node: '>=8'} 965 994 966 - browserslist@4.24.5: 967 - resolution: {integrity: sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==} 995 + browserslist@4.28.1: 996 + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} 968 997 engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} 969 998 hasBin: true 970 999 ··· 975 1004 resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} 976 1005 engines: {node: '>= 6'} 977 1006 978 - caniuse-lite@1.0.30001717: 979 - resolution: {integrity: sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw==} 1007 + caniuse-lite@1.0.30001760: 1008 + resolution: {integrity: sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==} 980 1009 981 1010 chokidar@3.6.0: 982 1011 resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} ··· 1006 1035 convert-source-map@2.0.0: 1007 1036 resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} 1008 1037 1009 - cookie@0.7.2: 1010 - resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} 1011 - engines: {node: '>= 0.6'} 1012 - 1013 - cross-spawn@7.0.6: 1014 - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 1015 - engines: {node: '>= 8'} 1038 + cookie@1.1.1: 1039 + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} 1040 + engines: {node: '>=18'} 1016 1041 1017 1042 cssesc@3.0.0: 1018 1043 resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} 1019 1044 engines: {node: '>=4'} 1020 1045 hasBin: true 1021 1046 1022 - csstype@3.1.3: 1023 - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 1047 + csstype@3.2.3: 1048 + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} 1024 1049 1025 - data-uri-to-buffer@2.0.2: 1026 - resolution: {integrity: sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==} 1027 - 1028 - debug@4.4.0: 1029 - resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} 1050 + debug@4.4.3: 1051 + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} 1030 1052 engines: {node: '>=6.0'} 1031 1053 peerDependencies: 1032 1054 supports-color: '*' ··· 1034 1056 supports-color: 1035 1057 optional: true 1036 1058 1037 - defu@6.1.4: 1038 - resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} 1039 - 1040 - detect-libc@2.0.4: 1041 - resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} 1059 + detect-libc@2.1.2: 1060 + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} 1042 1061 engines: {node: '>=8'} 1043 1062 1044 1063 didyoumean@1.2.2: ··· 1047 1066 dlv@1.1.3: 1048 1067 resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} 1049 1068 1050 - eastasianwidth@0.2.0: 1051 - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} 1069 + electron-to-chromium@1.5.267: 1070 + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} 1052 1071 1053 - electron-to-chromium@1.5.150: 1054 - resolution: {integrity: sha512-rOOkP2ZUMx1yL4fCxXQKDHQ8ZXwisb2OycOQVKHgvB3ZI4CvehOd4y2tfnnLDieJ3Zs1RL1Dlp3cMkyIn7nnXA==} 1055 - 1056 - emoji-regex@8.0.0: 1057 - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 1058 - 1059 - emoji-regex@9.2.2: 1060 - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} 1061 - 1062 - entities@6.0.0: 1063 - resolution: {integrity: sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==} 1072 + entities@6.0.1: 1073 + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} 1064 1074 engines: {node: '>=0.12'} 1065 1075 1066 - esbuild@0.25.2: 1067 - resolution: {integrity: sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==} 1076 + error-stack-parser-es@1.0.5: 1077 + resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} 1078 + 1079 + esbuild@0.25.12: 1080 + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} 1068 1081 engines: {node: '>=18'} 1069 1082 hasBin: true 1070 1083 1071 - esbuild@0.25.4: 1072 - resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==} 1084 + esbuild@0.27.0: 1085 + resolution: {integrity: sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==} 1073 1086 engines: {node: '>=18'} 1074 1087 hasBin: true 1075 1088 ··· 1077 1090 resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} 1078 1091 engines: {node: '>=6'} 1079 1092 1093 + esm-env@1.2.2: 1094 + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} 1095 + 1080 1096 exit-hook@2.2.1: 1081 1097 resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==} 1082 1098 engines: {node: '>=6'} 1083 - 1084 - exsolve@1.0.5: 1085 - resolution: {integrity: sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==} 1086 1099 1087 1100 fast-glob@3.3.3: 1088 1101 resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} ··· 1091 1104 fastq@1.19.1: 1092 1105 resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} 1093 1106 1094 - fdir@6.4.4: 1095 - resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} 1107 + fdir@6.5.0: 1108 + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} 1109 + engines: {node: '>=12.0.0'} 1096 1110 peerDependencies: 1097 1111 picomatch: ^3 || ^4 1098 1112 peerDependenciesMeta: ··· 1107 1121 resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} 1108 1122 engines: {node: '>=8'} 1109 1123 1110 - foreground-child@3.3.1: 1111 - resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} 1112 - engines: {node: '>=14'} 1113 - 1114 - fraction.js@4.3.7: 1115 - resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} 1124 + fraction.js@5.3.4: 1125 + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} 1116 1126 1117 1127 fsevents@2.3.3: 1118 1128 resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} ··· 1126 1136 resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} 1127 1137 engines: {node: '>=6.9.0'} 1128 1138 1129 - get-source@2.0.12: 1130 - resolution: {integrity: sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==} 1131 - 1132 1139 glob-parent@5.1.2: 1133 1140 resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 1134 1141 engines: {node: '>= 6'} ··· 1140 1147 glob-to-regexp@0.4.1: 1141 1148 resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} 1142 1149 1143 - glob@10.4.5: 1144 - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} 1145 - hasBin: true 1146 - 1147 - globals@11.12.0: 1148 - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} 1149 - engines: {node: '>=4'} 1150 - 1151 1150 hasown@2.0.2: 1152 1151 resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 1153 1152 engines: {node: '>= 0.4'} ··· 1155 1154 html-entities@2.3.3: 1156 1155 resolution: {integrity: sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==} 1157 1156 1158 - is-arrayish@0.3.2: 1159 - resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} 1157 + is-arrayish@0.3.4: 1158 + resolution: {integrity: sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==} 1160 1159 1161 1160 is-binary-path@2.1.0: 1162 1161 resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} ··· 1170 1169 resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 1171 1170 engines: {node: '>=0.10.0'} 1172 1171 1173 - is-fullwidth-code-point@3.0.0: 1174 - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 1175 - engines: {node: '>=8'} 1176 - 1177 1172 is-glob@4.0.3: 1178 1173 resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 1179 1174 engines: {node: '>=0.10.0'} ··· 1186 1181 resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} 1187 1182 engines: {node: '>=12.13'} 1188 1183 1189 - isexe@2.0.0: 1190 - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 1191 - 1192 - jackspeak@3.4.3: 1193 - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} 1194 - 1195 1184 jiti@1.21.7: 1196 1185 resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} 1197 1186 hasBin: true ··· 1209 1198 engines: {node: '>=6'} 1210 1199 hasBin: true 1211 1200 1201 + kleur@4.1.5: 1202 + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} 1203 + engines: {node: '>=6'} 1204 + 1212 1205 lilconfig@3.1.3: 1213 1206 resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} 1214 1207 engines: {node: '>=14'} 1215 1208 1216 1209 lines-and-columns@1.2.4: 1217 1210 resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} 1218 - 1219 - lru-cache@10.4.3: 1220 - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} 1221 1211 1222 1212 lru-cache@5.1.1: 1223 1213 resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} ··· 1243 1233 resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} 1244 1234 hasBin: true 1245 1235 1246 - miniflare@4.20250428.1: 1247 - resolution: {integrity: sha512-M3qcJXjeAEimHrEeWXEhrJiC3YHB5M3QSqqK67pOTI+lHn0QyVG/2iFUjVJ/nv+i10uxeAEva8GRGeu+tKRCmQ==} 1236 + miniflare@4.20251202.1: 1237 + resolution: {integrity: sha512-cRp2QNgnt9wpLMoNs4MOzzomyfe9UTS9sPRxIpUvxMl+mweCZ0FHpWWQvCnU7wWlfAP8VGZrHwqSsV5ERA6ahQ==} 1248 1238 engines: {node: '>=18.0.0'} 1249 1239 hasBin: true 1250 1240 1251 - minimatch@9.0.5: 1252 - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 1253 - engines: {node: '>=16 || 14 >=14.17'} 1254 - 1255 - minipass@7.1.2: 1256 - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} 1257 - engines: {node: '>=16 || 14 >=14.17'} 1258 - 1259 1241 ms@2.1.3: 1260 1242 resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 1261 1243 1262 - mustache@4.2.0: 1263 - resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} 1264 - hasBin: true 1265 - 1266 1244 mz@2.7.0: 1267 1245 resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} 1268 1246 ··· 1271 1249 engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 1272 1250 hasBin: true 1273 1251 1274 - nanoid@5.1.5: 1275 - resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==} 1252 + nanoid@5.1.6: 1253 + resolution: {integrity: sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==} 1276 1254 engines: {node: ^18 || >=20} 1277 1255 hasBin: true 1278 1256 ··· 1285 1263 engines: {node: '>=10.5.0'} 1286 1264 deprecated: Use your platform's native DOMException instead 1287 1265 1288 - node-releases@2.0.19: 1289 - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} 1266 + node-releases@2.0.27: 1267 + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} 1290 1268 1291 1269 normalize-path@3.0.0: 1292 1270 resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} ··· 1304 1282 resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} 1305 1283 engines: {node: '>= 6'} 1306 1284 1307 - ohash@2.0.11: 1308 - resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} 1309 - 1310 - package-json-from-dist@1.0.1: 1311 - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} 1312 - 1313 1285 parse5@7.3.0: 1314 1286 resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} 1315 1287 1316 - path-key@3.1.1: 1317 - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 1318 - engines: {node: '>=8'} 1319 - 1320 1288 path-parse@1.0.7: 1321 1289 resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 1322 - 1323 - path-scurry@1.11.1: 1324 - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} 1325 - engines: {node: '>=16 || 14 >=14.18'} 1326 1290 1327 1291 path-to-regexp@6.3.0: 1328 1292 resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} ··· 1337 1301 resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 1338 1302 engines: {node: '>=8.6'} 1339 1303 1340 - picomatch@4.0.2: 1341 - resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} 1304 + picomatch@4.0.3: 1305 + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} 1342 1306 engines: {node: '>=12'} 1343 1307 1344 1308 pify@2.3.0: ··· 1355 1319 peerDependencies: 1356 1320 postcss: ^8.0.0 1357 1321 1358 - postcss-js@4.0.1: 1359 - resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} 1322 + postcss-js@4.1.0: 1323 + resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} 1360 1324 engines: {node: ^12 || ^14 || >= 16} 1361 1325 peerDependencies: 1362 1326 postcss: ^8.4.21 1363 1327 1364 - postcss-load-config@4.0.2: 1365 - resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} 1366 - engines: {node: '>= 14'} 1328 + postcss-load-config@6.0.1: 1329 + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} 1330 + engines: {node: '>= 18'} 1367 1331 peerDependencies: 1332 + jiti: '>=1.21.0' 1368 1333 postcss: '>=8.0.9' 1369 - ts-node: '>=9.0.0' 1334 + tsx: ^4.8.1 1335 + yaml: ^2.4.2 1370 1336 peerDependenciesMeta: 1337 + jiti: 1338 + optional: true 1371 1339 postcss: 1372 1340 optional: true 1373 - ts-node: 1341 + tsx: 1342 + optional: true 1343 + yaml: 1374 1344 optional: true 1375 1345 1376 1346 postcss-nested@6.2.0: ··· 1386 1356 postcss-value-parser@4.2.0: 1387 1357 resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} 1388 1358 1389 - postcss@8.5.3: 1390 - resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} 1359 + postcss@8.5.6: 1360 + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} 1391 1361 engines: {node: ^10 || ^12 || >=14} 1392 1362 1393 - prettier-plugin-tailwindcss@0.6.11: 1394 - resolution: {integrity: sha512-YxaYSIvZPAqhrrEpRtonnrXdghZg1irNg4qrjboCXrpybLWVs55cW2N3juhspVJiO0JBvYJT8SYsJpc8OQSnsA==} 1363 + prettier-plugin-tailwindcss@0.6.14: 1364 + resolution: {integrity: sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg==} 1395 1365 engines: {node: '>=14.21.3'} 1396 1366 peerDependencies: 1397 1367 '@ianvs/prettier-plugin-sort-imports': '*' 1368 + '@prettier/plugin-hermes': '*' 1369 + '@prettier/plugin-oxc': '*' 1398 1370 '@prettier/plugin-pug': '*' 1399 1371 '@shopify/prettier-plugin-liquid': '*' 1400 1372 '@trivago/prettier-plugin-sort-imports': '*' ··· 1414 1386 peerDependenciesMeta: 1415 1387 '@ianvs/prettier-plugin-sort-imports': 1416 1388 optional: true 1389 + '@prettier/plugin-hermes': 1390 + optional: true 1391 + '@prettier/plugin-oxc': 1392 + optional: true 1417 1393 '@prettier/plugin-pug': 1418 1394 optional: true 1419 1395 '@shopify/prettier-plugin-liquid': ··· 1445 1421 prettier-plugin-svelte: 1446 1422 optional: true 1447 1423 1448 - prettier@3.5.3: 1449 - resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} 1424 + prettier@3.7.4: 1425 + resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} 1450 1426 engines: {node: '>=14'} 1451 1427 hasBin: true 1452 - 1453 - printable-characters@1.0.42: 1454 - resolution: {integrity: sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==} 1455 1428 1456 1429 queue-microtask@1.2.3: 1457 1430 resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} ··· 1463 1436 resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} 1464 1437 engines: {node: '>=8.10.0'} 1465 1438 1466 - resolve@1.22.10: 1467 - resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} 1439 + resolve@1.22.11: 1440 + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} 1468 1441 engines: {node: '>= 0.4'} 1469 1442 hasBin: true 1470 1443 ··· 1472 1445 resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} 1473 1446 engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 1474 1447 1475 - rollup@4.40.2: 1476 - resolution: {integrity: sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==} 1448 + rollup@4.53.3: 1449 + resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} 1477 1450 engines: {node: '>=18.0.0', npm: '>=8.0.0'} 1478 1451 hasBin: true 1479 1452 ··· 1484 1457 resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} 1485 1458 hasBin: true 1486 1459 1487 - semver@7.7.1: 1488 - resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} 1460 + semver@7.7.3: 1461 + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} 1489 1462 engines: {node: '>=10'} 1490 1463 hasBin: true 1491 1464 1492 - seroval-plugins@1.2.1: 1493 - resolution: {integrity: sha512-H5vs53+39+x4Udwp4J5rNZfgFuA+Lt+uU+09w1gYBVWomtAl98B+E9w7yC05Xc81/HgLvJdlyqJbU0fJCKCmdw==} 1465 + seroval-plugins@1.3.3: 1466 + resolution: {integrity: sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==} 1494 1467 engines: {node: '>=10'} 1495 1468 peerDependencies: 1496 1469 seroval: ^1.0 1497 1470 1498 - seroval@1.2.1: 1499 - resolution: {integrity: sha512-yBxFFs3zmkvKNmR0pFSU//rIsYjuX418TnlDmc2weaq5XFDqDIV/NOMPBoLrbxjLH42p4UzRuXHryXh9dYcKcw==} 1471 + seroval@1.3.2: 1472 + resolution: {integrity: sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==} 1500 1473 engines: {node: '>=10'} 1501 1474 1502 1475 sharp@0.33.5: 1503 1476 resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} 1504 1477 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 1505 1478 1506 - shebang-command@2.0.0: 1507 - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 1508 - engines: {node: '>=8'} 1479 + simple-swizzle@0.2.4: 1480 + resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==} 1509 1481 1510 - shebang-regex@3.0.0: 1511 - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 1512 - engines: {node: '>=8'} 1513 - 1514 - signal-exit@4.1.0: 1515 - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 1516 - engines: {node: '>=14'} 1517 - 1518 - simple-swizzle@0.2.2: 1519 - resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} 1520 - 1521 - solid-js@1.9.6: 1522 - resolution: {integrity: sha512-PoasAJvLk60hRtOTe9ulvALOdLjjqxuxcGZRolBQqxOnXrBXHGzqMT4ijNhGsDAYdOgEa8ZYaAE94PSldrFSkA==} 1482 + solid-js@1.9.10: 1483 + resolution: {integrity: sha512-Coz956cos/EPDlhs6+jsdTxKuJDPT7B5SVIWgABwROyxjY7Xbr8wkzD68Et+NxnV7DLJ3nJdAC2r9InuV/4Jew==} 1523 1484 1524 1485 solid-refresh@0.6.3: 1525 1486 resolution: {integrity: sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==} ··· 1537 1498 resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} 1538 1499 engines: {node: '>=0.10.0'} 1539 1500 1540 - stacktracey@2.1.8: 1541 - resolution: {integrity: sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==} 1542 - 1543 1501 stoppable@1.1.0: 1544 1502 resolution: {integrity: sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==} 1545 1503 engines: {node: '>=4', npm: '>=6'} 1546 1504 1547 - string-width@4.2.3: 1548 - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 1549 - engines: {node: '>=8'} 1550 - 1551 - string-width@5.1.2: 1552 - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} 1553 - engines: {node: '>=12'} 1554 - 1555 - strip-ansi@6.0.1: 1556 - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 1557 - engines: {node: '>=8'} 1558 - 1559 - strip-ansi@7.1.0: 1560 - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} 1561 - engines: {node: '>=12'} 1562 - 1563 - sucrase@3.35.0: 1564 - resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} 1505 + sucrase@3.35.1: 1506 + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} 1565 1507 engines: {node: '>=16 || 14 >=14.17'} 1566 1508 hasBin: true 1567 1509 1510 + supports-color@10.2.2: 1511 + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} 1512 + engines: {node: '>=18'} 1513 + 1568 1514 supports-preserve-symlinks-flag@1.0.0: 1569 1515 resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 1570 1516 engines: {node: '>= 0.4'} 1571 1517 1572 - tailwindcss@3.4.17: 1573 - resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} 1518 + tailwindcss@3.4.18: 1519 + resolution: {integrity: sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==} 1574 1520 engines: {node: '>=14.0.0'} 1575 1521 hasBin: true 1576 1522 1577 - terser@5.39.0: 1578 - resolution: {integrity: sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==} 1523 + terser@5.44.1: 1524 + resolution: {integrity: sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==} 1579 1525 engines: {node: '>=10'} 1580 1526 hasBin: true 1581 1527 ··· 1586 1532 thenify@3.3.1: 1587 1533 resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} 1588 1534 1589 - tinyglobby@0.2.13: 1590 - resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} 1535 + tinyglobby@0.2.15: 1536 + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} 1591 1537 engines: {node: '>=12.0.0'} 1592 1538 1593 1539 to-regex-range@5.0.1: ··· 1600 1546 tslib@2.8.1: 1601 1547 resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 1602 1548 1603 - typescript@5.8.3: 1604 - resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} 1549 + typescript@5.9.3: 1550 + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} 1605 1551 engines: {node: '>=14.17'} 1606 1552 hasBin: true 1607 1553 1608 - ufo@1.6.1: 1609 - resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} 1610 - 1611 1554 undici-types@6.21.0: 1612 1555 resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} 1613 1556 1614 - undici@5.29.0: 1615 - resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==} 1616 - engines: {node: '>=14.0'} 1557 + undici@7.14.0: 1558 + resolution: {integrity: sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ==} 1559 + engines: {node: '>=20.18.1'} 1617 1560 1618 - unenv@2.0.0-rc.15: 1619 - resolution: {integrity: sha512-J/rEIZU8w6FOfLNz/hNKsnY+fFHWnu9MH4yRbSZF3xbbGHovcetXPs7sD+9p8L6CeNC//I9bhRYAOsBt2u7/OA==} 1561 + unenv@2.0.0-rc.24: 1562 + resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} 1620 1563 1621 - update-browserslist-db@1.1.3: 1622 - resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} 1564 + update-browserslist-db@1.2.2: 1565 + resolution: {integrity: sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==} 1623 1566 hasBin: true 1624 1567 peerDependencies: 1625 1568 browserslist: '>= 4.21.0' ··· 1627 1570 util-deprecate@1.0.2: 1628 1571 resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} 1629 1572 1630 - validate-html-nesting@1.2.2: 1631 - resolution: {integrity: sha512-hGdgQozCsQJMyfK5urgFcWEqsSSrK63Awe0t/IMR0bZ0QMtnuaiHzThW81guu3qx9abLi99NEuiaN6P9gVYsNg==} 1632 - 1633 - vite-plugin-solid@2.11.6: 1634 - resolution: {integrity: sha512-Sl5CTqJTGyEeOsmdH6BOgalIZlwH3t4/y0RQuFLMGnvWMBvxb4+lq7x3BSiAw6etf0QexfNJW7HSOO/Qf7pigg==} 1573 + vite-plugin-solid@2.11.10: 1574 + resolution: {integrity: sha512-Yr1dQybmtDtDAHkii6hXuc1oVH9CPcS/Zb2jN/P36qqcrkNnVPsMTzQ06jyzFPFjj3U1IYKMVt/9ZqcwGCEbjw==} 1635 1575 peerDependencies: 1636 1576 '@testing-library/jest-dom': ^5.16.6 || ^5.17.0 || ^6.* 1637 1577 solid-js: ^1.7.2 1638 - vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 1578 + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 1639 1579 peerDependenciesMeta: 1640 1580 '@testing-library/jest-dom': 1641 1581 optional: true 1642 1582 1643 - vite@6.3.5: 1644 - resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} 1645 - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} 1583 + vite@7.2.7: 1584 + resolution: {integrity: sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==} 1585 + engines: {node: ^20.19.0 || >=22.12.0} 1646 1586 hasBin: true 1647 1587 peerDependencies: 1648 - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 1588 + '@types/node': ^20.19.0 || >=22.12.0 1649 1589 jiti: '>=1.21.0' 1650 - less: '*' 1590 + less: ^4.0.0 1651 1591 lightningcss: ^1.21.0 1652 - sass: '*' 1653 - sass-embedded: '*' 1654 - stylus: '*' 1655 - sugarss: '*' 1592 + sass: ^1.70.0 1593 + sass-embedded: ^1.70.0 1594 + stylus: '>=0.54.8' 1595 + sugarss: ^5.0.0 1656 1596 terser: ^5.16.0 1657 1597 tsx: ^4.8.1 1658 1598 yaml: ^2.4.2 ··· 1680 1620 yaml: 1681 1621 optional: true 1682 1622 1683 - vitefu@1.0.6: 1684 - resolution: {integrity: sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA==} 1623 + vitefu@1.1.1: 1624 + resolution: {integrity: sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==} 1685 1625 peerDependencies: 1686 - vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 1626 + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0 1687 1627 peerDependenciesMeta: 1688 1628 vite: 1689 1629 optional: true ··· 1692 1632 resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} 1693 1633 engines: {node: '>= 8'} 1694 1634 1695 - which@2.0.2: 1696 - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 1697 - engines: {node: '>= 8'} 1698 - hasBin: true 1699 - 1700 - workerd@1.20250428.0: 1701 - resolution: {integrity: sha512-JJNWkHkwPQKQdvtM9UORijgYdcdJsihA4SfYjwh02IUQsdMyZ9jizV1sX9yWi9B9ptlohTW8UNHJEATuphGgdg==} 1635 + workerd@1.20251202.0: 1636 + resolution: {integrity: sha512-p08YfrUMHkjCECNdT36r+6DpJIZX4kixbZ4n6GMUcLR5Gh18fakSCsiQrh72iOm4M9QHv/rM7P8YvCrUPWT5sg==} 1702 1637 engines: {node: '>=16'} 1703 1638 hasBin: true 1704 1639 1705 - wrangler@4.14.1: 1706 - resolution: {integrity: sha512-EU7IThP7i68TBftJJSveogvWZ5k/WRijcJh3UclDWiWWhDZTPbL6LOJEFhHKqFzHOaC4Y2Aewt48rfTz0e7oCw==} 1707 - engines: {node: '>=18.0.0'} 1640 + wrangler@4.53.0: 1641 + resolution: {integrity: sha512-/wvnHlRnlHsqaeIgGbmcEJE5NFYdTUWHCKow+U5Tv2XwQXI9vXUqBwCLAGy/BwqyS5nnycRt2kppqCzgHgyb7Q==} 1642 + engines: {node: '>=20.0.0'} 1708 1643 hasBin: true 1709 1644 peerDependencies: 1710 - '@cloudflare/workers-types': ^4.20250428.0 1645 + '@cloudflare/workers-types': ^4.20251202.0 1711 1646 peerDependenciesMeta: 1712 1647 '@cloudflare/workers-types': 1713 1648 optional: true 1714 - 1715 - wrap-ansi@7.0.0: 1716 - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 1717 - engines: {node: '>=10'} 1718 - 1719 - wrap-ansi@8.1.0: 1720 - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} 1721 - engines: {node: '>=12'} 1722 1649 1723 1650 ws@8.18.0: 1724 1651 resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} ··· 1735 1662 yallist@3.1.1: 1736 1663 resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} 1737 1664 1738 - yaml@2.7.1: 1739 - resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==} 1740 - engines: {node: '>= 14'} 1741 - hasBin: true 1665 + youch-core@0.3.3: 1666 + resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==} 1742 1667 1743 - youch@3.3.4: 1744 - resolution: {integrity: sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg==} 1668 + youch@4.1.0-beta.10: 1669 + resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==} 1745 1670 1746 1671 zod@3.22.3: 1747 1672 resolution: {integrity: sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==} ··· 1750 1675 1751 1676 '@alloc/quick-lru@5.2.0': {} 1752 1677 1753 - '@ampproject/remapping@2.3.0': 1678 + '@atcute/atproto@3.1.9': 1754 1679 dependencies: 1755 - '@jridgewell/gen-mapping': 0.3.8 1756 - '@jridgewell/trace-mapping': 0.3.25 1680 + '@atcute/lexicons': 1.2.5 1757 1681 1758 - '@atcute/bluesky@2.1.1(@atcute/client@3.1.0)': 1682 + '@atcute/bluesky@3.2.13': 1759 1683 dependencies: 1760 - '@atcute/client': 3.1.0 1684 + '@atcute/atproto': 3.1.9 1685 + '@atcute/lexicons': 1.2.5 1761 1686 1762 - '@atcute/car@3.0.4': 1687 + '@atcute/car@5.0.0': 1763 1688 dependencies: 1764 - '@atcute/cbor': 2.2.3 1765 - '@atcute/cid': 2.2.2 1766 - '@atcute/varint': 1.0.2 1689 + '@atcute/cbor': 2.2.8 1690 + '@atcute/cid': 2.2.6 1691 + '@atcute/uint8array': 1.0.6 1692 + '@atcute/varint': 1.0.3 1767 1693 1768 - '@atcute/cbor@2.2.3': 1694 + '@atcute/cbor@2.2.8': 1769 1695 dependencies: 1770 - '@atcute/cid': 2.2.2 1771 - '@atcute/multibase': 1.1.3 1772 - '@atcute/uint8array': 1.0.1 1696 + '@atcute/cid': 2.2.6 1697 + '@atcute/multibase': 1.1.6 1698 + '@atcute/uint8array': 1.0.6 1773 1699 1774 - '@atcute/cid@2.2.2': 1700 + '@atcute/cid@2.2.6': 1775 1701 dependencies: 1776 - '@atcute/multibase': 1.1.3 1777 - '@atcute/uint8array': 1.0.1 1702 + '@atcute/multibase': 1.1.6 1703 + '@atcute/uint8array': 1.0.6 1778 1704 1779 - '@atcute/client@3.1.0': {} 1705 + '@atcute/client@4.1.1': 1706 + dependencies: 1707 + '@atcute/identity': 1.1.3 1708 + '@atcute/lexicons': 1.2.5 1780 1709 1781 - '@atcute/crypto@2.2.1': 1710 + '@atcute/crypto@2.3.0': 1782 1711 dependencies: 1783 - '@atcute/multibase': 1.1.3 1784 - '@atcute/uint8array': 1.0.1 1785 - '@noble/secp256k1': 2.2.3 1712 + '@atcute/multibase': 1.1.6 1713 + '@atcute/uint8array': 1.0.6 1714 + '@noble/secp256k1': 3.0.0 1786 1715 1787 - '@atcute/did-plc@0.1.4': 1716 + '@atcute/did-plc@0.2.0': 1788 1717 dependencies: 1789 - '@atcute/cbor': 2.2.3 1790 - '@atcute/cid': 2.2.2 1791 - '@atcute/crypto': 2.2.1 1792 - '@atcute/multibase': 1.1.3 1793 - '@atcute/uint8array': 1.0.1 1794 - '@badrap/valita': 0.4.4 1718 + '@atcute/cbor': 2.2.8 1719 + '@atcute/cid': 2.2.6 1720 + '@atcute/crypto': 2.3.0 1721 + '@atcute/identity': 1.1.3 1722 + '@atcute/lexicons': 1.2.5 1723 + '@atcute/multibase': 1.1.6 1724 + '@atcute/uint8array': 1.0.6 1725 + '@badrap/valita': 0.4.6 1795 1726 1796 - '@atcute/identity-resolver@0.1.2(@atcute/identity@0.1.3)': 1727 + '@atcute/identity-resolver@1.2.0(@atcute/identity@1.1.3)': 1797 1728 dependencies: 1798 - '@atcute/identity': 0.1.3 1799 - '@atcute/util-fetch': 1.0.1 1800 - '@badrap/valita': 0.4.4 1729 + '@atcute/identity': 1.1.3 1730 + '@atcute/lexicons': 1.2.5 1731 + '@atcute/util-fetch': 1.0.4 1732 + '@badrap/valita': 0.4.6 1801 1733 1802 - '@atcute/identity@0.1.3': 1734 + '@atcute/identity@1.1.3': 1803 1735 dependencies: 1804 - '@badrap/valita': 0.4.4 1736 + '@atcute/lexicons': 1.2.5 1737 + '@badrap/valita': 0.4.6 1738 + 1739 + '@atcute/lexicons@1.2.5': 1740 + dependencies: 1741 + '@standard-schema/spec': 1.0.0 1742 + esm-env: 1.2.2 1743 + 1744 + '@atcute/mst@0.1.0': 1745 + dependencies: 1746 + '@atcute/cbor': 2.2.8 1747 + '@atcute/cid': 2.2.6 1748 + '@atcute/uint8array': 1.0.6 1749 + 1750 + '@atcute/multibase@1.1.6': 1751 + dependencies: 1752 + '@atcute/uint8array': 1.0.6 1805 1753 1806 - '@atcute/multibase@1.1.3': 1754 + '@atcute/repo@0.1.0': 1807 1755 dependencies: 1808 - '@atcute/uint8array': 1.0.1 1756 + '@atcute/car': 5.0.0 1757 + '@atcute/cbor': 2.2.8 1758 + '@atcute/cid': 2.2.6 1759 + '@atcute/crypto': 2.3.0 1760 + '@atcute/lexicons': 1.2.5 1761 + '@atcute/mst': 0.1.0 1762 + '@atcute/uint8array': 1.0.6 1809 1763 1810 - '@atcute/tid@1.0.2': {} 1764 + '@atcute/tid@1.0.3': {} 1811 1765 1812 - '@atcute/uint8array@1.0.1': {} 1766 + '@atcute/uint8array@1.0.6': {} 1813 1767 1814 - '@atcute/util-fetch@1.0.1': 1768 + '@atcute/util-fetch@1.0.4': 1815 1769 dependencies: 1816 - '@badrap/valita': 0.4.4 1770 + '@badrap/valita': 0.4.6 1817 1771 1818 - '@atcute/varint@1.0.2': {} 1772 + '@atcute/varint@1.0.3': {} 1819 1773 1820 1774 '@babel/code-frame@7.27.1': 1821 1775 dependencies: 1822 - '@babel/helper-validator-identifier': 7.27.1 1776 + '@babel/helper-validator-identifier': 7.28.5 1823 1777 js-tokens: 4.0.0 1824 1778 picocolors: 1.1.1 1825 1779 1826 - '@babel/compat-data@7.27.1': {} 1780 + '@babel/compat-data@7.28.5': {} 1827 1781 1828 - '@babel/core@7.27.1': 1782 + '@babel/core@7.28.5': 1829 1783 dependencies: 1830 - '@ampproject/remapping': 2.3.0 1831 1784 '@babel/code-frame': 7.27.1 1832 - '@babel/generator': 7.27.1 1833 - '@babel/helper-compilation-targets': 7.27.1 1834 - '@babel/helper-module-transforms': 7.27.1(@babel/core@7.27.1) 1835 - '@babel/helpers': 7.27.1 1836 - '@babel/parser': 7.27.1 1837 - '@babel/template': 7.27.1 1838 - '@babel/traverse': 7.27.1 1839 - '@babel/types': 7.27.1 1785 + '@babel/generator': 7.28.5 1786 + '@babel/helper-compilation-targets': 7.27.2 1787 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) 1788 + '@babel/helpers': 7.28.4 1789 + '@babel/parser': 7.28.5 1790 + '@babel/template': 7.27.2 1791 + '@babel/traverse': 7.28.5 1792 + '@babel/types': 7.28.5 1793 + '@jridgewell/remapping': 2.3.5 1840 1794 convert-source-map: 2.0.0 1841 - debug: 4.4.0 1795 + debug: 4.4.3 1842 1796 gensync: 1.0.0-beta.2 1843 1797 json5: 2.2.3 1844 1798 semver: 6.3.1 1845 1799 transitivePeerDependencies: 1846 1800 - supports-color 1847 1801 1848 - '@babel/generator@7.27.1': 1802 + '@babel/generator@7.28.5': 1849 1803 dependencies: 1850 - '@babel/parser': 7.27.1 1851 - '@babel/types': 7.27.1 1852 - '@jridgewell/gen-mapping': 0.3.8 1853 - '@jridgewell/trace-mapping': 0.3.25 1804 + '@babel/parser': 7.28.5 1805 + '@babel/types': 7.28.5 1806 + '@jridgewell/gen-mapping': 0.3.13 1807 + '@jridgewell/trace-mapping': 0.3.31 1854 1808 jsesc: 3.1.0 1855 1809 1856 - '@babel/helper-compilation-targets@7.27.1': 1810 + '@babel/helper-compilation-targets@7.27.2': 1857 1811 dependencies: 1858 - '@babel/compat-data': 7.27.1 1812 + '@babel/compat-data': 7.28.5 1859 1813 '@babel/helper-validator-option': 7.27.1 1860 - browserslist: 4.24.5 1814 + browserslist: 4.28.1 1861 1815 lru-cache: 5.1.1 1862 1816 semver: 6.3.1 1863 1817 1818 + '@babel/helper-globals@7.28.0': {} 1819 + 1864 1820 '@babel/helper-module-imports@7.18.6': 1865 1821 dependencies: 1866 - '@babel/types': 7.27.1 1822 + '@babel/types': 7.28.5 1867 1823 1868 1824 '@babel/helper-module-imports@7.27.1': 1869 1825 dependencies: 1870 - '@babel/traverse': 7.27.1 1871 - '@babel/types': 7.27.1 1826 + '@babel/traverse': 7.28.5 1827 + '@babel/types': 7.28.5 1872 1828 transitivePeerDependencies: 1873 1829 - supports-color 1874 1830 1875 - '@babel/helper-module-transforms@7.27.1(@babel/core@7.27.1)': 1831 + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': 1876 1832 dependencies: 1877 - '@babel/core': 7.27.1 1833 + '@babel/core': 7.28.5 1878 1834 '@babel/helper-module-imports': 7.27.1 1879 - '@babel/helper-validator-identifier': 7.27.1 1880 - '@babel/traverse': 7.27.1 1835 + '@babel/helper-validator-identifier': 7.28.5 1836 + '@babel/traverse': 7.28.5 1881 1837 transitivePeerDependencies: 1882 1838 - supports-color 1883 1839 ··· 1885 1841 1886 1842 '@babel/helper-string-parser@7.27.1': {} 1887 1843 1888 - '@babel/helper-validator-identifier@7.27.1': {} 1844 + '@babel/helper-validator-identifier@7.28.5': {} 1889 1845 1890 1846 '@babel/helper-validator-option@7.27.1': {} 1891 1847 1892 - '@babel/helpers@7.27.1': 1848 + '@babel/helpers@7.28.4': 1893 1849 dependencies: 1894 - '@babel/template': 7.27.1 1895 - '@babel/types': 7.27.1 1850 + '@babel/template': 7.27.2 1851 + '@babel/types': 7.28.5 1896 1852 1897 - '@babel/parser@7.27.1': 1853 + '@babel/parser@7.28.5': 1898 1854 dependencies: 1899 - '@babel/types': 7.27.1 1855 + '@babel/types': 7.28.5 1900 1856 1901 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.27.1)': 1857 + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)': 1902 1858 dependencies: 1903 - '@babel/core': 7.27.1 1859 + '@babel/core': 7.28.5 1904 1860 '@babel/helper-plugin-utils': 7.27.1 1905 1861 1906 - '@babel/template@7.27.1': 1862 + '@babel/template@7.27.2': 1907 1863 dependencies: 1908 1864 '@babel/code-frame': 7.27.1 1909 - '@babel/parser': 7.27.1 1910 - '@babel/types': 7.27.1 1865 + '@babel/parser': 7.28.5 1866 + '@babel/types': 7.28.5 1911 1867 1912 - '@babel/traverse@7.27.1': 1868 + '@babel/traverse@7.28.5': 1913 1869 dependencies: 1914 1870 '@babel/code-frame': 7.27.1 1915 - '@babel/generator': 7.27.1 1916 - '@babel/parser': 7.27.1 1917 - '@babel/template': 7.27.1 1918 - '@babel/types': 7.27.1 1919 - debug: 4.4.0 1920 - globals: 11.12.0 1871 + '@babel/generator': 7.28.5 1872 + '@babel/helper-globals': 7.28.0 1873 + '@babel/parser': 7.28.5 1874 + '@babel/template': 7.27.2 1875 + '@babel/types': 7.28.5 1876 + debug: 4.4.3 1921 1877 transitivePeerDependencies: 1922 1878 - supports-color 1923 1879 1924 - '@babel/types@7.27.1': 1880 + '@babel/types@7.28.5': 1925 1881 dependencies: 1926 1882 '@babel/helper-string-parser': 7.27.1 1927 - '@babel/helper-validator-identifier': 7.27.1 1883 + '@babel/helper-validator-identifier': 7.28.5 1928 1884 1929 - '@badrap/valita@0.4.4': {} 1885 + '@badrap/valita@0.4.6': {} 1930 1886 1931 - '@cloudflare/kv-asset-handler@0.4.0': 1887 + '@cloudflare/kv-asset-handler@0.4.1': 1932 1888 dependencies: 1933 1889 mime: 3.0.0 1934 1890 1935 - '@cloudflare/unenv-preset@2.3.1(unenv@2.0.0-rc.15)(workerd@1.20250428.0)': 1891 + '@cloudflare/unenv-preset@2.7.13(unenv@2.0.0-rc.24)(workerd@1.20251202.0)': 1936 1892 dependencies: 1937 - unenv: 2.0.0-rc.15 1893 + unenv: 2.0.0-rc.24 1938 1894 optionalDependencies: 1939 - workerd: 1.20250428.0 1895 + workerd: 1.20251202.0 1940 1896 1941 - '@cloudflare/workerd-darwin-64@1.20250428.0': 1897 + '@cloudflare/workerd-darwin-64@1.20251202.0': 1942 1898 optional: true 1943 1899 1944 - '@cloudflare/workerd-darwin-arm64@1.20250428.0': 1900 + '@cloudflare/workerd-darwin-arm64@1.20251202.0': 1945 1901 optional: true 1946 1902 1947 - '@cloudflare/workerd-linux-64@1.20250428.0': 1903 + '@cloudflare/workerd-linux-64@1.20251202.0': 1948 1904 optional: true 1949 1905 1950 - '@cloudflare/workerd-linux-arm64@1.20250428.0': 1906 + '@cloudflare/workerd-linux-arm64@1.20251202.0': 1951 1907 optional: true 1952 1908 1953 - '@cloudflare/workerd-windows-64@1.20250428.0': 1909 + '@cloudflare/workerd-windows-64@1.20251202.0': 1954 1910 optional: true 1955 1911 1956 1912 '@cspotcode/source-map-support@0.8.1': 1957 1913 dependencies: 1958 1914 '@jridgewell/trace-mapping': 0.3.9 1959 1915 1960 - '@emnapi/runtime@1.4.3': 1916 + '@emnapi/runtime@1.7.1': 1961 1917 dependencies: 1962 1918 tslib: 2.8.1 1963 1919 optional: true 1964 1920 1965 - '@esbuild/aix-ppc64@0.25.2': 1921 + '@esbuild/aix-ppc64@0.25.12': 1966 1922 optional: true 1967 1923 1968 - '@esbuild/aix-ppc64@0.25.4': 1924 + '@esbuild/aix-ppc64@0.27.0': 1969 1925 optional: true 1970 1926 1971 - '@esbuild/android-arm64@0.25.2': 1927 + '@esbuild/android-arm64@0.25.12': 1972 1928 optional: true 1973 1929 1974 - '@esbuild/android-arm64@0.25.4': 1930 + '@esbuild/android-arm64@0.27.0': 1975 1931 optional: true 1976 1932 1977 - '@esbuild/android-arm@0.25.2': 1933 + '@esbuild/android-arm@0.25.12': 1978 1934 optional: true 1979 1935 1980 - '@esbuild/android-arm@0.25.4': 1936 + '@esbuild/android-arm@0.27.0': 1981 1937 optional: true 1982 1938 1983 - '@esbuild/android-x64@0.25.2': 1939 + '@esbuild/android-x64@0.25.12': 1984 1940 optional: true 1985 1941 1986 - '@esbuild/android-x64@0.25.4': 1942 + '@esbuild/android-x64@0.27.0': 1987 1943 optional: true 1988 1944 1989 - '@esbuild/darwin-arm64@0.25.2': 1945 + '@esbuild/darwin-arm64@0.25.12': 1990 1946 optional: true 1991 1947 1992 - '@esbuild/darwin-arm64@0.25.4': 1948 + '@esbuild/darwin-arm64@0.27.0': 1993 1949 optional: true 1994 1950 1995 - '@esbuild/darwin-x64@0.25.2': 1951 + '@esbuild/darwin-x64@0.25.12': 1996 1952 optional: true 1997 1953 1998 - '@esbuild/darwin-x64@0.25.4': 1954 + '@esbuild/darwin-x64@0.27.0': 1999 1955 optional: true 2000 1956 2001 - '@esbuild/freebsd-arm64@0.25.2': 1957 + '@esbuild/freebsd-arm64@0.25.12': 2002 1958 optional: true 2003 1959 2004 - '@esbuild/freebsd-arm64@0.25.4': 1960 + '@esbuild/freebsd-arm64@0.27.0': 2005 1961 optional: true 2006 1962 2007 - '@esbuild/freebsd-x64@0.25.2': 1963 + '@esbuild/freebsd-x64@0.25.12': 2008 1964 optional: true 2009 1965 2010 - '@esbuild/freebsd-x64@0.25.4': 1966 + '@esbuild/freebsd-x64@0.27.0': 2011 1967 optional: true 2012 1968 2013 - '@esbuild/linux-arm64@0.25.2': 1969 + '@esbuild/linux-arm64@0.25.12': 2014 1970 optional: true 2015 1971 2016 - '@esbuild/linux-arm64@0.25.4': 1972 + '@esbuild/linux-arm64@0.27.0': 2017 1973 optional: true 2018 1974 2019 - '@esbuild/linux-arm@0.25.2': 1975 + '@esbuild/linux-arm@0.25.12': 2020 1976 optional: true 2021 1977 2022 - '@esbuild/linux-arm@0.25.4': 1978 + '@esbuild/linux-arm@0.27.0': 2023 1979 optional: true 2024 1980 2025 - '@esbuild/linux-ia32@0.25.2': 1981 + '@esbuild/linux-ia32@0.25.12': 2026 1982 optional: true 2027 1983 2028 - '@esbuild/linux-ia32@0.25.4': 1984 + '@esbuild/linux-ia32@0.27.0': 2029 1985 optional: true 2030 1986 2031 - '@esbuild/linux-loong64@0.25.2': 1987 + '@esbuild/linux-loong64@0.25.12': 2032 1988 optional: true 2033 1989 2034 - '@esbuild/linux-loong64@0.25.4': 1990 + '@esbuild/linux-loong64@0.27.0': 2035 1991 optional: true 2036 1992 2037 - '@esbuild/linux-mips64el@0.25.2': 1993 + '@esbuild/linux-mips64el@0.25.12': 2038 1994 optional: true 2039 1995 2040 - '@esbuild/linux-mips64el@0.25.4': 1996 + '@esbuild/linux-mips64el@0.27.0': 2041 1997 optional: true 2042 1998 2043 - '@esbuild/linux-ppc64@0.25.2': 1999 + '@esbuild/linux-ppc64@0.25.12': 2044 2000 optional: true 2045 2001 2046 - '@esbuild/linux-ppc64@0.25.4': 2002 + '@esbuild/linux-ppc64@0.27.0': 2047 2003 optional: true 2048 2004 2049 - '@esbuild/linux-riscv64@0.25.2': 2005 + '@esbuild/linux-riscv64@0.25.12': 2050 2006 optional: true 2051 2007 2052 - '@esbuild/linux-riscv64@0.25.4': 2008 + '@esbuild/linux-riscv64@0.27.0': 2053 2009 optional: true 2054 2010 2055 - '@esbuild/linux-s390x@0.25.2': 2011 + '@esbuild/linux-s390x@0.25.12': 2056 2012 optional: true 2057 2013 2058 - '@esbuild/linux-s390x@0.25.4': 2014 + '@esbuild/linux-s390x@0.27.0': 2059 2015 optional: true 2060 2016 2061 - '@esbuild/linux-x64@0.25.2': 2017 + '@esbuild/linux-x64@0.25.12': 2062 2018 optional: true 2063 2019 2064 - '@esbuild/linux-x64@0.25.4': 2020 + '@esbuild/linux-x64@0.27.0': 2065 2021 optional: true 2066 2022 2067 - '@esbuild/netbsd-arm64@0.25.2': 2023 + '@esbuild/netbsd-arm64@0.25.12': 2068 2024 optional: true 2069 2025 2070 - '@esbuild/netbsd-arm64@0.25.4': 2026 + '@esbuild/netbsd-arm64@0.27.0': 2071 2027 optional: true 2072 2028 2073 - '@esbuild/netbsd-x64@0.25.2': 2029 + '@esbuild/netbsd-x64@0.25.12': 2074 2030 optional: true 2075 2031 2076 - '@esbuild/netbsd-x64@0.25.4': 2032 + '@esbuild/netbsd-x64@0.27.0': 2077 2033 optional: true 2078 2034 2079 - '@esbuild/openbsd-arm64@0.25.2': 2035 + '@esbuild/openbsd-arm64@0.25.12': 2080 2036 optional: true 2081 2037 2082 - '@esbuild/openbsd-arm64@0.25.4': 2038 + '@esbuild/openbsd-arm64@0.27.0': 2083 2039 optional: true 2084 2040 2085 - '@esbuild/openbsd-x64@0.25.2': 2041 + '@esbuild/openbsd-x64@0.25.12': 2086 2042 optional: true 2087 2043 2088 - '@esbuild/openbsd-x64@0.25.4': 2044 + '@esbuild/openbsd-x64@0.27.0': 2089 2045 optional: true 2090 2046 2091 - '@esbuild/sunos-x64@0.25.2': 2047 + '@esbuild/openharmony-arm64@0.25.12': 2092 2048 optional: true 2093 2049 2094 - '@esbuild/sunos-x64@0.25.4': 2050 + '@esbuild/openharmony-arm64@0.27.0': 2095 2051 optional: true 2096 2052 2097 - '@esbuild/win32-arm64@0.25.2': 2053 + '@esbuild/sunos-x64@0.25.12': 2098 2054 optional: true 2099 2055 2100 - '@esbuild/win32-arm64@0.25.4': 2056 + '@esbuild/sunos-x64@0.27.0': 2101 2057 optional: true 2102 2058 2103 - '@esbuild/win32-ia32@0.25.2': 2059 + '@esbuild/win32-arm64@0.25.12': 2104 2060 optional: true 2105 2061 2106 - '@esbuild/win32-ia32@0.25.4': 2062 + '@esbuild/win32-arm64@0.27.0': 2107 2063 optional: true 2108 2064 2109 - '@esbuild/win32-x64@0.25.2': 2065 + '@esbuild/win32-ia32@0.25.12': 2066 + optional: true 2067 + 2068 + '@esbuild/win32-ia32@0.27.0': 2069 + optional: true 2070 + 2071 + '@esbuild/win32-x64@0.25.12': 2110 2072 optional: true 2111 2073 2112 - '@esbuild/win32-x64@0.25.4': 2074 + '@esbuild/win32-x64@0.27.0': 2113 2075 optional: true 2114 2076 2115 - '@externdefs/solid-freeze@0.1.1(solid-js@1.9.6)': 2077 + '@externdefs/solid-freeze@0.1.1(solid-js@1.9.10)': 2116 2078 dependencies: 2117 - solid-js: 1.9.6 2118 - 2119 - '@fastify/busboy@2.1.1': {} 2079 + solid-js: 1.9.10 2120 2080 2121 2081 '@img/sharp-darwin-arm64@0.33.5': 2122 2082 optionalDependencies: ··· 2184 2144 2185 2145 '@img/sharp-wasm32@0.33.5': 2186 2146 dependencies: 2187 - '@emnapi/runtime': 1.4.3 2147 + '@emnapi/runtime': 1.7.1 2188 2148 optional: true 2189 2149 2190 2150 '@img/sharp-win32-ia32@0.33.5': ··· 2193 2153 '@img/sharp-win32-x64@0.33.5': 2194 2154 optional: true 2195 2155 2196 - '@isaacs/cliui@8.0.2': 2156 + '@jridgewell/gen-mapping@0.3.13': 2197 2157 dependencies: 2198 - string-width: 5.1.2 2199 - string-width-cjs: string-width@4.2.3 2200 - strip-ansi: 7.1.0 2201 - strip-ansi-cjs: strip-ansi@6.0.1 2202 - wrap-ansi: 8.1.0 2203 - wrap-ansi-cjs: wrap-ansi@7.0.0 2158 + '@jridgewell/sourcemap-codec': 1.5.5 2159 + '@jridgewell/trace-mapping': 0.3.31 2204 2160 2205 - '@jridgewell/gen-mapping@0.3.8': 2161 + '@jridgewell/remapping@2.3.5': 2206 2162 dependencies: 2207 - '@jridgewell/set-array': 1.2.1 2208 - '@jridgewell/sourcemap-codec': 1.5.0 2209 - '@jridgewell/trace-mapping': 0.3.25 2163 + '@jridgewell/gen-mapping': 0.3.13 2164 + '@jridgewell/trace-mapping': 0.3.31 2210 2165 2211 2166 '@jridgewell/resolve-uri@3.1.2': {} 2212 2167 2213 - '@jridgewell/set-array@1.2.1': {} 2214 - 2215 - '@jridgewell/source-map@0.3.6': 2168 + '@jridgewell/source-map@0.3.11': 2216 2169 dependencies: 2217 - '@jridgewell/gen-mapping': 0.3.8 2218 - '@jridgewell/trace-mapping': 0.3.25 2170 + '@jridgewell/gen-mapping': 0.3.13 2171 + '@jridgewell/trace-mapping': 0.3.31 2219 2172 2220 - '@jridgewell/sourcemap-codec@1.5.0': {} 2173 + '@jridgewell/sourcemap-codec@1.5.5': {} 2221 2174 2222 - '@jridgewell/trace-mapping@0.3.25': 2175 + '@jridgewell/trace-mapping@0.3.31': 2223 2176 dependencies: 2224 2177 '@jridgewell/resolve-uri': 3.1.2 2225 - '@jridgewell/sourcemap-codec': 1.5.0 2178 + '@jridgewell/sourcemap-codec': 1.5.5 2226 2179 2227 2180 '@jridgewell/trace-mapping@0.3.9': 2228 2181 dependencies: 2229 2182 '@jridgewell/resolve-uri': 3.1.2 2230 - '@jridgewell/sourcemap-codec': 1.5.0 2183 + '@jridgewell/sourcemap-codec': 1.5.5 2231 2184 2232 - '@jsr/mary__array-fns@0.1.4': {} 2185 + '@jsr/mary__array-fns@0.1.5': {} 2233 2186 2234 - '@jsr/mary__ds-queue@0.1.2': {} 2187 + '@jsr/mary__ds-queue@0.1.3': {} 2235 2188 2236 2189 '@jsr/mary__events@0.2.0': {} 2237 2190 2238 - '@jsr/mary__tar@0.2.4': {} 2191 + '@jsr/mary__tar@0.3.1': {} 2239 2192 2240 - '@noble/secp256k1@2.2.3': {} 2193 + '@noble/secp256k1@3.0.0': {} 2241 2194 2242 2195 '@nodelib/fs.scandir@2.1.5': 2243 2196 dependencies: ··· 2251 2204 '@nodelib/fs.scandir': 2.1.5 2252 2205 fastq: 1.19.1 2253 2206 2254 - '@pkgjs/parseargs@0.11.0': 2207 + '@poppinss/colors@4.1.5': 2208 + dependencies: 2209 + kleur: 4.1.5 2210 + 2211 + '@poppinss/dumper@0.6.5': 2212 + dependencies: 2213 + '@poppinss/colors': 4.1.5 2214 + '@sindresorhus/is': 7.1.1 2215 + supports-color: 10.2.2 2216 + 2217 + '@poppinss/exception@1.2.2': {} 2218 + 2219 + '@rollup/rollup-android-arm-eabi@4.53.3': 2255 2220 optional: true 2256 2221 2257 - '@rollup/rollup-android-arm-eabi@4.40.2': 2222 + '@rollup/rollup-android-arm64@4.53.3': 2258 2223 optional: true 2259 2224 2260 - '@rollup/rollup-android-arm64@4.40.2': 2225 + '@rollup/rollup-darwin-arm64@4.53.3': 2261 2226 optional: true 2262 2227 2263 - '@rollup/rollup-darwin-arm64@4.40.2': 2228 + '@rollup/rollup-darwin-x64@4.53.3': 2264 2229 optional: true 2265 2230 2266 - '@rollup/rollup-darwin-x64@4.40.2': 2231 + '@rollup/rollup-freebsd-arm64@4.53.3': 2267 2232 optional: true 2268 2233 2269 - '@rollup/rollup-freebsd-arm64@4.40.2': 2234 + '@rollup/rollup-freebsd-x64@4.53.3': 2235 + optional: true 2236 + 2237 + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': 2270 2238 optional: true 2271 2239 2272 - '@rollup/rollup-freebsd-x64@4.40.2': 2240 + '@rollup/rollup-linux-arm-musleabihf@4.53.3': 2273 2241 optional: true 2274 2242 2275 - '@rollup/rollup-linux-arm-gnueabihf@4.40.2': 2243 + '@rollup/rollup-linux-arm64-gnu@4.53.3': 2276 2244 optional: true 2277 2245 2278 - '@rollup/rollup-linux-arm-musleabihf@4.40.2': 2246 + '@rollup/rollup-linux-arm64-musl@4.53.3': 2279 2247 optional: true 2280 2248 2281 - '@rollup/rollup-linux-arm64-gnu@4.40.2': 2249 + '@rollup/rollup-linux-loong64-gnu@4.53.3': 2282 2250 optional: true 2283 2251 2284 - '@rollup/rollup-linux-arm64-musl@4.40.2': 2252 + '@rollup/rollup-linux-ppc64-gnu@4.53.3': 2285 2253 optional: true 2286 2254 2287 - '@rollup/rollup-linux-loongarch64-gnu@4.40.2': 2255 + '@rollup/rollup-linux-riscv64-gnu@4.53.3': 2288 2256 optional: true 2289 2257 2290 - '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': 2258 + '@rollup/rollup-linux-riscv64-musl@4.53.3': 2291 2259 optional: true 2292 2260 2293 - '@rollup/rollup-linux-riscv64-gnu@4.40.2': 2261 + '@rollup/rollup-linux-s390x-gnu@4.53.3': 2294 2262 optional: true 2295 2263 2296 - '@rollup/rollup-linux-riscv64-musl@4.40.2': 2264 + '@rollup/rollup-linux-x64-gnu@4.53.3': 2297 2265 optional: true 2298 2266 2299 - '@rollup/rollup-linux-s390x-gnu@4.40.2': 2267 + '@rollup/rollup-linux-x64-musl@4.53.3': 2300 2268 optional: true 2301 2269 2302 - '@rollup/rollup-linux-x64-gnu@4.40.2': 2270 + '@rollup/rollup-openharmony-arm64@4.53.3': 2303 2271 optional: true 2304 2272 2305 - '@rollup/rollup-linux-x64-musl@4.40.2': 2273 + '@rollup/rollup-win32-arm64-msvc@4.53.3': 2306 2274 optional: true 2307 2275 2308 - '@rollup/rollup-win32-arm64-msvc@4.40.2': 2276 + '@rollup/rollup-win32-ia32-msvc@4.53.3': 2309 2277 optional: true 2310 2278 2311 - '@rollup/rollup-win32-ia32-msvc@4.40.2': 2279 + '@rollup/rollup-win32-x64-gnu@4.53.3': 2312 2280 optional: true 2313 2281 2314 - '@rollup/rollup-win32-x64-msvc@4.40.2': 2282 + '@rollup/rollup-win32-x64-msvc@4.53.3': 2315 2283 optional: true 2316 2284 2317 - '@tailwindcss/forms@0.5.10(tailwindcss@3.4.17)': 2285 + '@sindresorhus/is@7.1.1': {} 2286 + 2287 + '@speed-highlight/core@1.2.12': {} 2288 + 2289 + '@standard-schema/spec@1.0.0': {} 2290 + 2291 + '@tailwindcss/forms@0.5.10(tailwindcss@3.4.18)': 2318 2292 dependencies: 2319 2293 mini-svg-data-uri: 1.4.4 2320 - tailwindcss: 3.4.17 2294 + tailwindcss: 3.4.18 2321 2295 2322 2296 '@types/babel__core@7.20.5': 2323 2297 dependencies: 2324 - '@babel/parser': 7.27.1 2325 - '@babel/types': 7.27.1 2298 + '@babel/parser': 7.28.5 2299 + '@babel/types': 7.28.5 2326 2300 '@types/babel__generator': 7.27.0 2327 2301 '@types/babel__template': 7.4.4 2328 - '@types/babel__traverse': 7.20.7 2302 + '@types/babel__traverse': 7.28.0 2329 2303 2330 2304 '@types/babel__generator@7.27.0': 2331 2305 dependencies: 2332 - '@babel/types': 7.27.1 2306 + '@babel/types': 7.28.5 2333 2307 2334 2308 '@types/babel__template@7.4.4': 2335 2309 dependencies: 2336 - '@babel/parser': 7.27.1 2337 - '@babel/types': 7.27.1 2310 + '@babel/parser': 7.28.5 2311 + '@babel/types': 7.28.5 2338 2312 2339 - '@types/babel__traverse@7.20.7': 2313 + '@types/babel__traverse@7.28.0': 2340 2314 dependencies: 2341 - '@babel/types': 7.27.1 2315 + '@babel/types': 7.28.5 2342 2316 2343 - '@types/estree@1.0.7': {} 2317 + '@types/estree@1.0.8': {} 2344 2318 2345 - '@types/node@22.15.12': 2319 + '@types/node@22.19.2': 2346 2320 dependencies: 2347 2321 undici-types: 6.21.0 2348 2322 ··· 2350 2324 2351 2325 acorn@8.14.0: {} 2352 2326 2353 - acorn@8.14.1: {} 2354 - 2355 - ansi-regex@5.0.1: {} 2356 - 2357 - ansi-regex@6.1.0: {} 2358 - 2359 - ansi-styles@4.3.0: 2360 - dependencies: 2361 - color-convert: 2.0.1 2362 - 2363 - ansi-styles@6.2.1: {} 2327 + acorn@8.15.0: {} 2364 2328 2365 2329 any-promise@1.3.0: {} 2366 2330 ··· 2371 2335 2372 2336 arg@5.0.2: {} 2373 2337 2374 - as-table@1.0.55: 2375 - dependencies: 2376 - printable-characters: 1.0.42 2377 - 2378 - autoprefixer@10.4.21(postcss@8.5.3): 2338 + autoprefixer@10.4.22(postcss@8.5.6): 2379 2339 dependencies: 2380 - browserslist: 4.24.5 2381 - caniuse-lite: 1.0.30001717 2382 - fraction.js: 4.3.7 2340 + browserslist: 4.28.1 2341 + caniuse-lite: 1.0.30001760 2342 + fraction.js: 5.3.4 2383 2343 normalize-range: 0.1.2 2384 2344 picocolors: 1.1.1 2385 - postcss: 8.5.3 2345 + postcss: 8.5.6 2386 2346 postcss-value-parser: 4.2.0 2387 2347 2388 - babel-plugin-jsx-dom-expressions@0.39.8(@babel/core@7.27.1): 2348 + babel-plugin-jsx-dom-expressions@0.40.3(@babel/core@7.28.5): 2389 2349 dependencies: 2390 - '@babel/core': 7.27.1 2350 + '@babel/core': 7.28.5 2391 2351 '@babel/helper-module-imports': 7.18.6 2392 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.1) 2393 - '@babel/types': 7.27.1 2352 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) 2353 + '@babel/types': 7.28.5 2394 2354 html-entities: 2.3.3 2395 2355 parse5: 7.3.0 2396 - validate-html-nesting: 1.2.2 2397 2356 2398 - babel-preset-solid@1.9.6(@babel/core@7.27.1): 2357 + babel-preset-solid@1.9.10(@babel/core@7.28.5)(solid-js@1.9.10): 2399 2358 dependencies: 2400 - '@babel/core': 7.27.1 2401 - babel-plugin-jsx-dom-expressions: 0.39.8(@babel/core@7.27.1) 2359 + '@babel/core': 7.28.5 2360 + babel-plugin-jsx-dom-expressions: 0.40.3(@babel/core@7.28.5) 2361 + optionalDependencies: 2362 + solid-js: 1.9.10 2402 2363 2403 - balanced-match@1.0.2: {} 2364 + baseline-browser-mapping@2.9.5: {} 2404 2365 2405 2366 binary-extensions@2.3.0: {} 2406 2367 2407 2368 blake3-wasm@2.1.5: {} 2408 2369 2409 - brace-expansion@2.0.1: 2410 - dependencies: 2411 - balanced-match: 1.0.2 2412 - 2413 2370 braces@3.0.3: 2414 2371 dependencies: 2415 2372 fill-range: 7.1.1 2416 2373 2417 - browserslist@4.24.5: 2374 + browserslist@4.28.1: 2418 2375 dependencies: 2419 - caniuse-lite: 1.0.30001717 2420 - electron-to-chromium: 1.5.150 2421 - node-releases: 2.0.19 2422 - update-browserslist-db: 1.1.3(browserslist@4.24.5) 2376 + baseline-browser-mapping: 2.9.5 2377 + caniuse-lite: 1.0.30001760 2378 + electron-to-chromium: 1.5.267 2379 + node-releases: 2.0.27 2380 + update-browserslist-db: 1.2.2(browserslist@4.28.1) 2423 2381 2424 2382 buffer-from@1.1.2: {} 2425 2383 2426 2384 camelcase-css@2.0.1: {} 2427 2385 2428 - caniuse-lite@1.0.30001717: {} 2386 + caniuse-lite@1.0.30001760: {} 2429 2387 2430 2388 chokidar@3.6.0: 2431 2389 dependencies: ··· 2448 2406 color-string@1.9.1: 2449 2407 dependencies: 2450 2408 color-name: 1.1.4 2451 - simple-swizzle: 0.2.2 2452 - optional: true 2409 + simple-swizzle: 0.2.4 2453 2410 2454 2411 color@4.2.3: 2455 2412 dependencies: 2456 2413 color-convert: 2.0.1 2457 2414 color-string: 1.9.1 2458 - optional: true 2459 2415 2460 2416 commander@2.20.3: {} 2461 2417 ··· 2463 2419 2464 2420 convert-source-map@2.0.0: {} 2465 2421 2466 - cookie@0.7.2: {} 2467 - 2468 - cross-spawn@7.0.6: 2469 - dependencies: 2470 - path-key: 3.1.1 2471 - shebang-command: 2.0.0 2472 - which: 2.0.2 2422 + cookie@1.1.1: {} 2473 2423 2474 2424 cssesc@3.0.0: {} 2475 2425 2476 - csstype@3.1.3: {} 2426 + csstype@3.2.3: {} 2477 2427 2478 - data-uri-to-buffer@2.0.2: {} 2479 - 2480 - debug@4.4.0: 2428 + debug@4.4.3: 2481 2429 dependencies: 2482 2430 ms: 2.1.3 2483 2431 2484 - defu@6.1.4: {} 2485 - 2486 - detect-libc@2.0.4: 2487 - optional: true 2432 + detect-libc@2.1.2: {} 2488 2433 2489 2434 didyoumean@1.2.2: {} 2490 2435 2491 2436 dlv@1.1.3: {} 2492 2437 2493 - eastasianwidth@0.2.0: {} 2438 + electron-to-chromium@1.5.267: {} 2494 2439 2495 - electron-to-chromium@1.5.150: {} 2440 + entities@6.0.1: {} 2496 2441 2497 - emoji-regex@8.0.0: {} 2442 + error-stack-parser-es@1.0.5: {} 2498 2443 2499 - emoji-regex@9.2.2: {} 2500 - 2501 - entities@6.0.0: {} 2502 - 2503 - esbuild@0.25.2: 2444 + esbuild@0.25.12: 2504 2445 optionalDependencies: 2505 - '@esbuild/aix-ppc64': 0.25.2 2506 - '@esbuild/android-arm': 0.25.2 2507 - '@esbuild/android-arm64': 0.25.2 2508 - '@esbuild/android-x64': 0.25.2 2509 - '@esbuild/darwin-arm64': 0.25.2 2510 - '@esbuild/darwin-x64': 0.25.2 2511 - '@esbuild/freebsd-arm64': 0.25.2 2512 - '@esbuild/freebsd-x64': 0.25.2 2513 - '@esbuild/linux-arm': 0.25.2 2514 - '@esbuild/linux-arm64': 0.25.2 2515 - '@esbuild/linux-ia32': 0.25.2 2516 - '@esbuild/linux-loong64': 0.25.2 2517 - '@esbuild/linux-mips64el': 0.25.2 2518 - '@esbuild/linux-ppc64': 0.25.2 2519 - '@esbuild/linux-riscv64': 0.25.2 2520 - '@esbuild/linux-s390x': 0.25.2 2521 - '@esbuild/linux-x64': 0.25.2 2522 - '@esbuild/netbsd-arm64': 0.25.2 2523 - '@esbuild/netbsd-x64': 0.25.2 2524 - '@esbuild/openbsd-arm64': 0.25.2 2525 - '@esbuild/openbsd-x64': 0.25.2 2526 - '@esbuild/sunos-x64': 0.25.2 2527 - '@esbuild/win32-arm64': 0.25.2 2528 - '@esbuild/win32-ia32': 0.25.2 2529 - '@esbuild/win32-x64': 0.25.2 2446 + '@esbuild/aix-ppc64': 0.25.12 2447 + '@esbuild/android-arm': 0.25.12 2448 + '@esbuild/android-arm64': 0.25.12 2449 + '@esbuild/android-x64': 0.25.12 2450 + '@esbuild/darwin-arm64': 0.25.12 2451 + '@esbuild/darwin-x64': 0.25.12 2452 + '@esbuild/freebsd-arm64': 0.25.12 2453 + '@esbuild/freebsd-x64': 0.25.12 2454 + '@esbuild/linux-arm': 0.25.12 2455 + '@esbuild/linux-arm64': 0.25.12 2456 + '@esbuild/linux-ia32': 0.25.12 2457 + '@esbuild/linux-loong64': 0.25.12 2458 + '@esbuild/linux-mips64el': 0.25.12 2459 + '@esbuild/linux-ppc64': 0.25.12 2460 + '@esbuild/linux-riscv64': 0.25.12 2461 + '@esbuild/linux-s390x': 0.25.12 2462 + '@esbuild/linux-x64': 0.25.12 2463 + '@esbuild/netbsd-arm64': 0.25.12 2464 + '@esbuild/netbsd-x64': 0.25.12 2465 + '@esbuild/openbsd-arm64': 0.25.12 2466 + '@esbuild/openbsd-x64': 0.25.12 2467 + '@esbuild/openharmony-arm64': 0.25.12 2468 + '@esbuild/sunos-x64': 0.25.12 2469 + '@esbuild/win32-arm64': 0.25.12 2470 + '@esbuild/win32-ia32': 0.25.12 2471 + '@esbuild/win32-x64': 0.25.12 2530 2472 2531 - esbuild@0.25.4: 2473 + esbuild@0.27.0: 2532 2474 optionalDependencies: 2533 - '@esbuild/aix-ppc64': 0.25.4 2534 - '@esbuild/android-arm': 0.25.4 2535 - '@esbuild/android-arm64': 0.25.4 2536 - '@esbuild/android-x64': 0.25.4 2537 - '@esbuild/darwin-arm64': 0.25.4 2538 - '@esbuild/darwin-x64': 0.25.4 2539 - '@esbuild/freebsd-arm64': 0.25.4 2540 - '@esbuild/freebsd-x64': 0.25.4 2541 - '@esbuild/linux-arm': 0.25.4 2542 - '@esbuild/linux-arm64': 0.25.4 2543 - '@esbuild/linux-ia32': 0.25.4 2544 - '@esbuild/linux-loong64': 0.25.4 2545 - '@esbuild/linux-mips64el': 0.25.4 2546 - '@esbuild/linux-ppc64': 0.25.4 2547 - '@esbuild/linux-riscv64': 0.25.4 2548 - '@esbuild/linux-s390x': 0.25.4 2549 - '@esbuild/linux-x64': 0.25.4 2550 - '@esbuild/netbsd-arm64': 0.25.4 2551 - '@esbuild/netbsd-x64': 0.25.4 2552 - '@esbuild/openbsd-arm64': 0.25.4 2553 - '@esbuild/openbsd-x64': 0.25.4 2554 - '@esbuild/sunos-x64': 0.25.4 2555 - '@esbuild/win32-arm64': 0.25.4 2556 - '@esbuild/win32-ia32': 0.25.4 2557 - '@esbuild/win32-x64': 0.25.4 2475 + '@esbuild/aix-ppc64': 0.27.0 2476 + '@esbuild/android-arm': 0.27.0 2477 + '@esbuild/android-arm64': 0.27.0 2478 + '@esbuild/android-x64': 0.27.0 2479 + '@esbuild/darwin-arm64': 0.27.0 2480 + '@esbuild/darwin-x64': 0.27.0 2481 + '@esbuild/freebsd-arm64': 0.27.0 2482 + '@esbuild/freebsd-x64': 0.27.0 2483 + '@esbuild/linux-arm': 0.27.0 2484 + '@esbuild/linux-arm64': 0.27.0 2485 + '@esbuild/linux-ia32': 0.27.0 2486 + '@esbuild/linux-loong64': 0.27.0 2487 + '@esbuild/linux-mips64el': 0.27.0 2488 + '@esbuild/linux-ppc64': 0.27.0 2489 + '@esbuild/linux-riscv64': 0.27.0 2490 + '@esbuild/linux-s390x': 0.27.0 2491 + '@esbuild/linux-x64': 0.27.0 2492 + '@esbuild/netbsd-arm64': 0.27.0 2493 + '@esbuild/netbsd-x64': 0.27.0 2494 + '@esbuild/openbsd-arm64': 0.27.0 2495 + '@esbuild/openbsd-x64': 0.27.0 2496 + '@esbuild/openharmony-arm64': 0.27.0 2497 + '@esbuild/sunos-x64': 0.27.0 2498 + '@esbuild/win32-arm64': 0.27.0 2499 + '@esbuild/win32-ia32': 0.27.0 2500 + '@esbuild/win32-x64': 0.27.0 2558 2501 2559 2502 escalade@3.2.0: {} 2560 2503 2561 - exit-hook@2.2.1: {} 2504 + esm-env@1.2.2: {} 2562 2505 2563 - exsolve@1.0.5: {} 2506 + exit-hook@2.2.1: {} 2564 2507 2565 2508 fast-glob@3.3.3: 2566 2509 dependencies: ··· 2574 2517 dependencies: 2575 2518 reusify: 1.1.0 2576 2519 2577 - fdir@6.4.4(picomatch@4.0.2): 2520 + fdir@6.5.0(picomatch@4.0.3): 2578 2521 optionalDependencies: 2579 - picomatch: 4.0.2 2522 + picomatch: 4.0.3 2580 2523 2581 2524 fetch-blob@3.2.0: 2582 2525 dependencies: ··· 2588 2531 dependencies: 2589 2532 to-regex-range: 5.0.1 2590 2533 2591 - foreground-child@3.3.1: 2592 - dependencies: 2593 - cross-spawn: 7.0.6 2594 - signal-exit: 4.1.0 2595 - 2596 - fraction.js@4.3.7: {} 2534 + fraction.js@5.3.4: {} 2597 2535 2598 2536 fsevents@2.3.3: 2599 2537 optional: true ··· 2602 2540 2603 2541 gensync@1.0.0-beta.2: {} 2604 2542 2605 - get-source@2.0.12: 2606 - dependencies: 2607 - data-uri-to-buffer: 2.0.2 2608 - source-map: 0.6.1 2609 - 2610 2543 glob-parent@5.1.2: 2611 2544 dependencies: 2612 2545 is-glob: 4.0.3 ··· 2617 2550 2618 2551 glob-to-regexp@0.4.1: {} 2619 2552 2620 - glob@10.4.5: 2621 - dependencies: 2622 - foreground-child: 3.3.1 2623 - jackspeak: 3.4.3 2624 - minimatch: 9.0.5 2625 - minipass: 7.1.2 2626 - package-json-from-dist: 1.0.1 2627 - path-scurry: 1.11.1 2628 - 2629 - globals@11.12.0: {} 2630 - 2631 2553 hasown@2.0.2: 2632 2554 dependencies: 2633 2555 function-bind: 1.1.2 2634 2556 2635 2557 html-entities@2.3.3: {} 2636 2558 2637 - is-arrayish@0.3.2: 2638 - optional: true 2559 + is-arrayish@0.3.4: {} 2639 2560 2640 2561 is-binary-path@2.1.0: 2641 2562 dependencies: ··· 2647 2568 2648 2569 is-extglob@2.1.1: {} 2649 2570 2650 - is-fullwidth-code-point@3.0.0: {} 2651 - 2652 2571 is-glob@4.0.3: 2653 2572 dependencies: 2654 2573 is-extglob: 2.1.1 ··· 2657 2576 2658 2577 is-what@4.1.16: {} 2659 2578 2660 - isexe@2.0.0: {} 2661 - 2662 - jackspeak@3.4.3: 2663 - dependencies: 2664 - '@isaacs/cliui': 8.0.2 2665 - optionalDependencies: 2666 - '@pkgjs/parseargs': 0.11.0 2667 - 2668 2579 jiti@1.21.7: {} 2669 2580 2670 2581 js-tokens@4.0.0: {} ··· 2673 2584 2674 2585 json5@2.2.3: {} 2675 2586 2587 + kleur@4.1.5: {} 2588 + 2676 2589 lilconfig@3.1.3: {} 2677 2590 2678 2591 lines-and-columns@1.2.4: {} 2679 2592 2680 - lru-cache@10.4.3: {} 2681 - 2682 2593 lru-cache@5.1.1: 2683 2594 dependencies: 2684 2595 yallist: 3.1.1 ··· 2698 2609 2699 2610 mini-svg-data-uri@1.4.4: {} 2700 2611 2701 - miniflare@4.20250428.1: 2612 + miniflare@4.20251202.1: 2702 2613 dependencies: 2703 2614 '@cspotcode/source-map-support': 0.8.1 2704 2615 acorn: 8.14.0 2705 2616 acorn-walk: 8.3.2 2706 2617 exit-hook: 2.2.1 2707 2618 glob-to-regexp: 0.4.1 2619 + sharp: 0.33.5 2708 2620 stoppable: 1.1.0 2709 - undici: 5.29.0 2710 - workerd: 1.20250428.0 2621 + undici: 7.14.0 2622 + workerd: 1.20251202.0 2711 2623 ws: 8.18.0 2712 - youch: 3.3.4 2624 + youch: 4.1.0-beta.10 2713 2625 zod: 3.22.3 2714 2626 transitivePeerDependencies: 2715 2627 - bufferutil 2716 2628 - utf-8-validate 2717 2629 2718 - minimatch@9.0.5: 2719 - dependencies: 2720 - brace-expansion: 2.0.1 2721 - 2722 - minipass@7.1.2: {} 2723 - 2724 2630 ms@2.1.3: {} 2725 2631 2726 - mustache@4.2.0: {} 2727 - 2728 2632 mz@2.7.0: 2729 2633 dependencies: 2730 2634 any-promise: 1.3.0 ··· 2733 2637 2734 2638 nanoid@3.3.11: {} 2735 2639 2736 - nanoid@5.1.5: {} 2640 + nanoid@5.1.6: {} 2737 2641 2738 2642 native-file-system-adapter@3.0.1: 2739 2643 optionalDependencies: ··· 2742 2646 node-domexception@1.0.0: 2743 2647 optional: true 2744 2648 2745 - node-releases@2.0.19: {} 2649 + node-releases@2.0.27: {} 2746 2650 2747 2651 normalize-path@3.0.0: {} 2748 2652 ··· 2752 2656 2753 2657 object-hash@3.0.0: {} 2754 2658 2755 - ohash@2.0.11: {} 2756 - 2757 - package-json-from-dist@1.0.1: {} 2758 - 2759 2659 parse5@7.3.0: 2760 2660 dependencies: 2761 - entities: 6.0.0 2762 - 2763 - path-key@3.1.1: {} 2661 + entities: 6.0.1 2764 2662 2765 2663 path-parse@1.0.7: {} 2766 - 2767 - path-scurry@1.11.1: 2768 - dependencies: 2769 - lru-cache: 10.4.3 2770 - minipass: 7.1.2 2771 2664 2772 2665 path-to-regexp@6.3.0: {} 2773 2666 ··· 2777 2670 2778 2671 picomatch@2.3.1: {} 2779 2672 2780 - picomatch@4.0.2: {} 2673 + picomatch@4.0.3: {} 2781 2674 2782 2675 pify@2.3.0: {} 2783 2676 2784 2677 pirates@4.0.7: {} 2785 2678 2786 - postcss-import@15.1.0(postcss@8.5.3): 2679 + postcss-import@15.1.0(postcss@8.5.6): 2787 2680 dependencies: 2788 - postcss: 8.5.3 2681 + postcss: 8.5.6 2789 2682 postcss-value-parser: 4.2.0 2790 2683 read-cache: 1.0.0 2791 - resolve: 1.22.10 2684 + resolve: 1.22.11 2792 2685 2793 - postcss-js@4.0.1(postcss@8.5.3): 2686 + postcss-js@4.1.0(postcss@8.5.6): 2794 2687 dependencies: 2795 2688 camelcase-css: 2.0.1 2796 - postcss: 8.5.3 2689 + postcss: 8.5.6 2797 2690 2798 - postcss-load-config@4.0.2(postcss@8.5.3): 2691 + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6): 2799 2692 dependencies: 2800 2693 lilconfig: 3.1.3 2801 - yaml: 2.7.1 2802 2694 optionalDependencies: 2803 - postcss: 8.5.3 2695 + jiti: 1.21.7 2696 + postcss: 8.5.6 2804 2697 2805 - postcss-nested@6.2.0(postcss@8.5.3): 2698 + postcss-nested@6.2.0(postcss@8.5.6): 2806 2699 dependencies: 2807 - postcss: 8.5.3 2700 + postcss: 8.5.6 2808 2701 postcss-selector-parser: 6.1.2 2809 2702 2810 2703 postcss-selector-parser@6.1.2: ··· 2814 2707 2815 2708 postcss-value-parser@4.2.0: {} 2816 2709 2817 - postcss@8.5.3: 2710 + postcss@8.5.6: 2818 2711 dependencies: 2819 2712 nanoid: 3.3.11 2820 2713 picocolors: 1.1.1 2821 2714 source-map-js: 1.2.1 2822 2715 2823 - prettier-plugin-tailwindcss@0.6.11(prettier@3.5.3): 2716 + prettier-plugin-tailwindcss@0.6.14(prettier@3.7.4): 2824 2717 dependencies: 2825 - prettier: 3.5.3 2826 - 2827 - prettier@3.5.3: {} 2718 + prettier: 3.7.4 2828 2719 2829 - printable-characters@1.0.42: {} 2720 + prettier@3.7.4: {} 2830 2721 2831 2722 queue-microtask@1.2.3: {} 2832 2723 ··· 2838 2729 dependencies: 2839 2730 picomatch: 2.3.1 2840 2731 2841 - resolve@1.22.10: 2732 + resolve@1.22.11: 2842 2733 dependencies: 2843 2734 is-core-module: 2.16.1 2844 2735 path-parse: 1.0.7 ··· 2846 2737 2847 2738 reusify@1.1.0: {} 2848 2739 2849 - rollup@4.40.2: 2740 + rollup@4.53.3: 2850 2741 dependencies: 2851 - '@types/estree': 1.0.7 2742 + '@types/estree': 1.0.8 2852 2743 optionalDependencies: 2853 - '@rollup/rollup-android-arm-eabi': 4.40.2 2854 - '@rollup/rollup-android-arm64': 4.40.2 2855 - '@rollup/rollup-darwin-arm64': 4.40.2 2856 - '@rollup/rollup-darwin-x64': 4.40.2 2857 - '@rollup/rollup-freebsd-arm64': 4.40.2 2858 - '@rollup/rollup-freebsd-x64': 4.40.2 2859 - '@rollup/rollup-linux-arm-gnueabihf': 4.40.2 2860 - '@rollup/rollup-linux-arm-musleabihf': 4.40.2 2861 - '@rollup/rollup-linux-arm64-gnu': 4.40.2 2862 - '@rollup/rollup-linux-arm64-musl': 4.40.2 2863 - '@rollup/rollup-linux-loongarch64-gnu': 4.40.2 2864 - '@rollup/rollup-linux-powerpc64le-gnu': 4.40.2 2865 - '@rollup/rollup-linux-riscv64-gnu': 4.40.2 2866 - '@rollup/rollup-linux-riscv64-musl': 4.40.2 2867 - '@rollup/rollup-linux-s390x-gnu': 4.40.2 2868 - '@rollup/rollup-linux-x64-gnu': 4.40.2 2869 - '@rollup/rollup-linux-x64-musl': 4.40.2 2870 - '@rollup/rollup-win32-arm64-msvc': 4.40.2 2871 - '@rollup/rollup-win32-ia32-msvc': 4.40.2 2872 - '@rollup/rollup-win32-x64-msvc': 4.40.2 2744 + '@rollup/rollup-android-arm-eabi': 4.53.3 2745 + '@rollup/rollup-android-arm64': 4.53.3 2746 + '@rollup/rollup-darwin-arm64': 4.53.3 2747 + '@rollup/rollup-darwin-x64': 4.53.3 2748 + '@rollup/rollup-freebsd-arm64': 4.53.3 2749 + '@rollup/rollup-freebsd-x64': 4.53.3 2750 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 2751 + '@rollup/rollup-linux-arm-musleabihf': 4.53.3 2752 + '@rollup/rollup-linux-arm64-gnu': 4.53.3 2753 + '@rollup/rollup-linux-arm64-musl': 4.53.3 2754 + '@rollup/rollup-linux-loong64-gnu': 4.53.3 2755 + '@rollup/rollup-linux-ppc64-gnu': 4.53.3 2756 + '@rollup/rollup-linux-riscv64-gnu': 4.53.3 2757 + '@rollup/rollup-linux-riscv64-musl': 4.53.3 2758 + '@rollup/rollup-linux-s390x-gnu': 4.53.3 2759 + '@rollup/rollup-linux-x64-gnu': 4.53.3 2760 + '@rollup/rollup-linux-x64-musl': 4.53.3 2761 + '@rollup/rollup-openharmony-arm64': 4.53.3 2762 + '@rollup/rollup-win32-arm64-msvc': 4.53.3 2763 + '@rollup/rollup-win32-ia32-msvc': 4.53.3 2764 + '@rollup/rollup-win32-x64-gnu': 4.53.3 2765 + '@rollup/rollup-win32-x64-msvc': 4.53.3 2873 2766 fsevents: 2.3.3 2874 2767 2875 2768 run-parallel@1.2.0: ··· 2878 2771 2879 2772 semver@6.3.1: {} 2880 2773 2881 - semver@7.7.1: 2882 - optional: true 2774 + semver@7.7.3: {} 2883 2775 2884 - seroval-plugins@1.2.1(seroval@1.2.1): 2776 + seroval-plugins@1.3.3(seroval@1.3.2): 2885 2777 dependencies: 2886 - seroval: 1.2.1 2778 + seroval: 1.3.2 2887 2779 2888 - seroval@1.2.1: {} 2780 + seroval@1.3.2: {} 2889 2781 2890 2782 sharp@0.33.5: 2891 2783 dependencies: 2892 2784 color: 4.2.3 2893 - detect-libc: 2.0.4 2894 - semver: 7.7.1 2785 + detect-libc: 2.1.2 2786 + semver: 7.7.3 2895 2787 optionalDependencies: 2896 2788 '@img/sharp-darwin-arm64': 0.33.5 2897 2789 '@img/sharp-darwin-x64': 0.33.5 ··· 2912 2804 '@img/sharp-wasm32': 0.33.5 2913 2805 '@img/sharp-win32-ia32': 0.33.5 2914 2806 '@img/sharp-win32-x64': 0.33.5 2915 - optional: true 2916 - 2917 - shebang-command@2.0.0: 2918 - dependencies: 2919 - shebang-regex: 3.0.0 2920 - 2921 - shebang-regex@3.0.0: {} 2922 - 2923 - signal-exit@4.1.0: {} 2924 2807 2925 - simple-swizzle@0.2.2: 2808 + simple-swizzle@0.2.4: 2926 2809 dependencies: 2927 - is-arrayish: 0.3.2 2928 - optional: true 2810 + is-arrayish: 0.3.4 2929 2811 2930 - solid-js@1.9.6: 2812 + solid-js@1.9.10: 2931 2813 dependencies: 2932 - csstype: 3.1.3 2933 - seroval: 1.2.1 2934 - seroval-plugins: 1.2.1(seroval@1.2.1) 2814 + csstype: 3.2.3 2815 + seroval: 1.3.2 2816 + seroval-plugins: 1.3.3(seroval@1.3.2) 2935 2817 2936 - solid-refresh@0.6.3(solid-js@1.9.6): 2818 + solid-refresh@0.6.3(solid-js@1.9.10): 2937 2819 dependencies: 2938 - '@babel/generator': 7.27.1 2820 + '@babel/generator': 7.28.5 2939 2821 '@babel/helper-module-imports': 7.27.1 2940 - '@babel/types': 7.27.1 2941 - solid-js: 1.9.6 2822 + '@babel/types': 7.28.5 2823 + solid-js: 1.9.10 2942 2824 transitivePeerDependencies: 2943 2825 - supports-color 2944 2826 ··· 2950 2832 source-map: 0.6.1 2951 2833 2952 2834 source-map@0.6.1: {} 2953 - 2954 - stacktracey@2.1.8: 2955 - dependencies: 2956 - as-table: 1.0.55 2957 - get-source: 2.0.12 2958 2835 2959 2836 stoppable@1.1.0: {} 2960 2837 2961 - string-width@4.2.3: 2838 + sucrase@3.35.1: 2962 2839 dependencies: 2963 - emoji-regex: 8.0.0 2964 - is-fullwidth-code-point: 3.0.0 2965 - strip-ansi: 6.0.1 2966 - 2967 - string-width@5.1.2: 2968 - dependencies: 2969 - eastasianwidth: 0.2.0 2970 - emoji-regex: 9.2.2 2971 - strip-ansi: 7.1.0 2972 - 2973 - strip-ansi@6.0.1: 2974 - dependencies: 2975 - ansi-regex: 5.0.1 2976 - 2977 - strip-ansi@7.1.0: 2978 - dependencies: 2979 - ansi-regex: 6.1.0 2980 - 2981 - sucrase@3.35.0: 2982 - dependencies: 2983 - '@jridgewell/gen-mapping': 0.3.8 2840 + '@jridgewell/gen-mapping': 0.3.13 2984 2841 commander: 4.1.1 2985 - glob: 10.4.5 2986 2842 lines-and-columns: 1.2.4 2987 2843 mz: 2.7.0 2988 2844 pirates: 4.0.7 2845 + tinyglobby: 0.2.15 2989 2846 ts-interface-checker: 0.1.13 2847 + 2848 + supports-color@10.2.2: {} 2990 2849 2991 2850 supports-preserve-symlinks-flag@1.0.0: {} 2992 2851 2993 - tailwindcss@3.4.17: 2852 + tailwindcss@3.4.18: 2994 2853 dependencies: 2995 2854 '@alloc/quick-lru': 5.2.0 2996 2855 arg: 5.0.2 ··· 3006 2865 normalize-path: 3.0.0 3007 2866 object-hash: 3.0.0 3008 2867 picocolors: 1.1.1 3009 - postcss: 8.5.3 3010 - postcss-import: 15.1.0(postcss@8.5.3) 3011 - postcss-js: 4.0.1(postcss@8.5.3) 3012 - postcss-load-config: 4.0.2(postcss@8.5.3) 3013 - postcss-nested: 6.2.0(postcss@8.5.3) 2868 + postcss: 8.5.6 2869 + postcss-import: 15.1.0(postcss@8.5.6) 2870 + postcss-js: 4.1.0(postcss@8.5.6) 2871 + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6) 2872 + postcss-nested: 6.2.0(postcss@8.5.6) 3014 2873 postcss-selector-parser: 6.1.2 3015 - resolve: 1.22.10 3016 - sucrase: 3.35.0 2874 + resolve: 1.22.11 2875 + sucrase: 3.35.1 3017 2876 transitivePeerDependencies: 3018 - - ts-node 2877 + - tsx 2878 + - yaml 3019 2879 3020 - terser@5.39.0: 2880 + terser@5.44.1: 3021 2881 dependencies: 3022 - '@jridgewell/source-map': 0.3.6 3023 - acorn: 8.14.1 2882 + '@jridgewell/source-map': 0.3.11 2883 + acorn: 8.15.0 3024 2884 commander: 2.20.3 3025 2885 source-map-support: 0.5.21 3026 2886 ··· 3032 2892 dependencies: 3033 2893 any-promise: 1.3.0 3034 2894 3035 - tinyglobby@0.2.13: 2895 + tinyglobby@0.2.15: 3036 2896 dependencies: 3037 - fdir: 6.4.4(picomatch@4.0.2) 3038 - picomatch: 4.0.2 2897 + fdir: 6.5.0(picomatch@4.0.3) 2898 + picomatch: 4.0.3 3039 2899 3040 2900 to-regex-range@5.0.1: 3041 2901 dependencies: ··· 3046 2906 tslib@2.8.1: 3047 2907 optional: true 3048 2908 3049 - typescript@5.8.3: {} 3050 - 3051 - ufo@1.6.1: {} 2909 + typescript@5.9.3: {} 3052 2910 3053 2911 undici-types@6.21.0: {} 3054 2912 3055 - undici@5.29.0: 3056 - dependencies: 3057 - '@fastify/busboy': 2.1.1 2913 + undici@7.14.0: {} 3058 2914 3059 - unenv@2.0.0-rc.15: 2915 + unenv@2.0.0-rc.24: 3060 2916 dependencies: 3061 - defu: 6.1.4 3062 - exsolve: 1.0.5 3063 - ohash: 2.0.11 3064 2917 pathe: 2.0.3 3065 - ufo: 1.6.1 3066 2918 3067 - update-browserslist-db@1.1.3(browserslist@4.24.5): 2919 + update-browserslist-db@1.2.2(browserslist@4.28.1): 3068 2920 dependencies: 3069 - browserslist: 4.24.5 2921 + browserslist: 4.28.1 3070 2922 escalade: 3.2.0 3071 2923 picocolors: 1.1.1 3072 2924 3073 2925 util-deprecate@1.0.2: {} 3074 2926 3075 - validate-html-nesting@1.2.2: {} 3076 - 3077 - vite-plugin-solid@2.11.6(solid-js@1.9.6)(vite@6.3.5(@types/node@22.15.12)(jiti@1.21.7)(terser@5.39.0)(yaml@2.7.1)): 2927 + vite-plugin-solid@2.11.10(solid-js@1.9.10)(vite@7.2.7(@types/node@22.19.2)(jiti@1.21.7)(terser@5.44.1)): 3078 2928 dependencies: 3079 - '@babel/core': 7.27.1 2929 + '@babel/core': 7.28.5 3080 2930 '@types/babel__core': 7.20.5 3081 - babel-preset-solid: 1.9.6(@babel/core@7.27.1) 2931 + babel-preset-solid: 1.9.10(@babel/core@7.28.5)(solid-js@1.9.10) 3082 2932 merge-anything: 5.1.7 3083 - solid-js: 1.9.6 3084 - solid-refresh: 0.6.3(solid-js@1.9.6) 3085 - vite: 6.3.5(@types/node@22.15.12)(jiti@1.21.7)(terser@5.39.0)(yaml@2.7.1) 3086 - vitefu: 1.0.6(vite@6.3.5(@types/node@22.15.12)(jiti@1.21.7)(terser@5.39.0)(yaml@2.7.1)) 2933 + solid-js: 1.9.10 2934 + solid-refresh: 0.6.3(solid-js@1.9.10) 2935 + vite: 7.2.7(@types/node@22.19.2)(jiti@1.21.7)(terser@5.44.1) 2936 + vitefu: 1.1.1(vite@7.2.7(@types/node@22.19.2)(jiti@1.21.7)(terser@5.44.1)) 3087 2937 transitivePeerDependencies: 3088 2938 - supports-color 3089 2939 3090 - vite@6.3.5(@types/node@22.15.12)(jiti@1.21.7)(terser@5.39.0)(yaml@2.7.1): 2940 + vite@7.2.7(@types/node@22.19.2)(jiti@1.21.7)(terser@5.44.1): 3091 2941 dependencies: 3092 - esbuild: 0.25.4 3093 - fdir: 6.4.4(picomatch@4.0.2) 3094 - picomatch: 4.0.2 3095 - postcss: 8.5.3 3096 - rollup: 4.40.2 3097 - tinyglobby: 0.2.13 2942 + esbuild: 0.25.12 2943 + fdir: 6.5.0(picomatch@4.0.3) 2944 + picomatch: 4.0.3 2945 + postcss: 8.5.6 2946 + rollup: 4.53.3 2947 + tinyglobby: 0.2.15 3098 2948 optionalDependencies: 3099 - '@types/node': 22.15.12 2949 + '@types/node': 22.19.2 3100 2950 fsevents: 2.3.3 3101 2951 jiti: 1.21.7 3102 - terser: 5.39.0 3103 - yaml: 2.7.1 2952 + terser: 5.44.1 3104 2953 3105 - vitefu@1.0.6(vite@6.3.5(@types/node@22.15.12)(jiti@1.21.7)(terser@5.39.0)(yaml@2.7.1)): 2954 + vitefu@1.1.1(vite@7.2.7(@types/node@22.19.2)(jiti@1.21.7)(terser@5.44.1)): 3106 2955 optionalDependencies: 3107 - vite: 6.3.5(@types/node@22.15.12)(jiti@1.21.7)(terser@5.39.0)(yaml@2.7.1) 2956 + vite: 7.2.7(@types/node@22.19.2)(jiti@1.21.7)(terser@5.44.1) 3108 2957 3109 2958 web-streams-polyfill@3.3.3: 3110 2959 optional: true 3111 2960 3112 - which@2.0.2: 3113 - dependencies: 3114 - isexe: 2.0.0 3115 - 3116 - workerd@1.20250428.0: 2961 + workerd@1.20251202.0: 3117 2962 optionalDependencies: 3118 - '@cloudflare/workerd-darwin-64': 1.20250428.0 3119 - '@cloudflare/workerd-darwin-arm64': 1.20250428.0 3120 - '@cloudflare/workerd-linux-64': 1.20250428.0 3121 - '@cloudflare/workerd-linux-arm64': 1.20250428.0 3122 - '@cloudflare/workerd-windows-64': 1.20250428.0 2963 + '@cloudflare/workerd-darwin-64': 1.20251202.0 2964 + '@cloudflare/workerd-darwin-arm64': 1.20251202.0 2965 + '@cloudflare/workerd-linux-64': 1.20251202.0 2966 + '@cloudflare/workerd-linux-arm64': 1.20251202.0 2967 + '@cloudflare/workerd-windows-64': 1.20251202.0 3123 2968 3124 - wrangler@4.14.1: 2969 + wrangler@4.53.0: 3125 2970 dependencies: 3126 - '@cloudflare/kv-asset-handler': 0.4.0 3127 - '@cloudflare/unenv-preset': 2.3.1(unenv@2.0.0-rc.15)(workerd@1.20250428.0) 2971 + '@cloudflare/kv-asset-handler': 0.4.1 2972 + '@cloudflare/unenv-preset': 2.7.13(unenv@2.0.0-rc.24)(workerd@1.20251202.0) 3128 2973 blake3-wasm: 2.1.5 3129 - esbuild: 0.25.2 3130 - miniflare: 4.20250428.1 2974 + esbuild: 0.27.0 2975 + miniflare: 4.20251202.1 3131 2976 path-to-regexp: 6.3.0 3132 - unenv: 2.0.0-rc.15 3133 - workerd: 1.20250428.0 2977 + unenv: 2.0.0-rc.24 2978 + workerd: 1.20251202.0 3134 2979 optionalDependencies: 3135 2980 fsevents: 2.3.3 3136 - sharp: 0.33.5 3137 2981 transitivePeerDependencies: 3138 2982 - bufferutil 3139 2983 - utf-8-validate 3140 2984 3141 - wrap-ansi@7.0.0: 3142 - dependencies: 3143 - ansi-styles: 4.3.0 3144 - string-width: 4.2.3 3145 - strip-ansi: 6.0.1 3146 - 3147 - wrap-ansi@8.1.0: 3148 - dependencies: 3149 - ansi-styles: 6.2.1 3150 - string-width: 5.1.2 3151 - strip-ansi: 7.1.0 3152 - 3153 2985 ws@8.18.0: {} 3154 2986 3155 2987 yallist@3.1.1: {} 3156 2988 3157 - yaml@2.7.1: {} 2989 + youch-core@0.3.3: 2990 + dependencies: 2991 + '@poppinss/exception': 1.2.2 2992 + error-stack-parser-es: 1.0.5 3158 2993 3159 - youch@3.3.4: 2994 + youch@4.1.0-beta.10: 3160 2995 dependencies: 3161 - cookie: 0.7.2 3162 - mustache: 4.2.0 3163 - stacktracey: 2.1.8 2996 + '@poppinss/colors': 4.1.5 2997 + '@poppinss/dumper': 0.6.5 2998 + '@speed-highlight/core': 1.2.12 2999 + cookie: 1.1.1 3000 + youch-core: 0.3.3 3164 3001 3165 3002 zod@3.22.3: {}
+2
pnpm-workspace.yaml
··· 1 + onlyBuiltDependencies: 2 + - esbuild
+2 -1
src/api/queries/did-doc.ts
··· 1 - import type { AtprotoDid, DidDocument } from '@atcute/identity'; 1 + import type { DidDocument } from '@atcute/identity'; 2 2 import { 3 3 CompositeDidDocumentResolver, 4 4 PlcDidDocumentResolver, 5 5 WebDidDocumentResolver, 6 6 } from '@atcute/identity-resolver'; 7 + import type { AtprotoDid } from '@atcute/lexicons/syntax'; 7 8 8 9 const didDocumentResolver = new CompositeDidDocumentResolver({ 9 10 methods: {
+1 -1
src/api/queries/handle.ts
··· 1 - import { type AtprotoDid, type Handle, isHandle } from '@atcute/identity'; 2 1 import { XrpcHandleResolver } from '@atcute/identity-resolver'; 2 + import { type AtprotoDid, type Handle, isHandle } from '@atcute/lexicons/syntax'; 3 3 4 4 const handleResolver = new XrpcHandleResolver({ 5 5 serviceUrl: import.meta.env.VITE_APPVIEW_URL,
+1 -1
src/api/queries/plc.ts
··· 1 1 import { defs } from '@atcute/did-plc'; 2 - import { Did } from '@atcute/identity'; 2 + import type { Did } from '@atcute/lexicons/syntax'; 3 3 4 4 export const getPlcAuditLogs = async ({ did, signal }: { did: Did<'plc'>; signal?: AbortSignal }) => { 5 5 const origin = import.meta.env.VITE_PLC_DIRECTORY_URL;
+2 -2
src/api/types/plc.ts
··· 1 1 import * as v from '@badrap/valita'; 2 2 3 - import { defs, UnsignedOperation } from '@atcute/did-plc'; 3 + import { defs, type UnsignedOperation } from '@atcute/did-plc'; 4 4 5 - import { ToValidator } from '../utils/valita'; 5 + import type { ToValidator } from '../utils/valita'; 6 6 import { serviceUrlString } from './strings'; 7 7 8 8 const _unsignedOperation = defs.unsignedOperation as ToValidator<UnsignedOperation>;
-49
src/api/utils/at-uri.ts
··· 1 - import type { At } from '@atcute/client/lexicons'; 2 - 3 - import { assert } from '~/lib/utils/invariant'; 4 - 5 - const DID_RE = /^did:([a-z]+):([a-zA-Z0-9._:%\-]*[a-zA-Z0-9._\-])$/; 6 - 7 - const NSID_RE = 8 - /^[a-zA-Z](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?:\.[a-zA-Z](?:[a-zA-Z0-9]{0,62})?)$/; 9 - 10 - const RECORD_KEY_RE = /^(?!\.{1,2}$)[a-zA-Z0-9_~.:-]{1,512}$/; 11 - 12 - const ATURI_RE = 13 - /^at:\/\/([a-zA-Z0-9._:%-]+)(?:\/([a-zA-Z0-9-.]+)(?:\/([a-zA-Z0-9._~:@!$&%')(*+,;=-]+))?)?(?:#(\/[a-zA-Z0-9._~:@!$&%')(*+,;=\-[\]/\\]*))?$/; 14 - 15 - const isDid = (input: unknown): input is At.Did => { 16 - return typeof input === 'string' && input.length >= 7 && input.length <= 2048 && DID_RE.test(input); 17 - }; 18 - 19 - const isNsid = (input: unknown): input is At.Nsid => { 20 - return typeof input === 'string' && input.length >= 5 && input.length <= 317 && NSID_RE.test(input); 21 - }; 22 - 23 - const isRecordKey = (input: unknown): input is At.RecordKey => { 24 - return typeof input === 'string' && input.length >= 1 && input.length <= 512 && RECORD_KEY_RE.test(input); 25 - }; 26 - 27 - export interface AddressedAtUri { 28 - repo: At.Did; 29 - collection: At.Nsid; 30 - rkey: At.RecordKey; 31 - fragment: string | undefined; 32 - } 33 - 34 - export const parseAddressedAtUri = (str: string): AddressedAtUri => { 35 - const match = ATURI_RE.exec(str); 36 - assert(match !== null, `invalid addressed-at-uri: ${str}`); 37 - 38 - const [, r, c, k, f] = match; 39 - assert(isDid(r), `invalid repo in addressed-at-uri: ${r}`); 40 - assert(isNsid(c), `invalid collection in addressed-at-uri: ${c}`); 41 - assert(isRecordKey(k), `invalid rkey in addressed-at-uri: ${k}`); 42 - 43 - return { 44 - repo: r, 45 - collection: c, 46 - rkey: k, 47 - fragment: f, 48 - }; 49 - };
+7 -12
src/api/utils/error.ts
··· 1 - import { XRPCError } from '@atcute/client'; 2 - 3 - export const formatXRPCError = (err: XRPCError): string => { 4 - const name = err.kind; 5 - return (name ? name + ': ' : '') + err.message; 6 - }; 1 + import { ClientResponseError } from '@atcute/client'; 7 2 8 3 export const formatQueryError = (err: unknown) => { 9 - if (err instanceof XRPCError) { 10 - const kind = err.kind; 4 + if (err instanceof ClientResponseError) { 5 + const error = err.error; 11 6 12 - if (kind === 'InvalidToken' || kind === 'ExpiredToken') { 7 + if (error === 'InvalidToken' || error === 'ExpiredToken') { 13 8 return `Account session is no longer valid`; 14 9 } 15 10 16 - if (kind === 'UpstreamFailure') { 11 + if (error === 'UpstreamFailure') { 17 12 return `Server appears to be experiencing issues, try again later`; 18 13 } 19 14 20 - if (kind === 'InternalServerError') { 15 + if (error === 'InternalServerError') { 21 16 return `Server is having issues processing this request, try again later`; 22 17 } 23 18 24 - return formatXRPCError(err); 19 + return err.message; 25 20 } 26 21 27 22 if (err instanceof Error) {
+69
src/api/utils/jwt.ts
··· 1 + /** 2 + * Decodes a JWT token 3 + * @param token The token string 4 + * @returns JSON object from the token 5 + */ 6 + export const decodeJwt = (token: string): unknown => { 7 + const pos = 1; 8 + const part = token.split('.')[1]; 9 + 10 + let decoded: string; 11 + 12 + if (typeof part !== 'string') { 13 + throw new Error('invalid token: missing part ' + (pos + 1)); 14 + } 15 + 16 + try { 17 + decoded = base64UrlDecode(part); 18 + } catch (e) { 19 + throw new Error('invalid token: invalid b64 for part ' + (pos + 1) + ' (' + (e as Error).message + ')'); 20 + } 21 + 22 + try { 23 + return JSON.parse(decoded); 24 + } catch (e) { 25 + throw new Error('invalid token: invalid json for part ' + (pos + 1) + ' (' + (e as Error).message + ')'); 26 + } 27 + }; 28 + 29 + /** 30 + * Decodes a URL-safe Base64 string 31 + * @param str URL-safe Base64 that needed to be decoded 32 + * @returns The actual string 33 + */ 34 + const base64UrlDecode = (str: string): string => { 35 + let output = str.replace(/-/g, '+').replace(/_/g, '/'); 36 + 37 + switch (output.length % 4) { 38 + case 0: 39 + break; 40 + case 2: 41 + output += '=='; 42 + break; 43 + case 3: 44 + output += '='; 45 + break; 46 + default: 47 + throw new Error('base64 string is not of the correct length'); 48 + } 49 + 50 + try { 51 + return b64DecodeUnicode(output); 52 + } catch { 53 + return atob(output); 54 + } 55 + }; 56 + 57 + const b64DecodeUnicode = (str: string): string => { 58 + return decodeURIComponent( 59 + atob(str).replace(/(.)/g, (_m, p) => { 60 + let code = p.charCodeAt(0).toString(16).toUpperCase(); 61 + 62 + if (code.length < 2) { 63 + code = '0' + code; 64 + } 65 + 66 + return '%' + code; 67 + }), 68 + ); 69 + };
-8
src/api/utils/strings.ts
··· 1 - import type { Records } from '@atcute/client/lexicons'; 2 - 3 - export const DID_OR_HANDLE_RE = 4 - /^[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*(?:\.[a-zA-Z]{2,})$|^did:[a-z]+:[a-zA-Z0-9._:%\-]*[a-zA-Z0-9._\-]$/; 5 - 6 - export const makeAtUri = (repo: string, collection: keyof Records | (string & {}), rkey: string) => { 7 - return `at://${repo}/${collection}/${rkey}`; 8 - };
+72
src/components/accordion.tsx
··· 1 + import { createSignal, type JSX, Show } from 'solid-js'; 2 + 3 + import ChevronRightIcon from '~/components/ic-icons/baseline-chevron-right'; 4 + 5 + export interface AccordionProps { 6 + title: string; 7 + children: JSX.Element; 8 + defaultOpen?: boolean; 9 + } 10 + 11 + export const Accordion = (props: AccordionProps) => { 12 + const [isOpen, setIsOpen] = createSignal(props.defaultOpen ?? false); 13 + 14 + return ( 15 + <div class="border-b border-gray-200"> 16 + <button 17 + type="button" 18 + onClick={() => setIsOpen(!isOpen())} 19 + class="flex w-full items-center gap-3 px-4 py-3 text-left hover:bg-gray-50" 20 + > 21 + <ChevronRightIcon 22 + class={`h-5 w-5 text-gray-500 transition-transform` + (isOpen() ? ` rotate-90` : ``)} 23 + /> 24 + <span class="font-semibold">{props.title}</span> 25 + </button> 26 + 27 + <Show when={isOpen()}> 28 + <div class="pb-4 pl-12 pr-4">{props.children}</div> 29 + </Show> 30 + </div> 31 + ); 32 + }; 33 + 34 + export interface SubsectionProps { 35 + title: string; 36 + children: JSX.Element; 37 + } 38 + 39 + export const Subsection = (props: SubsectionProps) => { 40 + return ( 41 + <div class="mb-4 last:mb-0"> 42 + <h4 class="mb-3 text-sm font-semibold text-gray-600">{props.title}</h4> 43 + <div class="flex flex-col gap-3">{props.children}</div> 44 + </div> 45 + ); 46 + }; 47 + 48 + export interface StatusBadgeProps { 49 + variant: 'idle' | 'pending' | 'success' | 'error'; 50 + children: JSX.Element; 51 + } 52 + 53 + export const StatusBadge = (props: StatusBadgeProps) => { 54 + const variantStyles = () => { 55 + switch (props.variant) { 56 + case 'idle': 57 + return 'bg-gray-100 text-gray-600'; 58 + case 'pending': 59 + return 'bg-yellow-100 text-yellow-800'; 60 + case 'success': 61 + return 'bg-green-100 text-green-800'; 62 + case 'error': 63 + return 'bg-red-100 text-red-800'; 64 + } 65 + }; 66 + 67 + return ( 68 + <span class={`inline-flex items-center rounded px-2 py-0.5 text-xs font-medium ${variantStyles()}`}> 69 + {props.children} 70 + </span> 71 + ); 72 + };
+65
src/components/file-drop-zone.tsx
··· 1 + import type { JSX } from 'solid-js'; 2 + 3 + import { createDropZone, type CreateDropZoneOptions } from '~/lib/hooks/dropzone'; 4 + 5 + import Button from './inputs/button'; 6 + 7 + interface FileDropZoneProps { 8 + accept?: string; 9 + disabled?: boolean; 10 + onFiles: (files: File[]) => void; 11 + dataTypes?: CreateDropZoneOptions['dataTypes']; 12 + multiple?: boolean; 13 + children?: JSX.Element; 14 + } 15 + 16 + const FileDropZone = (props: FileDropZoneProps) => { 17 + const { ref: dropRef, isDropping } = createDropZone({ 18 + dataTypes: props.dataTypes, 19 + multiple: props.multiple ?? false, 20 + onDrop(files) { 21 + if (files) { 22 + props.onFiles(files); 23 + } 24 + }, 25 + }); 26 + 27 + const handleBrowse = () => { 28 + const input = document.createElement('input'); 29 + input.type = 'file'; 30 + if (props.accept) { 31 + input.accept = props.accept; 32 + } 33 + if (props.multiple) { 34 + input.multiple = true; 35 + } 36 + input.oninput = () => { 37 + const files = Array.from(input.files!); 38 + if (files.length > 0) { 39 + props.onFiles(files); 40 + } 41 + }; 42 + input.click(); 43 + }; 44 + 45 + return ( 46 + <fieldset 47 + ref={dropRef} 48 + disabled={props.disabled} 49 + class={ 50 + `relative grid place-items-center rounded border border-gray-300 px-6 py-12 disabled:opacity-50` + 51 + (props.disabled || !isDropping() ? ` bg-gray-100` : ` bg-green-100`) 52 + } 53 + > 54 + <div class="flex flex-col items-center gap-4"> 55 + <Button variant="outline" onClick={handleBrowse}> 56 + Browse files 57 + </Button> 58 + <p class="select-none font-medium text-gray-600">or drop your file here</p> 59 + </div> 60 + {props.children} 61 + </fieldset> 62 + ); 63 + }; 64 + 65 + export default FileDropZone;
+27 -12
src/components/inputs/button.tsx
··· 1 - import { JSX } from 'solid-js'; 1 + import { createMemo, type JSX } from 'solid-js'; 2 2 3 3 interface ButtonProps { 4 4 children?: JSX.Element; 5 5 disabled?: boolean; 6 - variant?: 'primary' | 'secondary'; 6 + variant?: 'primary' | 'secondary' | 'outline'; 7 7 type?: 'button' | 'submit'; 8 + href?: string; 8 9 onClick?: JSX.EventHandlerUnion<HTMLButtonElement, MouseEvent>; 9 10 } 10 11 ··· 15 16 cn += ` bg-purple-800 text-white hover:bg-purple-700 active:bg-purple-700`; 16 17 } else if (variant === 'secondary') { 17 18 cn += ` bg-gray-200 text-black hover:bg-gray-300 active:bg-gray-300`; 19 + } else if (variant === 'outline') { 20 + cn += ` border border-gray-300 text-gray-800 hover:bg-gray-100 active:bg-gray-100`; 18 21 } 19 22 20 23 if (disabled) { ··· 25 28 }; 26 29 27 30 const Button = (props: ButtonProps) => { 28 - return ( 29 - <button 30 - type={props.type ?? 'button'} 31 - disabled={props.disabled} 32 - class={buttonStyles(props)} 33 - onClick={props.onClick} 34 - > 35 - {props.children} 36 - </button> 37 - ); 31 + const hasLink = createMemo(() => props.href !== undefined); 32 + 33 + return (() => { 34 + if (hasLink()) { 35 + return ( 36 + <a href={!props.disabled ? props.href : undefined} class={buttonStyles(props)}> 37 + {props.children} 38 + </a> 39 + ); 40 + } 41 + 42 + return ( 43 + <button 44 + type={props.type ?? 'button'} 45 + disabled={props.disabled} 46 + class={buttonStyles(props)} 47 + onClick={props.onClick} 48 + > 49 + {props.children} 50 + </button> 51 + ); 52 + }) as unknown as JSX.Element; 38 53 }; 39 54 40 55 export default Button;
+2 -3
src/components/inputs/multiline-input.tsx
··· 1 - import { createEffect, JSX } from 'solid-js'; 1 + import { createEffect, type JSX } from 'solid-js'; 2 2 3 3 import { createId } from '~/lib/hooks/id'; 4 4 5 - import { BoundInputEvent } from './_types'; 5 + import type { BoundInputEvent } from './_types'; 6 6 7 7 interface MultilineInputProps { 8 8 label: JSX.Element; ··· 39 39 name={props.name} 40 40 required={props.required} 41 41 autocomplete={props.autocomplete} 42 - // @ts-expect-error 43 42 autocorrect={props.autocorrect} 44 43 rows={22} 45 44 value={props.value}
+2 -2
src/components/inputs/radio-input.tsx
··· 1 - import { JSX } from 'solid-js'; 1 + import type { JSX } from 'solid-js'; 2 2 3 3 import { createId } from '~/lib/hooks/id'; 4 4 5 - import { BoundInputEvent } from './_types'; 5 + import type { BoundInputEvent } from './_types'; 6 6 7 7 interface RadioInputProps<T extends string> { 8 8 label: JSX.Element;
+2 -2
src/components/inputs/select-input.tsx
··· 1 - import { createEffect, JSX } from 'solid-js'; 1 + import { createEffect, type JSX } from 'solid-js'; 2 2 3 3 import { createId } from '~/lib/hooks/id'; 4 4 5 - import { BoundInputEvent } from './_types'; 5 + import type { BoundInputEvent } from './_types'; 6 6 7 7 interface SelectInputProps<T extends string> { 8 8 label: JSX.Element;
+5 -3
src/components/inputs/text-input.tsx
··· 1 - import { createEffect, JSX } from 'solid-js'; 1 + import { createEffect, type JSX } from 'solid-js'; 2 2 3 3 import { createId } from '~/lib/hooks/id'; 4 4 5 - import { BoundInputEvent } from './_types'; 5 + import type { BoundInputEvent } from './_types'; 6 6 7 - interface TextInputProps { 7 + export interface TextInputProps { 8 8 label: JSX.Element; 9 9 blurb?: JSX.Element; 10 10 monospace?: boolean; 11 11 type?: 'text' | 'password' | 'url' | 'email'; 12 12 name?: string; 13 13 required?: boolean; 14 + disabled?: boolean; 14 15 autocomplete?: 'off' | 'on' | 'one-time-code' | 'username'; 15 16 autocorrect?: 'off' | 'on'; 16 17 pattern?: string; ··· 55 56 id={fieldId} 56 57 name={props.name} 57 58 required={props.required} 59 + disabled={props.disabled} 58 60 autocomplete={props.autocomplete} 59 61 pattern={props.pattern} 60 62 placeholder={props.placeholder}
+1 -1
src/components/inputs/toggle-input.tsx
··· 2 2 3 3 import { createId } from '~/lib/hooks/id'; 4 4 5 - import { BoundInputEvent } from './_types'; 5 + import type { BoundInputEvent } from './_types'; 6 6 7 7 export interface ToggleInputProps { 8 8 label: string;
+22
src/components/page-header.tsx
··· 1 + import type { JSX } from 'solid-js'; 2 + 3 + interface PageHeaderProps { 4 + title: string; 5 + subtitle?: string; 6 + children?: JSX.Element; 7 + } 8 + 9 + const PageHeader = (props: PageHeaderProps) => { 10 + return ( 11 + <> 12 + <div class="p-4"> 13 + <h1 class="text-lg font-bold text-purple-800">{props.title}</h1> 14 + {props.subtitle && <p class="text-gray-600">{props.subtitle}</p>} 15 + {props.children} 16 + </div> 17 + <hr class="mx-4 border-gray-300" /> 18 + </> 19 + ); 20 + }; 21 + 22 + export default PageHeader;
+1 -1
src/components/wizard.tsx
··· 1 - import { Component, createMemo, createSignal, For, JSX } from 'solid-js'; 1 + import { type Component, createMemo, createSignal, For, type JSX } from 'solid-js'; 2 2 3 3 type EmptyObjectKeys<T> = { 4 4 [K in keyof T]: T[K] extends Record<string, never> ? K : never;
+13 -12
src/components/wizards/bluesky-login-step.tsx
··· 1 1 import { batch, createSignal, Match, Show, Switch } from 'solid-js'; 2 2 3 - import { CredentialManager, XRPCError } from '@atcute/client'; 4 - import { type AtprotoDid, type DidDocument, getPdsEndpoint, isAtprotoDid, isHandle } from '@atcute/identity'; 3 + import { ClientResponseError, CredentialManager } from '@atcute/client'; 4 + import { getPdsEndpoint, isAtprotoDid, type DidDocument } from '@atcute/identity'; 5 + import { isHandle, type AtprotoDid } from '@atcute/lexicons/syntax'; 5 6 6 7 import { getDidDocument } from '~/api/queries/did-doc'; 7 8 import { resolveHandleViaAppView } from '~/api/queries/handle'; ··· 88 89 setIsTotpRequired(false); 89 90 }); 90 91 }, 91 - onError(error) { 92 + onError(err) { 92 93 let message: string | undefined; 93 94 94 - if (error instanceof XRPCError) { 95 - if (error.kind === 'AuthFactorTokenRequired') { 95 + if (err instanceof ClientResponseError) { 96 + if (err.error === 'AuthFactorTokenRequired') { 96 97 setOtp(''); 97 98 setIsTotpRequired(true); 98 99 return; 99 100 } 100 101 101 - if (error.kind === 'AuthenticationRequired') { 102 + if (err.error === 'AuthenticationRequired') { 102 103 message = `Invalid identifier or password`; 103 - } else if (error.kind === 'AccountTakedown') { 104 + } else if (err.error === 'AccountTakedown') { 104 105 message = `Account has been taken down`; 105 - } else if (error.message.includes('Token is invalid')) { 106 + } else if (err.message.includes('Token is invalid')) { 106 107 message = `Invalid one-time confirmation code`; 107 108 setIsTotpRequired(true); 108 109 } 109 - } else if (error instanceof InsufficientLoginError) { 110 - message = error.message; 110 + } else if (err instanceof InsufficientLoginError) { 111 + message = err.message; 111 112 } 112 113 113 114 if (message !== undefined) { 114 115 setError(message); 115 116 } else { 116 - console.error(error); 117 - setError(`Something went wrong: ${error}`); 117 + console.error(err); 118 + setError(`Something went wrong: ${err}`); 118 119 } 119 120 }, 120 121 });
+2 -2
src/globals/rpc.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, simpleFetchHandler } from '@atcute/client'; 2 2 3 3 const APPVIEW_URL = import.meta.env.VITE_APPVIEW_URL; 4 4 5 - export const appViewRpc = new XRPC({ handler: simpleFetchHandler({ service: APPVIEW_URL }) }); 5 + export const appViewRpc = new Client({ handler: simpleFetchHandler({ service: APPVIEW_URL }) });
+104 -4
src/lib/utils/confirmation-code.ts
··· 1 - import { customAlphabet } from 'nanoid'; 1 + import { sample } from '@mary/array-fns'; 2 2 3 - const generateCode = customAlphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', 10); 3 + const words = [ 4 + 'abroad', 5 + 'acorn', 6 + 'anaconda', 7 + 'anchovy', 8 + 'aorta', 9 + 'argue', 10 + 'ashy', 11 + 'astound', 12 + 'attest', 13 + 'babied', 14 + 'bobcat', 15 + 'bondless', 16 + 'bullion', 17 + 'bunny', 18 + 'celtic', 19 + 'chivalry', 20 + 'circling', 21 + 'civic', 22 + 'clobber', 23 + 'conform', 24 + 'cosmic', 25 + 'crier', 26 + 'curtly', 27 + 'depose', 28 + 'diagnosis', 29 + 'disfigure', 30 + 'drank', 31 + 'ducktail', 32 + 'eel', 33 + 'effort', 34 + 'equipment', 35 + 'eternal', 36 + 'exemplify', 37 + 'filtrate', 38 + 'fit', 39 + 'flaccid', 40 + 'fool', 41 + 'germinate', 42 + 'glade', 43 + 'graveness', 44 + 'gray', 45 + 'hydrant', 46 + 'italicize', 47 + 'landowner', 48 + 'lavender', 49 + 'mandatory', 50 + 'molecule', 51 + 'multitude', 52 + 'music', 53 + 'national', 54 + 'neatly', 55 + 'omnivore', 56 + 'other', 57 + 'overdrive', 58 + 'overhang', 59 + 'overlying', 60 + 'padded', 61 + 'pang', 62 + 'paralyses', 63 + 'partner', 64 + 'pedometer', 65 + 'plaything', 66 + 'pointy', 67 + 'prescribe', 68 + 'pueblo', 69 + 'pursuant', 70 + 'reprise', 71 + 'resilient', 72 + 'reusable', 73 + 'roster', 74 + 'scenic', 75 + 'selected', 76 + 'singer', 77 + 'slacker', 78 + 'smirk', 79 + 'smoked', 80 + 'smugly', 81 + 'startle', 82 + 'sternum', 83 + 'strut', 84 + 'subsystem', 85 + 'supper', 86 + 'swifter', 87 + 'tacking', 88 + 'traffic', 89 + 'tragedy', 90 + 'trapper', 91 + 'tummy', 92 + 'twiddle', 93 + 'unglazed', 94 + 'ungloved', 95 + 'unicorn', 96 + 'unissued', 97 + 'unmovable', 98 + 'unwary', 99 + 'uselessly', 100 + 'venus', 101 + 'vertebrae', 102 + 'wildly', 103 + 'wrecker', 104 + ]; 4 105 5 106 export const generateConfirmationCode = () => { 6 - const code = generateCode(); 7 - return `${code.slice(0, 5)}-${code.slice(5, 10)}`; 107 + return sample(words, 3).join(' '); 8 108 };
+5 -6
src/lib/utils/search-params.ts
··· 1 1 import { batch, createSignal } from 'solid-js'; 2 2 3 - import { At } from '@atcute/client/lexicons'; 4 - import { isDid, isHandle } from '@atcute/identity'; 3 + import { isDid, isHandle } from '@atcute/lexicons/syntax'; 5 4 6 - import { UnwrapArray } from '~/api/utils/types'; 5 + import type { UnwrapArray } from '~/api/utils/types'; 7 6 8 7 export interface ParamParser<T> { 9 8 parse: (value: string | string[] | null) => T | null; ··· 223 222 224 223 export const asDID = createParser({ 225 224 parse(value) { 226 - if (typeof value === 'string' && isDid(value)) { 227 - return value as At.Did; 225 + if (isDid(value)) { 226 + return value; 228 227 } 229 228 230 229 return null; ··· 236 235 237 236 export const asHandle = createParser({ 238 237 parse(value) { 239 - if (typeof value === 'string' && isHandle(value)) { 238 + if (isHandle(value)) { 240 239 return value; 241 240 } 242 241
+17
src/lib/utils/stream.ts
··· 1 + export async function* iterateStream<T>(stream: ReadableStream<T>) { 2 + const reader = stream.getReader(); 3 + 4 + try { 5 + while (true) { 6 + const { done, value } = await reader.read(); 7 + 8 + if (done) { 9 + return; 10 + } 11 + 12 + yield value; 13 + } 14 + } finally { 15 + reader.releaseLock(); 16 + } 17 + }
+3
src/main.tsx
··· 22 22 if (Symbol.dispose === undefined) { 23 23 Object.defineProperty(Symbol, 'dispose', { value: Symbol.for(`Symbol.dispose`) }); 24 24 } 25 + if (Symbol.asyncDispose === undefined) { 26 + Object.defineProperty(Symbol, 'asyncDispose', { value: Symbol.for(`Symbol.asyncDispose`) }); 27 + } 25 28 26 29 render(App, document.body);
+9
src/routes.ts
··· 22 22 path: '/crypto-generate', 23 23 component: lazy(() => import('./views/crypto/crypto-generate')), 24 24 }, 25 + { 26 + path: '/crypto-info', 27 + component: lazy(() => import('./views/crypto/crypto-info')), 28 + }, 25 29 26 30 { 27 31 path: '/did-lookup', ··· 47 51 { 48 52 path: '/repo-archive-explore', 49 53 component: lazy(() => import('./views/repository/repo-archive-explore/page')), 54 + }, 55 + 56 + { 57 + path: '/account-migrate', 58 + component: lazy(() => import('./views/account/account-migrate/page')), 50 59 }, 51 60 52 61 {
+49
src/views/account/account-migrate/context.tsx
··· 1 + import { createContext, createSignal, useContext, type JSX } from 'solid-js'; 2 + 3 + import type { CredentialManager } from '@atcute/client'; 4 + import type { DidDocument } from '@atcute/identity'; 5 + import type { AtprotoDid, Did } from '@atcute/lexicons/syntax'; 6 + 7 + export interface SourceAccount { 8 + did: AtprotoDid; 9 + didDoc: DidDocument; 10 + pdsUrl: string; 11 + manager: CredentialManager | null; 12 + } 13 + 14 + export interface DestinationAccount { 15 + pdsUrl: string; 16 + serviceDid: Did; 17 + manager: CredentialManager | null; 18 + } 19 + 20 + export interface MigrationContextValue { 21 + source: () => SourceAccount | null; 22 + setSource: (account: SourceAccount | null) => void; 23 + destination: () => DestinationAccount | null; 24 + setDestination: (account: DestinationAccount | null) => void; 25 + } 26 + 27 + const MigrationContext = createContext<MigrationContextValue>(); 28 + 29 + export const MigrationProvider = (props: { children: JSX.Element }) => { 30 + const [source, setSource] = createSignal<SourceAccount | null>(null); 31 + const [destination, setDestination] = createSignal<DestinationAccount | null>(null); 32 + 33 + const value: MigrationContextValue = { 34 + source, 35 + setSource, 36 + destination, 37 + setDestination, 38 + }; 39 + 40 + return <MigrationContext.Provider value={value}>{props.children}</MigrationContext.Provider>; 41 + }; 42 + 43 + export const useMigration = (): MigrationContextValue => { 44 + const context = useContext(MigrationContext); 45 + if (!context) { 46 + throw new Error('useMigration must be used within a MigrationProvider'); 47 + } 48 + return context; 49 + };
+54
src/views/account/account-migrate/page.tsx
··· 1 + import { createEffect, createSignal, onCleanup } from 'solid-js'; 2 + 3 + import { history } from '~/globals/navigation'; 4 + 5 + import { useTitle } from '~/lib/navigation/router'; 6 + 7 + import PageHeader from '~/components/page-header'; 8 + 9 + import { MigrationProvider } from './context'; 10 + 11 + import SourceAccountSection from './sections/source-account'; 12 + import DestinationAccountSection from './sections/destination-account'; 13 + import RepositorySection from './sections/repository'; 14 + import BlobsSection from './sections/blobs'; 15 + import PreferencesSection from './sections/preferences'; 16 + import IdentitySection from './sections/identity'; 17 + import AccountStatusSection from './sections/account-status'; 18 + 19 + const AccountMigratePage = () => { 20 + const [hasStarted, setHasStarted] = createSignal(false); 21 + 22 + createEffect(() => { 23 + if (hasStarted()) { 24 + const cleanup = history.block((tx) => { 25 + if (window.confirm(`You have a migration in progress. Leave this page?`)) { 26 + cleanup(); 27 + tx.retry(); 28 + } 29 + }); 30 + 31 + onCleanup(cleanup); 32 + } 33 + }); 34 + 35 + useTitle(() => `Migrate account โ€” boat`); 36 + 37 + return ( 38 + <MigrationProvider> 39 + <PageHeader title="Migrate account" subtitle="Move your account data to another server" /> 40 + 41 + <div class="flex flex-col"> 42 + <SourceAccountSection onStarted={() => setHasStarted(true)} /> 43 + <DestinationAccountSection /> 44 + <RepositorySection /> 45 + <BlobsSection /> 46 + <PreferencesSection /> 47 + <IdentitySection /> 48 + <AccountStatusSection /> 49 + </div> 50 + </MigrationProvider> 51 + ); 52 + }; 53 + 54 + export default AccountMigratePage;
+207
src/views/account/account-migrate/sections/account-status.tsx
··· 1 + import { Show } from 'solid-js'; 2 + 3 + import { Client, type CredentialManager, ok } from '@atcute/client'; 4 + 5 + import { createMutation } from '~/lib/utils/mutation'; 6 + 7 + import { Accordion, StatusBadge, Subsection } from '~/components/accordion'; 8 + import Button from '~/components/inputs/button'; 9 + 10 + import { useMigration } from '../context'; 11 + 12 + interface AccountStatus { 13 + activated: boolean; 14 + validDid: boolean; 15 + repoCommit: string; 16 + repoRev: string; 17 + repoBlocks: number; 18 + indexedRecords: number; 19 + privateStateValues: number; 20 + expectedBlobs: number; 21 + importedBlobs: number; 22 + } 23 + 24 + const AccountStatusSection = () => { 25 + const { source, destination } = useMigration(); 26 + 27 + const checkSourceMutation = createMutation({ 28 + async mutationFn({ manager }: { manager: CredentialManager }) { 29 + const sourceClient = new Client({ handler: manager }); 30 + return await ok(sourceClient.get('com.atproto.server.checkAccountStatus')) as AccountStatus; 31 + }, 32 + onError(err) { 33 + console.error(err); 34 + }, 35 + }); 36 + 37 + const checkDestMutation = createMutation({ 38 + async mutationFn({ manager }: { manager: CredentialManager }) { 39 + const destClient = new Client({ handler: manager }); 40 + return await ok(destClient.get('com.atproto.server.checkAccountStatus')) as AccountStatus; 41 + }, 42 + onError(err) { 43 + console.error(err); 44 + }, 45 + }); 46 + 47 + const activateMutation = createMutation({ 48 + async mutationFn({ manager }: { manager: CredentialManager }) { 49 + const destClient = new Client({ handler: manager }); 50 + await ok(destClient.post('com.atproto.server.activateAccount', { as: null })); 51 + }, 52 + onSuccess() { 53 + const dest = destination(); 54 + if (dest?.manager) { 55 + checkDestMutation.mutate({ manager: dest.manager }); 56 + } 57 + }, 58 + onError(err) { 59 + console.error(err); 60 + }, 61 + }); 62 + 63 + const deactivateMutation = createMutation({ 64 + async mutationFn({ manager }: { manager: CredentialManager }) { 65 + if (!confirm('Are you sure you want to deactivate your source account? This will prevent the old PDS from serving your data.')) { 66 + throw new Error('Cancelled'); 67 + } 68 + const sourceClient = new Client({ handler: manager }); 69 + await ok(sourceClient.post('com.atproto.server.deactivateAccount', { as: null, input: {} })); 70 + }, 71 + onSuccess() { 72 + const src = source(); 73 + if (src?.manager) { 74 + checkSourceMutation.mutate({ manager: src.manager }); 75 + } 76 + }, 77 + onError(err) { 78 + if (err instanceof Error && err.message === 'Cancelled') return; 79 + console.error(err); 80 + }, 81 + }); 82 + 83 + const renderStatus = (status: AccountStatus) => ( 84 + <div class="space-y-1 text-sm"> 85 + <p> 86 + <span class="text-gray-500">Status:</span>{' '} 87 + <StatusBadge variant={status.activated ? 'success' : 'idle'}> 88 + {status.activated ? 'Active' : 'Deactivated'} 89 + </StatusBadge> 90 + </p> 91 + <p> 92 + <span class="text-gray-500">Records:</span>{' '} 93 + <span class="font-mono">{status.indexedRecords}</span> 94 + </p> 95 + <p> 96 + <span class="text-gray-500">Blobs:</span>{' '} 97 + <span class="font-mono">{status.importedBlobs}/{status.expectedBlobs}</span> 98 + </p> 99 + <p> 100 + <span class="text-gray-500">Repo blocks:</span>{' '} 101 + <span class="font-mono">{status.repoBlocks}</span> 102 + </p> 103 + </div> 104 + ); 105 + 106 + return ( 107 + <Accordion title="Account Status"> 108 + <Subsection title="Source account"> 109 + <Show 110 + when={source()?.manager} 111 + fallback={<p class="text-sm text-gray-500">Sign in to source account first.</p>} 112 + > 113 + {(manager) => ( 114 + <> 115 + <div class="flex items-center gap-3"> 116 + <Button 117 + variant="outline" 118 + onClick={() => checkSourceMutation.mutate({ manager: manager() })} 119 + disabled={checkSourceMutation.isPending} 120 + > 121 + {checkSourceMutation.isPending ? 'Checking...' : 'Check status'} 122 + </Button> 123 + </div> 124 + 125 + <Show when={checkSourceMutation.isError}> 126 + <p class="text-sm text-red-600">{`${checkSourceMutation.error}`}</p> 127 + </Show> 128 + 129 + <Show when={checkSourceMutation.data}> 130 + {(status) => ( 131 + <> 132 + {renderStatus(status())} 133 + 134 + <Show when={status().activated}> 135 + <div class="mt-3"> 136 + <Button 137 + variant="secondary" 138 + onClick={() => deactivateMutation.mutate({ manager: manager() })} 139 + disabled={deactivateMutation.isPending} 140 + > 141 + {deactivateMutation.isPending ? 'Deactivating...' : 'Deactivate source account'} 142 + </Button> 143 + </div> 144 + </Show> 145 + </> 146 + )} 147 + </Show> 148 + </> 149 + )} 150 + </Show> 151 + </Subsection> 152 + 153 + <Subsection title="Destination account"> 154 + <Show 155 + when={destination()?.manager} 156 + fallback={<p class="text-sm text-gray-500">Sign in to destination account first.</p>} 157 + > 158 + {(manager) => ( 159 + <> 160 + <div class="flex items-center gap-3"> 161 + <Button 162 + variant="outline" 163 + onClick={() => checkDestMutation.mutate({ manager: manager() })} 164 + disabled={checkDestMutation.isPending} 165 + > 166 + {checkDestMutation.isPending ? 'Checking...' : 'Check status'} 167 + </Button> 168 + </div> 169 + 170 + <Show when={checkDestMutation.isError}> 171 + <p class="text-sm text-red-600">{`${checkDestMutation.error}`}</p> 172 + </Show> 173 + 174 + <Show when={checkDestMutation.data}> 175 + {(status) => ( 176 + <> 177 + {renderStatus(status())} 178 + 179 + <Show when={!status().activated}> 180 + <div class="mt-3"> 181 + <Button 182 + onClick={() => activateMutation.mutate({ manager: manager() })} 183 + disabled={activateMutation.isPending} 184 + > 185 + {activateMutation.isPending ? 'Activating...' : 'Activate destination account'} 186 + </Button> 187 + </div> 188 + </Show> 189 + </> 190 + )} 191 + </Show> 192 + </> 193 + )} 194 + </Show> 195 + </Subsection> 196 + 197 + <Show when={activateMutation.isError || deactivateMutation.isError}> 198 + <p class="text-sm text-red-600"> 199 + {activateMutation.isError ? `Failed to activate: ${activateMutation.error}` : ''} 200 + {deactivateMutation.isError ? `Failed to deactivate: ${deactivateMutation.error}` : ''} 201 + </p> 202 + </Show> 203 + </Accordion> 204 + ); 205 + }; 206 + 207 + export default AccountStatusSection;
+455
src/views/account/account-migrate/sections/blobs.tsx
··· 1 + import { showOpenFilePicker, showSaveFilePicker } from 'native-file-system-adapter'; 2 + import { createSignal, For, Show } from 'solid-js'; 3 + 4 + import { Client, ClientResponseError, type CredentialManager, ok, simpleFetchHandler } from '@atcute/client'; 5 + import { untar, writeTarEntry } from '@mary/tar'; 6 + 7 + import { createMutation } from '~/lib/utils/mutation'; 8 + 9 + import { Accordion, StatusBadge, Subsection } from '~/components/accordion'; 10 + import Button from '~/components/inputs/button'; 11 + 12 + import { useMigration, type SourceAccount } from '../context'; 13 + 14 + const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); 15 + 16 + const BlobsSection = () => { 17 + const { source, destination } = useMigration(); 18 + 19 + // Progress state (kept separate since mutations don't handle incremental updates) 20 + const [exportProgress, setExportProgress] = createSignal<string>(); 21 + const [importProgress, setImportProgress] = createSignal<string>(); 22 + 23 + const exportMutation = createMutation({ 24 + async mutationFn({ source }: { source: SourceAccount }) { 25 + const sourceClient = new Client({ handler: simpleFetchHandler({ service: source.pdsUrl }) }); 26 + 27 + setExportProgress('Retrieving list of blobs...'); 28 + 29 + // Get list of all blobs 30 + let blobs: string[] = []; 31 + let cursor: string | undefined; 32 + do { 33 + const data = await ok( 34 + sourceClient.get('com.atproto.sync.listBlobs', { 35 + params: { did: source.did, cursor, limit: 1_000 }, 36 + }), 37 + ); 38 + cursor = data.cursor; 39 + blobs = blobs.concat(data.cids); 40 + setExportProgress(`Retrieving list of blobs (found ${blobs.length})`); 41 + } while (cursor !== undefined); 42 + 43 + if (blobs.length === 0) { 44 + return { count: 0, cancelled: false }; 45 + } 46 + 47 + setExportProgress('Waiting for file picker...'); 48 + 49 + const fd = await showSaveFilePicker({ 50 + suggestedName: `blobs-${source.did}-${new Date().toISOString()}.tar`, 51 + // @ts-expect-error: ponyfill doesn't have the full typings 52 + id: 'blob-export', 53 + startIn: 'downloads', 54 + types: [ 55 + { 56 + description: 'Tarball archive', 57 + accept: { 'application/tar': ['.tar'] }, 58 + }, 59 + ], 60 + }).catch((err) => { 61 + if (err instanceof DOMException && err.name === 'AbortError') { 62 + return undefined; 63 + } 64 + throw err; 65 + }); 66 + 67 + if (!fd) { 68 + return { count: 0, cancelled: true }; 69 + } 70 + 71 + const writable = await fd.createWritable(); 72 + 73 + let downloaded = 0; 74 + for (const cid of blobs) { 75 + setExportProgress(`Downloading blobs (${downloaded}/${blobs.length})`); 76 + 77 + const downloadBlob = async (): Promise<Uint8Array | undefined> => { 78 + let attempts = 0; 79 + while (true) { 80 + if (attempts > 0) await sleep(2_000); 81 + attempts++; 82 + 83 + try { 84 + const response = await sourceClient.get('com.atproto.sync.getBlob', { 85 + as: 'bytes', 86 + params: { did: source.did, cid }, 87 + }); 88 + 89 + if (response.ok) { 90 + return response.data; 91 + } 92 + 93 + if (response.status === 400 && response.data.message === 'Blob not found') { 94 + return undefined; 95 + } 96 + 97 + if (response.status === 429) { 98 + await sleep(10_000); 99 + } 100 + 101 + if (attempts < 3) continue; 102 + throw new ClientResponseError(response); 103 + } catch (err) { 104 + if (attempts < 3) continue; 105 + throw err; 106 + } 107 + } 108 + }; 109 + 110 + const data = await downloadBlob(); 111 + if (data !== undefined) { 112 + const entry = writeTarEntry({ filename: `blobs/${cid}`, data }); 113 + await writable.write(entry); 114 + } 115 + 116 + downloaded++; 117 + } 118 + 119 + await writable.close(); 120 + return { count: blobs.length, cancelled: false }; 121 + }, 122 + onError(err) { 123 + console.error(err); 124 + }, 125 + onSettled() { 126 + setExportProgress(); 127 + }, 128 + }); 129 + 130 + const importFromFileMutation = createMutation({ 131 + async mutationFn({ destManager }: { destManager: CredentialManager }) { 132 + setImportProgress('Waiting for file picker...'); 133 + 134 + const [fd] = await showOpenFilePicker({ 135 + // @ts-expect-error: ponyfill doesn't have the full typings 136 + id: 'blob-import', 137 + types: [ 138 + { 139 + description: 'Tarball archive', 140 + accept: { 'application/tar': ['.tar'] }, 141 + }, 142 + ], 143 + }).catch((err) => { 144 + if (err instanceof DOMException && err.name === 'AbortError') { 145 + return [undefined]; 146 + } 147 + throw err; 148 + }); 149 + 150 + if (!fd) { 151 + return { uploaded: 0, failed: 0, cancelled: true }; 152 + } 153 + 154 + setImportProgress('Reading archive...'); 155 + const file = await fd.getFile(); 156 + 157 + const destClient = new Client({ handler: destManager }); 158 + 159 + let uploaded = 0; 160 + let failed = 0; 161 + 162 + for await (const entry of untar(file.stream())) { 163 + if (entry.type !== 'file') continue; 164 + 165 + const filename = entry.name; 166 + // Extract CID from path like "blobs/bafk..." 167 + const cid = filename.split('/').pop(); 168 + if (!cid) continue; 169 + 170 + setImportProgress(`Uploading blobs (${uploaded} uploaded, ${failed} failed)`); 171 + 172 + try { 173 + const data = await entry.bytes(); 174 + await destClient.post('com.atproto.repo.uploadBlob', { 175 + input: data, 176 + headers: { 177 + 'content-type': 'application/octet-stream', 178 + }, 179 + }); 180 + uploaded++; 181 + } catch (err) { 182 + console.error(`Failed to upload blob ${cid}:`, err); 183 + failed++; 184 + } 185 + } 186 + 187 + return { uploaded, failed, cancelled: false }; 188 + }, 189 + onError(err) { 190 + console.error(err); 191 + }, 192 + onSettled() { 193 + setImportProgress(); 194 + }, 195 + }); 196 + 197 + const importFromSourceMutation = createMutation({ 198 + async mutationFn({ source, destManager }: { source: SourceAccount; destManager: CredentialManager }) { 199 + setImportProgress('Checking for missing blobs...'); 200 + 201 + const sourceClient = new Client({ handler: simpleFetchHandler({ service: source.pdsUrl }) }); 202 + const destClient = new Client({ handler: destManager }); 203 + 204 + let uploaded = 0; 205 + let failed = 0; 206 + let cursor: string | undefined; 207 + 208 + do { 209 + const data = await ok( 210 + destClient.get('com.atproto.repo.listMissingBlobs', { 211 + params: { cursor, limit: 100 }, 212 + }), 213 + ); 214 + cursor = data.cursor; 215 + 216 + for (const blob of data.blobs) { 217 + setImportProgress(`Uploading missing blobs (${uploaded} uploaded, ${failed} failed)`); 218 + 219 + try { 220 + const response = await sourceClient.get('com.atproto.sync.getBlob', { 221 + as: 'stream', 222 + params: { did: source.did, cid: blob.cid }, 223 + }); 224 + 225 + if (!response.ok) { 226 + failed++; 227 + continue; 228 + } 229 + 230 + const contentType = response.headers.get('content-type') || 'application/octet-stream'; 231 + 232 + await destClient.post('com.atproto.repo.uploadBlob', { 233 + input: response.data, 234 + headers: { 235 + 'content-type': contentType, 236 + }, 237 + }); 238 + 239 + uploaded++; 240 + } catch (err) { 241 + console.error(`Failed to transfer blob ${blob.cid}:`, err); 242 + failed++; 243 + } 244 + } 245 + } while (cursor !== undefined); 246 + 247 + return { uploaded, failed }; 248 + }, 249 + onError(err) { 250 + console.error(err); 251 + }, 252 + onSettled() { 253 + setImportProgress(); 254 + }, 255 + }); 256 + 257 + const checkStatusMutation = createMutation({ 258 + async mutationFn({ destManager }: { destManager: CredentialManager }) { 259 + const destClient = new Client({ handler: destManager }); 260 + const status = await ok(destClient.get('com.atproto.server.checkAccountStatus')); 261 + 262 + let missingBlobs: string[] = []; 263 + 264 + // Get list of missing blobs if any 265 + if (status.expectedBlobs > status.importedBlobs) { 266 + let cursor: string | undefined; 267 + do { 268 + const data = await ok( 269 + destClient.get('com.atproto.repo.listMissingBlobs', { 270 + params: { cursor, limit: 100 }, 271 + }), 272 + ); 273 + cursor = data.cursor; 274 + missingBlobs.push(...data.blobs.map((b) => b.cid)); 275 + } while (cursor !== undefined); 276 + } 277 + 278 + return { 279 + expected: status.expectedBlobs, 280 + imported: status.importedBlobs, 281 + missingBlobs, 282 + }; 283 + }, 284 + onError(err) { 285 + console.error(err); 286 + }, 287 + }); 288 + 289 + const isImporting = () => importFromFileMutation.isPending || importFromSourceMutation.isPending; 290 + 291 + const getExportStatusText = () => { 292 + const data = exportMutation.data; 293 + if (data?.cancelled) return undefined; 294 + if (data?.count === 0) return 'No blobs to export'; 295 + if (data) return `Exported ${data.count} blobs`; 296 + return exportProgress(); 297 + }; 298 + 299 + const getImportStatusText = () => { 300 + const fileData = importFromFileMutation.data; 301 + const sourceData = importFromSourceMutation.data; 302 + 303 + if (fileData && !fileData.cancelled) { 304 + return ( 305 + `Uploaded ${fileData.uploaded} blobs` + (fileData.failed > 0 ? ` (${fileData.failed} failed)` : '') 306 + ); 307 + } 308 + if (sourceData) { 309 + if (sourceData.uploaded === 0 && sourceData.failed === 0) return 'No missing blobs'; 310 + return ( 311 + `Uploaded ${sourceData.uploaded} blobs` + 312 + (sourceData.failed > 0 ? ` (${sourceData.failed} failed)` : '') 313 + ); 314 + } 315 + return importProgress(); 316 + }; 317 + 318 + const getImportError = () => importFromFileMutation.error || importFromSourceMutation.error; 319 + 320 + return ( 321 + <Accordion title="Blobs"> 322 + <Subsection title="Export from source"> 323 + <p class="text-sm text-gray-600">Download all blobs as a tarball for backup or manual import.</p> 324 + 325 + <Show when={source()} fallback={<p class="text-sm text-gray-500">Resolve source account first.</p>}> 326 + {(src) => ( 327 + <> 328 + <div class="flex items-center gap-3"> 329 + <Button 330 + onClick={() => exportMutation.mutate({ source: src() })} 331 + disabled={exportMutation.isPending} 332 + > 333 + {exportMutation.isPending ? 'Exporting...' : 'Export to file'} 334 + </Button> 335 + <Show when={getExportStatusText()}> 336 + {(text) => <span class="text-sm text-gray-600">{text()}</span>} 337 + </Show> 338 + </div> 339 + 340 + <Show when={exportMutation.error}> 341 + {(err) => <p class="text-sm text-red-600">{`${err()}`}</p>} 342 + </Show> 343 + </> 344 + )} 345 + </Show> 346 + </Subsection> 347 + 348 + <Subsection title="Import to destination"> 349 + <p class="text-sm text-gray-600">Upload blobs from a tarball or transfer directly from source.</p> 350 + 351 + <Show 352 + when={destination()?.manager} 353 + fallback={<p class="text-sm text-gray-500">Sign in to destination account first.</p>} 354 + > 355 + {(destManager) => ( 356 + <> 357 + <div class="flex flex-wrap items-center gap-3"> 358 + <Button 359 + onClick={() => importFromFileMutation.mutate({ destManager: destManager() })} 360 + disabled={isImporting()} 361 + > 362 + {isImporting() ? 'Importing...' : 'Import from file'} 363 + </Button> 364 + 365 + <Show when={source()}> 366 + {(src) => ( 367 + <Button 368 + variant="secondary" 369 + onClick={() => 370 + importFromSourceMutation.mutate({ source: src(), destManager: destManager() }) 371 + } 372 + disabled={isImporting()} 373 + > 374 + Transfer from source 375 + </Button> 376 + )} 377 + </Show> 378 + </div> 379 + 380 + <Show when={getImportStatusText()}> 381 + {(text) => <span class="text-sm text-gray-600">{text()}</span>} 382 + </Show> 383 + 384 + <Show when={getImportError()}>{(err) => <p class="text-sm text-red-600">{`${err()}`}</p>}</Show> 385 + </> 386 + )} 387 + </Show> 388 + </Subsection> 389 + 390 + <Subsection title="Status"> 391 + <Show 392 + when={destination()?.manager} 393 + fallback={<p class="text-sm text-gray-500">Sign in to destination account first.</p>} 394 + > 395 + {(destManager) => ( 396 + <> 397 + <div class="flex items-center gap-3"> 398 + <Button 399 + variant="outline" 400 + onClick={() => checkStatusMutation.mutate({ destManager: destManager() })} 401 + disabled={checkStatusMutation.isPending} 402 + > 403 + {checkStatusMutation.isPending ? 'Checking...' : 'Check status'} 404 + </Button> 405 + 406 + <Show when={checkStatusMutation.data}> 407 + {(status) => ( 408 + <span class="text-sm"> 409 + <StatusBadge variant={status().imported === status().expected ? 'success' : 'pending'}> 410 + {status().imported}/{status().expected} blobs 411 + </StatusBadge> 412 + </span> 413 + )} 414 + </Show> 415 + </div> 416 + 417 + <Show when={checkStatusMutation.data?.missingBlobs.length}> 418 + {(count) => ( 419 + <div class="mt-2 rounded border border-yellow-300 bg-yellow-50 p-3"> 420 + <p class="mb-2 text-sm font-medium text-yellow-800">{count()} missing blobs</p> 421 + 422 + <Show when={source()}> 423 + {(src) => ( 424 + <Button 425 + variant="secondary" 426 + onClick={() => 427 + importFromSourceMutation.mutate({ source: src(), destManager: destManager() }) 428 + } 429 + disabled={isImporting()} 430 + > 431 + Transfer missing from source 432 + </Button> 433 + )} 434 + </Show> 435 + 436 + <details class="mt-2"> 437 + <summary class="cursor-pointer text-sm text-yellow-700">Show CIDs</summary> 438 + <div class="mt-1 max-h-32 overflow-auto font-mono text-xs"> 439 + <For each={checkStatusMutation.data?.missingBlobs}> 440 + {(cid) => <div class="truncate">{cid}</div>} 441 + </For> 442 + </div> 443 + </details> 444 + </div> 445 + )} 446 + </Show> 447 + </> 448 + )} 449 + </Show> 450 + </Subsection> 451 + </Accordion> 452 + ); 453 + }; 454 + 455 + export default BlobsSection;
+437
src/views/account/account-migrate/sections/destination-account.tsx
··· 1 + import { createSignal, Show } from 'solid-js'; 2 + 3 + import { 4 + type AtpAccessJwt, 5 + Client, 6 + ClientResponseError, 7 + CredentialManager, 8 + ok, 9 + simpleFetchHandler, 10 + } from '@atcute/client'; 11 + import type { Did, Handle } from '@atcute/lexicons/syntax'; 12 + 13 + import { formatTotpCode, TOTP_RE } from '~/api/utils/auth'; 14 + import { decodeJwt } from '~/api/utils/jwt'; 15 + import { isServiceUrlString } from '~/api/types/strings'; 16 + 17 + import { createMutation } from '~/lib/utils/mutation'; 18 + 19 + import { Accordion, StatusBadge, Subsection } from '~/components/accordion'; 20 + import Button from '~/components/inputs/button'; 21 + import TextInput from '~/components/inputs/text-input'; 22 + 23 + import { useMigration } from '../context'; 24 + 25 + class InsufficientLoginError extends Error {} 26 + 27 + const DestinationAccountSection = () => { 28 + const { source, destination, setDestination } = useMigration(); 29 + 30 + // Connect state 31 + const [pdsUrl, setPdsUrl] = createSignal(''); 32 + const [connectError, setConnectError] = createSignal<string>(); 33 + 34 + // Create account state 35 + const [newHandle, setNewHandle] = createSignal(''); 36 + const [newEmail, setNewEmail] = createSignal(''); 37 + const [newPassword, setNewPassword] = createSignal(''); 38 + const [inviteCode, setInviteCode] = createSignal(''); 39 + const [createError, setCreateError] = createSignal<string>(); 40 + 41 + // Login state 42 + const [loginPassword, setLoginPassword] = createSignal(''); 43 + const [loginOtp, setLoginOtp] = createSignal(''); 44 + const [isLoginTotpRequired, setIsLoginTotpRequired] = createSignal(false); 45 + const [loginError, setLoginError] = createSignal<string>(); 46 + 47 + const connectMutation = createMutation({ 48 + async mutationFn({ pdsUrl }: { pdsUrl: string }) { 49 + const destClient = new Client({ handler: simpleFetchHandler({ service: pdsUrl }) }); 50 + const desc = await ok(destClient.get('com.atproto.server.describeServer')); 51 + 52 + return { serviceDid: desc.did }; 53 + }, 54 + onMutate() { 55 + setConnectError(); 56 + }, 57 + onSuccess({ serviceDid }) { 58 + setDestination({ pdsUrl: pdsUrl(), serviceDid, manager: null }); 59 + }, 60 + onError(err) { 61 + console.error(err); 62 + setConnectError(`Failed to connect: ${err}`); 63 + }, 64 + }); 65 + 66 + const createAccountMutation = createMutation({ 67 + async mutationFn({ 68 + sourceDid, 69 + sourceManager, 70 + destPdsUrl, 71 + destServiceDid, 72 + handle, 73 + email, 74 + password, 75 + inviteCode, 76 + }: { 77 + sourceDid: Did; 78 + sourceManager: CredentialManager; 79 + destPdsUrl: string; 80 + destServiceDid: string; 81 + handle: Handle; 82 + email: string; 83 + password: string; 84 + inviteCode: string; 85 + }) { 86 + // Get service auth token from old PDS 87 + const sourceClient = new Client({ handler: sourceManager }); 88 + const authResp = await ok( 89 + sourceClient.get('com.atproto.server.getServiceAuth', { 90 + params: { 91 + aud: destServiceDid as Did, 92 + lxm: 'com.atproto.server.createAccount', 93 + }, 94 + }), 95 + ); 96 + const serviceJwt = authResp.token; 97 + 98 + // Create account on new PDS with service auth 99 + const destClient = new Client({ handler: simpleFetchHandler({ service: destPdsUrl }) }); 100 + const createResp = await destClient.post('com.atproto.server.createAccount', { 101 + headers: { Authorization: `Bearer ${serviceJwt}` }, 102 + input: { 103 + did: sourceDid, 104 + handle: handle, 105 + email: email, 106 + password: password, 107 + inviteCode: inviteCode || undefined, 108 + }, 109 + }); 110 + 111 + if (!createResp.ok) { 112 + throw new ClientResponseError(createResp); 113 + } 114 + 115 + if (createResp.data.did !== sourceDid) { 116 + throw new Error(`Created account has different DID: ${createResp.data.did}`); 117 + } 118 + 119 + // Login to the new account 120 + const manager = new CredentialManager({ service: destPdsUrl }); 121 + await manager.login({ 122 + identifier: sourceDid, 123 + password: password, 124 + }); 125 + 126 + return manager; 127 + }, 128 + onMutate() { 129 + setCreateError(); 130 + }, 131 + onSuccess(manager) { 132 + setDestination({ ...destination()!, manager }); 133 + setNewPassword(''); 134 + }, 135 + onError(err) { 136 + if (err instanceof ClientResponseError) { 137 + if (err.error === 'InvalidInviteCode') { 138 + setCreateError(`Invalid invite code`); 139 + return; 140 + } 141 + if (err.error === 'HandleNotAvailable') { 142 + setCreateError(`Handle is not available`); 143 + return; 144 + } 145 + if (err.description) { 146 + setCreateError(err.description); 147 + return; 148 + } 149 + } 150 + console.error(err); 151 + setCreateError(`${err}`); 152 + }, 153 + }); 154 + 155 + const loginMutation = createMutation({ 156 + async mutationFn({ 157 + pdsUrl, 158 + did, 159 + password, 160 + otp, 161 + }: { 162 + pdsUrl: string; 163 + did: string; 164 + password: string; 165 + otp: string; 166 + }) { 167 + const manager = new CredentialManager({ service: pdsUrl }); 168 + const session = await manager.login({ 169 + identifier: did, 170 + password: password, 171 + code: formatTotpCode(otp), 172 + }); 173 + 174 + const decoded = decodeJwt(session.accessJwt) as AtpAccessJwt; 175 + if (decoded.scope !== 'com.atproto.access') { 176 + throw new InsufficientLoginError(`You need to sign in with a main password, not an app password`); 177 + } 178 + 179 + return manager; 180 + }, 181 + onMutate() { 182 + setLoginError(); 183 + }, 184 + onSuccess(manager) { 185 + setDestination({ ...destination()!, manager }); 186 + setLoginPassword(''); 187 + setLoginOtp(''); 188 + setIsLoginTotpRequired(false); 189 + }, 190 + onError(err) { 191 + if (err instanceof ClientResponseError) { 192 + if (err.error === 'AuthFactorTokenRequired') { 193 + setLoginOtp(''); 194 + setIsLoginTotpRequired(true); 195 + return; 196 + } 197 + if (err.error === 'AuthenticationRequired') { 198 + setLoginError(`Invalid identifier or password`); 199 + return; 200 + } 201 + if (err.description?.includes('Token is invalid')) { 202 + setLoginError(`Invalid one-time confirmation code`); 203 + setIsLoginTotpRequired(true); 204 + return; 205 + } 206 + } 207 + if (err instanceof InsufficientLoginError) { 208 + setLoginError(err.message); 209 + return; 210 + } 211 + console.error(err); 212 + setLoginError(`${err}`); 213 + }, 214 + }); 215 + 216 + const isConnected = () => destination() !== null; 217 + const isAuthenticated = () => destination()?.manager != null; 218 + const canCreateAccount = () => source()?.manager != null; 219 + 220 + return ( 221 + <Accordion title="Destination Account"> 222 + <Subsection title="Connect to PDS"> 223 + <Show when={!isConnected()}> 224 + <form 225 + onSubmit={(ev) => { 226 + ev.preventDefault(); 227 + connectMutation.mutate({ pdsUrl: pdsUrl() }); 228 + }} 229 + class="flex flex-col gap-3" 230 + > 231 + <TextInput 232 + label="PDS URL" 233 + type="url" 234 + placeholder="https://pds.example.com" 235 + value={pdsUrl()} 236 + required 237 + onChange={(text, event) => { 238 + setPdsUrl(text); 239 + const input = event.currentTarget; 240 + if (text !== '' && !isServiceUrlString(text)) { 241 + input.setCustomValidity('Must be a valid URL'); 242 + } else { 243 + input.setCustomValidity(''); 244 + } 245 + }} 246 + /> 247 + 248 + <Show when={connectError()}> 249 + <p class="text-sm text-red-600">{connectError()}</p> 250 + </Show> 251 + 252 + <div> 253 + <Button type="submit" disabled={connectMutation.isPending}> 254 + {connectMutation.isPending ? 'Connecting...' : 'Connect'} 255 + </Button> 256 + </div> 257 + </form> 258 + </Show> 259 + 260 + <Show when={isConnected()}> 261 + <div class="flex flex-col gap-2 text-sm"> 262 + <p> 263 + <span class="text-gray-500">URL:</span>{' '} 264 + <span class="font-mono">{destination()!.pdsUrl}</span> 265 + </p> 266 + <p> 267 + <span class="text-gray-500">Service DID:</span>{' '} 268 + <span class="font-mono">{destination()!.serviceDid}</span> 269 + </p> 270 + <div class="mt-1"> 271 + <button 272 + type="button" 273 + onClick={() => setDestination(null)} 274 + class="text-sm text-purple-800 hover:underline" 275 + > 276 + Change PDS 277 + </button> 278 + </div> 279 + </div> 280 + </Show> 281 + </Subsection> 282 + 283 + <Show when={isConnected() && !isAuthenticated()}> 284 + <Subsection title="Create new account"> 285 + <Show when={!canCreateAccount()}> 286 + <p class="text-sm text-gray-600"> 287 + You need to authenticate to your source account first to create an account on the 288 + destination PDS. 289 + </p> 290 + </Show> 291 + 292 + <Show when={canCreateAccount()}> 293 + <form 294 + onSubmit={(ev) => { 295 + ev.preventDefault(); 296 + const src = source()!; 297 + const dest = destination()!; 298 + createAccountMutation.mutate({ 299 + sourceDid: src.did, 300 + sourceManager: src.manager!, 301 + destPdsUrl: dest.pdsUrl, 302 + destServiceDid: dest.serviceDid, 303 + handle: newHandle() as Handle, 304 + email: newEmail(), 305 + password: newPassword(), 306 + inviteCode: inviteCode(), 307 + }); 308 + }} 309 + class="flex flex-col gap-3" 310 + > 311 + <TextInput 312 + label="Handle" 313 + placeholder="alice.pds.example.com" 314 + value={newHandle()} 315 + required 316 + onChange={setNewHandle} 317 + /> 318 + 319 + <TextInput 320 + label="Email" 321 + type="email" 322 + placeholder="alice@example.com" 323 + value={newEmail()} 324 + required 325 + onChange={setNewEmail} 326 + /> 327 + 328 + <TextInput 329 + label="Password" 330 + type="password" 331 + value={newPassword()} 332 + required 333 + onChange={setNewPassword} 334 + /> 335 + 336 + <TextInput 337 + label="Invite code (if required)" 338 + placeholder="pds-example-com-xxxxx" 339 + value={inviteCode()} 340 + onChange={setInviteCode} 341 + /> 342 + 343 + <Show when={createError()}> 344 + <p class="text-sm text-red-600">{createError()}</p> 345 + </Show> 346 + 347 + <div> 348 + <Button type="submit" disabled={createAccountMutation.isPending}> 349 + {createAccountMutation.isPending ? 'Creating...' : 'Create account'} 350 + </Button> 351 + </div> 352 + </form> 353 + </Show> 354 + </Subsection> 355 + 356 + <Subsection title="Or login to existing account"> 357 + <p class="mb-2 text-sm text-gray-600"> 358 + If you already have a deactivated account on the destination PDS. 359 + </p> 360 + 361 + <Show when={!source()}> 362 + <p class="text-sm text-gray-600"> 363 + Resolve your source account first so we know which DID to use. 364 + </p> 365 + </Show> 366 + 367 + <Show when={source()}> 368 + <form 369 + onSubmit={(ev) => { 370 + ev.preventDefault(); 371 + const src = source()!; 372 + const dest = destination()!; 373 + loginMutation.mutate({ 374 + pdsUrl: dest.pdsUrl, 375 + did: src.did, 376 + password: loginPassword(), 377 + otp: loginOtp(), 378 + }); 379 + }} 380 + class="flex flex-col gap-3" 381 + > 382 + <TextInput 383 + label="Password" 384 + type="password" 385 + value={loginPassword()} 386 + required 387 + onChange={setLoginPassword} 388 + /> 389 + 390 + <Show when={isLoginTotpRequired()}> 391 + <TextInput 392 + label="One-time confirmation code" 393 + blurb="A code has been sent to your email address." 394 + type="text" 395 + autocomplete="one-time-code" 396 + pattern={TOTP_RE.source} 397 + placeholder="AAAAA-BBBBB" 398 + value={loginOtp()} 399 + required 400 + onChange={setLoginOtp} 401 + monospace 402 + /> 403 + </Show> 404 + 405 + <Show when={loginError()}> 406 + <p class="text-sm text-red-600">{loginError()}</p> 407 + </Show> 408 + 409 + <div> 410 + <Button type="submit" disabled={loginMutation.isPending}> 411 + {loginMutation.isPending ? 'Signing in...' : 'Sign in'} 412 + </Button> 413 + </div> 414 + </form> 415 + </Show> 416 + </Subsection> 417 + </Show> 418 + 419 + <Show when={isAuthenticated()}> 420 + <Subsection title="Account status"> 421 + <div class="flex items-center gap-2"> 422 + <StatusBadge variant="success">Signed in</StatusBadge> 423 + <button 424 + type="button" 425 + onClick={() => setDestination({ ...destination()!, manager: null })} 426 + class="text-sm text-purple-800 hover:underline" 427 + > 428 + Sign out 429 + </button> 430 + </div> 431 + </Subsection> 432 + </Show> 433 + </Accordion> 434 + ); 435 + }; 436 + 437 + export default DestinationAccountSection;
+545
src/views/account/account-migrate/sections/identity.tsx
··· 1 + import { createSignal, For, Index, Show } from 'solid-js'; 2 + 3 + import { Client, ClientResponseError, type CredentialManager, ok } from '@atcute/client'; 4 + import { type DidKeyString, Secp256k1PrivateKeyExportable } from '@atcute/crypto'; 5 + import type { Did } from '@atcute/lexicons/syntax'; 6 + 7 + import { getPlcAuditLogs } from '~/api/queries/plc'; 8 + import { formatTotpCode, TOTP_RE } from '~/api/utils/auth'; 9 + 10 + import { createMutation } from '~/lib/utils/mutation'; 11 + 12 + import { Accordion, StatusBadge, Subsection } from '~/components/accordion'; 13 + import Button from '~/components/inputs/button'; 14 + import TextInput from '~/components/inputs/text-input'; 15 + import ToggleInput from '~/components/inputs/toggle-input'; 16 + 17 + import { getPlcPayload } from '~/views/identity/plc-applicator/plc-utils'; 18 + 19 + import { useMigration } from '../context'; 20 + 21 + interface RecommendedCredentials { 22 + alsoKnownAs?: string[]; 23 + rotationKeys?: string[]; 24 + verificationMethods?: Record<string, unknown>; 25 + services?: Record<string, unknown>; 26 + } 27 + 28 + interface GeneratedKeypair { 29 + publicDidKey: DidKeyString; 30 + privateHex: string; 31 + privateMultikey: string; 32 + } 33 + 34 + const IdentitySection = () => { 35 + const { source, destination } = useMigration(); 36 + 37 + // Rotation key state 38 + const [useGeneratedKey, setUseGeneratedKey] = createSignal(false); 39 + const [customKeys, setCustomKeys] = createSignal<string[]>([]); 40 + const [plcToken, setPlcToken] = createSignal(''); 41 + 42 + const requestTokenMutation = createMutation({ 43 + async mutationFn({ manager }: { manager: CredentialManager }) { 44 + const client = new Client({ handler: manager }); 45 + await ok(client.post('com.atproto.identity.requestPlcOperationSignature', { as: null })); 46 + }, 47 + onError(err) { 48 + console.error(err); 49 + }, 50 + }); 51 + 52 + const loadCredentialsMutation = createMutation({ 53 + async mutationFn({ manager }: { manager: CredentialManager }) { 54 + const client = new Client({ handler: manager }); 55 + return (await ok( 56 + client.get('com.atproto.identity.getRecommendedDidCredentials', {}), 57 + )) as RecommendedCredentials; 58 + }, 59 + onError(err) { 60 + console.error(err); 61 + }, 62 + }); 63 + 64 + // Analyze current rotation keys to find user-controlled keys that should be preserved 65 + const analyzeRotationKeysMutation = createMutation({ 66 + async mutationFn({ did, sourceManager }: { did: Did<'plc'>; sourceManager: CredentialManager }, signal) { 67 + // Get current rotation keys from PLC audit log 68 + const auditLogs = await getPlcAuditLogs({ did, signal }); 69 + const latestEntry = auditLogs[auditLogs.length - 1]; 70 + const currentPayload = getPlcPayload(latestEntry); 71 + const currentRotationKeys = currentPayload.rotationKeys ?? []; 72 + 73 + // Get source PDS's recommended credentials to identify PDS-controlled keys 74 + const sourceClient = new Client({ handler: sourceManager }); 75 + const sourcePdsCredentials = (await ok( 76 + sourceClient.get('com.atproto.identity.getRecommendedDidCredentials', {}), 77 + )) as RecommendedCredentials; 78 + const sourcePdsKeys = new Set(sourcePdsCredentials.rotationKeys ?? []); 79 + 80 + // Keys in current doc that aren't from source PDS are user-controlled 81 + const userControlledKeys = currentRotationKeys.filter((key) => !sourcePdsKeys.has(key)); 82 + 83 + return { 84 + currentRotationKeys, 85 + sourcePdsKeys: sourcePdsCredentials.rotationKeys ?? [], 86 + userControlledKeys, 87 + }; 88 + }, 89 + onSuccess(data) { 90 + // Pre-populate custom keys with user-controlled keys 91 + if (data.userControlledKeys.length > 0) { 92 + setCustomKeys(data.userControlledKeys); 93 + } 94 + }, 95 + onError(err) { 96 + console.error(err); 97 + }, 98 + }); 99 + 100 + const generateKeyMutation = createMutation({ 101 + async mutationFn() { 102 + const keypair = await Secp256k1PrivateKeyExportable.createKeypair(); 103 + const [publicDidKey, privateHex, privateMultikey] = await Promise.all([ 104 + keypair.exportPublicKey('did'), 105 + keypair.exportPrivateKey('rawHex'), 106 + keypair.exportPrivateKey('multikey'), 107 + ]); 108 + return { publicDidKey, privateHex, privateMultikey } as GeneratedKeypair; 109 + }, 110 + onError(err) { 111 + console.error(err); 112 + }, 113 + }); 114 + 115 + const signAndSubmitMutation = createMutation({ 116 + async mutationFn({ 117 + sourceManager, 118 + destManager, 119 + token, 120 + credentials, 121 + generatedKey, 122 + customKeys, 123 + }: { 124 + sourceManager: CredentialManager; 125 + destManager: CredentialManager; 126 + token: string; 127 + credentials: RecommendedCredentials; 128 + generatedKey?: GeneratedKeypair; 129 + customKeys: string[]; 130 + }) { 131 + const sourceClient = new Client({ handler: sourceManager }); 132 + const destClient = new Client({ handler: destManager }); 133 + 134 + // Prepend user keys to PDS-provided keys (so user keys appear first for recovery) 135 + const pdsRotationKeys = credentials.rotationKeys ?? []; 136 + const userKeys: string[] = []; 137 + if (generatedKey) { 138 + userKeys.push(generatedKey.publicDidKey); 139 + } 140 + userKeys.push(...customKeys.filter((k) => k.trim())); 141 + const rotationKeys = [...userKeys, ...pdsRotationKeys]; 142 + 143 + // Sign the PLC operation on the source PDS 144 + const signage = await ok( 145 + sourceClient.post('com.atproto.identity.signPlcOperation', { 146 + input: { 147 + token: formatTotpCode(token), 148 + alsoKnownAs: credentials.alsoKnownAs, 149 + rotationKeys: rotationKeys, 150 + services: credentials.services, 151 + verificationMethods: credentials.verificationMethods, 152 + }, 153 + }), 154 + ); 155 + 156 + // Submit via the destination PDS 157 + await ok( 158 + destClient.post('com.atproto.identity.submitPlcOperation', { 159 + as: null, 160 + input: { 161 + operation: signage.operation, 162 + }, 163 + }), 164 + ); 165 + }, 166 + onSuccess() { 167 + setPlcToken(''); 168 + }, 169 + onError(err) { 170 + console.error(err); 171 + }, 172 + }); 173 + 174 + // Calculate rotation key counts 175 + const pdsKeyCount = () => loadCredentialsMutation.data?.rotationKeys?.length ?? 0; 176 + const totalKeyCount = () => { 177 + const custom = customKeys().filter((k) => k.trim()).length; 178 + const generated = useGeneratedKey() && generateKeyMutation.data ? 1 : 0; 179 + return pdsKeyCount() + custom + generated; 180 + }; 181 + const canAddCustomKey = () => totalKeyCount() < 5; 182 + const isOverLimit = () => totalKeyCount() > 5; 183 + 184 + const addCustomKey = () => { 185 + if (canAddCustomKey()) { 186 + setCustomKeys([...customKeys(), '']); 187 + } 188 + }; 189 + 190 + const removeCustomKey = (index: number) => { 191 + setCustomKeys(customKeys().filter((_, i) => i !== index)); 192 + }; 193 + 194 + const updateCustomKey = (index: number, value: string) => { 195 + setCustomKeys(customKeys().map((k, i) => (i === index ? value : k))); 196 + }; 197 + 198 + const canSignAndSubmit = () => { 199 + const src = source(); 200 + const dest = destination(); 201 + const creds = loadCredentialsMutation.data; 202 + const token = plcToken().trim(); 203 + 204 + return !!(src?.manager && dest?.manager && creds && token && !isOverLimit()); 205 + }; 206 + 207 + const handleSignAndSubmit = () => { 208 + const src = source(); 209 + const dest = destination(); 210 + const creds = loadCredentialsMutation.data; 211 + const token = plcToken().trim(); 212 + 213 + if (!src?.manager || !dest?.manager || !creds || !token || isOverLimit()) return; 214 + 215 + signAndSubmitMutation.mutate({ 216 + sourceManager: src.manager, 217 + destManager: dest.manager, 218 + token, 219 + credentials: creds, 220 + generatedKey: useGeneratedKey() ? generateKeyMutation.data : undefined, 221 + customKeys: customKeys(), 222 + }); 223 + }; 224 + 225 + const getSubmitErrorMessage = () => { 226 + const err = signAndSubmitMutation.error; 227 + if (err instanceof ClientResponseError) { 228 + if (err.error === 'InvalidToken' || err.error === 'ExpiredToken') { 229 + return 'Confirmation code has expired or is invalid'; 230 + } 231 + } 232 + return `${err}`; 233 + }; 234 + 235 + return ( 236 + <Accordion title="Identity (PLC)"> 237 + <div class="mb-4 rounded border border-yellow-300 bg-yellow-50 p-3"> 238 + <p class="text-sm font-medium text-yellow-800"> 239 + This updates your DID document to point to the new PDS. This is the critical step that makes the 240 + migration official. 241 + </p> 242 + </div> 243 + 244 + <Subsection title="1. Preview new credentials"> 245 + <p class="text-sm text-gray-600">View what your DID document will look like after the migration.</p> 246 + 247 + <Show 248 + when={destination()?.manager} 249 + fallback={<p class="text-sm text-gray-500">Sign in to destination account first.</p>} 250 + > 251 + {(manager) => ( 252 + <> 253 + <div class="flex items-center gap-3"> 254 + <Button 255 + variant="outline" 256 + onClick={() => loadCredentialsMutation.mutate({ manager: manager() })} 257 + disabled={loadCredentialsMutation.isPending} 258 + > 259 + {loadCredentialsMutation.isPending ? 'Loading...' : 'Load credentials'} 260 + </Button> 261 + 262 + <Show when={loadCredentialsMutation.isSuccess}> 263 + <StatusBadge variant="success">Loaded</StatusBadge> 264 + </Show> 265 + </div> 266 + 267 + <Show when={loadCredentialsMutation.isError}> 268 + <p class="text-sm text-red-600">{`${loadCredentialsMutation.error}`}</p> 269 + </Show> 270 + 271 + <Show when={loadCredentialsMutation.data}> 272 + {(creds) => ( 273 + <> 274 + <div class="mt-2 text-sm"> 275 + <p class="text-gray-500"> 276 + Destination PDS rotation keys ({creds().rotationKeys?.length ?? 0}/5): 277 + </p> 278 + <div class="mt-1 flex flex-col gap-1"> 279 + <For each={creds().rotationKeys ?? []}> 280 + {(key) => <code class="block truncate text-xs text-gray-700">{key}</code>} 281 + </For> 282 + </div> 283 + </div> 284 + 285 + <Show when={source()?.manager && source()}> 286 + {(src) => ( 287 + <div class="mt-3 rounded border border-blue-200 bg-blue-50 p-3"> 288 + <div class="flex items-center justify-between"> 289 + <p class="text-sm font-medium text-blue-800">Analyze existing rotation keys</p> 290 + <Button 291 + variant="outline" 292 + onClick={() => 293 + analyzeRotationKeysMutation.mutate({ 294 + did: src().did as Did<'plc'>, 295 + sourceManager: src().manager!, 296 + }) 297 + } 298 + disabled={analyzeRotationKeysMutation.isPending} 299 + > 300 + {analyzeRotationKeysMutation.isPending ? 'Analyzing...' : 'Analyze'} 301 + </Button> 302 + </div> 303 + <p class="mt-1 text-xs text-blue-600"> 304 + Check if you have any user-controlled rotation keys that should be preserved 305 + during migration. 306 + </p> 307 + 308 + <Show when={analyzeRotationKeysMutation.error}> 309 + <p class="mt-2 text-sm text-red-600">{`${analyzeRotationKeysMutation.error}`}</p> 310 + </Show> 311 + 312 + <Show when={analyzeRotationKeysMutation.data}> 313 + {(analysis) => ( 314 + <div class="mt-2 text-sm"> 315 + <Show 316 + when={analysis().userControlledKeys.length > 0} 317 + fallback={ 318 + <p class="text-blue-700"> 319 + No user-controlled rotation keys found. Your current keys are all 320 + managed by your source PDS. 321 + </p> 322 + } 323 + > 324 + <p class="font-medium text-blue-800"> 325 + Found {analysis().userControlledKeys.length} user-controlled key(s) to 326 + preserve: 327 + </p> 328 + <div class="mt-1 flex flex-col gap-1"> 329 + <For each={analysis().userControlledKeys}> 330 + {(key) => ( 331 + <code class="block truncate text-xs text-blue-700">{key}</code> 332 + )} 333 + </For> 334 + </div> 335 + <p class="mt-2 text-xs text-blue-600"> 336 + These keys have been added to the custom keys section below. 337 + </p> 338 + </Show> 339 + </div> 340 + )} 341 + </Show> 342 + </div> 343 + )} 344 + </Show> 345 + 346 + <details class="mt-2"> 347 + <summary class="cursor-pointer text-sm text-gray-600">View full credentials</summary> 348 + <pre class="mt-2 max-h-48 overflow-auto rounded border border-gray-200 bg-gray-50 p-2 font-mono text-xs"> 349 + {JSON.stringify(creds(), null, 2)} 350 + </pre> 351 + </details> 352 + </> 353 + )} 354 + </Show> 355 + </> 356 + )} 357 + </Show> 358 + </Subsection> 359 + 360 + <Subsection title="2. Rotation keys (optional)"> 361 + <p class="text-sm text-gray-600"> 362 + Add a rotation key to recover your account if your new PDS goes rogue. This will be prepended to the 363 + PDS rotation keys shown above. 364 + </p> 365 + 366 + <ToggleInput 367 + label="Generate a new rotation key" 368 + checked={useGeneratedKey()} 369 + onChange={(checked) => { 370 + setUseGeneratedKey(checked); 371 + // Auto-generate if checked and no key exists yet 372 + if (checked && !generateKeyMutation.data && !generateKeyMutation.isPending) { 373 + generateKeyMutation.mutate(); 374 + } 375 + }} 376 + /> 377 + 378 + <Show when={useGeneratedKey() && generateKeyMutation.isPending}> 379 + <p class="mt-2 text-sm text-gray-500">Generating key...</p> 380 + </Show> 381 + 382 + <Show when={useGeneratedKey() && generateKeyMutation.isError}> 383 + <p class="mt-2 text-sm text-red-600">{`${generateKeyMutation.error}`}</p> 384 + </Show> 385 + 386 + <Show when={useGeneratedKey() && generateKeyMutation.data}> 387 + {(keypair) => ( 388 + <div class="rounded border border-green-300 bg-green-50 p-3"> 389 + <p class="mb-2 text-sm font-semibold text-green-800">Save your rotation key private key!</p> 390 + <p class="mb-3 text-xs text-green-700"> 391 + Store this securely. You'll need it to recover your account if your PDS becomes unavailable or 392 + malicious. 393 + </p> 394 + 395 + <div class="flex flex-col gap-2 text-sm"> 396 + <div> 397 + <p class="font-medium text-gray-600">Public key (did:key)</p> 398 + <p class="break-all font-mono text-xs">{keypair().publicDidKey}</p> 399 + </div> 400 + <div> 401 + <p class="font-medium text-gray-600">Private key (hex)</p> 402 + <p class="break-all font-mono text-xs">{keypair().privateHex}</p> 403 + </div> 404 + <div> 405 + <p class="font-medium text-gray-600">Private key (multikey)</p> 406 + <p class="break-all font-mono text-xs">{keypair().privateMultikey}</p> 407 + </div> 408 + </div> 409 + </div> 410 + )} 411 + </Show> 412 + 413 + <div class="rounded border border-gray-200 bg-gray-50 p-3"> 414 + <p class="mb-2 text-sm font-medium text-gray-700">Custom rotation keys</p> 415 + <p class="mb-3 text-xs text-gray-500"> 416 + Add existing rotation keys (did:key format) you already control. 417 + </p> 418 + 419 + <Index each={customKeys()}> 420 + {(key, index) => ( 421 + <div class="mb-2 flex items-center gap-2"> 422 + <TextInput 423 + label="" 424 + placeholder="did:key:z..." 425 + monospace 426 + autocomplete="off" 427 + value={key()} 428 + onChange={(value) => updateCustomKey(index, value)} 429 + /> 430 + <button 431 + type="button" 432 + class="shrink-0 rounded px-2 py-1 text-sm text-red-600 hover:bg-red-50" 433 + onClick={() => removeCustomKey(index)} 434 + > 435 + Remove 436 + </button> 437 + </div> 438 + )} 439 + </Index> 440 + 441 + <Button variant="outline" onClick={addCustomKey} disabled={!canAddCustomKey()}> 442 + Add rotation key 443 + </Button> 444 + 445 + <Show when={isOverLimit()}> 446 + <p class="mt-2 text-sm text-red-600"> 447 + Too many rotation keys. PLC documents can only have up to 5 rotation keys total. 448 + </p> 449 + </Show> 450 + 451 + <p class="mt-2 text-xs text-gray-500"> 452 + Total keys: {totalKeyCount()}/5 (PDS: {pdsKeyCount()} 453 + {useGeneratedKey() && generateKeyMutation.data ? ', generated: 1' : ''} 454 + {customKeys().filter((k) => k.trim()).length > 0 455 + ? `, custom: ${customKeys().filter((k) => k.trim()).length}` 456 + : ''} 457 + ) 458 + </p> 459 + </div> 460 + </Subsection> 461 + 462 + <Subsection title="3. Request operation signature"> 463 + <p class="text-sm text-gray-600">Request a confirmation token via email from your source PDS.</p> 464 + 465 + <Show 466 + when={source()?.manager} 467 + fallback={<p class="text-sm text-gray-500">Sign in to source account first.</p>} 468 + > 469 + {(manager) => ( 470 + <> 471 + <div class="flex items-center gap-3"> 472 + <Button 473 + onClick={() => requestTokenMutation.mutate({ manager: manager() })} 474 + disabled={requestTokenMutation.isPending} 475 + > 476 + {requestTokenMutation.isPending ? 'Requesting...' : 'Request token'} 477 + </Button> 478 + 479 + <Show when={requestTokenMutation.isSuccess}> 480 + <StatusBadge variant="success">Email sent</StatusBadge> 481 + </Show> 482 + </div> 483 + 484 + <Show when={requestTokenMutation.isError}> 485 + <p class="text-sm text-red-600">{`${requestTokenMutation.error}`}</p> 486 + </Show> 487 + 488 + <Show when={requestTokenMutation.isSuccess}> 489 + <p class="text-sm text-gray-600">Check your email inbox for the confirmation code.</p> 490 + </Show> 491 + </> 492 + )} 493 + </Show> 494 + </Subsection> 495 + 496 + <Subsection title="4. Sign and submit"> 497 + <p class="text-sm text-gray-600">Enter the confirmation code and submit the PLC operation.</p> 498 + 499 + <Show when={!source()?.manager || !destination()?.manager}> 500 + <p class="text-sm text-gray-500">Sign in to both source and destination accounts first.</p> 501 + </Show> 502 + 503 + <Show when={!loadCredentialsMutation.data}> 504 + <p class="text-sm text-gray-500">Load credentials first.</p> 505 + </Show> 506 + 507 + <Show when={useGeneratedKey() && !generateKeyMutation.data}> 508 + <p class="text-sm text-gray-500">Generate your rotation key first.</p> 509 + </Show> 510 + 511 + <Show when={source()?.manager && destination()?.manager && loadCredentialsMutation.data}> 512 + <TextInput 513 + label="Confirmation code from email" 514 + type="text" 515 + autocomplete="one-time-code" 516 + pattern={TOTP_RE.source} 517 + placeholder="AAAAA-BBBBB" 518 + value={plcToken()} 519 + onChange={setPlcToken} 520 + monospace 521 + /> 522 + 523 + <div class="flex items-center gap-3"> 524 + <Button 525 + onClick={handleSignAndSubmit} 526 + disabled={signAndSubmitMutation.isPending || !canSignAndSubmit()} 527 + > 528 + {signAndSubmitMutation.isPending ? 'Submitting...' : 'Sign and submit'} 529 + </Button> 530 + 531 + <Show when={signAndSubmitMutation.isSuccess}> 532 + <StatusBadge variant="success">Identity updated successfully</StatusBadge> 533 + </Show> 534 + </div> 535 + 536 + <Show when={signAndSubmitMutation.isError}> 537 + <p class="text-sm text-red-600">{getSubmitErrorMessage()}</p> 538 + </Show> 539 + </Show> 540 + </Subsection> 541 + </Accordion> 542 + ); 543 + }; 544 + 545 + export default IdentitySection;
+180
src/views/account/account-migrate/sections/preferences.tsx
··· 1 + import { showSaveFilePicker } from 'native-file-system-adapter'; 2 + import { createSignal, Show } from 'solid-js'; 3 + 4 + import { Client, type CredentialManager, ok } from '@atcute/client'; 5 + 6 + import { createMutation } from '~/lib/utils/mutation'; 7 + 8 + import { Accordion, StatusBadge, Subsection } from '~/components/accordion'; 9 + import Button from '~/components/inputs/button'; 10 + import MultilineInput from '~/components/inputs/multiline-input'; 11 + 12 + import { useMigration } from '../context'; 13 + 14 + const PreferencesSection = () => { 15 + const { source, destination } = useMigration(); 16 + 17 + const [prefsInput, setPrefsInput] = createSignal(''); 18 + 19 + const exportMutation = createMutation({ 20 + async mutationFn({ sourceManager }: { sourceManager: CredentialManager }) { 21 + const sourceClient = new Client({ handler: sourceManager }); 22 + const prefs = await ok(sourceClient.get('app.bsky.actor.getPreferences', { params: {} })); 23 + return JSON.stringify(prefs, null, 2); 24 + }, 25 + onSuccess(json) { 26 + setPrefsInput(json); 27 + }, 28 + onError(err) { 29 + console.error(err); 30 + }, 31 + }); 32 + 33 + const downloadPrefs = async () => { 34 + const prefs = exportMutation.data; 35 + if (!prefs) return; 36 + 37 + try { 38 + const fd = await showSaveFilePicker({ 39 + suggestedName: `preferences-${source()?.did}-${new Date().toISOString()}.json`, 40 + // @ts-expect-error: ponyfill doesn't have the full typings 41 + id: 'prefs-export', 42 + startIn: 'downloads', 43 + types: [ 44 + { 45 + description: 'JSON file', 46 + accept: { 'application/json': ['.json'] }, 47 + }, 48 + ], 49 + }).catch((err) => { 50 + if (err instanceof DOMException && err.name === 'AbortError') { 51 + return undefined; 52 + } 53 + throw err; 54 + }); 55 + 56 + if (!fd) return; 57 + 58 + const writable = await fd.createWritable(); 59 + await writable.write(prefs); 60 + await writable.close(); 61 + } catch (err) { 62 + console.error(err); 63 + } 64 + }; 65 + 66 + const importMutation = createMutation({ 67 + async mutationFn({ destManager, input }: { destManager: CredentialManager; input: string }) { 68 + const prefs = JSON.parse(input); 69 + 70 + // Validate that it has a preferences array 71 + if (!prefs.preferences || !Array.isArray(prefs.preferences)) { 72 + throw new Error('Invalid preferences format: missing preferences array'); 73 + } 74 + 75 + const destClient = new Client({ handler: destManager }); 76 + await destClient.post('app.bsky.actor.putPreferences', { 77 + as: null, 78 + input: prefs, 79 + }); 80 + }, 81 + onError(err) { 82 + console.error(err); 83 + }, 84 + }); 85 + 86 + const getImportErrorMessage = () => { 87 + const err = importMutation.error; 88 + if (err instanceof SyntaxError) { 89 + return 'Invalid JSON format'; 90 + } 91 + return `${err}`; 92 + }; 93 + 94 + return ( 95 + <Accordion title="Preferences"> 96 + <Subsection title="Export from source"> 97 + <p class="text-sm text-gray-600"> 98 + Export your Bluesky preferences (muted words, content filters, saved feeds, etc). 99 + </p> 100 + 101 + <Show 102 + when={source()?.manager} 103 + fallback={<p class="text-sm text-gray-500">Sign in to source account first.</p>} 104 + > 105 + {(sourceManager) => ( 106 + <> 107 + <div class="flex items-center gap-3"> 108 + <Button 109 + onClick={() => exportMutation.mutate({ sourceManager: sourceManager() })} 110 + disabled={exportMutation.isPending} 111 + > 112 + {exportMutation.isPending ? 'Exporting...' : 'Export preferences'} 113 + </Button> 114 + 115 + <Show when={exportMutation.data}> 116 + <Button variant="secondary" onClick={downloadPrefs}> 117 + Download as file 118 + </Button> 119 + </Show> 120 + </div> 121 + 122 + <Show when={exportMutation.error}> 123 + {(err) => <p class="text-sm text-red-600">{`${err()}`}</p>} 124 + </Show> 125 + 126 + <Show when={exportMutation.data}> 127 + {(prefs) => ( 128 + <details class="mt-2"> 129 + <summary class="cursor-pointer text-sm text-gray-600"> 130 + View exported preferences 131 + </summary> 132 + <pre class="mt-2 max-h-48 overflow-auto rounded border border-gray-200 bg-gray-50 p-2 font-mono text-xs"> 133 + {prefs()} 134 + </pre> 135 + </details> 136 + )} 137 + </Show> 138 + </> 139 + )} 140 + </Show> 141 + </Subsection> 142 + 143 + <Subsection title="Import to destination"> 144 + <p class="text-sm text-gray-600">Paste preferences JSON or use the exported data above.</p> 145 + 146 + <Show 147 + when={destination()?.manager} 148 + fallback={<p class="text-sm text-gray-500">Sign in to destination account first.</p>} 149 + > 150 + {(destManager) => ( 151 + <> 152 + <MultilineInput label="Preferences JSON" value={prefsInput()} onChange={setPrefsInput} /> 153 + 154 + <div class="flex items-center gap-3"> 155 + <Button 156 + onClick={() => 157 + importMutation.mutate({ destManager: destManager(), input: prefsInput().trim() }) 158 + } 159 + disabled={importMutation.isPending || !prefsInput().trim()} 160 + > 161 + {importMutation.isPending ? 'Importing...' : 'Import preferences'} 162 + </Button> 163 + 164 + <Show when={importMutation.isSuccess}> 165 + <StatusBadge variant="success">Preferences imported successfully</StatusBadge> 166 + </Show> 167 + </div> 168 + 169 + <Show when={importMutation.error}> 170 + <p class="text-sm text-red-600">{getImportErrorMessage()}</p> 171 + </Show> 172 + </> 173 + )} 174 + </Show> 175 + </Subsection> 176 + </Accordion> 177 + ); 178 + }; 179 + 180 + export default PreferencesSection;
+291
src/views/account/account-migrate/sections/repository.tsx
··· 1 + import { showOpenFilePicker, showSaveFilePicker } from 'native-file-system-adapter'; 2 + import { createSignal, Show } from 'solid-js'; 3 + 4 + import { Client, type CredentialManager, ok, simpleFetchHandler } from '@atcute/client'; 5 + import type { Did } from '@atcute/lexicons/syntax'; 6 + 7 + import { formatBytes } from '~/lib/utils/intl/bytes'; 8 + import { createMutation } from '~/lib/utils/mutation'; 9 + import { iterateStream } from '~/lib/utils/stream'; 10 + 11 + import { Accordion, StatusBadge, Subsection } from '~/components/accordion'; 12 + import Button from '~/components/inputs/button'; 13 + 14 + import { useMigration } from '../context'; 15 + 16 + const RepositorySection = () => { 17 + const { source, destination } = useMigration(); 18 + 19 + // Export state 20 + const [exportStatus, setExportStatus] = createSignal<string>(); 21 + 22 + // Import state 23 + const [importStatus, setImportStatus] = createSignal<string>(); 24 + const [importedRecords, setImportedRecords] = createSignal<number>(); 25 + 26 + const exportMutation = createMutation({ 27 + async mutationFn({ pdsUrl, did }: { pdsUrl: string; did: Did }) { 28 + setExportStatus('Waiting for file picker...'); 29 + 30 + const fd = await showSaveFilePicker({ 31 + suggestedName: `repo-${did}-${new Date().toISOString()}.car`, 32 + // @ts-expect-error: ponyfill doesn't have the full typings 33 + id: 'repo-export', 34 + startIn: 'downloads', 35 + types: [ 36 + { 37 + description: 'CAR archive file', 38 + accept: { 'application/vnd.ipld.car': ['.car'] }, 39 + }, 40 + ], 41 + }).catch((err) => { 42 + if (err instanceof DOMException && err.name === 'AbortError') { 43 + return undefined; 44 + } 45 + throw err; 46 + }); 47 + 48 + if (!fd) { 49 + setExportStatus(); 50 + return null; 51 + } 52 + 53 + const writable = await fd.createWritable(); 54 + 55 + setExportStatus('Downloading repository...'); 56 + 57 + const sourceClient = new Client({ handler: simpleFetchHandler({ service: pdsUrl }) }); 58 + const response = await sourceClient.get('com.atproto.sync.getRepo', { 59 + as: 'stream', 60 + params: { did }, 61 + }); 62 + 63 + if (!response.ok) { 64 + throw new Error(`Failed to download repository: ${response.status}`); 65 + } 66 + 67 + let size = 0; 68 + for await (const chunk of iterateStream(response.data)) { 69 + size += chunk.length; 70 + await writable.write(chunk); 71 + setExportStatus(`Downloading repository... (${formatBytes(size)})`); 72 + } 73 + 74 + await writable.close(); 75 + setExportStatus(`Exported ${formatBytes(size)}`); 76 + return size; 77 + }, 78 + onMutate() { 79 + setExportStatus(); 80 + }, 81 + onError(err) { 82 + console.error(err); 83 + setExportStatus(); 84 + }, 85 + }); 86 + 87 + const importFromFileMutation = createMutation({ 88 + async mutationFn({ manager }: { manager: CredentialManager }) { 89 + setImportStatus('Waiting for file picker...'); 90 + 91 + const [fd] = await showOpenFilePicker({ 92 + // @ts-expect-error: ponyfill doesn't have the full typings 93 + id: 'repo-import', 94 + types: [ 95 + { 96 + description: 'CAR archive file', 97 + accept: { 'application/vnd.ipld.car': ['.car'] }, 98 + }, 99 + ], 100 + }).catch((err) => { 101 + if (err instanceof DOMException && err.name === 'AbortError') { 102 + return [undefined]; 103 + } 104 + throw err; 105 + }); 106 + 107 + if (!fd) { 108 + setImportStatus(); 109 + return null; 110 + } 111 + 112 + const file = await fd.getFile(); 113 + 114 + setImportStatus(`Uploading repository (${formatBytes(file.size)})...`); 115 + 116 + const destClient = new Client({ handler: manager }); 117 + const importResp = await destClient.post('com.atproto.repo.importRepo', { 118 + as: null, 119 + input: file, 120 + headers: { 121 + 'content-type': 'application/vnd.ipld.car', 122 + }, 123 + }); 124 + 125 + if (!importResp.ok) { 126 + throw new Error(`Failed to import repository: ${importResp.status}`); 127 + } 128 + 129 + // Check account status to get record count 130 + const status = await ok(destClient.get('com.atproto.server.checkAccountStatus', {})); 131 + setImportedRecords(status.indexedRecords); 132 + 133 + setImportStatus(`Imported successfully`); 134 + return status.indexedRecords; 135 + }, 136 + onMutate() { 137 + setImportStatus(); 138 + setImportedRecords(); 139 + }, 140 + onError(err) { 141 + console.error(err); 142 + setImportStatus(); 143 + }, 144 + }); 145 + 146 + const importFromSourceMutation = createMutation({ 147 + async mutationFn({ 148 + sourcePdsUrl, 149 + sourceDid, 150 + destManager, 151 + }: { 152 + sourcePdsUrl: string; 153 + sourceDid: Did; 154 + destManager: CredentialManager; 155 + }) { 156 + setImportStatus('Downloading from source PDS...'); 157 + 158 + const sourceClient = new Client({ handler: simpleFetchHandler({ service: sourcePdsUrl }) }); 159 + const response = await sourceClient.get('com.atproto.sync.getRepo', { 160 + as: 'bytes', 161 + params: { did: sourceDid }, 162 + }); 163 + 164 + if (!response.ok) { 165 + throw new Error(`Failed to download repository: ${response.status}`); 166 + } 167 + 168 + setImportStatus(`Uploading to destination (${formatBytes(response.data.length)})...`); 169 + 170 + const destClient = new Client({ handler: destManager }); 171 + const importResp = await destClient.post('com.atproto.repo.importRepo', { 172 + as: null, 173 + input: response.data, 174 + headers: { 175 + 'content-type': 'application/vnd.ipld.car', 176 + }, 177 + }); 178 + 179 + if (!importResp.ok) { 180 + throw new Error(`Failed to import repository: ${importResp.status}`); 181 + } 182 + 183 + // Check account status to get record count 184 + const status = await ok(destClient.get('com.atproto.server.checkAccountStatus', {})); 185 + setImportedRecords(status.indexedRecords); 186 + 187 + setImportStatus(`Imported successfully`); 188 + return status.indexedRecords; 189 + }, 190 + onMutate() { 191 + setImportStatus(); 192 + setImportedRecords(); 193 + }, 194 + onError(err) { 195 + console.error(err); 196 + setImportStatus(); 197 + }, 198 + }); 199 + 200 + const isExporting = () => exportMutation.isPending; 201 + const isImporting = () => importFromFileMutation.isPending || importFromSourceMutation.isPending; 202 + 203 + return ( 204 + <Accordion title="Repository"> 205 + <Subsection title="Export from source"> 206 + <p class="text-sm text-gray-600"> 207 + Download the repository as a CAR file for backup or manual import. 208 + </p> 209 + 210 + <Show when={source()} fallback={<p class="text-sm text-gray-500">Resolve source account first.</p>}> 211 + {(src) => ( 212 + <> 213 + <div class="flex items-center gap-3"> 214 + <Button 215 + onClick={() => exportMutation.mutate({ pdsUrl: src().pdsUrl, did: src().did })} 216 + disabled={isExporting()} 217 + > 218 + {isExporting() ? 'Exporting...' : 'Export to file'} 219 + </Button> 220 + <Show when={exportStatus()}> 221 + <span class="text-sm text-gray-600">{exportStatus()}</span> 222 + </Show> 223 + </div> 224 + 225 + <Show when={exportMutation.isError}> 226 + <p class="text-sm text-red-600">{`${exportMutation.error}`}</p> 227 + </Show> 228 + </> 229 + )} 230 + </Show> 231 + </Subsection> 232 + 233 + <Subsection title="Import to destination"> 234 + <p class="text-sm text-gray-600">Upload a repository CAR file or transfer directly from source.</p> 235 + 236 + <Show 237 + when={destination()?.manager} 238 + fallback={<p class="text-sm text-gray-500">Sign in to destination account first.</p>} 239 + > 240 + {(manager) => ( 241 + <> 242 + <div class="flex flex-wrap items-center gap-3"> 243 + <Button 244 + onClick={() => importFromFileMutation.mutate({ manager: manager() })} 245 + disabled={isImporting()} 246 + > 247 + {isImporting() ? 'Importing...' : 'Import from file'} 248 + </Button> 249 + 250 + <Show when={source()}> 251 + {(src) => ( 252 + <Button 253 + variant="secondary" 254 + onClick={() => 255 + importFromSourceMutation.mutate({ 256 + sourcePdsUrl: src().pdsUrl, 257 + sourceDid: src().did, 258 + destManager: manager(), 259 + }) 260 + } 261 + disabled={isImporting()} 262 + > 263 + Transfer from source 264 + </Button> 265 + )} 266 + </Show> 267 + </div> 268 + 269 + <Show when={importStatus()}> 270 + <div class="flex items-center gap-2"> 271 + <span class="text-sm text-gray-600">{importStatus()}</span> 272 + <Show when={importedRecords() !== undefined}> 273 + <StatusBadge variant="success">{importedRecords()} records</StatusBadge> 274 + </Show> 275 + </div> 276 + </Show> 277 + 278 + <Show when={importFromFileMutation.isError || importFromSourceMutation.isError}> 279 + <p class="text-sm text-red-600"> 280 + {`${importFromFileMutation.error || importFromSourceMutation.error}`} 281 + </p> 282 + </Show> 283 + </> 284 + )} 285 + </Show> 286 + </Subsection> 287 + </Accordion> 288 + ); 289 + }; 290 + 291 + export default RepositorySection;
+265
src/views/account/account-migrate/sections/source-account.tsx
··· 1 + import { createSignal, Show } from 'solid-js'; 2 + 3 + import { type AtpAccessJwt, ClientResponseError, CredentialManager } from '@atcute/client'; 4 + import { getPdsEndpoint, isAtprotoDid } from '@atcute/identity'; 5 + import { isHandle, type AtprotoDid } from '@atcute/lexicons/syntax'; 6 + 7 + import { getDidDocument } from '~/api/queries/did-doc'; 8 + import { resolveHandleViaAppView } from '~/api/queries/handle'; 9 + import { formatTotpCode, TOTP_RE } from '~/api/utils/auth'; 10 + import { decodeJwt } from '~/api/utils/jwt'; 11 + 12 + import { createMutation } from '~/lib/utils/mutation'; 13 + 14 + import { Accordion, StatusBadge, Subsection } from '~/components/accordion'; 15 + import Button from '~/components/inputs/button'; 16 + import TextInput from '~/components/inputs/text-input'; 17 + 18 + import { useMigration } from '../context'; 19 + 20 + interface SourceAccountSectionProps { 21 + onStarted?: () => void; 22 + } 23 + 24 + class InsufficientLoginError extends Error {} 25 + 26 + const SourceAccountSection = (props: SourceAccountSectionProps) => { 27 + const { source, setSource } = useMigration(); 28 + 29 + // Resolve state 30 + const [identifier, setIdentifier] = createSignal(''); 31 + const [resolveError, setResolveError] = createSignal<string>(); 32 + 33 + // Auth state 34 + const [password, setPassword] = createSignal(''); 35 + const [otp, setOtp] = createSignal(''); 36 + const [isTotpRequired, setIsTotpRequired] = createSignal(false); 37 + const [authError, setAuthError] = createSignal<string>(); 38 + 39 + const resolveMutation = createMutation({ 40 + async mutationFn({ identifier }: { identifier: string }) { 41 + let did: AtprotoDid; 42 + if (isAtprotoDid(identifier)) { 43 + did = identifier; 44 + } else if (isHandle(identifier)) { 45 + did = await resolveHandleViaAppView({ handle: identifier }); 46 + } else { 47 + throw new Error(`${identifier} is not a valid DID or handle`); 48 + } 49 + 50 + const didDoc = await getDidDocument({ did }); 51 + const pdsUrl = getPdsEndpoint(didDoc); 52 + 53 + if (!pdsUrl) { 54 + throw new Error(`No PDS endpoint found in DID document`); 55 + } 56 + 57 + return { did, didDoc, pdsUrl }; 58 + }, 59 + onMutate() { 60 + setResolveError(); 61 + }, 62 + onSuccess({ did, didDoc, pdsUrl }) { 63 + setSource({ did, didDoc, pdsUrl, manager: null }); 64 + props.onStarted?.(); 65 + }, 66 + onError(err) { 67 + if (err instanceof ClientResponseError) { 68 + if (err.error === 'InvalidRequest' && err.description?.includes('resolve handle')) { 69 + setResolveError(`Can't resolve handle, is it typed correctly?`); 70 + return; 71 + } 72 + } 73 + console.error(err); 74 + setResolveError(`${err}`); 75 + }, 76 + }); 77 + 78 + const authMutation = createMutation({ 79 + async mutationFn({ pdsUrl, did, password, otp }: { pdsUrl: string; did: string; password: string; otp: string }) { 80 + const manager = new CredentialManager({ service: pdsUrl }); 81 + const session = await manager.login({ 82 + identifier: did, 83 + password: password, 84 + code: formatTotpCode(otp), 85 + }); 86 + 87 + const decoded = decodeJwt(session.accessJwt) as AtpAccessJwt; 88 + if (decoded.scope !== 'com.atproto.access') { 89 + throw new InsufficientLoginError(`You need to sign in with a main password, not an app password`); 90 + } 91 + 92 + return manager; 93 + }, 94 + onMutate() { 95 + setAuthError(); 96 + }, 97 + onSuccess(manager) { 98 + setSource({ ...source()!, manager }); 99 + setPassword(''); 100 + setOtp(''); 101 + setIsTotpRequired(false); 102 + }, 103 + onError(err) { 104 + if (err instanceof ClientResponseError) { 105 + if (err.error === 'AuthFactorTokenRequired') { 106 + setOtp(''); 107 + setIsTotpRequired(true); 108 + return; 109 + } 110 + if (err.error === 'AuthenticationRequired') { 111 + setAuthError(`Invalid identifier or password`); 112 + return; 113 + } 114 + if (err.error === 'AccountTakedown') { 115 + setAuthError(`Account has been taken down`); 116 + return; 117 + } 118 + if (err.description?.includes('Token is invalid')) { 119 + setAuthError(`Invalid one-time confirmation code`); 120 + setIsTotpRequired(true); 121 + return; 122 + } 123 + } 124 + if (err instanceof InsufficientLoginError) { 125 + setAuthError(err.message); 126 + return; 127 + } 128 + console.error(err); 129 + setAuthError(`${err}`); 130 + }, 131 + }); 132 + 133 + const isResolved = () => source() !== null; 134 + const isAuthenticated = () => source()?.manager != null; 135 + 136 + return ( 137 + <Accordion title="Source Account" defaultOpen> 138 + <Subsection title="Resolve identity"> 139 + <Show when={!isResolved()}> 140 + <form 141 + onSubmit={(ev) => { 142 + ev.preventDefault(); 143 + resolveMutation.mutate({ identifier: identifier() }); 144 + }} 145 + class="flex flex-col gap-3" 146 + > 147 + <TextInput 148 + label="Handle or DID" 149 + placeholder="alice.bsky.social" 150 + value={identifier()} 151 + required 152 + autofocus 153 + onChange={setIdentifier} 154 + /> 155 + 156 + <Show when={resolveError()}> 157 + <p class="text-sm text-red-600">{resolveError()}</p> 158 + </Show> 159 + 160 + <div> 161 + <Button type="submit" disabled={resolveMutation.isPending}> 162 + {resolveMutation.isPending ? 'Resolving...' : 'Resolve'} 163 + </Button> 164 + </div> 165 + </form> 166 + </Show> 167 + 168 + <Show when={isResolved()}> 169 + <div class="flex flex-col gap-2 text-sm"> 170 + <p> 171 + <span class="text-gray-500">DID:</span>{' '} 172 + <span class="font-mono">{source()!.did}</span> 173 + </p> 174 + <p> 175 + <span class="text-gray-500">PDS:</span>{' '} 176 + <span class="font-mono">{source()!.pdsUrl}</span> 177 + </p> 178 + <div class="mt-1"> 179 + <button 180 + type="button" 181 + onClick={() => setSource(null)} 182 + class="text-sm text-purple-800 hover:underline" 183 + > 184 + Change account 185 + </button> 186 + </div> 187 + </div> 188 + </Show> 189 + </Subsection> 190 + 191 + <Show when={isResolved()}> 192 + <Subsection title="Authenticate"> 193 + <p class="text-sm text-gray-600"> 194 + Authentication is required for some operations like exporting preferences or signing PLC operations. 195 + </p> 196 + 197 + <Show when={!isAuthenticated()}> 198 + <form 199 + onSubmit={(ev) => { 200 + ev.preventDefault(); 201 + const src = source()!; 202 + authMutation.mutate({ 203 + pdsUrl: src.pdsUrl, 204 + did: src.did, 205 + password: password(), 206 + otp: otp(), 207 + }); 208 + }} 209 + class="flex flex-col gap-3" 210 + > 211 + <TextInput 212 + label="Main password" 213 + blurb="Your credentials stay entirely within your browser." 214 + type="password" 215 + value={password()} 216 + required 217 + onChange={setPassword} 218 + /> 219 + 220 + <Show when={isTotpRequired()}> 221 + <TextInput 222 + label="One-time confirmation code" 223 + blurb="A code has been sent to your email address." 224 + type="text" 225 + autocomplete="one-time-code" 226 + pattern={TOTP_RE.source} 227 + placeholder="AAAAA-BBBBB" 228 + value={otp()} 229 + required 230 + onChange={setOtp} 231 + monospace 232 + /> 233 + </Show> 234 + 235 + <Show when={authError()}> 236 + <p class="text-sm text-red-600">{authError()}</p> 237 + </Show> 238 + 239 + <div> 240 + <Button type="submit" disabled={authMutation.isPending}> 241 + {authMutation.isPending ? 'Signing in...' : 'Sign in'} 242 + </Button> 243 + </div> 244 + </form> 245 + </Show> 246 + 247 + <Show when={isAuthenticated()}> 248 + <div class="flex items-center gap-2"> 249 + <StatusBadge variant="success">Signed in</StatusBadge> 250 + <button 251 + type="button" 252 + onClick={() => setSource({ ...source()!, manager: null })} 253 + class="text-sm text-purple-800 hover:underline" 254 + > 255 + Sign out 256 + </button> 257 + </div> 258 + </Show> 259 + </Subsection> 260 + </Show> 261 + </Accordion> 262 + ); 263 + }; 264 + 265 + export default SourceAccountSection;
+4 -8
src/views/blob/blob-export.tsx
··· 2 2 import { createSignal } from 'solid-js'; 3 3 4 4 import { Client, ClientResponseError, ok, simpleFetchHandler } from '@atcute/client'; 5 - import { type AtprotoDid, getPdsEndpoint, isAtprotoDid, isHandle } from '@atcute/identity'; 5 + import { getPdsEndpoint, isAtprotoDid } from '@atcute/identity'; 6 + import { isHandle, type AtprotoDid } from '@atcute/lexicons/syntax'; 6 7 import { writeTarEntry } from '@mary/tar'; 7 8 8 9 import { getDidDocument } from '~/api/queries/did-doc'; 9 10 import { resolveHandleViaAppView, resolveHandleViaPds } from '~/api/queries/handle'; 10 11 import { isServiceUrlString } from '~/api/types/strings'; 11 - import { DID_OR_HANDLE_RE } from '~/api/utils/strings'; 12 12 13 13 import { useTitle } from '~/lib/navigation/router'; 14 14 import { makeAbortable } from '~/lib/utils/abortable'; ··· 17 17 import Button from '~/components/inputs/button'; 18 18 import TextInput from '~/components/inputs/text-input'; 19 19 import Logger, { createLogger } from '~/components/logger'; 20 + import PageHeader from '~/components/page-header'; 20 21 21 22 const BlobExportPage = () => { 22 23 const logger = createLogger(); ··· 229 230 230 231 return ( 231 232 <> 232 - <div class="p-4"> 233 - <h1 class="text-lg font-bold text-purple-800">Export blobs</h1> 234 - <p class="text-gray-600">Download all blobs from an account into a tarball</p> 235 - </div> 236 - <hr class="mx-4 border-gray-300" /> 233 + <PageHeader title="Export blobs" subtitle="Download all blobs from an account into a tarball" /> 237 234 238 235 <form 239 236 onSubmit={(ev) => { ··· 284 281 type="text" 285 282 name="ident" 286 283 autocomplete="username" 287 - pattern={/* @once */ DID_OR_HANDLE_RE.source} 288 284 placeholder="paul.bsky.social" 289 285 autofocus 290 286 />
+10 -11
src/views/bluesky/threadgate-applicator/page.tsx
··· 1 1 import { createEffect, createSignal, onCleanup } from 'solid-js'; 2 2 3 + import { AppBskyFeedDefs, AppBskyFeedThreadgate } from '@atcute/bluesky'; 3 4 import { CredentialManager } from '@atcute/client'; 4 - import { AppBskyFeedDefs, AppBskyFeedThreadgate } from '@atcute/client/lexicons'; 5 - import { DidDocument } from '@atcute/identity'; 5 + import type { DidDocument } from '@atcute/identity'; 6 6 7 - import { UnwrapArray } from '~/api/utils/types'; 7 + import type { UnwrapArray } from '~/api/utils/types'; 8 8 9 9 import { history } from '~/globals/navigation'; 10 10 11 11 import { useTitle } from '~/lib/navigation/router'; 12 12 13 + import PageHeader from '~/components/page-header'; 13 14 import { Wizard } from '~/components/wizard'; 14 15 15 16 import Step1_HandleInput from './steps/step1_handle-input'; ··· 18 19 import Step4_Confirmation from './steps/step4_confirmation'; 19 20 import Step5_Finished from './steps/step5_finished'; 20 21 21 - export interface ThreadgateState 22 - extends Pick<AppBskyFeedThreadgate.Record, 'allow' | 'hiddenReplies' | 'createdAt'> { 22 + export interface ThreadgateState extends Pick< 23 + AppBskyFeedThreadgate.Main, 24 + 'allow' | 'hiddenReplies' | 'createdAt' 25 + > { 23 26 uri: string; 24 27 } 25 28 26 - export type ThreadgateRule = UnwrapArray<AppBskyFeedThreadgate.Record['allow']>; 29 + export type ThreadgateRule = UnwrapArray<AppBskyFeedThreadgate.Main['allow']>; 27 30 28 31 export interface ThreadItem { 29 32 post: AppBskyFeedDefs.PostView; ··· 78 81 79 82 return ( 80 83 <> 81 - <div class="p-4"> 82 - <h1 class="text-lg font-bold text-purple-800">Retroactive thread gating</h1> 83 - <p class="text-gray-600">Set reply permissions on all of your past Bluesky posts</p> 84 - </div> 85 - <hr class="mx-4 border-gray-300" /> 84 + <PageHeader title="Retroactive thread gating" subtitle="Set reply permissions on all of your past Bluesky posts" /> 86 85 87 86 <Wizard<ThreadgateApplicatorConstraints> 88 87 initialStep="Step1_HandleInput"
+18 -16
src/views/bluesky/threadgate-applicator/steps/step1_handle-input.tsx
··· 1 1 import { createSignal } from 'solid-js'; 2 2 3 - import type { AppBskyFeedThreadgate } from '@atcute/client/lexicons'; 4 - import { type AtprotoDid, isAtprotoDid, isHandle } from '@atcute/identity'; 3 + import type { AppBskyFeedThreadgate } from '@atcute/bluesky'; 4 + import { ok } from '@atcute/client'; 5 + import { isAtprotoDid } from '@atcute/identity'; 6 + import { isHandle, type AtprotoDid } from '@atcute/lexicons/syntax'; 5 7 6 8 import { getDidDocument } from '~/api/queries/did-doc'; 7 9 import { resolveHandleViaAppView } from '~/api/queries/handle'; 8 - import { DID_OR_HANDLE_RE } from '~/api/utils/strings'; 9 10 10 11 import { appViewRpc } from '~/globals/rpc'; 11 12 ··· 13 14 14 15 import Button from '~/components/inputs/button'; 15 16 import TextInput from '~/components/inputs/text-input'; 16 - import { Stage, StageActions, StageErrorView, WizardStepProps } from '~/components/wizard'; 17 + import { Stage, StageActions, StageErrorView, type WizardStepProps } from '~/components/wizard'; 17 18 18 - import { ThreadgateApplicatorConstraints, ThreadgateState, ThreadItem } from '../page'; 19 + import type { ThreadgateApplicatorConstraints, ThreadgateState, ThreadItem } from '../page'; 19 20 import { sortThreadgateState } from '../utils'; 20 21 21 22 class NoThreadsError extends Error {} ··· 50 51 51 52 let cursor: string | undefined; 52 53 do { 53 - const { data } = await appViewRpc.get('app.bsky.feed.getAuthorFeed', { 54 - signal, 55 - params: { 56 - actor: did, 57 - filter: 'posts_no_replies', 58 - limit: 100, 59 - cursor, 60 - }, 61 - }); 54 + const data = await ok( 55 + appViewRpc.get('app.bsky.feed.getAuthorFeed', { 56 + signal, 57 + params: { 58 + actor: did, 59 + filter: 'posts_no_replies', 60 + limit: 100, 61 + cursor, 62 + }, 63 + }), 64 + ); 62 65 63 66 cursor = data.cursor; 64 67 ··· 83 86 let threadgate: ThreadgateState | null = null; 84 87 85 88 if (tg?.record) { 86 - const record = tg.record as AppBskyFeedThreadgate.Record; 89 + const record = tg.record as AppBskyFeedThreadgate.Main; 87 90 88 91 const allow = record?.allow; 89 92 const hiddenReplies = record?.hiddenReplies; ··· 153 156 placeholder="paul.bsky.social" 154 157 value={identifier()} 155 158 required 156 - pattern={/* @once */ DID_OR_HANDLE_RE.source} 157 159 autofocus={isActive()} 158 160 onChange={setIdentifier} 159 161 />
+19 -26
src/views/bluesky/threadgate-applicator/steps/step2_rules-input.tsx
··· 1 1 import { batch, createMemo, createSignal, For, Show } from 'solid-js'; 2 2 3 - import { AppBskyFeedThreadgate, Brand } from '@atcute/client/lexicons'; 4 - 5 - import { UnwrapArray } from '~/api/utils/types'; 3 + import { AppBskyFeedThreadgate } from '@atcute/bluesky'; 4 + import { ok } from '@atcute/client'; 5 + import type { $type } from '@atcute/lexicons'; 6 6 7 7 import { appViewRpc } from '~/globals/rpc'; 8 8 ··· 11 11 import { createQuery } from '~/lib/utils/query'; 12 12 13 13 import RadioInput from '~/components/inputs/radio-input'; 14 - import { Stage, StageActions, WizardStepProps } from '~/components/wizard'; 14 + import { Stage, StageActions, type WizardStepProps } from '~/components/wizard'; 15 15 16 16 import CircularProgressView from '~/components/circular-progress-view'; 17 17 import Button from '~/components/inputs/button'; 18 18 import ToggleInput from '~/components/inputs/toggle-input'; 19 19 20 - import { ThreadgateApplicatorConstraints } from '../page'; 20 + import type { ThreadgateApplicatorConstraints, ThreadgateRule } from '../page'; 21 21 import { sortThreadgateAllow } from '../utils'; 22 22 23 23 const enum FilterType { ··· 30 30 NO_ONE = 'no_one', 31 31 CUSTOM = 'custom', 32 32 } 33 - 34 - type ThreadRule = UnwrapArray<AppBskyFeedThreadgate.Record['allow']>; 35 33 36 34 const Step2_RulesInput = ({ 37 35 data, ··· 41 39 }: WizardStepProps<ThreadgateApplicatorConstraints, 'Step2_RulesInput'>) => { 42 40 const [filter, setFilter] = createSignal(FilterType.MISSING_ONLY); 43 41 44 - const [threadRules, _setThreadRules] = createSignal<ThreadRule[] | undefined>([ 42 + const [threadRules, _setThreadRules] = createSignal<ThreadgateRule[] | undefined>([ 45 43 { $type: 'app.bsky.feed.threadgate#followingRule' }, 46 44 { $type: 'app.bsky.feed.threadgate#mentionRule' }, 47 45 ]); ··· 64 62 () => data.profile.didDoc.id, 65 63 async (did, signal) => { 66 64 const lists = await accumulate(async (cursor) => { 67 - const { data } = await appViewRpc.get('app.bsky.graph.getLists', { 68 - signal, 69 - params: { 70 - actor: did, 71 - cursor, 72 - limit: 100, 73 - }, 74 - }); 65 + const data = await ok( 66 + appViewRpc.get('app.bsky.graph.getLists', { 67 + signal, 68 + params: { 69 + actor: did, 70 + cursor, 71 + limit: 100, 72 + }, 73 + }), 74 + ); 75 75 76 76 return { 77 77 cursor: data.cursor, ··· 121 121 ); 122 122 }); 123 123 124 - const hasThreadRule = (predicate: ThreadRule): boolean => { 124 + const hasThreadRule = (predicate: ThreadgateRule): boolean => { 125 125 return !!threadRules()?.find((rule) => dequal(rule, predicate)); 126 126 }; 127 127 128 - const setCustomThreadRules = (next: ThreadRule[] | undefined) => { 128 + const setCustomThreadRules = (next: ThreadgateRule[] | undefined) => { 129 129 batch(() => { 130 130 _setThreadRules(next); 131 131 setThreadRulesPreset(ThreadRulePreset.CUSTOM); ··· 226 226 } 227 227 > 228 228 {(list) => { 229 - const rule: Brand.Union<AppBskyFeedThreadgate.ListRule> = { 229 + const rule: $type.enforce<AppBskyFeedThreadgate.ListRule> = { 230 230 $type: 'app.bsky.feed.threadgate#listRule', 231 231 list: list.uri, 232 232 }; ··· 255 255 blurb={ 256 256 <> 257 257 <span>This will apply to {filteredThreads().length} threads. </span> 258 - {/* <button 259 - type="button" 260 - hidden={filteredThreads().length < 1} 261 - class="font-medium text-purple-800 hover:underline" 262 - > 263 - View 264 - </button> */} 265 258 </> 266 259 } 267 260 value={filter()}
+2 -2
src/views/bluesky/threadgate-applicator/steps/step3_authentication.tsx
··· 2 2 3 3 import { CredentialManager } from '@atcute/client'; 4 4 5 - import { WizardStepProps } from '~/components/wizard'; 5 + import type { WizardStepProps } from '~/components/wizard'; 6 6 import BlueskyLoginStep from '~/components/wizards/bluesky-login-step'; 7 7 8 - import { ThreadgateApplicatorConstraints } from '../page'; 8 + import type { ThreadgateApplicatorConstraints } from '../page'; 9 9 10 10 const Step3_Authentication = ({ 11 11 data,
+70 -52
src/views/bluesky/threadgate-applicator/steps/step4_confirmation.tsx
··· 1 1 import { createSignal, Show } from 'solid-js'; 2 2 3 - import { XRPC, XRPCError } from '@atcute/client'; 4 - import type { AppBskyFeedThreadgate, ComAtprotoRepoApplyWrites } from '@atcute/client/lexicons'; 3 + import type { ComAtprotoRepoApplyWrites } from '@atcute/atproto'; 4 + import type { AppBskyFeedThreadgate } from '@atcute/bluesky'; 5 + import { Client, ClientResponseError } from '@atcute/client'; 6 + import { parseCanonicalResourceUri } from '@atcute/lexicons'; 5 7 import { chunked } from '@mary/array-fns'; 6 - 7 - import { parseAddressedAtUri } from '~/api/utils/at-uri'; 8 8 9 9 import { dequal } from '~/lib/utils/dequal'; 10 10 import { createMutation } from '~/lib/utils/mutation'; ··· 12 12 import Button from '~/components/inputs/button'; 13 13 import ToggleInput from '~/components/inputs/toggle-input'; 14 14 import Logger, { createLogger } from '~/components/logger'; 15 - import { Stage, StageActions, StageErrorView, WizardStepProps } from '~/components/wizard'; 15 + import { Stage, StageActions, StageErrorView, type WizardStepProps } from '~/components/wizard'; 16 16 17 - import { ThreadgateApplicatorConstraints } from '../page'; 17 + import type { ThreadgateApplicatorConstraints } from '../page'; 18 18 19 19 const Step4_Confirmation = ({ 20 20 data, ··· 34 34 logger.log(`Preparing writes`); 35 35 36 36 const rules = data.rules; 37 - const writes: ComAtprotoRepoApplyWrites.Input['writes'] = []; 37 + const writes: ComAtprotoRepoApplyWrites.$input['writes'] = []; 38 38 39 39 const now = new Date().toISOString(); 40 40 for (const { post, threadgate } of data.threads) { 41 41 if (threadgate === null) { 42 42 if (rules !== undefined) { 43 - const { rkey } = parseAddressedAtUri(post.uri); 43 + const postUri = parseCanonicalResourceUri(post.uri); 44 + if (!postUri.ok) { 45 + throw new Error(`failed to parse ${post.uri}`); 46 + } 44 47 45 - const record: AppBskyFeedThreadgate.Record = { 48 + const record: AppBskyFeedThreadgate.Main = { 46 49 $type: 'app.bsky.feed.threadgate', 47 50 createdAt: now, 48 51 post: post.uri, ··· 53 56 writes.push({ 54 57 $type: 'com.atproto.repo.applyWrites#create', 55 58 collection: 'app.bsky.feed.threadgate', 56 - rkey: rkey, 59 + rkey: postUri.value.rkey, 57 60 value: record, 58 61 }); 59 62 } 60 63 } else { 61 64 if (rules === undefined && !threadgate.hiddenReplies?.length) { 62 - const { rkey } = parseAddressedAtUri(threadgate.uri); 65 + const threadgateUri = parseCanonicalResourceUri(threadgate.uri); 66 + if (!threadgateUri.ok) { 67 + throw new Error(`failed to parse ${threadgate.uri}`); 68 + } 63 69 64 70 writes.push({ 65 71 $type: 'com.atproto.repo.applyWrites#delete', 66 72 collection: 'app.bsky.feed.threadgate', 67 - rkey: rkey, 73 + rkey: threadgateUri.value.rkey, 68 74 }); 69 75 } else if (!dequal(threadgate.allow, rules)) { 70 - const { rkey } = parseAddressedAtUri(threadgate.uri); 76 + const threadgateUri = parseCanonicalResourceUri(threadgate.uri); 77 + if (!threadgateUri.ok) { 78 + throw new Error(`failed to parse ${threadgate.uri}`); 79 + } 71 80 72 - const record: AppBskyFeedThreadgate.Record = { 81 + const record: AppBskyFeedThreadgate.Main = { 73 82 $type: 'app.bsky.feed.threadgate', 74 83 createdAt: threadgate.createdAt, 75 84 post: post.uri, ··· 80 89 writes.push({ 81 90 $type: 'com.atproto.repo.applyWrites#update', 82 91 collection: 'app.bsky.feed.threadgate', 83 - rkey: rkey, 92 + rkey: threadgateUri.value.rkey, 84 93 value: record, 85 94 }); 86 95 } ··· 90 99 logger.log(`${writes.length} write operations to apply`); 91 100 92 101 const did = data.profile.didDoc.id; 93 - const rpc = new XRPC({ handler: data.manager }); 94 - 95 - const RATELIMIT_POINT_LIMIT = 150 * 3; 102 + const client = new Client({ handler: data.manager }); 96 103 97 104 { 98 105 using progress = logger.progress(`Applying writes`); 99 106 100 107 let written = 0; 101 108 for (const chunk of chunked(writes, 200)) { 102 - try { 103 - const { headers } = await rpc.call('com.atproto.repo.applyWrites', { 104 - data: { 105 - repo: did, 106 - writes: chunk, 107 - }, 108 - }); 109 + let attempts = 0; 109 110 110 - written += chunk.length; 111 - progress.update(`Applying writes (${written} applied)`); 111 + while (true) { 112 + if (attempts > 0) { 113 + await sleep(2_000); 114 + } 112 115 113 - if ('ratelimit-remaining' in headers) { 114 - const remaining = +headers['ratelimit-remaining']; 115 - const reset = +headers['ratelimit-reset'] * 1_000; 116 + attempts++; 116 117 117 - if (remaining < RATELIMIT_POINT_LIMIT) { 118 - // add some delay to be sure 119 - const delta = reset - Date.now() + 5_000; 120 - using _progress = logger.progress(`Reached ratelimit, waiting ${delta}ms`); 118 + try { 119 + const response = await client.post('com.atproto.repo.applyWrites', { 120 + input: { 121 + repo: did, 122 + writes: chunk, 123 + }, 124 + }); 121 125 122 - await new Promise((resolve) => setTimeout(resolve, delta)); 126 + if (response.ok) { 127 + written += chunk.length; 128 + progress.update(`Applying writes (${written} applied)`); 129 + break; 123 130 } 124 - } 125 - } catch (err) { 126 - if (!(err instanceof XRPCError) || err.kind !== 'RateLimitExceeded') { 127 - throw err; 128 - } 131 + 132 + if (response.status === 429) { 133 + // not exposed by CORS, hoping that someday it will 134 + const reset = response.headers.get('ratelimit-reset'); 135 + 136 + using _progress = logger.progress(`Ratelimited, waiting`); 137 + 138 + if (reset !== null) { 139 + const refreshAt = +reset * 1_000; 140 + const delta = refreshAt - Date.now(); 129 141 130 - const headers = err.headers; 131 - if ('ratelimit-remaining' in headers) { 132 - const remaining = +headers['ratelimit-remaining']; 133 - const reset = +headers['ratelimit-reset'] * 1_000; 142 + await sleep(delta); 143 + } else { 144 + await sleep(10_000); 145 + } 146 + } 134 147 135 - if (remaining < RATELIMIT_POINT_LIMIT) { 136 - // add some delay to be sure 137 - const delta = reset - Date.now() + 5_000; 138 - using _progress = logger.progress(`Ratelimited, waiting ${delta}ms`); 148 + if (attempts < 3) { 149 + continue; 150 + } 139 151 140 - await new Promise((resolve) => setTimeout(resolve, delta)); 152 + throw new ClientResponseError(response); 153 + } catch (err) { 154 + // Network errors, etc 155 + if (attempts < 3) { 156 + continue; 141 157 } 142 - } else { 143 - using _progress = logger.progress(`Ratelimited, waiting`); 144 158 145 - await new Promise((resolve) => setTimeout(resolve, 60 * 1_000)); 159 + throw err; 146 160 } 147 161 } 148 162 } ··· 202 216 }; 203 217 204 218 export default Step4_Confirmation; 219 + 220 + const sleep = (ms: number): Promise<void> => { 221 + return new Promise((resolve) => setTimeout(resolve, ms)); 222 + };
+2 -2
src/views/bluesky/threadgate-applicator/steps/step5_finished.tsx
··· 1 - import { Stage, WizardStepProps } from '~/components/wizard'; 1 + import { Stage, type WizardStepProps } from '~/components/wizard'; 2 2 3 - import { ThreadgateApplicatorConstraints } from '../page'; 3 + import type { ThreadgateApplicatorConstraints } from '../page'; 4 4 5 5 export const Step5_Finished = ({}: WizardStepProps<ThreadgateApplicatorConstraints, 'Step5_Finished'>) => { 6 6 return (
+1 -1
src/views/bluesky/threadgate-applicator/utils.ts
··· 1 - import { ThreadgateState } from './page'; 1 + import type { ThreadgateState } from './page'; 2 2 3 3 const collator = new Intl.Collator('en'); 4 4
+3 -6
src/views/crypto/crypto-generate.tsx
··· 6 6 7 7 import Button from '~/components/inputs/button'; 8 8 import RadioInput from '~/components/inputs/radio-input'; 9 + import PageHeader from '~/components/page-header'; 9 10 10 11 type KeyType = 'p256' | 'secp256k1'; 11 12 ··· 26 27 27 28 return ( 28 29 <> 29 - <div class="p-4"> 30 - <h1 class="text-lg font-bold text-purple-800">Generate secret keys</h1> 31 - <p class="text-gray-600">Create a new secp256k1/nistp256 keypair</p> 32 - </div> 33 - <hr class="mx-4 border-gray-300" /> 30 + <PageHeader title="Generate secret keys" subtitle="Create a new secp256k1/nistp256 keypair" /> 34 31 35 32 <form 36 33 onSubmit={async (ev) => { ··· 54 51 ]); 55 52 56 53 const result: KeypairResult = { 57 - type: keypair.type, 54 + type: keypair.type as KeyType, 58 55 publicDidKey, 59 56 privateHex, 60 57 privateMultikey,
+255
src/views/crypto/crypto-info.tsx
··· 1 + import { createMemo, createSignal, Match, Show, Switch } from 'solid-js'; 2 + 3 + import { 4 + type DidKeyString, 5 + P256PrivateKeyExportable, 6 + P256PublicKey, 7 + parseDidKey, 8 + parsePrivateMultikey, 9 + parsePublicMultikey, 10 + Secp256k1PrivateKeyExportable, 11 + Secp256k1PublicKey, 12 + } from '@atcute/crypto'; 13 + import { fromBase16 } from '@atcute/multibase'; 14 + 15 + import { useTitle } from '~/lib/navigation/router'; 16 + 17 + import Button from '~/components/inputs/button'; 18 + import RadioInput from '~/components/inputs/radio-input'; 19 + import TextInput from '~/components/inputs/text-input'; 20 + import PageHeader from '~/components/page-header'; 21 + 22 + type KeyType = 'p256' | 'secp256k1'; 23 + type KeyFormat = 'did:key' | 'multikey' | 'hex'; 24 + 25 + interface KeyInfo { 26 + keyType: KeyType; 27 + isPrivate: boolean; 28 + inputFormat: KeyFormat; 29 + publicDidKey: DidKeyString; 30 + publicMultikey: string; 31 + privateHex?: string; 32 + privateMultikey?: string; 33 + } 34 + 35 + const DID_KEY_REGEX = /^did:key:z[a-km-zA-HJ-NP-Z1-9]+$/; 36 + const MULTIKEY_REGEX = /^z[a-km-zA-HJ-NP-Z1-9]+$/; 37 + const HEX_REGEX = /^[0-9a-f]+$/; 38 + 39 + const CryptoInfoPage = () => { 40 + const [input, setInput] = createSignal(''); 41 + const [hexKeyType, setHexKeyType] = createSignal<KeyType>(); 42 + const [result, setResult] = createSignal<KeyInfo>(); 43 + const [error, setError] = createSignal<string>(); 44 + 45 + const detectedFormat = createMemo((): KeyFormat | undefined => { 46 + const $input = input().trim(); 47 + 48 + if (DID_KEY_REGEX.test($input)) { 49 + return 'did:key'; 50 + } 51 + if (MULTIKEY_REGEX.test($input)) { 52 + return 'multikey'; 53 + } 54 + if (HEX_REGEX.test($input)) { 55 + return 'hex'; 56 + } 57 + }); 58 + 59 + const canSubmit = createMemo(() => { 60 + const format = detectedFormat(); 61 + if (!format) { 62 + return false; 63 + } 64 + if (format === 'hex' && !hexKeyType()) { 65 + return false; 66 + } 67 + return true; 68 + }); 69 + 70 + useTitle(() => `View crypto key info โ€” boat`); 71 + 72 + return ( 73 + <> 74 + <PageHeader title="View crypto key info" subtitle="Show basic metadata about a public or private key" /> 75 + 76 + <form 77 + onSubmit={async (ev) => { 78 + ev.preventDefault(); 79 + 80 + const $input = input().trim(); 81 + const format = detectedFormat(); 82 + 83 + setResult(); 84 + setError(); 85 + 86 + try { 87 + let info: KeyInfo; 88 + 89 + if (format === 'did:key') { 90 + const parsed = parseDidKey($input); 91 + const pubKey = 92 + parsed.type === 'p256' 93 + ? await P256PublicKey.importRaw(parsed.publicKeyBytes) 94 + : await Secp256k1PublicKey.importRaw(parsed.publicKeyBytes); 95 + 96 + info = { 97 + keyType: parsed.type, 98 + isPrivate: false, 99 + inputFormat: 'did:key', 100 + publicDidKey: await pubKey.exportPublicKey('did'), 101 + publicMultikey: await pubKey.exportPublicKey('multikey'), 102 + }; 103 + } else if (format === 'multikey') { 104 + // try parsing as private key first 105 + try { 106 + const parsed = parsePrivateMultikey($input); 107 + const privKey = 108 + parsed.type === 'p256' 109 + ? await P256PrivateKeyExportable.importRaw(parsed.privateKeyBytes) 110 + : await Secp256k1PrivateKeyExportable.importRaw(parsed.privateKeyBytes); 111 + 112 + info = { 113 + keyType: parsed.type, 114 + isPrivate: true, 115 + inputFormat: 'multikey', 116 + publicDidKey: await privKey.exportPublicKey('did'), 117 + publicMultikey: await privKey.exportPublicKey('multikey'), 118 + privateHex: await privKey.exportPrivateKey('rawHex'), 119 + privateMultikey: await privKey.exportPrivateKey('multikey'), 120 + }; 121 + } catch { 122 + // try parsing as public key 123 + const parsed = parsePublicMultikey($input); 124 + const pubKey = 125 + parsed.type === 'p256' 126 + ? await P256PublicKey.importRaw(parsed.publicKeyBytes) 127 + : await Secp256k1PublicKey.importRaw(parsed.publicKeyBytes); 128 + 129 + info = { 130 + keyType: parsed.type, 131 + isPrivate: false, 132 + inputFormat: 'multikey', 133 + publicDidKey: await pubKey.exportPublicKey('did'), 134 + publicMultikey: await pubKey.exportPublicKey('multikey'), 135 + }; 136 + } 137 + } else if (format === 'hex') { 138 + const keyType = hexKeyType()!; 139 + const privateKeyBytes = fromBase16($input); 140 + 141 + const privKey = 142 + keyType === 'p256' 143 + ? await P256PrivateKeyExportable.importRaw(privateKeyBytes) 144 + : await Secp256k1PrivateKeyExportable.importRaw(privateKeyBytes); 145 + 146 + info = { 147 + keyType: keyType, 148 + isPrivate: true, 149 + inputFormat: 'hex', 150 + publicDidKey: await privKey.exportPublicKey('did'), 151 + publicMultikey: await privKey.exportPublicKey('multikey'), 152 + privateHex: await privKey.exportPrivateKey('rawHex'), 153 + privateMultikey: await privKey.exportPrivateKey('multikey'), 154 + }; 155 + } else { 156 + throw new Error('Unknown key format'); 157 + } 158 + 159 + setResult(info); 160 + } catch (err) { 161 + console.error(err); 162 + setError(`Failed to parse key: ${err}`); 163 + } 164 + }} 165 + class="flex flex-col gap-4 p-4" 166 + > 167 + <TextInput 168 + label="Public or private key" 169 + blurb="Accepts did:key, multikey, or hex format" 170 + monospace 171 + autocomplete="off" 172 + autocorrect="off" 173 + placeholder="did:key:z... or z... or a5973930f9d348..." 174 + value={input()} 175 + required 176 + onChange={setInput} 177 + /> 178 + 179 + <Show when={detectedFormat() === 'hex'}> 180 + <RadioInput 181 + label="This is a..." 182 + value={hexKeyType()} 183 + required 184 + options={[ 185 + { value: 'secp256k1', label: `ES256K (secp256k1) private key` }, 186 + { value: 'p256', label: `ES256 (p256) private key` }, 187 + ]} 188 + onChange={setHexKeyType} 189 + /> 190 + </Show> 191 + 192 + <div> 193 + <Button type="submit" disabled={!canSubmit()}> 194 + Inspect 195 + </Button> 196 + </div> 197 + </form> 198 + 199 + <hr class="mx-4 border-gray-300" /> 200 + 201 + <Switch> 202 + <Match when={error()}> 203 + <div class="p-4 text-red-600">{error()}</div> 204 + </Match> 205 + 206 + <Match when={result()} keyed> 207 + {(info) => ( 208 + <div class="flex flex-col gap-6 break-words p-4 text-gray-900"> 209 + <div> 210 + <p class="font-semibold text-gray-600">Key type</p> 211 + <span> 212 + {/* @once */ info.keyType === 'p256' 213 + ? 'ES256 (p256)' 214 + : 'ES256K (secp256k1)'}{' '} 215 + {/* @once */ info.isPrivate ? 'private' : 'public'} key 216 + </span> 217 + </div> 218 + 219 + <div> 220 + <p class="font-semibold text-gray-600">Input encoding</p> 221 + <span>{/* @once */ info.inputFormat}</span> 222 + </div> 223 + 224 + <div> 225 + <p class="font-semibold text-gray-600">Public key (did:key)</p> 226 + <span class="font-mono">{/* @once */ info.publicDidKey}</span> 227 + </div> 228 + 229 + <div> 230 + <p class="font-semibold text-gray-600">Public key (multikey)</p> 231 + <span class="font-mono">{/* @once */ info.publicMultikey}</span> 232 + </div> 233 + 234 + <Show when={info.privateHex}> 235 + <div> 236 + <p class="font-semibold text-gray-600">Private key (hex)</p> 237 + <span class="font-mono">{/* @once */ info.privateHex}</span> 238 + </div> 239 + </Show> 240 + 241 + <Show when={info.privateMultikey}> 242 + <div> 243 + <p class="font-semibold text-gray-600">Private key (multikey)</p> 244 + <span class="font-mono">{/* @once */ info.privateMultikey}</span> 245 + </div> 246 + </Show> 247 + </div> 248 + )} 249 + </Match> 250 + </Switch> 251 + </> 252 + ); 253 + }; 254 + 255 + export default CryptoInfoPage;
+6 -8
src/views/frontpage.tsx
··· 1 - import { Component, ComponentProps } from 'solid-js'; 1 + import type { Component, ComponentProps } from 'solid-js'; 2 2 3 3 import { useTitle } from '~/lib/navigation/router'; 4 + 5 + import PageHeader from '~/components/page-header'; 4 6 5 7 import HistoryIcon from '~/components/ic-icons/baseline-history'; 6 8 import KeyIcon from '~/components/ic-icons/baseline-key'; ··· 102 104 { 103 105 name: `Migrate account`, 104 106 description: `Move your account data to another server`, 105 - href: null, 107 + href: '/account-migrate', 106 108 icon: MoveUpOutlinedIcon, 107 109 }, 108 110 ], ··· 119 121 { 120 122 name: `View crypto key info`, 121 123 description: `Show basic metadata about a public or private key`, 122 - href: null, 124 + href: `/crypto-info`, 123 125 icon: KeyVisualizerIcon, 124 126 }, 125 127 ], ··· 170 172 171 173 return ( 172 174 <> 173 - <div class="p-4"> 174 - <h1 class="text-lg font-bold text-purple-800">boat</h1> 175 - <p class="text-gray-600">handy online tools for AT Protocol</p> 176 - </div> 177 - <hr class="mx-4 border-gray-300" /> 175 + <PageHeader title="boat" subtitle="handy online tools for AT Protocol" /> 178 176 179 177 <div class="flex grow flex-col pb-2">{nodes}</div> 180 178
+20 -32
src/views/identity/did-lookup.tsx
··· 1 1 import { Match, Switch } from 'solid-js'; 2 2 3 - import { isAtprotoDid, isHandle, type AtprotoDid, type Did, type Handle } from '@atcute/identity'; 3 + import { isAtprotoDid } from '@atcute/identity'; 4 + import { isHandle, type AtprotoDid, type Did, type Handle } from '@atcute/lexicons/syntax'; 4 5 5 6 import { getDidDocument } from '~/api/queries/did-doc'; 6 7 import { resolveHandleViaAppView } from '~/api/queries/handle'; 7 8 import { isServiceUrlString } from '~/api/types/strings'; 8 - import { DID_OR_HANDLE_RE } from '~/api/utils/strings'; 9 9 10 10 import { useTitle } from '~/lib/navigation/router'; 11 11 import { createQuery } from '~/lib/utils/query'; ··· 15 15 import ErrorView from '~/components/error-view'; 16 16 import Button from '~/components/inputs/button'; 17 17 import TextInput from '~/components/inputs/text-input'; 18 + import PageHeader from '~/components/page-header'; 18 19 19 20 const DidLookupPage = () => { 20 21 const [params, setParams] = useSearchParams({ ··· 46 47 47 48 return ( 48 49 <> 49 - <div class="p-4"> 50 - <h1 class="text-lg font-bold text-purple-800">View identity info</h1> 51 - <p class="text-gray-600">Look up an account's DID document</p> 52 - </div> 53 - <hr class="mx-4 border-gray-300" /> 50 + <PageHeader title="View identity info" subtitle="Look up an account's DID document" /> 54 51 55 52 <form 56 53 onSubmit={(ev) => { ··· 67 64 type="text" 68 65 name="ident" 69 66 autocomplete="username" 70 - pattern={/* @once */ DID_OR_HANDLE_RE.source} 71 67 placeholder="paul.bsky.social" 72 68 autofocus 73 69 /> ··· 101 97 102 98 <div> 103 99 <p class="font-semibold text-gray-600">Identifies as</p> 104 - <ol class="list-disc pl-4">{doc.alsoKnownAs?.map((ident) => <li>{ident}</li>)}</ol> 100 + <ol class="list-disc pl-4"> 101 + {doc.alsoKnownAs?.map((ident) => ( 102 + <li>{ident}</li> 103 + ))} 104 + </ol> 105 105 </div> 106 106 107 107 <div> ··· 130 130 131 131 <div class="mt-2 flex flex-wrap gap-2 empty:hidden"> 132 132 {isPDS && isServiceUrl && ( 133 - <button 134 - disabled 135 - class="flex h-9 select-none items-center rounded border border-gray-300 px-4 text-sm font-semibold text-gray-800 hover:bg-gray-100 active:bg-gray-100 disabled:pointer-events-none disabled:opacity-50" 136 - > 133 + <Button variant="outline" disabled> 137 134 View PDS info 138 - </button> 135 + </Button> 139 136 )} 140 137 141 138 {isPDS && isServiceUrl && ( 142 - <button 143 - disabled 144 - class="flex h-9 select-none items-center rounded border border-gray-300 px-4 text-sm font-semibold text-gray-800 hover:bg-gray-100 active:bg-gray-100 disabled:pointer-events-none disabled:opacity-50" 145 - > 139 + <Button variant="outline" disabled> 146 140 Explore account repository 147 - </button> 141 + </Button> 148 142 )} 149 143 150 144 {isLabeler && isServiceUrl && ( 151 - <button 152 - disabled 153 - class="flex h-9 select-none items-center rounded border border-gray-300 px-4 text-sm font-semibold text-gray-800 hover:bg-gray-100 active:bg-gray-100 disabled:pointer-events-none disabled:opacity-50" 154 - > 145 + <Button variant="outline" disabled> 155 146 View emitted labels 156 - </button> 147 + </Button> 157 148 )} 158 149 </div> 159 150 </li> ··· 182 173 </div> 183 174 184 175 <div class="flex flex-wrap gap-4 p-4 pt-2"> 185 - <button 176 + <Button 177 + variant="outline" 186 178 onClick={() => { 187 179 navigator.clipboard.writeText(JSON.stringify(doc, null, 2)); 188 180 }} 189 - class="flex h-9 select-none items-center rounded border border-gray-300 px-4 text-sm font-semibold text-gray-800 hover:bg-gray-100 active:bg-gray-100" 190 181 > 191 182 Copy DID document 192 - </button> 183 + </Button> 193 184 194 185 {isDidPlc && ( 195 - <a 196 - href={`/plc-oplogs?q=${params.q!}`} 197 - class="flex h-9 select-none items-center rounded border border-gray-300 px-4 text-sm font-semibold text-gray-800 hover:bg-gray-100 active:bg-gray-100" 198 - > 186 + <Button variant="outline" href={`/plc-oplogs?q=${params.q!}`}> 199 187 View PLC operation logs 200 - </a> 188 + </Button> 201 189 )} 202 190 </div> 203 191 </>
+7 -9
src/views/identity/plc-applicator/page.tsx
··· 1 1 import { createEffect, createSignal, onCleanup } from 'solid-js'; 2 2 3 + import type { ComAtprotoIdentityGetRecommendedDidCredentials } from '@atcute/atproto'; 3 4 import type { CredentialManager } from '@atcute/client'; 4 - import type { ComAtprotoIdentityGetRecommendedDidCredentials } from '@atcute/client/lexicons'; 5 5 import type { P256PrivateKey, Secp256k1PrivateKey } from '@atcute/crypto'; 6 6 import type { CompatibleOperation, IndexedEntry, IndexedEntryWithSigner } from '@atcute/did-plc'; 7 - import type { Did, DidDocument } from '@atcute/identity'; 7 + import type { DidDocument } from '@atcute/identity'; 8 + import type { Did } from '@atcute/lexicons/syntax'; 8 9 9 - import { UpdatePayload } from '~/api/types/plc'; 10 + import type { UpdatePayload } from '~/api/types/plc'; 10 11 11 12 import { history } from '~/globals/navigation'; 12 13 13 14 import { useTitle } from '~/lib/navigation/router'; 14 15 16 + import PageHeader from '~/components/page-header'; 15 17 import { Wizard } from '~/components/wizard'; 16 18 17 19 import Step1_HandleInput from './steps/step1_handle-input'; ··· 31 33 export interface PdsSigningMethod { 32 34 type: 'pds'; 33 35 manager: CredentialManager; 34 - recommendedDidDoc: ComAtprotoIdentityGetRecommendedDidCredentials.Output; 36 + recommendedDidDoc: ComAtprotoIdentityGetRecommendedDidCredentials.$output; 35 37 } 36 38 37 39 export type Keypair = P256PrivateKey | Secp256k1PrivateKey; ··· 100 102 101 103 return ( 102 104 <> 103 - <div class="p-4"> 104 - <h1 class="text-lg font-bold text-purple-800">Apply PLC operations</h1> 105 - <p class="text-gray-600">Submit operations to your did:plc identity</p> 106 - </div> 107 - <hr class="mx-4 border-gray-300" /> 105 + <PageHeader title="Apply PLC operations" subtitle="Submit operations to your did:plc identity" /> 108 106 109 107 <Wizard<PlcApplicatorConstraints> 110 108 initialStep="Step1_HandleInput"
+1 -1
src/views/identity/plc-applicator/plc-utils.ts
··· 1 1 import type { IndexedEntry } from '@atcute/did-plc'; 2 2 3 - import { UpdatePayload } from '~/api/types/plc'; 3 + import type { UpdatePayload } from '~/api/types/plc'; 4 4 5 5 import { assert } from '~/lib/utils/invariant'; 6 6
+13 -12
src/views/identity/plc-applicator/steps/step1_handle-input.tsx
··· 1 1 import { createSignal } from 'solid-js'; 2 2 3 - import { XRPCError } from '@atcute/client'; 3 + import { ClientResponseError } from '@atcute/client'; 4 4 import { processIndexedEntryLog } from '@atcute/did-plc'; 5 - import { type Did, isHandle, isPlcDid } from '@atcute/identity'; 5 + import { isPlcDid } from '@atcute/identity'; 6 + import { isHandle, type Did } from '@atcute/lexicons/syntax'; 6 7 7 8 import { getDidDocument } from '~/api/queries/did-doc'; 8 9 import { resolveHandleViaAppView } from '~/api/queries/handle'; 9 10 import { getPlcAuditLogs } from '~/api/queries/plc'; 10 - import { DID_OR_HANDLE_RE } from '~/api/utils/strings'; 11 11 12 12 import { createMutation } from '~/lib/utils/mutation'; 13 13 14 14 import Button from '~/components/inputs/button'; 15 15 import RadioInput from '~/components/inputs/radio-input'; 16 16 import TextInput from '~/components/inputs/text-input'; 17 - import { Stage, StageActions, StageErrorView, WizardStepProps } from '~/components/wizard'; 17 + import { Stage, StageActions, StageErrorView, type WizardStepProps } from '~/components/wizard'; 18 18 19 - import { type PlcInformation, PlcApplicatorConstraints } from '../page'; 19 + import type { PlcApplicatorConstraints, PlcInformation } from '../page'; 20 20 21 21 type Method = 'pds' | 'key'; 22 22 ··· 70 70 onNext('Step2_PrivateKeyInput', { info }); 71 71 } 72 72 }, 73 - onError(error) { 73 + onError(err) { 74 74 let message: string | undefined; 75 75 76 - if (error instanceof XRPCError) { 77 - if (error.kind === 'InvalidRequest' && error.message.includes('resolve handle')) { 76 + if (err instanceof ClientResponseError) { 77 + if (err.error === 'InvalidRequest' && err.description?.includes('resolve handle')) { 78 78 message = `Can't seem to resolve handle, is it typed correctly?`; 79 79 } 80 - } else if (error instanceof DidIsNotPlcError) { 81 - message = error.message; 80 + } else if (err instanceof DidIsNotPlcError) { 81 + message = err.message; 82 82 } 83 83 84 84 if (message !== undefined) { 85 85 setError(message); 86 86 } else { 87 - setError(`Something went wrong: ${error}`); 87 + console.error(err); 88 + 89 + setError(`Something went wrong: ${err}`); 88 90 } 89 91 }, 90 92 }); ··· 105 107 placeholder="paul.bsky.social" 106 108 value={identifier()} 107 109 required 108 - pattern={/* @once */ DID_OR_HANDLE_RE.source} 109 110 autofocus={isActive()} 110 111 onChange={setIdentifier} 111 112 />
+16 -19
src/views/identity/plc-applicator/steps/step2_pds-authentication.tsx
··· 1 1 import { createSignal, Match, Show, Switch } from 'solid-js'; 2 2 3 - import { AtpAccessJwt, CredentialManager, XRPC, XRPCError } from '@atcute/client'; 4 - import { decodeJwt } from '@atcute/client/utils/jwt'; 3 + import { type AtpAccessJwt, Client, ClientResponseError, CredentialManager, ok } from '@atcute/client'; 5 4 import { getPdsEndpoint } from '@atcute/identity'; 6 5 7 6 import { formatTotpCode, TOTP_RE } from '~/api/utils/auth'; 7 + import { decodeJwt } from '~/api/utils/jwt'; 8 8 9 9 import { createMutation } from '~/lib/utils/mutation'; 10 10 11 11 import Button from '~/components/inputs/button'; 12 12 import TextInput from '~/components/inputs/text-input'; 13 - import { Stage, StageActions, StageErrorView, WizardStepProps } from '~/components/wizard'; 13 + import { Stage, StageActions, StageErrorView, type WizardStepProps } from '~/components/wizard'; 14 14 15 - import { PlcApplicatorConstraints } from '../page'; 15 + import type { PlcApplicatorConstraints } from '../page'; 16 16 17 17 class InsufficientLoginError extends Error {} 18 18 ··· 59 59 setPassword(''); 60 60 setIsTotpRequired(false); 61 61 }, 62 - onError(error) { 62 + onError(err) { 63 63 let message: string | undefined; 64 64 65 - if (error instanceof XRPCError) { 66 - if (error.kind === 'AuthFactorTokenRequired') { 65 + if (err instanceof ClientResponseError) { 66 + if (err.error === 'AuthFactorTokenRequired') { 67 67 setOtp(''); 68 68 setIsTotpRequired(true); 69 69 return; 70 70 } 71 71 72 - if (error.kind === 'AuthenticationRequired') { 72 + if (err.error === 'AuthenticationRequired') { 73 73 message = `Invalid identifier or password`; 74 - } else if (error.kind === 'AccountTakedown') { 74 + } else if (err.error === 'AccountTakedown') { 75 75 message = `Account has been taken down`; 76 - } else if (error.message.includes('Token is invalid')) { 76 + } else if (err.description?.includes('Token is invalid')) { 77 77 message = `Invalid one-time confirmation code`; 78 78 setIsTotpRequired(true); 79 79 } 80 - } else if (error instanceof InsufficientLoginError) { 81 - message = error.message; 80 + } else if (err instanceof InsufficientLoginError) { 81 + message = err.message; 82 82 } 83 83 84 84 if (message !== undefined) { 85 85 setError(message); 86 86 } else { 87 - console.error(error); 88 - setError(`Something went wrong: ${error}`); 87 + console.error(err); 88 + setError(`Something went wrong: ${err}`); 89 89 } 90 90 }, 91 91 }); 92 92 93 93 const dispatchMutation = createMutation({ 94 94 async mutationFn({ manager }: { manager: CredentialManager }) { 95 - const rpc = new XRPC({ handler: manager }); 96 - const { data: recommendedDidDoc } = await rpc.get( 97 - 'com.atproto.identity.getRecommendedDidCredentials', 98 - {}, 99 - ); 95 + const rpc = new Client({ handler: manager }); 96 + const recommendedDidDoc = await ok(rpc.get('com.atproto.identity.getRecommendedDidCredentials')); 100 97 101 98 return { recommendedDidDoc }; 102 99 },
+3 -9
src/views/identity/plc-applicator/steps/step2_private-key-input.tsx
··· 8 8 import Button from '~/components/inputs/button'; 9 9 import RadioInput from '~/components/inputs/radio-input'; 10 10 import TextInput from '~/components/inputs/text-input'; 11 - import { Stage, StageActions, StageErrorView, WizardStepProps } from '~/components/wizard'; 11 + import { Stage, StageActions, StageErrorView, type WizardStepProps } from '~/components/wizard'; 12 12 13 13 import type { Keypair, PlcApplicatorConstraints, PrivateKeySigningMethod } from '../page'; 14 14 ··· 97 97 }); 98 98 }, 99 99 onError(error) { 100 - let message: string | undefined; 101 - 102 - if (message !== undefined) { 103 - setError(message); 104 - } else { 105 - console.error(error); 106 - setError(`Something went wrong: ${error}`); 107 - } 100 + console.error(error); 101 + setError(`Something went wrong: ${error}`); 108 102 }, 109 103 }); 110 104
+4 -4
src/views/identity/plc-applicator/steps/step3_operation-select.tsx
··· 7 7 type IndexedEntryWithSigner, 8 8 normalizeOp, 9 9 } from '@atcute/did-plc'; 10 - import type { Did } from '@atcute/identity'; 10 + import type { Did } from '@atcute/lexicons'; 11 11 12 12 import Button from '~/components/inputs/button'; 13 + import RadioInput from '~/components/inputs/radio-input'; 13 14 import SelectInput from '~/components/inputs/select-input'; 14 - import { Stage, StageActions, StageErrorView, WizardStepProps } from '~/components/wizard'; 15 + import { Stage, StageActions, StageErrorView, type WizardStepProps } from '~/components/wizard'; 15 16 16 - import { PlcApplicatorConstraints } from '../page'; 17 - import RadioInput from '~/components/inputs/radio-input'; 17 + import type { PlcApplicatorConstraints } from '../page'; 18 18 19 19 const Step3_OperationSelect = ({ 20 20 data,
+2 -2
src/views/identity/plc-applicator/steps/step4_payload-input.tsx
··· 4 4 5 5 import Button from '~/components/inputs/button'; 6 6 import MultilineInput from '~/components/inputs/multiline-input'; 7 - import { Stage, StageActions, StageErrorView, WizardStepProps } from '~/components/wizard'; 7 + import { Stage, StageActions, StageErrorView, type WizardStepProps } from '~/components/wizard'; 8 8 9 - import { PlcApplicatorConstraints } from '../page'; 9 + import type { PlcApplicatorConstraints } from '../page'; 10 10 import { getPlcPayload } from '../plc-utils'; 11 11 12 12 export const Step4_PayloadInput = ({
+30 -25
src/views/identity/plc-applicator/steps/step5_pds-confirmation.tsx
··· 1 1 import { createSignal } from 'solid-js'; 2 2 3 - import { XRPC, XRPCError } from '@atcute/client'; 3 + import { Client, ClientResponseError, ok } from '@atcute/client'; 4 4 5 5 import { formatTotpCode, TOTP_RE } from '~/api/utils/auth'; 6 6 ··· 9 9 import CheckIcon from '~/components/ic-icons/baseline-check'; 10 10 import Button from '~/components/inputs/button'; 11 11 import TextInput from '~/components/inputs/text-input'; 12 - import { Stage, StageActions, StageErrorView, WizardStepProps } from '~/components/wizard'; 12 + import { Stage, StageActions, StageErrorView, type WizardStepProps } from '~/components/wizard'; 13 13 14 - import { PlcApplicatorConstraints } from '../page'; 14 + import type { PlcApplicatorConstraints } from '../page'; 15 15 16 16 export const Step5_PdsConfirmation = ({ 17 17 data, ··· 27 27 const requestMutation = createMutation({ 28 28 async mutationFn() { 29 29 const manager = data.method.manager; 30 - const rpc = new XRPC({ handler: manager }); 30 + const rpc = new Client({ handler: manager }); 31 31 32 - await rpc.call('com.atproto.identity.requestPlcOperationSignature', {}); 32 + await ok(rpc.post('com.atproto.identity.requestPlcOperationSignature', { as: null })); 33 33 }, 34 34 onMutate() { 35 35 setRequestError(); ··· 49 49 const applyMutation = createMutation({ 50 50 async mutationFn({ code }: { code: string }) { 51 51 const manager = data.method.manager; 52 - const rpc = new XRPC({ handler: manager }); 52 + const client = new Client({ handler: manager }); 53 53 54 54 const payload = data.payload; 55 55 56 - const { data: signage } = await rpc.call('com.atproto.identity.signPlcOperation', { 57 - data: { 58 - token: formatTotpCode(code), 59 - alsoKnownAs: payload.alsoKnownAs, 60 - rotationKeys: payload.rotationKeys, 61 - services: payload.services, 62 - verificationMethods: payload.verificationMethods, 63 - }, 64 - }); 56 + const signage = await ok( 57 + client.post('com.atproto.identity.signPlcOperation', { 58 + input: { 59 + token: formatTotpCode(code), 60 + alsoKnownAs: payload.alsoKnownAs, 61 + rotationKeys: payload.rotationKeys, 62 + services: payload.services, 63 + verificationMethods: payload.verificationMethods, 64 + }, 65 + }), 66 + ); 65 67 66 - await rpc.call('com.atproto.identity.submitPlcOperation', { 67 - data: { 68 - operation: signage.operation, 69 - }, 70 - }); 68 + await ok( 69 + client.post('com.atproto.identity.submitPlcOperation', { 70 + as: null, 71 + input: { 72 + operation: signage.operation, 73 + }, 74 + }), 75 + ); 71 76 }, 72 77 onMutate() { 73 78 setApplyError(); ··· 75 80 onSuccess() { 76 81 onNext('Step6_Finished', {}); 77 82 }, 78 - onError(error) { 83 + onError(err) { 79 84 let message: string | undefined; 80 85 81 - if (error instanceof XRPCError) { 82 - if (error.kind === 'InvalidToken' || error.kind === 'ExpiredToken') { 86 + if (err instanceof ClientResponseError) { 87 + if (err.error === 'InvalidToken' || err.error === 'ExpiredToken') { 83 88 message = `Confirmation code has expired`; 84 89 } 85 90 } ··· 87 92 if (message !== undefined) { 88 93 setApplyError(message); 89 94 } else { 90 - console.error(error); 91 - setApplyError(`Something went wrong: ${error}`); 95 + console.error(err); 96 + setApplyError(`Something went wrong: ${err}`); 92 97 } 93 98 }, 94 99 });
+5 -6
src/views/identity/plc-applicator/steps/step5_private-key-confirmation.tsx
··· 9 9 10 10 import Button from '~/components/inputs/button'; 11 11 import TextInput from '~/components/inputs/text-input'; 12 - import { Stage, StageActions, StageErrorView, WizardStepProps } from '~/components/wizard'; 12 + import { Stage, StageActions, StageErrorView, type WizardStepProps } from '~/components/wizard'; 13 13 14 - import { PlcApplicatorConstraints } from '../page'; 14 + import type { PlcApplicatorConstraints } from '../page'; 15 15 16 16 const Step5_PrivateKeyConfirmation = ({ 17 17 data, ··· 78 78 }} 79 79 > 80 80 <p class="text-pretty"> 81 - To continue with this submission, type in the following code{' '} 82 - <code class="whitespace-nowrap font-bold">{code}</code> to the confirmation box below. 81 + To continue with this submission, type in <code class="whitespace-nowrap font-bold">{code}</code> to 82 + the confirmation box below. 83 83 </p> 84 84 85 85 <TextInput 86 - label="Confirmation code" 86 + label="Confirmation" 87 87 type="text" 88 88 autocomplete="one-time-code" 89 89 autocorrect="off" 90 90 required 91 91 pattern={code} 92 - placeholder="AAAAA-BBBBB" 93 92 autofocus={isActive()} 94 93 monospace 95 94 />
+2 -2
src/views/identity/plc-applicator/steps/step6_finished.tsx
··· 1 - import { Stage, WizardStepProps } from '~/components/wizard'; 1 + import { Stage, type WizardStepProps } from '~/components/wizard'; 2 2 3 - import { PlcApplicatorConstraints } from '../page'; 3 + import type { PlcApplicatorConstraints } from '../page'; 4 4 5 5 export const Step6_Finished = ({}: WizardStepProps<PlcApplicatorConstraints, 'Step6_Finished'>) => { 6 6 return (
+5 -9
src/views/identity/plc-oplogs.tsx
··· 1 - import { createSignal, JSX, Match, onCleanup, Switch } from 'solid-js'; 1 + import { createSignal, Match, onCleanup, Switch, type JSX } from 'solid-js'; 2 2 3 3 import type { IndexedEntry, Service } from '@atcute/did-plc'; 4 - import { type Did, type Handle, isHandle, isPlcDid } from '@atcute/identity'; 4 + import { isPlcDid } from '@atcute/identity'; 5 + import { isHandle, type Did, type Handle } from '@atcute/lexicons/syntax'; 5 6 6 7 import { resolveHandleViaAppView } from '~/api/queries/handle'; 7 - import { DID_OR_HANDLE_RE } from '~/api/utils/strings'; 8 8 9 9 import { getPlcAuditLogs } from '~/api/queries/plc'; 10 10 import { useTitle } from '~/lib/navigation/router'; ··· 20 20 import ContentCopyIcon from '~/components/ic-icons/baseline-content-copy'; 21 21 import Button from '~/components/inputs/button'; 22 22 import TextInput from '~/components/inputs/text-input'; 23 + import PageHeader from '~/components/page-header'; 23 24 24 25 const PlcOperationLogPage = () => { 25 26 const [params, setParams] = useSearchParams({ ··· 55 56 56 57 return ( 57 58 <> 58 - <div class="p-4"> 59 - <h1 class="text-lg font-bold text-purple-800">View PLC operation logs</h1> 60 - <p class="text-gray-600">Show history of a did:plc identity</p> 61 - </div> 62 - <hr class="mx-4 border-gray-300" /> 59 + <PageHeader title="View PLC operation logs" subtitle="Show history of a did:plc identity" /> 63 60 64 61 <form 65 62 onSubmit={(ev) => { ··· 76 73 type="text" 77 74 name="ident" 78 75 autocomplete="username" 79 - pattern={/* @once */ DID_OR_HANDLE_RE.source} 80 76 placeholder="paul.bsky.social" 81 77 autofocus 82 78 />
+9 -6
src/views/repository/repo-archive-explore/page.tsx
··· 1 1 import { Match, Switch } from 'solid-js'; 2 2 3 - import { iterateAtpRepo } from '@atcute/car'; 3 + import { fromStream } from '@atcute/repo'; 4 4 5 5 import { createMutation } from '~/lib/utils/mutation'; 6 6 7 - import WelcomeView from './views/welcome'; 8 - 9 - import { Archive, RecordEntry } from './types'; 7 + import type { Archive, RecordEntry } from './types'; 10 8 import ExploreView from './views/explore'; 9 + import WelcomeView from './views/welcome'; 11 10 12 11 const ArchiveExplorePage = () => { 13 12 const mutation = createMutation({ 14 13 async mutationFn({ file }: { file: File }): Promise<Archive> { 15 - const buffer = new Uint8Array(await file.arrayBuffer()); 14 + const stream = file.stream(); 15 + await using repo = fromStream(stream); 16 16 17 17 const collections = new Map<string, RecordEntry[]>(); 18 18 const archive: Archive = { ··· 20 20 entries: [], 21 21 }; 22 22 23 - for (const entry of iterateAtpRepo(buffer)) { 23 + for await (const entry of repo) { 24 24 let list = collections.get(entry.collection); 25 25 if (list === undefined) { 26 26 collections.set(entry.collection, (list = [])); ··· 41 41 } 42 42 43 43 return archive; 44 + }, 45 + onError(err) { 46 + console.error(err); 44 47 }, 45 48 }); 46 49
+1 -1
src/views/repository/repo-archive-explore/views/explore/record.tsx
··· 4 4 5 5 import { createQuery } from '~/lib/utils/query'; 6 6 7 - import { Archive, RecordEntry } from '../../types'; 7 + import type { Archive, RecordEntry } from '../../types'; 8 8 9 9 interface RecordSubviewProps { 10 10 archive: Archive;
+9 -42
src/views/repository/repo-archive-explore/views/welcome.tsx
··· 3 3 import type { MutationReturn } from '~/lib/utils/mutation'; 4 4 5 5 import CircularProgress from '~/components/circular-progress'; 6 + import FileDropZone from '~/components/file-drop-zone'; 7 + import PageHeader from '~/components/page-header'; 6 8 7 - import { Archive } from '../types'; 8 - import { createDropZone } from '~/lib/hooks/dropzone'; 9 + import type { Archive } from '../types'; 9 10 10 11 interface WelcomeViewProps { 11 12 mutation: MutationReturn<Archive, { file: File }>; 12 13 } 13 14 14 15 const WelcomeView = ({ mutation }: WelcomeViewProps) => { 15 - const { ref: dropRef, isDropping } = createDropZone({ 16 - // Checked, the mime type for CAR files is blank. 17 - dataTypes: [''], 18 - multiple: false, 19 - onDrop(files) { 20 - if (files) { 21 - mutation.mutate({ file: files[0] }); 22 - } 23 - }, 24 - }); 25 - 26 16 return ( 27 17 <> 28 - <div class="p-4"> 29 - <h1 class="text-lg font-bold text-purple-800">Explore archive</h1> 30 - <p class="text-gray-600">Explore a repository archive</p> 31 - </div> 32 - <hr class="mx-4 border-gray-300" /> 18 + <PageHeader title="Explore archive" subtitle="Explore a repository archive" /> 33 19 34 20 <div class="flex flex-col gap-4 p-4"> 35 - <fieldset 36 - ref={dropRef} 37 - class={ 38 - `grid place-items-center rounded border border-gray-300 px-6 py-12 disabled:opacity-50` + 39 - (!isDropping() ? ` bg-gray-100` : ` bg-green-100`) 40 - } 21 + <FileDropZone 22 + accept=".car,application/vnd.ipld.car" 23 + dataTypes={['']} 24 + onFiles={(files) => mutation.mutate({ file: files[0] })} 41 25 > 42 - <div class="flex flex-col items-center gap-4"> 43 - <button 44 - onClick={() => { 45 - const input = document.createElement('input'); 46 - input.type = 'file'; 47 - input.accept = '.car,application/vnd.ipld.car'; 48 - input.oninput = () => mutation.mutate({ file: input.files![0] }); 49 - 50 - input.click(); 51 - }} 52 - class="flex h-9 select-none items-center rounded border border-gray-400 px-4 text-sm font-semibold text-gray-800 hover:bg-gray-200 active:bg-gray-200 disabled:pointer-events-none" 53 - > 54 - Browse files 55 - </button> 56 - <p class="select-none font-medium text-gray-600">or drop your file here</p> 57 - </div> 58 - 59 26 <div 60 27 hidden={!mutation.isPending} 61 28 class="absolute inset-0 flex flex-col items-center justify-center gap-3 bg-gray-50" ··· 63 30 <CircularProgress /> 64 31 <span class="font-medium">Reading CAR file</span> 65 32 </div> 66 - </fieldset> 33 + </FileDropZone> 67 34 68 35 <Show when={mutation.error}> 69 36 <p class="whitespace-pre-wrap text-[0.8125rem] font-medium leading-5 text-red-800">
+23 -62
src/views/repository/repo-archive-unpack.tsx
··· 1 1 import { FileSystemWritableFileStream, showSaveFilePicker } from 'native-file-system-adapter'; 2 2 import { createSignal } from 'solid-js'; 3 3 4 - import { iterateAtpRepo } from '@atcute/car'; 4 + import { fromStream } from '@atcute/repo'; 5 5 import { writeTarEntry } from '@mary/tar'; 6 6 7 - import { createDropZone } from '~/lib/hooks/dropzone'; 8 7 import { useTitle } from '~/lib/navigation/router'; 9 8 import { makeAbortable } from '~/lib/utils/abortable'; 10 9 10 + import FileDropZone from '~/components/file-drop-zone'; 11 11 import Logger, { createLogger } from '~/components/logger'; 12 + import PageHeader from '~/components/page-header'; 12 13 13 14 // @ts-expect-error: new API 14 15 const yieldToScheduler: () => Promise<void> = window?.scheduler?.yield ··· 16 17 window.scheduler.yield.bind(window.scheduler) 17 18 : undefined; 18 19 19 - const yieldToIdle = 20 - typeof requestIdleCallback === 'function' 21 - ? () => new Promise((resolve) => requestIdleCallback(resolve)) 22 - : () => new Promise((resolve) => setTimeout(resolve, 1)); 23 - 24 20 const UnpackCarPage = () => { 25 21 const logger = createLogger(); 26 22 27 23 const [getSignal, cleanup] = makeAbortable(); 28 24 const [pending, setPending] = createSignal(false); 29 25 30 - const { ref: dropRef, isDropping } = createDropZone({ 31 - // Checked, the mime type for CAR files is blank. 32 - dataTypes: [''], 33 - multiple: false, 34 - onDrop(files) { 35 - if (files) { 36 - onFileDrop(files); 37 - } 38 - }, 39 - }); 40 - 41 26 const mutate = async (file: File, signal: AbortSignal) => { 42 - logger.info(`Starting extraction for ${file.name}`); 27 + logger.log(`Starting extraction`); 43 28 44 - const buf = await file.arrayBuffer(); 45 - const ui8 = new Uint8Array(buf); 29 + const stream = file.stream(); 30 + await using repo = fromStream(stream); 46 31 47 - let currentCollection: string | undefined; 48 32 let count = 0; 49 33 50 34 let writable: FileSystemWritableFileStream | undefined; 51 35 52 - for (const { collection, rkey, record } of iterateAtpRepo(ui8)) { 36 + using progress = logger.progress(`Unpacking records (${count} entries)`); 37 + 38 + for await (const { collection, rkey, record } of repo) { 53 39 if (writable === undefined) { 54 40 using _progress = logger.progress(`Waiting for the user`); 55 41 ··· 87 73 88 74 signal.throwIfAborted(); 89 75 90 - if (currentCollection !== collection) { 91 - logger.log(`Current progress: ${collection}`); 92 - currentCollection = collection; 93 - 94 - if (yieldToScheduler === undefined) { 95 - await yieldToIdle(); 96 - } 97 - } 98 - 99 76 const entry = writeTarEntry({ 100 77 filename: `${collection}/${filenamify(rkey)}.json`, 101 78 data: JSON.stringify(record, null, 2), 102 79 }); 103 80 104 - writable.write(entry); 105 81 count++; 106 82 83 + if (count % 100 !== 0) { 84 + writable.write(entry); 85 + } else { 86 + await writable.write(entry); 87 + } 88 + 89 + progress.update(`Unpacking records (${count} entries)`); 90 + 107 91 if (yieldToScheduler !== undefined) { 108 92 await yieldToScheduler(); 109 93 } ··· 161 145 162 146 return ( 163 147 <> 164 - <div class="p-4"> 165 - <h1 class="text-lg font-bold text-purple-800">Unpack archive</h1> 166 - <p class="text-gray-600">Extract a repository archive into a tarball</p> 167 - </div> 168 - <hr class="mx-4 border-gray-300" /> 148 + <PageHeader title="Unpack archive" subtitle="Extract a repository archive into a tarball" /> 169 149 170 150 <div class="p-4"> 171 - <fieldset 172 - ref={dropRef} 151 + <FileDropZone 152 + accept=".car,application/vnd.ipld.car" 153 + dataTypes={['']} 173 154 disabled={pending()} 174 - class={ 175 - `grid place-items-center rounded border border-gray-300 px-6 py-12 disabled:opacity-50` + 176 - (pending() || !isDropping() ? ` bg-gray-100` : ` bg-green-100`) 177 - } 178 - > 179 - <div class="flex flex-col items-center gap-4"> 180 - <button 181 - onClick={() => { 182 - const input = document.createElement('input'); 183 - input.type = 'file'; 184 - input.accept = '.car,application/vnd.ipld.car'; 185 - input.oninput = () => onFileDrop(Array.from(input.files!)); 186 - 187 - input.click(); 188 - }} 189 - class="flex h-9 select-none items-center rounded border border-gray-400 px-4 text-sm font-semibold text-gray-800 hover:bg-gray-200 active:bg-gray-200 disabled:pointer-events-none" 190 - > 191 - Browse files 192 - </button> 193 - <p class="select-none font-medium text-gray-600">or drop your file here</p> 194 - </div> 195 - </fieldset> 155 + onFiles={onFileDrop} 156 + /> 196 157 </div> 197 158 <hr class="mx-4 border-gray-300" /> 198 159
+5 -27
src/views/repository/repo-export.tsx
··· 1 1 import { type FileSystemFileHandle, showSaveFilePicker } from 'native-file-system-adapter'; 2 2 import { createSignal } from 'solid-js'; 3 3 4 - import { type AtprotoDid, getPdsEndpoint, isAtprotoDid, isHandle } from '@atcute/identity'; 4 + import { getPdsEndpoint, isAtprotoDid } from '@atcute/identity'; 5 + import { type AtprotoDid, isHandle } from '@atcute/lexicons/syntax'; 5 6 6 7 import { getDidDocument } from '~/api/queries/did-doc'; 7 8 import { resolveHandleViaAppView, resolveHandleViaPds } from '~/api/queries/handle'; 8 9 import { isServiceUrlString } from '~/api/types/strings'; 9 - import { DID_OR_HANDLE_RE } from '~/api/utils/strings'; 10 10 11 11 import { useTitle } from '~/lib/navigation/router'; 12 12 import { makeAbortable } from '~/lib/utils/abortable'; 13 13 import { formatBytes } from '~/lib/utils/intl/bytes'; 14 + import { iterateStream } from '~/lib/utils/stream'; 14 15 15 16 import Button from '~/components/inputs/button'; 16 17 import TextInput from '~/components/inputs/text-input'; 17 18 import Logger, { createLogger } from '~/components/logger'; 19 + import PageHeader from '~/components/page-header'; 18 20 19 21 const RepoExportPage = () => { 20 22 const logger = createLogger(); ··· 135 137 136 138 return ( 137 139 <> 138 - <div class="p-4"> 139 - <h1 class="text-lg font-bold text-purple-800">Export repository</h1> 140 - <p class="text-gray-600">Download an archive of an account's repository</p> 141 - </div> 142 - <hr class="mx-4 border-gray-300" /> 140 + <PageHeader title="Export repository" subtitle="Download an archive of an account's repository" /> 143 141 144 142 <form 145 143 onSubmit={(ev) => { ··· 190 188 type="text" 191 189 name="ident" 192 190 autocomplete="username" 193 - pattern={/* @once */ DID_OR_HANDLE_RE.source} 194 191 placeholder="paul.bsky.social" 195 192 autofocus 196 193 /> ··· 223 220 }; 224 221 225 222 export default RepoExportPage; 226 - 227 - export async function* iterateStream<T>(stream: ReadableStream<T>) { 228 - // Get a lock on the stream 229 - const reader = stream.getReader(); 230 - 231 - try { 232 - while (true) { 233 - const { done, value } = await reader.read(); 234 - 235 - if (done) { 236 - return; 237 - } 238 - 239 - yield value; 240 - } 241 - } finally { 242 - reader.releaseLock(); 243 - } 244 - }
+2 -1
tsconfig.app.json
··· 3 3 "target": "ESNext", 4 4 "module": "ESNext", 5 5 "lib": ["ESNext", "DOM", "DOM.Iterable"], 6 - "types": [], 6 + "types": ["@atcute/atproto", "@atcute/bluesky"], 7 7 "skipLibCheck": true, 8 8 9 9 "moduleResolution": "Bundler", 10 10 "allowImportingTsExtensions": true, 11 11 "isolatedModules": true, 12 + "verbatimModuleSyntax": true, 12 13 "moduleDetection": "force", 13 14 "noEmit": true, 14 15
+9
wrangler.jsonc
··· 1 + { 2 + "$schema": "https://unpkg.com/wrangler@latest/config-schema.json", 3 + "name": "boat", 4 + "compatibility_date": "2025-10-05", 5 + "assets": { 6 + "directory": "dist", 7 + "not_found_handling": "single-page-application", 8 + }, 9 + }