+38
.github/workflows/sync-tangled.yaml
+38
.github/workflows/sync-tangled.yaml
···
1
+
name: sync-tangled
2
+
3
+
on:
4
+
push:
5
+
6
+
jobs:
7
+
sync:
8
+
name: ${{ matrix.name }}
9
+
runs-on: ubuntu-latest
10
+
steps:
11
+
- uses: actions/checkout@v4.1.7
12
+
with:
13
+
fetch-depth: 0
14
+
ref: ${{ github.event.pull_request.head.sha }}
15
+
- name: sync tangled
16
+
env:
17
+
TANGLED_SSH_KEY: ${{ secrets.TANGLED_SSH_KEY }}
18
+
run: |
19
+
set -euo pipefail
20
+
# Turn off strict SSH key checking
21
+
mkdir -p ~/.ssh
22
+
echo "Host *
23
+
StrictHostKeyChecking no
24
+
UserKnownHostsFile=/dev/null" > ~/.ssh/config
25
+
26
+
# Write SSH key to disk
27
+
echo "$TANGLED_SSH_KEY" > ~/.ssh/tangled_key
28
+
chmod 600 ~/.ssh/tangled_key
29
+
30
+
# Configure SSH to use the key for tangled.sh
31
+
echo "Host tangled.sh
32
+
IdentityFile ~/.ssh/tangled_key" >> ~/.ssh/config
33
+
34
+
chmod 600 ~/.ssh/config
35
+
36
+
git remote add tangled git@tangled.sh:noob.quest/atproto-migrator
37
+
git push -f --all tangled
38
+
git push -f --tags tangled
+1
.nvmrc
+1
.nvmrc
···
1
+
v24.1.0
+4
-2
README.md
+4
-2
README.md
···
1
1
# atproto-migrator
2
-
basic (NON-FUNCTIONAL) web application that allows for an user to migrate their bluesky/at protocol account to another PDS without using command line tools.
2
+
basic (NON-FUNCTIONAL) web application that allows for an user to migrate their bluesky/at protocol account to another PDS without using command line tools, as well as some other repo stuff
3
3
4
4
[live version available here](https://atproto-migrator.pages.dev)
5
5
6
-
## install
6
+
## run for developing
7
7
8
8
```bash
9
9
npm install
···
15
15
```bash
16
16
npm run build
17
17
```
18
+
19
+
if you type ```ANALYZE=1``` you will receive a bundle analysis, useful for optimizing
18
20
19
21
open the `dist` directory and you're set
-28
eslint.config.js
-28
eslint.config.js
···
1
-
import js from '@eslint/js'
2
-
import globals from 'globals'
3
-
import reactHooks from 'eslint-plugin-react-hooks'
4
-
import reactRefresh from 'eslint-plugin-react-refresh'
5
-
import tseslint from 'typescript-eslint'
6
-
7
-
export default tseslint.config(
8
-
{ ignores: ['dist'] },
9
-
{
10
-
extends: [js.configs.recommended, ...tseslint.configs.recommended],
11
-
files: ['**/*.{ts,tsx}'],
12
-
languageOptions: {
13
-
ecmaVersion: 2020,
14
-
globals: globals.browser,
15
-
},
16
-
plugins: {
17
-
'react-hooks': reactHooks,
18
-
'react-refresh': reactRefresh,
19
-
},
20
-
rules: {
21
-
...reactHooks.configs.recommended.rules,
22
-
'react-refresh/only-export-components': [
23
-
'warn',
24
-
{ allowConstantExport: true },
25
-
],
26
-
},
27
-
},
28
-
)
+40
eslint.config.ts
+40
eslint.config.ts
···
1
+
import { FlatCompat } from '@eslint/eslintrc'
2
+
import js from '@eslint/js'
3
+
import * as path from 'node:path'
4
+
import { fileURLToPath } from 'node:url'
5
+
import tseslint from 'typescript-eslint'
6
+
7
+
const __filename = fileURLToPath(import.meta.url)
8
+
const __dirname = path.dirname(__filename)
9
+
10
+
const compat = new FlatCompat({
11
+
baseDirectory: __dirname,
12
+
recommendedConfig: js.configs.recommended
13
+
})
14
+
15
+
export default tseslint.config(
16
+
...compat.config({
17
+
root: true,
18
+
extends: [
19
+
'eslint:recommended',
20
+
'plugin:@typescript-eslint/recommended',
21
+
'plugin:react-hooks/recommended'
22
+
],
23
+
parser: '@typescript-eslint/parser',
24
+
parserOptions: {
25
+
project: ['./tsconfig.app.json'],
26
+
tsconfigRootDir: __dirname,
27
+
},
28
+
plugins: ['@typescript-eslint', 'react-refresh'],
29
+
rules: {
30
+
'react-refresh/only-export-components': [
31
+
'warn',
32
+
{ allowConstantExport: true }
33
+
],
34
+
'@typescript-eslint/no-deprecated': 'error'
35
+
}
36
+
}),
37
+
{
38
+
ignores: ['dist/**/*']
39
+
}
40
+
)
+2
-2
index.html
+2
-2
index.html
···
2
2
<html lang="en">
3
3
<head>
4
4
<meta charset="UTF-8" />
5
-
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
5
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
6
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
<title>ATproto Migrator</title>
8
8
</head>
···
41
41
<div id="root"></div>
42
42
<script type="module" src="/src/main.tsx"></script>
43
43
</body>
44
-
</html>
44
+
</html>
+893
-1309
package-lock.json
+893
-1309
package-lock.json
···
1
1
{
2
2
"name": "atproto-migration",
3
-
"version": "0.0.0",
3
+
"version": "0.1.0",
4
4
"lockfileVersion": 3,
5
5
"requires": true,
6
6
"packages": {
7
7
"": {
8
8
"name": "atproto-migration",
9
-
"version": "0.0.0",
9
+
"version": "0.1.0",
10
10
"dependencies": {
11
11
"@atproto/api": "^0.15.5",
12
+
"@atproto/crypto": "^0.4.4",
13
+
"multiformats": "^13.3.6",
12
14
"react": "^19.0.0",
13
15
"react-dom": "^19.0.0",
14
-
"react-router-dom": "^7.5.3"
16
+
"react-router-dom": "^7.5.3",
17
+
"uint8arrays": "^5.1.0"
15
18
},
16
19
"devDependencies": {
20
+
"@eslint/eslintrc": "^3.3.1",
17
21
"@eslint/js": "^9.22.0",
18
-
"@types/react": "^19.0.10",
19
-
"@types/react-dom": "^19.0.4",
22
+
"@humanwhocodes/module-importer": "^1.0.1",
23
+
"@types/node": "^22.15.14",
24
+
"@types/react": "^19.1.6",
25
+
"@types/react-dom": "^19.1.5",
26
+
"@typescript-eslint/eslint-plugin": "^8.33.0",
27
+
"@typescript-eslint/parser": "^8.32.0",
20
28
"@vitejs/plugin-react": "^4.3.4",
21
29
"eslint": "^9.22.0",
22
30
"eslint-plugin-react-hooks": "^5.2.0",
23
31
"eslint-plugin-react-refresh": "^0.4.19",
24
32
"globals": "^16.0.0",
33
+
"jiti": "^2.4.2",
34
+
"rollup-plugin-visualizer": "^6.0.1",
35
+
"terser": "^5.40.0",
25
36
"typescript": "~5.7.2",
26
37
"typescript-eslint": "^8.26.1",
27
38
"vite": "^6.3.1"
···
42
53
}
43
54
},
44
55
"node_modules/@atproto/api": {
45
-
"version": "0.15.5",
46
-
"resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.15.5.tgz",
47
-
"integrity": "sha512-GiKOrjSXMm8OSpc+pfjFTBYQGX62jmorECkTx2VZbS6KtFKFY0cRQAI+JnQoOLF/8TvzpaAZB7+it73uIqDM7A==",
56
+
"version": "0.15.10",
57
+
"resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.15.10.tgz",
58
+
"integrity": "sha512-/PsvYoYMA6VGAbMEOU2rOuaNQHkWPU6CVQAUDK2XRlIgFO2d21KEjZsZ4Z3lELvZlcw25fuMp7gLgFRijpk78w==",
48
59
"license": "MIT",
49
60
"dependencies": {
50
-
"@atproto/common-web": "^0.4.1",
51
-
"@atproto/lexicon": "^0.4.10",
61
+
"@atproto/common-web": "^0.4.2",
62
+
"@atproto/lexicon": "^0.4.11",
52
63
"@atproto/syntax": "^0.4.0",
53
-
"@atproto/xrpc": "^0.6.12",
64
+
"@atproto/xrpc": "^0.7.0",
54
65
"await-lock": "^2.2.2",
55
66
"multiformats": "^9.9.0",
56
67
"tlds": "^1.234.0",
57
68
"zod": "^3.23.8"
58
69
}
59
70
},
71
+
"node_modules/@atproto/api/node_modules/multiformats": {
72
+
"version": "9.9.0",
73
+
"resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz",
74
+
"integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==",
75
+
"license": "(Apache-2.0 AND MIT)"
76
+
},
60
77
"node_modules/@atproto/common-web": {
61
-
"version": "0.4.1",
62
-
"resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.1.tgz",
63
-
"integrity": "sha512-Ghh+djHYMAUCktLKwr2IuGgtjcwSWGudp+K7+N7KBA9pDDloOXUEY8Agjc5SHSo9B1QIEFkegClU5n+apn2e0w==",
78
+
"version": "0.4.2",
79
+
"resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.2.tgz",
80
+
"integrity": "sha512-vrXwGNoFGogodjQvJDxAeP3QbGtawgZute2ed1XdRO0wMixLk3qewtikZm06H259QDJVu6voKC5mubml+WgQUw==",
64
81
"license": "MIT",
65
82
"dependencies": {
66
83
"graphemer": "^1.4.0",
···
69
86
"zod": "^3.23.8"
70
87
}
71
88
},
89
+
"node_modules/@atproto/common-web/node_modules/multiformats": {
90
+
"version": "9.9.0",
91
+
"resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz",
92
+
"integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==",
93
+
"license": "(Apache-2.0 AND MIT)"
94
+
},
95
+
"node_modules/@atproto/common-web/node_modules/uint8arrays": {
96
+
"version": "3.0.0",
97
+
"resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz",
98
+
"integrity": "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==",
99
+
"license": "MIT",
100
+
"dependencies": {
101
+
"multiformats": "^9.4.2"
102
+
}
103
+
},
104
+
"node_modules/@atproto/crypto": {
105
+
"version": "0.4.4",
106
+
"resolved": "https://registry.npmjs.org/@atproto/crypto/-/crypto-0.4.4.tgz",
107
+
"integrity": "sha512-Yq9+crJ7WQl7sxStVpHgie5Z51R05etaK9DLWYG/7bR5T4bhdcIgF6IfklLShtZwLYdVVj+K15s0BqW9a8PSDA==",
108
+
"license": "MIT",
109
+
"dependencies": {
110
+
"@noble/curves": "^1.7.0",
111
+
"@noble/hashes": "^1.6.1",
112
+
"uint8arrays": "3.0.0"
113
+
},
114
+
"engines": {
115
+
"node": ">=18.7.0"
116
+
}
117
+
},
118
+
"node_modules/@atproto/crypto/node_modules/multiformats": {
119
+
"version": "9.9.0",
120
+
"resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz",
121
+
"integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==",
122
+
"license": "(Apache-2.0 AND MIT)"
123
+
},
124
+
"node_modules/@atproto/crypto/node_modules/uint8arrays": {
125
+
"version": "3.0.0",
126
+
"resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz",
127
+
"integrity": "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==",
128
+
"license": "MIT",
129
+
"dependencies": {
130
+
"multiformats": "^9.4.2"
131
+
}
132
+
},
72
133
"node_modules/@atproto/lexicon": {
73
-
"version": "0.4.10",
74
-
"resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.4.10.tgz",
75
-
"integrity": "sha512-uDbP20vetBgtXPuxoyRcvOGBt2gNe1dFc9yYKcb6jWmXfseHiGTnIlORJOLBXIT2Pz15Eap4fLxAu6zFAykD5A==",
134
+
"version": "0.4.11",
135
+
"resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.4.11.tgz",
136
+
"integrity": "sha512-btefdnvNz2Ao2I+qbmj0F06HC8IlrM/IBz6qOBS50r0S6uDf5tOO+Mv2tSVdimFkdzyDdLtBI1sV36ONxz2cOw==",
76
137
"license": "MIT",
77
138
"dependencies": {
78
-
"@atproto/common-web": "^0.4.1",
139
+
"@atproto/common-web": "^0.4.2",
79
140
"@atproto/syntax": "^0.4.0",
80
141
"iso-datestring-validator": "^2.2.2",
81
142
"multiformats": "^9.9.0",
82
143
"zod": "^3.23.8"
83
144
}
84
145
},
146
+
"node_modules/@atproto/lexicon/node_modules/multiformats": {
147
+
"version": "9.9.0",
148
+
"resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz",
149
+
"integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==",
150
+
"license": "(Apache-2.0 AND MIT)"
151
+
},
85
152
"node_modules/@atproto/syntax": {
86
153
"version": "0.4.0",
87
154
"resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.0.tgz",
···
89
156
"license": "MIT"
90
157
},
91
158
"node_modules/@atproto/xrpc": {
92
-
"version": "0.6.12",
93
-
"resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.6.12.tgz",
94
-
"integrity": "sha512-Ut3iISNLujlmY9Gu8sNU+SPDJDvqlVzWddU8qUr0Yae5oD4SguaUFjjhireMGhQ3M5E0KljQgDbTmnBo1kIZ3w==",
159
+
"version": "0.7.0",
160
+
"resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.0.tgz",
161
+
"integrity": "sha512-SfhP9dGx2qclaScFDb58Jnrmim5nk4geZXCqg6sB0I/KZhZEkr9iIx1hLCp+sxkIfEsmEJjeWO4B0rjUIJW5cw==",
95
162
"license": "MIT",
96
163
"dependencies": {
97
-
"@atproto/lexicon": "^0.4.10",
164
+
"@atproto/lexicon": "^0.4.11",
98
165
"zod": "^3.23.8"
99
166
}
100
167
},
···
114
181
}
115
182
},
116
183
"node_modules/@babel/compat-data": {
117
-
"version": "7.27.1",
118
-
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.1.tgz",
119
-
"integrity": "sha512-Q+E+rd/yBzNQhXkG+zQnF58e4zoZfBedaxwzPmicKsiK3nt8iJYrSrDbjwFFDGC4f+rPafqRaPH6TsDoSvMf7A==",
184
+
"version": "7.27.3",
185
+
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.3.tgz",
186
+
"integrity": "sha512-V42wFfx1ymFte+ecf6iXghnnP8kWTO+ZLXIyZq+1LAXHHvTZdVxicn4yiVYdYMGaCO3tmqub11AorKkv+iodqw==",
120
187
"dev": true,
121
188
"license": "MIT",
122
189
"engines": {
···
124
191
}
125
192
},
126
193
"node_modules/@babel/core": {
127
-
"version": "7.27.1",
128
-
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz",
129
-
"integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==",
194
+
"version": "7.27.3",
195
+
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.3.tgz",
196
+
"integrity": "sha512-hyrN8ivxfvJ4i0fIJuV4EOlV0WDMz5Ui4StRTgVaAvWeiRCilXgwVvxJKtFQ3TKtHgJscB2YiXKGNJuVwhQMtA==",
130
197
"dev": true,
131
198
"license": "MIT",
132
199
"dependencies": {
133
200
"@ampproject/remapping": "^2.2.0",
134
201
"@babel/code-frame": "^7.27.1",
135
-
"@babel/generator": "^7.27.1",
136
-
"@babel/helper-compilation-targets": "^7.27.1",
137
-
"@babel/helper-module-transforms": "^7.27.1",
138
-
"@babel/helpers": "^7.27.1",
139
-
"@babel/parser": "^7.27.1",
140
-
"@babel/template": "^7.27.1",
141
-
"@babel/traverse": "^7.27.1",
142
-
"@babel/types": "^7.27.1",
202
+
"@babel/generator": "^7.27.3",
203
+
"@babel/helper-compilation-targets": "^7.27.2",
204
+
"@babel/helper-module-transforms": "^7.27.3",
205
+
"@babel/helpers": "^7.27.3",
206
+
"@babel/parser": "^7.27.3",
207
+
"@babel/template": "^7.27.2",
208
+
"@babel/traverse": "^7.27.3",
209
+
"@babel/types": "^7.27.3",
143
210
"convert-source-map": "^2.0.0",
144
211
"debug": "^4.1.0",
145
212
"gensync": "^1.0.0-beta.2",
···
154
221
"url": "https://opencollective.com/babel"
155
222
}
156
223
},
224
+
"node_modules/@babel/core/node_modules/semver": {
225
+
"version": "6.3.1",
226
+
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
227
+
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
228
+
"dev": true,
229
+
"license": "ISC",
230
+
"bin": {
231
+
"semver": "bin/semver.js"
232
+
}
233
+
},
157
234
"node_modules/@babel/generator": {
158
-
"version": "7.27.1",
159
-
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz",
160
-
"integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==",
235
+
"version": "7.27.3",
236
+
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.3.tgz",
237
+
"integrity": "sha512-xnlJYj5zepml8NXtjkG0WquFUv8RskFqyFcVgTBp5k+NaA/8uw/K+OSVf8AMGw5e9HKP2ETd5xpK5MLZQD6b4Q==",
161
238
"dev": true,
162
239
"license": "MIT",
163
240
"dependencies": {
164
-
"@babel/parser": "^7.27.1",
165
-
"@babel/types": "^7.27.1",
241
+
"@babel/parser": "^7.27.3",
242
+
"@babel/types": "^7.27.3",
166
243
"@jridgewell/gen-mapping": "^0.3.5",
167
244
"@jridgewell/trace-mapping": "^0.3.25",
168
245
"jsesc": "^3.0.2"
···
172
249
}
173
250
},
174
251
"node_modules/@babel/helper-compilation-targets": {
175
-
"version": "7.27.1",
176
-
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.1.tgz",
177
-
"integrity": "sha512-2YaDd/Rd9E598B5+WIc8wJPmWETiiJXFYVE60oX8FDohv7rAUU3CQj+A1MgeEmcsk2+dQuEjIe/GDvig0SqL4g==",
252
+
"version": "7.27.2",
253
+
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
254
+
"integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
178
255
"dev": true,
179
256
"license": "MIT",
180
257
"dependencies": {
181
-
"@babel/compat-data": "^7.27.1",
258
+
"@babel/compat-data": "^7.27.2",
182
259
"@babel/helper-validator-option": "^7.27.1",
183
260
"browserslist": "^4.24.0",
184
261
"lru-cache": "^5.1.1",
···
188
265
"node": ">=6.9.0"
189
266
}
190
267
},
268
+
"node_modules/@babel/helper-compilation-targets/node_modules/semver": {
269
+
"version": "6.3.1",
270
+
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
271
+
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
272
+
"dev": true,
273
+
"license": "ISC",
274
+
"bin": {
275
+
"semver": "bin/semver.js"
276
+
}
277
+
},
191
278
"node_modules/@babel/helper-module-imports": {
192
279
"version": "7.27.1",
193
280
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
···
203
290
}
204
291
},
205
292
"node_modules/@babel/helper-module-transforms": {
206
-
"version": "7.27.1",
207
-
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz",
208
-
"integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==",
293
+
"version": "7.27.3",
294
+
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz",
295
+
"integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==",
209
296
"dev": true,
210
297
"license": "MIT",
211
298
"dependencies": {
212
299
"@babel/helper-module-imports": "^7.27.1",
213
300
"@babel/helper-validator-identifier": "^7.27.1",
214
-
"@babel/traverse": "^7.27.1"
301
+
"@babel/traverse": "^7.27.3"
215
302
},
216
303
"engines": {
217
304
"node": ">=6.9.0"
···
261
348
}
262
349
},
263
350
"node_modules/@babel/helpers": {
264
-
"version": "7.27.1",
265
-
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz",
266
-
"integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==",
351
+
"version": "7.27.3",
352
+
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.3.tgz",
353
+
"integrity": "sha512-h/eKy9agOya1IGuLaZ9tEUgz+uIRXcbtOhRtUyyMf8JFmn1iT13vnl/IGVWSkdOCG/pC57U4S1jnAabAavTMwg==",
267
354
"dev": true,
268
355
"license": "MIT",
269
356
"dependencies": {
270
-
"@babel/template": "^7.27.1",
271
-
"@babel/types": "^7.27.1"
357
+
"@babel/template": "^7.27.2",
358
+
"@babel/types": "^7.27.3"
272
359
},
273
360
"engines": {
274
361
"node": ">=6.9.0"
275
362
}
276
363
},
277
364
"node_modules/@babel/parser": {
278
-
"version": "7.27.1",
279
-
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.1.tgz",
280
-
"integrity": "sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ==",
365
+
"version": "7.27.3",
366
+
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.3.tgz",
367
+
"integrity": "sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw==",
281
368
"dev": true,
282
369
"license": "MIT",
283
370
"dependencies": {
284
-
"@babel/types": "^7.27.1"
371
+
"@babel/types": "^7.27.3"
285
372
},
286
373
"bin": {
287
374
"parser": "bin/babel-parser.js"
···
323
410
}
324
411
},
325
412
"node_modules/@babel/template": {
326
-
"version": "7.27.1",
327
-
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.1.tgz",
328
-
"integrity": "sha512-Fyo3ghWMqkHHpHQCoBs2VnYjR4iWFFjguTDEqA5WgZDOrFesVjMhMM2FSqTKSoUSDO1VQtavj8NFpdRBEvJTtg==",
413
+
"version": "7.27.2",
414
+
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
415
+
"integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
329
416
"dev": true,
330
417
"license": "MIT",
331
418
"dependencies": {
332
419
"@babel/code-frame": "^7.27.1",
333
-
"@babel/parser": "^7.27.1",
420
+
"@babel/parser": "^7.27.2",
334
421
"@babel/types": "^7.27.1"
335
422
},
336
423
"engines": {
···
338
425
}
339
426
},
340
427
"node_modules/@babel/traverse": {
341
-
"version": "7.27.1",
342
-
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz",
343
-
"integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==",
428
+
"version": "7.27.3",
429
+
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.3.tgz",
430
+
"integrity": "sha512-lId/IfN/Ye1CIu8xG7oKBHXd2iNb2aW1ilPszzGcJug6M8RCKfVNcYhpI5+bMvFYjK7lXIM0R+a+6r8xhHp2FQ==",
344
431
"dev": true,
345
432
"license": "MIT",
346
433
"dependencies": {
347
434
"@babel/code-frame": "^7.27.1",
348
-
"@babel/generator": "^7.27.1",
349
-
"@babel/parser": "^7.27.1",
350
-
"@babel/template": "^7.27.1",
351
-
"@babel/types": "^7.27.1",
435
+
"@babel/generator": "^7.27.3",
436
+
"@babel/parser": "^7.27.3",
437
+
"@babel/template": "^7.27.2",
438
+
"@babel/types": "^7.27.3",
352
439
"debug": "^4.3.1",
353
440
"globals": "^11.1.0"
354
441
},
···
367
454
}
368
455
},
369
456
"node_modules/@babel/types": {
370
-
"version": "7.27.1",
371
-
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz",
372
-
"integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==",
457
+
"version": "7.27.3",
458
+
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.3.tgz",
459
+
"integrity": "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==",
373
460
"dev": true,
374
461
"license": "MIT",
375
462
"dependencies": {
···
381
468
}
382
469
},
383
470
"node_modules/@esbuild/aix-ppc64": {
384
-
"version": "0.25.3",
385
-
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz",
386
-
"integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==",
471
+
"version": "0.25.5",
472
+
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
473
+
"integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
387
474
"cpu": [
388
475
"ppc64"
389
476
],
···
398
485
}
399
486
},
400
487
"node_modules/@esbuild/android-arm": {
401
-
"version": "0.25.3",
402
-
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz",
403
-
"integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==",
488
+
"version": "0.25.5",
489
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
490
+
"integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
404
491
"cpu": [
405
492
"arm"
406
493
],
···
415
502
}
416
503
},
417
504
"node_modules/@esbuild/android-arm64": {
418
-
"version": "0.25.3",
419
-
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz",
420
-
"integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==",
505
+
"version": "0.25.5",
506
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz",
507
+
"integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
421
508
"cpu": [
422
509
"arm64"
423
510
],
···
432
519
}
433
520
},
434
521
"node_modules/@esbuild/android-x64": {
435
-
"version": "0.25.3",
436
-
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz",
437
-
"integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==",
522
+
"version": "0.25.5",
523
+
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz",
524
+
"integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
438
525
"cpu": [
439
526
"x64"
440
527
],
···
449
536
}
450
537
},
451
538
"node_modules/@esbuild/darwin-arm64": {
452
-
"version": "0.25.3",
453
-
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz",
454
-
"integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==",
539
+
"version": "0.25.5",
540
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
541
+
"integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
455
542
"cpu": [
456
543
"arm64"
457
544
],
···
466
553
}
467
554
},
468
555
"node_modules/@esbuild/darwin-x64": {
469
-
"version": "0.25.3",
470
-
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz",
471
-
"integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==",
556
+
"version": "0.25.5",
557
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz",
558
+
"integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
472
559
"cpu": [
473
560
"x64"
474
561
],
···
483
570
}
484
571
},
485
572
"node_modules/@esbuild/freebsd-arm64": {
486
-
"version": "0.25.3",
487
-
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz",
488
-
"integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==",
573
+
"version": "0.25.5",
574
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz",
575
+
"integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
489
576
"cpu": [
490
577
"arm64"
491
578
],
···
500
587
}
501
588
},
502
589
"node_modules/@esbuild/freebsd-x64": {
503
-
"version": "0.25.3",
504
-
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz",
505
-
"integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==",
590
+
"version": "0.25.5",
591
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz",
592
+
"integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
506
593
"cpu": [
507
594
"x64"
508
595
],
···
517
604
}
518
605
},
519
606
"node_modules/@esbuild/linux-arm": {
520
-
"version": "0.25.3",
521
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz",
522
-
"integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==",
607
+
"version": "0.25.5",
608
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
609
+
"integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
523
610
"cpu": [
524
611
"arm"
525
612
],
···
534
621
}
535
622
},
536
623
"node_modules/@esbuild/linux-arm64": {
537
-
"version": "0.25.3",
538
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz",
539
-
"integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==",
624
+
"version": "0.25.5",
625
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz",
626
+
"integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
540
627
"cpu": [
541
628
"arm64"
542
629
],
···
551
638
}
552
639
},
553
640
"node_modules/@esbuild/linux-ia32": {
554
-
"version": "0.25.3",
555
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz",
556
-
"integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==",
641
+
"version": "0.25.5",
642
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz",
643
+
"integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
557
644
"cpu": [
558
645
"ia32"
559
646
],
···
568
655
}
569
656
},
570
657
"node_modules/@esbuild/linux-loong64": {
571
-
"version": "0.25.3",
572
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz",
573
-
"integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==",
658
+
"version": "0.25.5",
659
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
660
+
"integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
574
661
"cpu": [
575
662
"loong64"
576
663
],
···
585
672
}
586
673
},
587
674
"node_modules/@esbuild/linux-mips64el": {
588
-
"version": "0.25.3",
589
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz",
590
-
"integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==",
675
+
"version": "0.25.5",
676
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz",
677
+
"integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
591
678
"cpu": [
592
679
"mips64el"
593
680
],
···
602
689
}
603
690
},
604
691
"node_modules/@esbuild/linux-ppc64": {
605
-
"version": "0.25.3",
606
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz",
607
-
"integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==",
692
+
"version": "0.25.5",
693
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz",
694
+
"integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
608
695
"cpu": [
609
696
"ppc64"
610
697
],
···
619
706
}
620
707
},
621
708
"node_modules/@esbuild/linux-riscv64": {
622
-
"version": "0.25.3",
623
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz",
624
-
"integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==",
709
+
"version": "0.25.5",
710
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz",
711
+
"integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
625
712
"cpu": [
626
713
"riscv64"
627
714
],
···
636
723
}
637
724
},
638
725
"node_modules/@esbuild/linux-s390x": {
639
-
"version": "0.25.3",
640
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz",
641
-
"integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==",
726
+
"version": "0.25.5",
727
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
728
+
"integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
642
729
"cpu": [
643
730
"s390x"
644
731
],
···
653
740
}
654
741
},
655
742
"node_modules/@esbuild/linux-x64": {
656
-
"version": "0.25.3",
657
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz",
658
-
"integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==",
743
+
"version": "0.25.5",
744
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
745
+
"integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
659
746
"cpu": [
660
747
"x64"
661
748
],
···
670
757
}
671
758
},
672
759
"node_modules/@esbuild/netbsd-arm64": {
673
-
"version": "0.25.3",
674
-
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz",
675
-
"integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==",
760
+
"version": "0.25.5",
761
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz",
762
+
"integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
676
763
"cpu": [
677
764
"arm64"
678
765
],
···
687
774
}
688
775
},
689
776
"node_modules/@esbuild/netbsd-x64": {
690
-
"version": "0.25.3",
691
-
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz",
692
-
"integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==",
777
+
"version": "0.25.5",
778
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz",
779
+
"integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
693
780
"cpu": [
694
781
"x64"
695
782
],
···
704
791
}
705
792
},
706
793
"node_modules/@esbuild/openbsd-arm64": {
707
-
"version": "0.25.3",
708
-
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz",
709
-
"integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==",
794
+
"version": "0.25.5",
795
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz",
796
+
"integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
710
797
"cpu": [
711
798
"arm64"
712
799
],
···
721
808
}
722
809
},
723
810
"node_modules/@esbuild/openbsd-x64": {
724
-
"version": "0.25.3",
725
-
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz",
726
-
"integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==",
811
+
"version": "0.25.5",
812
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz",
813
+
"integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
727
814
"cpu": [
728
815
"x64"
729
816
],
···
738
825
}
739
826
},
740
827
"node_modules/@esbuild/sunos-x64": {
741
-
"version": "0.25.3",
742
-
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz",
743
-
"integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==",
828
+
"version": "0.25.5",
829
+
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz",
830
+
"integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
744
831
"cpu": [
745
832
"x64"
746
833
],
···
755
842
}
756
843
},
757
844
"node_modules/@esbuild/win32-arm64": {
758
-
"version": "0.25.3",
759
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz",
760
-
"integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==",
845
+
"version": "0.25.5",
846
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz",
847
+
"integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
761
848
"cpu": [
762
849
"arm64"
763
850
],
···
772
859
}
773
860
},
774
861
"node_modules/@esbuild/win32-ia32": {
775
-
"version": "0.25.3",
776
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz",
777
-
"integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==",
862
+
"version": "0.25.5",
863
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
864
+
"integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
778
865
"cpu": [
779
866
"ia32"
780
867
],
···
789
876
}
790
877
},
791
878
"node_modules/@esbuild/win32-x64": {
792
-
"version": "0.25.3",
793
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz",
794
-
"integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==",
879
+
"version": "0.25.5",
880
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz",
881
+
"integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
795
882
"cpu": [
796
883
"x64"
797
884
],
···
824
911
"eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
825
912
}
826
913
},
827
-
"node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
828
-
"version": "3.4.3",
829
-
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
830
-
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
831
-
"dev": true,
832
-
"license": "Apache-2.0",
833
-
"engines": {
834
-
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
835
-
},
836
-
"funding": {
837
-
"url": "https://opencollective.com/eslint"
838
-
}
839
-
},
840
914
"node_modules/@eslint-community/regexpp": {
841
915
"version": "4.12.1",
842
916
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
···
873
947
}
874
948
},
875
949
"node_modules/@eslint/core": {
876
-
"version": "0.13.0",
877
-
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
878
-
"integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
950
+
"version": "0.14.0",
951
+
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz",
952
+
"integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==",
879
953
"dev": true,
880
954
"license": "Apache-2.0",
881
955
"dependencies": {
···
923
997
}
924
998
},
925
999
"node_modules/@eslint/js": {
926
-
"version": "9.26.0",
927
-
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.26.0.tgz",
928
-
"integrity": "sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==",
1000
+
"version": "9.27.0",
1001
+
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz",
1002
+
"integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==",
929
1003
"dev": true,
930
1004
"license": "MIT",
931
1005
"engines": {
932
1006
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1007
+
},
1008
+
"funding": {
1009
+
"url": "https://eslint.org/donate"
933
1010
}
934
1011
},
935
1012
"node_modules/@eslint/object-schema": {
···
943
1020
}
944
1021
},
945
1022
"node_modules/@eslint/plugin-kit": {
946
-
"version": "0.2.8",
947
-
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz",
948
-
"integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==",
1023
+
"version": "0.3.1",
1024
+
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz",
1025
+
"integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==",
949
1026
"dev": true,
950
1027
"license": "Apache-2.0",
951
1028
"dependencies": {
952
-
"@eslint/core": "^0.13.0",
1029
+
"@eslint/core": "^0.14.0",
953
1030
"levn": "^0.4.1"
954
1031
},
955
1032
"engines": {
···
1009
1086
}
1010
1087
},
1011
1088
"node_modules/@humanwhocodes/retry": {
1012
-
"version": "0.4.2",
1013
-
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz",
1014
-
"integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==",
1089
+
"version": "0.4.3",
1090
+
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
1091
+
"integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
1015
1092
"dev": true,
1016
1093
"license": "Apache-2.0",
1017
1094
"engines": {
···
1057
1134
"node": ">=6.0.0"
1058
1135
}
1059
1136
},
1137
+
"node_modules/@jridgewell/source-map": {
1138
+
"version": "0.3.6",
1139
+
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
1140
+
"integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
1141
+
"dev": true,
1142
+
"license": "MIT",
1143
+
"dependencies": {
1144
+
"@jridgewell/gen-mapping": "^0.3.5",
1145
+
"@jridgewell/trace-mapping": "^0.3.25"
1146
+
}
1147
+
},
1060
1148
"node_modules/@jridgewell/sourcemap-codec": {
1061
1149
"version": "1.5.0",
1062
1150
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
···
1075
1163
"@jridgewell/sourcemap-codec": "^1.4.14"
1076
1164
}
1077
1165
},
1078
-
"node_modules/@modelcontextprotocol/sdk": {
1079
-
"version": "1.11.0",
1080
-
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.11.0.tgz",
1081
-
"integrity": "sha512-k/1pb70eD638anoi0e8wUGAlbMJXyvdV4p62Ko+EZ7eBe1xMx8Uhak1R5DgfoofsK5IBBnRwsYGTaLZl+6/+RQ==",
1082
-
"dev": true,
1166
+
"node_modules/@noble/curves": {
1167
+
"version": "1.9.1",
1168
+
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz",
1169
+
"integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==",
1083
1170
"license": "MIT",
1084
1171
"dependencies": {
1085
-
"content-type": "^1.0.5",
1086
-
"cors": "^2.8.5",
1087
-
"cross-spawn": "^7.0.3",
1088
-
"eventsource": "^3.0.2",
1089
-
"express": "^5.0.1",
1090
-
"express-rate-limit": "^7.5.0",
1091
-
"pkce-challenge": "^5.0.0",
1092
-
"raw-body": "^3.0.0",
1093
-
"zod": "^3.23.8",
1094
-
"zod-to-json-schema": "^3.24.1"
1172
+
"@noble/hashes": "1.8.0"
1173
+
},
1174
+
"engines": {
1175
+
"node": "^14.21.3 || >=16"
1095
1176
},
1177
+
"funding": {
1178
+
"url": "https://paulmillr.com/funding/"
1179
+
}
1180
+
},
1181
+
"node_modules/@noble/hashes": {
1182
+
"version": "1.8.0",
1183
+
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
1184
+
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
1185
+
"license": "MIT",
1096
1186
"engines": {
1097
-
"node": ">=18"
1187
+
"node": "^14.21.3 || >=16"
1188
+
},
1189
+
"funding": {
1190
+
"url": "https://paulmillr.com/funding/"
1098
1191
}
1099
1192
},
1100
1193
"node_modules/@nodelib/fs.scandir": {
···
1135
1228
"node": ">= 8"
1136
1229
}
1137
1230
},
1231
+
"node_modules/@rolldown/pluginutils": {
1232
+
"version": "1.0.0-beta.9",
1233
+
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.9.tgz",
1234
+
"integrity": "sha512-e9MeMtVWo186sgvFFJOPGy7/d2j2mZhLJIdVW0C/xDluuOvymEATqz6zKsP0ZmXGzQtqlyjz5sC1sYQUoJG98w==",
1235
+
"dev": true,
1236
+
"license": "MIT"
1237
+
},
1138
1238
"node_modules/@rollup/rollup-android-arm-eabi": {
1139
-
"version": "4.40.1",
1140
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.1.tgz",
1141
-
"integrity": "sha512-kxz0YeeCrRUHz3zyqvd7n+TVRlNyTifBsmnmNPtk3hQURUyG9eAB+usz6DAwagMusjx/zb3AjvDUvhFGDAexGw==",
1239
+
"version": "4.41.1",
1240
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.1.tgz",
1241
+
"integrity": "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==",
1142
1242
"cpu": [
1143
1243
"arm"
1144
1244
],
···
1150
1250
]
1151
1251
},
1152
1252
"node_modules/@rollup/rollup-android-arm64": {
1153
-
"version": "4.40.1",
1154
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.1.tgz",
1155
-
"integrity": "sha512-PPkxTOisoNC6TpnDKatjKkjRMsdaWIhyuMkA4UsBXT9WEZY4uHezBTjs6Vl4PbqQQeu6oION1w2voYZv9yquCw==",
1253
+
"version": "4.41.1",
1254
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.1.tgz",
1255
+
"integrity": "sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==",
1156
1256
"cpu": [
1157
1257
"arm64"
1158
1258
],
···
1164
1264
]
1165
1265
},
1166
1266
"node_modules/@rollup/rollup-darwin-arm64": {
1167
-
"version": "4.40.1",
1168
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.1.tgz",
1169
-
"integrity": "sha512-VWXGISWFY18v/0JyNUy4A46KCFCb9NVsH+1100XP31lud+TzlezBbz24CYzbnA4x6w4hx+NYCXDfnvDVO6lcAA==",
1267
+
"version": "4.41.1",
1268
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.1.tgz",
1269
+
"integrity": "sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==",
1170
1270
"cpu": [
1171
1271
"arm64"
1172
1272
],
···
1178
1278
]
1179
1279
},
1180
1280
"node_modules/@rollup/rollup-darwin-x64": {
1181
-
"version": "4.40.1",
1182
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.1.tgz",
1183
-
"integrity": "sha512-nIwkXafAI1/QCS7pxSpv/ZtFW6TXcNUEHAIA9EIyw5OzxJZQ1YDrX+CL6JAIQgZ33CInl1R6mHet9Y/UZTg2Bw==",
1281
+
"version": "4.41.1",
1282
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.1.tgz",
1283
+
"integrity": "sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==",
1184
1284
"cpu": [
1185
1285
"x64"
1186
1286
],
···
1192
1292
]
1193
1293
},
1194
1294
"node_modules/@rollup/rollup-freebsd-arm64": {
1195
-
"version": "4.40.1",
1196
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.1.tgz",
1197
-
"integrity": "sha512-BdrLJ2mHTrIYdaS2I99mriyJfGGenSaP+UwGi1kB9BLOCu9SR8ZpbkmmalKIALnRw24kM7qCN0IOm6L0S44iWw==",
1295
+
"version": "4.41.1",
1296
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.1.tgz",
1297
+
"integrity": "sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==",
1198
1298
"cpu": [
1199
1299
"arm64"
1200
1300
],
···
1206
1306
]
1207
1307
},
1208
1308
"node_modules/@rollup/rollup-freebsd-x64": {
1209
-
"version": "4.40.1",
1210
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.1.tgz",
1211
-
"integrity": "sha512-VXeo/puqvCG8JBPNZXZf5Dqq7BzElNJzHRRw3vjBE27WujdzuOPecDPc/+1DcdcTptNBep3861jNq0mYkT8Z6Q==",
1309
+
"version": "4.41.1",
1310
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.1.tgz",
1311
+
"integrity": "sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==",
1212
1312
"cpu": [
1213
1313
"x64"
1214
1314
],
···
1220
1320
]
1221
1321
},
1222
1322
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
1223
-
"version": "4.40.1",
1224
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.1.tgz",
1225
-
"integrity": "sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg==",
1323
+
"version": "4.41.1",
1324
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.1.tgz",
1325
+
"integrity": "sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==",
1226
1326
"cpu": [
1227
1327
"arm"
1228
1328
],
···
1234
1334
]
1235
1335
},
1236
1336
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
1237
-
"version": "4.40.1",
1238
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.1.tgz",
1239
-
"integrity": "sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg==",
1337
+
"version": "4.41.1",
1338
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.1.tgz",
1339
+
"integrity": "sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==",
1240
1340
"cpu": [
1241
1341
"arm"
1242
1342
],
···
1248
1348
]
1249
1349
},
1250
1350
"node_modules/@rollup/rollup-linux-arm64-gnu": {
1251
-
"version": "4.40.1",
1252
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.1.tgz",
1253
-
"integrity": "sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg==",
1351
+
"version": "4.41.1",
1352
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.1.tgz",
1353
+
"integrity": "sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==",
1254
1354
"cpu": [
1255
1355
"arm64"
1256
1356
],
···
1262
1362
]
1263
1363
},
1264
1364
"node_modules/@rollup/rollup-linux-arm64-musl": {
1265
-
"version": "4.40.1",
1266
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.1.tgz",
1267
-
"integrity": "sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ==",
1365
+
"version": "4.41.1",
1366
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.1.tgz",
1367
+
"integrity": "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==",
1268
1368
"cpu": [
1269
1369
"arm64"
1270
1370
],
···
1276
1376
]
1277
1377
},
1278
1378
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
1279
-
"version": "4.40.1",
1280
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.1.tgz",
1281
-
"integrity": "sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ==",
1379
+
"version": "4.41.1",
1380
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.1.tgz",
1381
+
"integrity": "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==",
1282
1382
"cpu": [
1283
1383
"loong64"
1284
1384
],
···
1290
1390
]
1291
1391
},
1292
1392
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
1293
-
"version": "4.40.1",
1294
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.1.tgz",
1295
-
"integrity": "sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg==",
1393
+
"version": "4.41.1",
1394
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.1.tgz",
1395
+
"integrity": "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==",
1296
1396
"cpu": [
1297
1397
"ppc64"
1298
1398
],
···
1304
1404
]
1305
1405
},
1306
1406
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
1307
-
"version": "4.40.1",
1308
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.1.tgz",
1309
-
"integrity": "sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ==",
1407
+
"version": "4.41.1",
1408
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.1.tgz",
1409
+
"integrity": "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==",
1310
1410
"cpu": [
1311
1411
"riscv64"
1312
1412
],
···
1318
1418
]
1319
1419
},
1320
1420
"node_modules/@rollup/rollup-linux-riscv64-musl": {
1321
-
"version": "4.40.1",
1322
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.1.tgz",
1323
-
"integrity": "sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA==",
1421
+
"version": "4.41.1",
1422
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.1.tgz",
1423
+
"integrity": "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==",
1324
1424
"cpu": [
1325
1425
"riscv64"
1326
1426
],
···
1332
1432
]
1333
1433
},
1334
1434
"node_modules/@rollup/rollup-linux-s390x-gnu": {
1335
-
"version": "4.40.1",
1336
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.1.tgz",
1337
-
"integrity": "sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg==",
1435
+
"version": "4.41.1",
1436
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.1.tgz",
1437
+
"integrity": "sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==",
1338
1438
"cpu": [
1339
1439
"s390x"
1340
1440
],
···
1346
1446
]
1347
1447
},
1348
1448
"node_modules/@rollup/rollup-linux-x64-gnu": {
1349
-
"version": "4.40.1",
1350
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.1.tgz",
1351
-
"integrity": "sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ==",
1449
+
"version": "4.41.1",
1450
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.1.tgz",
1451
+
"integrity": "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==",
1352
1452
"cpu": [
1353
1453
"x64"
1354
1454
],
···
1360
1460
]
1361
1461
},
1362
1462
"node_modules/@rollup/rollup-linux-x64-musl": {
1363
-
"version": "4.40.1",
1364
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.1.tgz",
1365
-
"integrity": "sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ==",
1463
+
"version": "4.41.1",
1464
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.1.tgz",
1465
+
"integrity": "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==",
1366
1466
"cpu": [
1367
1467
"x64"
1368
1468
],
···
1374
1474
]
1375
1475
},
1376
1476
"node_modules/@rollup/rollup-win32-arm64-msvc": {
1377
-
"version": "4.40.1",
1378
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.1.tgz",
1379
-
"integrity": "sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg==",
1477
+
"version": "4.41.1",
1478
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.1.tgz",
1479
+
"integrity": "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==",
1380
1480
"cpu": [
1381
1481
"arm64"
1382
1482
],
···
1388
1488
]
1389
1489
},
1390
1490
"node_modules/@rollup/rollup-win32-ia32-msvc": {
1391
-
"version": "4.40.1",
1392
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.1.tgz",
1393
-
"integrity": "sha512-DfcogW8N7Zg7llVEfpqWMZcaErKfsj9VvmfSyRjCyo4BI3wPEfrzTtJkZG6gKP/Z92wFm6rz2aDO7/JfiR/whA==",
1491
+
"version": "4.41.1",
1492
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.1.tgz",
1493
+
"integrity": "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==",
1394
1494
"cpu": [
1395
1495
"ia32"
1396
1496
],
···
1402
1502
]
1403
1503
},
1404
1504
"node_modules/@rollup/rollup-win32-x64-msvc": {
1405
-
"version": "4.40.1",
1406
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.1.tgz",
1407
-
"integrity": "sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA==",
1505
+
"version": "4.41.1",
1506
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.1.tgz",
1507
+
"integrity": "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==",
1408
1508
"cpu": [
1409
1509
"x64"
1410
1510
],
···
1474
1574
"dev": true,
1475
1575
"license": "MIT"
1476
1576
},
1577
+
"node_modules/@types/node": {
1578
+
"version": "22.15.23",
1579
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.23.tgz",
1580
+
"integrity": "sha512-7Ec1zaFPF4RJ0eXu1YT/xgiebqwqoJz8rYPDi/O2BcZ++Wpt0Kq9cl0eg6NN6bYbPnR67ZLo7St5Q3UK0SnARw==",
1581
+
"dev": true,
1582
+
"license": "MIT",
1583
+
"dependencies": {
1584
+
"undici-types": "~6.21.0"
1585
+
}
1586
+
},
1477
1587
"node_modules/@types/react": {
1478
-
"version": "19.1.2",
1479
-
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz",
1480
-
"integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==",
1588
+
"version": "19.1.6",
1589
+
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.6.tgz",
1590
+
"integrity": "sha512-JeG0rEWak0N6Itr6QUx+X60uQmN+5t3j9r/OVDtWzFXKaj6kD1BwJzOksD0FF6iWxZlbE1kB0q9vtnU2ekqa1Q==",
1481
1591
"dev": true,
1482
1592
"license": "MIT",
1483
1593
"dependencies": {
···
1485
1595
}
1486
1596
},
1487
1597
"node_modules/@types/react-dom": {
1488
-
"version": "19.1.3",
1489
-
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.3.tgz",
1490
-
"integrity": "sha512-rJXC08OG0h3W6wDMFxQrZF00Kq6qQvw0djHRdzl3U5DnIERz0MRce3WVc7IS6JYBwtaP/DwYtRRjVlvivNveKg==",
1598
+
"version": "19.1.5",
1599
+
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.5.tgz",
1600
+
"integrity": "sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg==",
1491
1601
"dev": true,
1492
1602
"license": "MIT",
1493
1603
"peerDependencies": {
···
1495
1605
}
1496
1606
},
1497
1607
"node_modules/@typescript-eslint/eslint-plugin": {
1498
-
"version": "8.31.1",
1499
-
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.1.tgz",
1500
-
"integrity": "sha512-oUlH4h1ABavI4F0Xnl8/fOtML/eu8nI2A1nYd+f+55XI0BLu+RIqKoCiZKNo6DtqZBEQm5aNKA20G3Z5w3R6GQ==",
1608
+
"version": "8.33.0",
1609
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.33.0.tgz",
1610
+
"integrity": "sha512-CACyQuqSHt7ma3Ns601xykeBK/rDeZa3w6IS6UtMQbixO5DWy+8TilKkviGDH6jtWCo8FGRKEK5cLLkPvEammQ==",
1501
1611
"dev": true,
1502
1612
"license": "MIT",
1503
1613
"dependencies": {
1504
1614
"@eslint-community/regexpp": "^4.10.0",
1505
-
"@typescript-eslint/scope-manager": "8.31.1",
1506
-
"@typescript-eslint/type-utils": "8.31.1",
1507
-
"@typescript-eslint/utils": "8.31.1",
1508
-
"@typescript-eslint/visitor-keys": "8.31.1",
1615
+
"@typescript-eslint/scope-manager": "8.33.0",
1616
+
"@typescript-eslint/type-utils": "8.33.0",
1617
+
"@typescript-eslint/utils": "8.33.0",
1618
+
"@typescript-eslint/visitor-keys": "8.33.0",
1509
1619
"graphemer": "^1.4.0",
1510
-
"ignore": "^5.3.1",
1620
+
"ignore": "^7.0.0",
1511
1621
"natural-compare": "^1.4.0",
1512
-
"ts-api-utils": "^2.0.1"
1622
+
"ts-api-utils": "^2.1.0"
1513
1623
},
1514
1624
"engines": {
1515
1625
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
···
1519
1629
"url": "https://opencollective.com/typescript-eslint"
1520
1630
},
1521
1631
"peerDependencies": {
1522
-
"@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
1632
+
"@typescript-eslint/parser": "^8.33.0",
1523
1633
"eslint": "^8.57.0 || ^9.0.0",
1524
1634
"typescript": ">=4.8.4 <5.9.0"
1525
1635
}
1526
1636
},
1637
+
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
1638
+
"version": "7.0.4",
1639
+
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz",
1640
+
"integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==",
1641
+
"dev": true,
1642
+
"license": "MIT",
1643
+
"engines": {
1644
+
"node": ">= 4"
1645
+
}
1646
+
},
1527
1647
"node_modules/@typescript-eslint/parser": {
1528
-
"version": "8.31.1",
1529
-
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.31.1.tgz",
1530
-
"integrity": "sha512-oU/OtYVydhXnumd0BobL9rkJg7wFJ9bFFPmSmB/bf/XWN85hlViji59ko6bSKBXyseT9V8l+CN1nwmlbiN0G7Q==",
1648
+
"version": "8.33.0",
1649
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.33.0.tgz",
1650
+
"integrity": "sha512-JaehZvf6m0yqYp34+RVnihBAChkqeH+tqqhS0GuX1qgPpwLvmTPheKEs6OeCK6hVJgXZHJ2vbjnC9j119auStQ==",
1531
1651
"dev": true,
1532
1652
"license": "MIT",
1533
1653
"dependencies": {
1534
-
"@typescript-eslint/scope-manager": "8.31.1",
1535
-
"@typescript-eslint/types": "8.31.1",
1536
-
"@typescript-eslint/typescript-estree": "8.31.1",
1537
-
"@typescript-eslint/visitor-keys": "8.31.1",
1654
+
"@typescript-eslint/scope-manager": "8.33.0",
1655
+
"@typescript-eslint/types": "8.33.0",
1656
+
"@typescript-eslint/typescript-estree": "8.33.0",
1657
+
"@typescript-eslint/visitor-keys": "8.33.0",
1538
1658
"debug": "^4.3.4"
1539
1659
},
1540
1660
"engines": {
···
1549
1669
"typescript": ">=4.8.4 <5.9.0"
1550
1670
}
1551
1671
},
1672
+
"node_modules/@typescript-eslint/project-service": {
1673
+
"version": "8.33.0",
1674
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.33.0.tgz",
1675
+
"integrity": "sha512-d1hz0u9l6N+u/gcrk6s6gYdl7/+pp8yHheRTqP6X5hVDKALEaTn8WfGiit7G511yueBEL3OpOEpD+3/MBdoN+A==",
1676
+
"dev": true,
1677
+
"license": "MIT",
1678
+
"dependencies": {
1679
+
"@typescript-eslint/tsconfig-utils": "^8.33.0",
1680
+
"@typescript-eslint/types": "^8.33.0",
1681
+
"debug": "^4.3.4"
1682
+
},
1683
+
"engines": {
1684
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1685
+
},
1686
+
"funding": {
1687
+
"type": "opencollective",
1688
+
"url": "https://opencollective.com/typescript-eslint"
1689
+
}
1690
+
},
1552
1691
"node_modules/@typescript-eslint/scope-manager": {
1553
-
"version": "8.31.1",
1554
-
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.31.1.tgz",
1555
-
"integrity": "sha512-BMNLOElPxrtNQMIsFHE+3P0Yf1z0dJqV9zLdDxN/xLlWMlXK/ApEsVEKzpizg9oal8bAT5Sc7+ocal7AC1HCVw==",
1692
+
"version": "8.33.0",
1693
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.33.0.tgz",
1694
+
"integrity": "sha512-LMi/oqrzpqxyO72ltP+dBSP6V0xiUb4saY7WLtxSfiNEBI8m321LLVFU9/QDJxjDQG9/tjSqKz/E3380TEqSTw==",
1556
1695
"dev": true,
1557
1696
"license": "MIT",
1558
1697
"dependencies": {
1559
-
"@typescript-eslint/types": "8.31.1",
1560
-
"@typescript-eslint/visitor-keys": "8.31.1"
1698
+
"@typescript-eslint/types": "8.33.0",
1699
+
"@typescript-eslint/visitor-keys": "8.33.0"
1561
1700
},
1562
1701
"engines": {
1563
1702
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
···
1567
1706
"url": "https://opencollective.com/typescript-eslint"
1568
1707
}
1569
1708
},
1709
+
"node_modules/@typescript-eslint/tsconfig-utils": {
1710
+
"version": "8.33.0",
1711
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.33.0.tgz",
1712
+
"integrity": "sha512-sTkETlbqhEoiFmGr1gsdq5HyVbSOF0145SYDJ/EQmXHtKViCaGvnyLqWFFHtEXoS0J1yU8Wyou2UGmgW88fEug==",
1713
+
"dev": true,
1714
+
"license": "MIT",
1715
+
"engines": {
1716
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1717
+
},
1718
+
"funding": {
1719
+
"type": "opencollective",
1720
+
"url": "https://opencollective.com/typescript-eslint"
1721
+
},
1722
+
"peerDependencies": {
1723
+
"typescript": ">=4.8.4 <5.9.0"
1724
+
}
1725
+
},
1570
1726
"node_modules/@typescript-eslint/type-utils": {
1571
-
"version": "8.31.1",
1572
-
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.31.1.tgz",
1573
-
"integrity": "sha512-fNaT/m9n0+dpSp8G/iOQ05GoHYXbxw81x+yvr7TArTuZuCA6VVKbqWYVZrV5dVagpDTtj/O8k5HBEE/p/HM5LA==",
1727
+
"version": "8.33.0",
1728
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.33.0.tgz",
1729
+
"integrity": "sha512-lScnHNCBqL1QayuSrWeqAL5GmqNdVUQAAMTaCwdYEdWfIrSrOGzyLGRCHXcCixa5NK6i5l0AfSO2oBSjCjf4XQ==",
1574
1730
"dev": true,
1575
1731
"license": "MIT",
1576
1732
"dependencies": {
1577
-
"@typescript-eslint/typescript-estree": "8.31.1",
1578
-
"@typescript-eslint/utils": "8.31.1",
1733
+
"@typescript-eslint/typescript-estree": "8.33.0",
1734
+
"@typescript-eslint/utils": "8.33.0",
1579
1735
"debug": "^4.3.4",
1580
-
"ts-api-utils": "^2.0.1"
1736
+
"ts-api-utils": "^2.1.0"
1581
1737
},
1582
1738
"engines": {
1583
1739
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
···
1592
1748
}
1593
1749
},
1594
1750
"node_modules/@typescript-eslint/types": {
1595
-
"version": "8.31.1",
1596
-
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.31.1.tgz",
1597
-
"integrity": "sha512-SfepaEFUDQYRoA70DD9GtytljBePSj17qPxFHA/h3eg6lPTqGJ5mWOtbXCk1YrVU1cTJRd14nhaXWFu0l2troQ==",
1751
+
"version": "8.33.0",
1752
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.33.0.tgz",
1753
+
"integrity": "sha512-DKuXOKpM5IDT1FA2g9x9x1Ug81YuKrzf4mYX8FAVSNu5Wo/LELHWQyM1pQaDkI42bX15PWl0vNPt1uGiIFUOpg==",
1598
1754
"dev": true,
1599
1755
"license": "MIT",
1600
1756
"engines": {
···
1606
1762
}
1607
1763
},
1608
1764
"node_modules/@typescript-eslint/typescript-estree": {
1609
-
"version": "8.31.1",
1610
-
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.1.tgz",
1611
-
"integrity": "sha512-kaA0ueLe2v7KunYOyWYtlf/QhhZb7+qh4Yw6Ni5kgukMIG+iP773tjgBiLWIXYumWCwEq3nLW+TUywEp8uEeag==",
1765
+
"version": "8.33.0",
1766
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.33.0.tgz",
1767
+
"integrity": "sha512-vegY4FQoB6jL97Tu/lWRsAiUUp8qJTqzAmENH2k59SJhw0Th1oszb9Idq/FyyONLuNqT1OADJPXfyUNOR8SzAQ==",
1612
1768
"dev": true,
1613
1769
"license": "MIT",
1614
1770
"dependencies": {
1615
-
"@typescript-eslint/types": "8.31.1",
1616
-
"@typescript-eslint/visitor-keys": "8.31.1",
1771
+
"@typescript-eslint/project-service": "8.33.0",
1772
+
"@typescript-eslint/tsconfig-utils": "8.33.0",
1773
+
"@typescript-eslint/types": "8.33.0",
1774
+
"@typescript-eslint/visitor-keys": "8.33.0",
1617
1775
"debug": "^4.3.4",
1618
1776
"fast-glob": "^3.3.2",
1619
1777
"is-glob": "^4.0.3",
1620
1778
"minimatch": "^9.0.4",
1621
1779
"semver": "^7.6.0",
1622
-
"ts-api-utils": "^2.0.1"
1780
+
"ts-api-utils": "^2.1.0"
1623
1781
},
1624
1782
"engines": {
1625
1783
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
···
1658
1816
"url": "https://github.com/sponsors/isaacs"
1659
1817
}
1660
1818
},
1661
-
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
1662
-
"version": "7.7.1",
1663
-
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
1664
-
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
1665
-
"dev": true,
1666
-
"license": "ISC",
1667
-
"bin": {
1668
-
"semver": "bin/semver.js"
1669
-
},
1670
-
"engines": {
1671
-
"node": ">=10"
1672
-
}
1673
-
},
1674
1819
"node_modules/@typescript-eslint/utils": {
1675
-
"version": "8.31.1",
1676
-
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.31.1.tgz",
1677
-
"integrity": "sha512-2DSI4SNfF5T4oRveQ4nUrSjUqjMND0nLq9rEkz0gfGr3tg0S5KB6DhwR+WZPCjzkZl3cH+4x2ce3EsL50FubjQ==",
1820
+
"version": "8.33.0",
1821
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.33.0.tgz",
1822
+
"integrity": "sha512-lPFuQaLA9aSNa7D5u2EpRiqdAUhzShwGg/nhpBlc4GR6kcTABttCuyjFs8BcEZ8VWrjCBof/bePhP3Q3fS+Yrw==",
1678
1823
"dev": true,
1679
1824
"license": "MIT",
1680
1825
"dependencies": {
1681
-
"@eslint-community/eslint-utils": "^4.4.0",
1682
-
"@typescript-eslint/scope-manager": "8.31.1",
1683
-
"@typescript-eslint/types": "8.31.1",
1684
-
"@typescript-eslint/typescript-estree": "8.31.1"
1826
+
"@eslint-community/eslint-utils": "^4.7.0",
1827
+
"@typescript-eslint/scope-manager": "8.33.0",
1828
+
"@typescript-eslint/types": "8.33.0",
1829
+
"@typescript-eslint/typescript-estree": "8.33.0"
1685
1830
},
1686
1831
"engines": {
1687
1832
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
···
1696
1841
}
1697
1842
},
1698
1843
"node_modules/@typescript-eslint/visitor-keys": {
1699
-
"version": "8.31.1",
1700
-
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.1.tgz",
1701
-
"integrity": "sha512-I+/rgqOVBn6f0o7NDTmAPWWC6NuqhV174lfYvAm9fUaWeiefLdux9/YI3/nLugEn9L8fcSi0XmpKi/r5u0nmpw==",
1844
+
"version": "8.33.0",
1845
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.33.0.tgz",
1846
+
"integrity": "sha512-7RW7CMYoskiz5OOGAWjJFxgb7c5UNjTG292gYhWeOAcFmYCtVCSqjqSBj5zMhxbXo2JOW95YYrUWJfU0zrpaGQ==",
1702
1847
"dev": true,
1703
1848
"license": "MIT",
1704
1849
"dependencies": {
1705
-
"@typescript-eslint/types": "8.31.1",
1850
+
"@typescript-eslint/types": "8.33.0",
1706
1851
"eslint-visitor-keys": "^4.2.0"
1707
1852
},
1708
1853
"engines": {
···
1713
1858
"url": "https://opencollective.com/typescript-eslint"
1714
1859
}
1715
1860
},
1861
+
"node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
1862
+
"version": "4.2.0",
1863
+
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
1864
+
"integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
1865
+
"dev": true,
1866
+
"license": "Apache-2.0",
1867
+
"engines": {
1868
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1869
+
},
1870
+
"funding": {
1871
+
"url": "https://opencollective.com/eslint"
1872
+
}
1873
+
},
1716
1874
"node_modules/@vitejs/plugin-react": {
1717
-
"version": "4.4.1",
1718
-
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.1.tgz",
1719
-
"integrity": "sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==",
1875
+
"version": "4.5.0",
1876
+
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.5.0.tgz",
1877
+
"integrity": "sha512-JuLWaEqypaJmOJPLWwO335Ig6jSgC1FTONCWAxnqcQthLTK/Yc9aH6hr9z/87xciejbQcnP3GnA1FWUSWeXaeg==",
1720
1878
"dev": true,
1721
1879
"license": "MIT",
1722
1880
"dependencies": {
1723
1881
"@babel/core": "^7.26.10",
1724
1882
"@babel/plugin-transform-react-jsx-self": "^7.25.9",
1725
1883
"@babel/plugin-transform-react-jsx-source": "^7.25.9",
1884
+
"@rolldown/pluginutils": "1.0.0-beta.9",
1726
1885
"@types/babel__core": "^7.20.5",
1727
1886
"react-refresh": "^0.17.0"
1728
1887
},
···
1733
1892
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0"
1734
1893
}
1735
1894
},
1736
-
"node_modules/accepts": {
1737
-
"version": "2.0.0",
1738
-
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
1739
-
"integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
1740
-
"dev": true,
1741
-
"license": "MIT",
1742
-
"dependencies": {
1743
-
"mime-types": "^3.0.0",
1744
-
"negotiator": "^1.0.0"
1745
-
},
1746
-
"engines": {
1747
-
"node": ">= 0.6"
1748
-
}
1749
-
},
1750
1895
"node_modules/acorn": {
1751
1896
"version": "8.14.1",
1752
1897
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
···
1787
1932
"url": "https://github.com/sponsors/epoberezkin"
1788
1933
}
1789
1934
},
1935
+
"node_modules/ansi-regex": {
1936
+
"version": "5.0.1",
1937
+
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
1938
+
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
1939
+
"dev": true,
1940
+
"license": "MIT",
1941
+
"engines": {
1942
+
"node": ">=8"
1943
+
}
1944
+
},
1790
1945
"node_modules/ansi-styles": {
1791
1946
"version": "4.3.0",
1792
1947
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
···
1823
1978
"dev": true,
1824
1979
"license": "MIT"
1825
1980
},
1826
-
"node_modules/body-parser": {
1827
-
"version": "2.2.0",
1828
-
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
1829
-
"integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
1830
-
"dev": true,
1831
-
"license": "MIT",
1832
-
"dependencies": {
1833
-
"bytes": "^3.1.2",
1834
-
"content-type": "^1.0.5",
1835
-
"debug": "^4.4.0",
1836
-
"http-errors": "^2.0.0",
1837
-
"iconv-lite": "^0.6.3",
1838
-
"on-finished": "^2.4.1",
1839
-
"qs": "^6.14.0",
1840
-
"raw-body": "^3.0.0",
1841
-
"type-is": "^2.0.0"
1842
-
},
1843
-
"engines": {
1844
-
"node": ">=18"
1845
-
}
1846
-
},
1847
1981
"node_modules/brace-expansion": {
1848
1982
"version": "1.1.11",
1849
1983
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
···
1901
2035
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
1902
2036
}
1903
2037
},
1904
-
"node_modules/bytes": {
1905
-
"version": "3.1.2",
1906
-
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
1907
-
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
2038
+
"node_modules/buffer-from": {
2039
+
"version": "1.1.2",
2040
+
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
2041
+
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
1908
2042
"dev": true,
1909
-
"license": "MIT",
1910
-
"engines": {
1911
-
"node": ">= 0.8"
1912
-
}
1913
-
},
1914
-
"node_modules/call-bind-apply-helpers": {
1915
-
"version": "1.0.2",
1916
-
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
1917
-
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
1918
-
"dev": true,
1919
-
"license": "MIT",
1920
-
"dependencies": {
1921
-
"es-errors": "^1.3.0",
1922
-
"function-bind": "^1.1.2"
1923
-
},
1924
-
"engines": {
1925
-
"node": ">= 0.4"
1926
-
}
1927
-
},
1928
-
"node_modules/call-bound": {
1929
-
"version": "1.0.4",
1930
-
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
1931
-
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
1932
-
"dev": true,
1933
-
"license": "MIT",
1934
-
"dependencies": {
1935
-
"call-bind-apply-helpers": "^1.0.2",
1936
-
"get-intrinsic": "^1.3.0"
1937
-
},
1938
-
"engines": {
1939
-
"node": ">= 0.4"
1940
-
},
1941
-
"funding": {
1942
-
"url": "https://github.com/sponsors/ljharb"
1943
-
}
2043
+
"license": "MIT"
1944
2044
},
1945
2045
"node_modules/callsites": {
1946
2046
"version": "3.1.0",
···
1953
2053
}
1954
2054
},
1955
2055
"node_modules/caniuse-lite": {
1956
-
"version": "1.0.30001716",
1957
-
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001716.tgz",
1958
-
"integrity": "sha512-49/c1+x3Kwz7ZIWt+4DvK3aMJy9oYXXG6/97JKsnjdCk/6n9vVyWL8NAwVt95Lwt9eigI10Hl782kDfZUUlRXw==",
2056
+
"version": "1.0.30001718",
2057
+
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz",
2058
+
"integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==",
1959
2059
"dev": true,
1960
2060
"funding": [
1961
2061
{
···
1990
2090
"url": "https://github.com/chalk/chalk?sponsor=1"
1991
2091
}
1992
2092
},
2093
+
"node_modules/cliui": {
2094
+
"version": "8.0.1",
2095
+
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
2096
+
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
2097
+
"dev": true,
2098
+
"license": "ISC",
2099
+
"dependencies": {
2100
+
"string-width": "^4.2.0",
2101
+
"strip-ansi": "^6.0.1",
2102
+
"wrap-ansi": "^7.0.0"
2103
+
},
2104
+
"engines": {
2105
+
"node": ">=12"
2106
+
}
2107
+
},
1993
2108
"node_modules/color-convert": {
1994
2109
"version": "2.0.1",
1995
2110
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
···
2010
2125
"dev": true,
2011
2126
"license": "MIT"
2012
2127
},
2128
+
"node_modules/commander": {
2129
+
"version": "2.20.3",
2130
+
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
2131
+
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
2132
+
"dev": true,
2133
+
"license": "MIT"
2134
+
},
2013
2135
"node_modules/concat-map": {
2014
2136
"version": "0.0.1",
2015
2137
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
···
2017
2139
"dev": true,
2018
2140
"license": "MIT"
2019
2141
},
2020
-
"node_modules/content-disposition": {
2021
-
"version": "1.0.0",
2022
-
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
2023
-
"integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
2024
-
"dev": true,
2025
-
"license": "MIT",
2026
-
"dependencies": {
2027
-
"safe-buffer": "5.2.1"
2028
-
},
2029
-
"engines": {
2030
-
"node": ">= 0.6"
2031
-
}
2032
-
},
2033
-
"node_modules/content-type": {
2034
-
"version": "1.0.5",
2035
-
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
2036
-
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
2037
-
"dev": true,
2038
-
"license": "MIT",
2039
-
"engines": {
2040
-
"node": ">= 0.6"
2041
-
}
2042
-
},
2043
2142
"node_modules/convert-source-map": {
2044
2143
"version": "2.0.0",
2045
2144
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
···
2048
2147
"license": "MIT"
2049
2148
},
2050
2149
"node_modules/cookie": {
2051
-
"version": "0.7.2",
2052
-
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
2053
-
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
2054
-
"dev": true,
2055
-
"license": "MIT",
2056
-
"engines": {
2057
-
"node": ">= 0.6"
2058
-
}
2059
-
},
2060
-
"node_modules/cookie-signature": {
2061
-
"version": "1.2.2",
2062
-
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
2063
-
"integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
2064
-
"dev": true,
2150
+
"version": "1.0.2",
2151
+
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
2152
+
"integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
2065
2153
"license": "MIT",
2066
2154
"engines": {
2067
-
"node": ">=6.6.0"
2068
-
}
2069
-
},
2070
-
"node_modules/cors": {
2071
-
"version": "2.8.5",
2072
-
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
2073
-
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
2074
-
"dev": true,
2075
-
"license": "MIT",
2076
-
"dependencies": {
2077
-
"object-assign": "^4",
2078
-
"vary": "^1"
2079
-
},
2080
-
"engines": {
2081
-
"node": ">= 0.10"
2155
+
"node": ">=18"
2082
2156
}
2083
2157
},
2084
2158
"node_modules/cross-spawn": {
···
2104
2178
"license": "MIT"
2105
2179
},
2106
2180
"node_modules/debug": {
2107
-
"version": "4.4.0",
2108
-
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
2109
-
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
2181
+
"version": "4.4.1",
2182
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
2183
+
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
2110
2184
"dev": true,
2111
2185
"license": "MIT",
2112
2186
"dependencies": {
···
2128
2202
"dev": true,
2129
2203
"license": "MIT"
2130
2204
},
2131
-
"node_modules/depd": {
2205
+
"node_modules/define-lazy-prop": {
2132
2206
"version": "2.0.0",
2133
-
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
2134
-
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
2207
+
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
2208
+
"integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
2135
2209
"dev": true,
2136
2210
"license": "MIT",
2137
2211
"engines": {
2138
-
"node": ">= 0.8"
2139
-
}
2140
-
},
2141
-
"node_modules/dunder-proto": {
2142
-
"version": "1.0.1",
2143
-
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
2144
-
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
2145
-
"dev": true,
2146
-
"license": "MIT",
2147
-
"dependencies": {
2148
-
"call-bind-apply-helpers": "^1.0.1",
2149
-
"es-errors": "^1.3.0",
2150
-
"gopd": "^1.2.0"
2151
-
},
2152
-
"engines": {
2153
-
"node": ">= 0.4"
2212
+
"node": ">=8"
2154
2213
}
2155
2214
},
2156
-
"node_modules/ee-first": {
2157
-
"version": "1.1.1",
2158
-
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
2159
-
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
2160
-
"dev": true,
2161
-
"license": "MIT"
2162
-
},
2163
2215
"node_modules/electron-to-chromium": {
2164
-
"version": "1.5.149",
2165
-
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.149.tgz",
2166
-
"integrity": "sha512-UyiO82eb9dVOx8YO3ajDf9jz2kKyt98DEITRdeLPstOEuTlLzDA4Gyq5K9he71TQziU5jUVu2OAu5N48HmQiyQ==",
2216
+
"version": "1.5.159",
2217
+
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.159.tgz",
2218
+
"integrity": "sha512-CEvHptWAMV5p6GJ0Lq8aheyvVbfzVrv5mmidu1D3pidoVNkB3tTBsTMVtPJ+rzRK5oV229mCLz9Zj/hNvU8GBA==",
2167
2219
"dev": true,
2168
2220
"license": "ISC"
2169
2221
},
2170
-
"node_modules/encodeurl": {
2171
-
"version": "2.0.0",
2172
-
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
2173
-
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
2222
+
"node_modules/emoji-regex": {
2223
+
"version": "8.0.0",
2224
+
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
2225
+
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
2174
2226
"dev": true,
2175
-
"license": "MIT",
2176
-
"engines": {
2177
-
"node": ">= 0.8"
2178
-
}
2179
-
},
2180
-
"node_modules/es-define-property": {
2181
-
"version": "1.0.1",
2182
-
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
2183
-
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
2184
-
"dev": true,
2185
-
"license": "MIT",
2186
-
"engines": {
2187
-
"node": ">= 0.4"
2188
-
}
2189
-
},
2190
-
"node_modules/es-errors": {
2191
-
"version": "1.3.0",
2192
-
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
2193
-
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
2194
-
"dev": true,
2195
-
"license": "MIT",
2196
-
"engines": {
2197
-
"node": ">= 0.4"
2198
-
}
2199
-
},
2200
-
"node_modules/es-object-atoms": {
2201
-
"version": "1.1.1",
2202
-
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
2203
-
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
2204
-
"dev": true,
2205
-
"license": "MIT",
2206
-
"dependencies": {
2207
-
"es-errors": "^1.3.0"
2208
-
},
2209
-
"engines": {
2210
-
"node": ">= 0.4"
2211
-
}
2227
+
"license": "MIT"
2212
2228
},
2213
2229
"node_modules/esbuild": {
2214
-
"version": "0.25.3",
2215
-
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz",
2216
-
"integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==",
2230
+
"version": "0.25.5",
2231
+
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz",
2232
+
"integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
2217
2233
"dev": true,
2218
2234
"hasInstallScript": true,
2219
2235
"license": "MIT",
···
2224
2240
"node": ">=18"
2225
2241
},
2226
2242
"optionalDependencies": {
2227
-
"@esbuild/aix-ppc64": "0.25.3",
2228
-
"@esbuild/android-arm": "0.25.3",
2229
-
"@esbuild/android-arm64": "0.25.3",
2230
-
"@esbuild/android-x64": "0.25.3",
2231
-
"@esbuild/darwin-arm64": "0.25.3",
2232
-
"@esbuild/darwin-x64": "0.25.3",
2233
-
"@esbuild/freebsd-arm64": "0.25.3",
2234
-
"@esbuild/freebsd-x64": "0.25.3",
2235
-
"@esbuild/linux-arm": "0.25.3",
2236
-
"@esbuild/linux-arm64": "0.25.3",
2237
-
"@esbuild/linux-ia32": "0.25.3",
2238
-
"@esbuild/linux-loong64": "0.25.3",
2239
-
"@esbuild/linux-mips64el": "0.25.3",
2240
-
"@esbuild/linux-ppc64": "0.25.3",
2241
-
"@esbuild/linux-riscv64": "0.25.3",
2242
-
"@esbuild/linux-s390x": "0.25.3",
2243
-
"@esbuild/linux-x64": "0.25.3",
2244
-
"@esbuild/netbsd-arm64": "0.25.3",
2245
-
"@esbuild/netbsd-x64": "0.25.3",
2246
-
"@esbuild/openbsd-arm64": "0.25.3",
2247
-
"@esbuild/openbsd-x64": "0.25.3",
2248
-
"@esbuild/sunos-x64": "0.25.3",
2249
-
"@esbuild/win32-arm64": "0.25.3",
2250
-
"@esbuild/win32-ia32": "0.25.3",
2251
-
"@esbuild/win32-x64": "0.25.3"
2243
+
"@esbuild/aix-ppc64": "0.25.5",
2244
+
"@esbuild/android-arm": "0.25.5",
2245
+
"@esbuild/android-arm64": "0.25.5",
2246
+
"@esbuild/android-x64": "0.25.5",
2247
+
"@esbuild/darwin-arm64": "0.25.5",
2248
+
"@esbuild/darwin-x64": "0.25.5",
2249
+
"@esbuild/freebsd-arm64": "0.25.5",
2250
+
"@esbuild/freebsd-x64": "0.25.5",
2251
+
"@esbuild/linux-arm": "0.25.5",
2252
+
"@esbuild/linux-arm64": "0.25.5",
2253
+
"@esbuild/linux-ia32": "0.25.5",
2254
+
"@esbuild/linux-loong64": "0.25.5",
2255
+
"@esbuild/linux-mips64el": "0.25.5",
2256
+
"@esbuild/linux-ppc64": "0.25.5",
2257
+
"@esbuild/linux-riscv64": "0.25.5",
2258
+
"@esbuild/linux-s390x": "0.25.5",
2259
+
"@esbuild/linux-x64": "0.25.5",
2260
+
"@esbuild/netbsd-arm64": "0.25.5",
2261
+
"@esbuild/netbsd-x64": "0.25.5",
2262
+
"@esbuild/openbsd-arm64": "0.25.5",
2263
+
"@esbuild/openbsd-x64": "0.25.5",
2264
+
"@esbuild/sunos-x64": "0.25.5",
2265
+
"@esbuild/win32-arm64": "0.25.5",
2266
+
"@esbuild/win32-ia32": "0.25.5",
2267
+
"@esbuild/win32-x64": "0.25.5"
2252
2268
}
2253
2269
},
2254
2270
"node_modules/escalade": {
···
2261
2277
"node": ">=6"
2262
2278
}
2263
2279
},
2264
-
"node_modules/escape-html": {
2265
-
"version": "1.0.3",
2266
-
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
2267
-
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
2268
-
"dev": true,
2269
-
"license": "MIT"
2270
-
},
2271
2280
"node_modules/escape-string-regexp": {
2272
2281
"version": "4.0.0",
2273
2282
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
···
2282
2291
}
2283
2292
},
2284
2293
"node_modules/eslint": {
2285
-
"version": "9.26.0",
2286
-
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.26.0.tgz",
2287
-
"integrity": "sha512-Hx0MOjPh6uK9oq9nVsATZKE/Wlbai7KFjfCuw9UHaguDW3x+HF0O5nIi3ud39TWgrTjTO5nHxmL3R1eANinWHQ==",
2294
+
"version": "9.27.0",
2295
+
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz",
2296
+
"integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==",
2288
2297
"dev": true,
2289
2298
"license": "MIT",
2290
2299
"dependencies": {
···
2292
2301
"@eslint-community/regexpp": "^4.12.1",
2293
2302
"@eslint/config-array": "^0.20.0",
2294
2303
"@eslint/config-helpers": "^0.2.1",
2295
-
"@eslint/core": "^0.13.0",
2304
+
"@eslint/core": "^0.14.0",
2296
2305
"@eslint/eslintrc": "^3.3.1",
2297
-
"@eslint/js": "9.26.0",
2298
-
"@eslint/plugin-kit": "^0.2.8",
2306
+
"@eslint/js": "9.27.0",
2307
+
"@eslint/plugin-kit": "^0.3.1",
2299
2308
"@humanfs/node": "^0.16.6",
2300
2309
"@humanwhocodes/module-importer": "^1.0.1",
2301
2310
"@humanwhocodes/retry": "^0.4.2",
2302
-
"@modelcontextprotocol/sdk": "^1.8.0",
2303
2311
"@types/estree": "^1.0.6",
2304
2312
"@types/json-schema": "^7.0.15",
2305
2313
"ajv": "^6.12.4",
···
2323
2331
"lodash.merge": "^4.6.2",
2324
2332
"minimatch": "^3.1.2",
2325
2333
"natural-compare": "^1.4.0",
2326
-
"optionator": "^0.9.3",
2327
-
"zod": "^3.24.2"
2334
+
"optionator": "^0.9.3"
2328
2335
},
2329
2336
"bin": {
2330
2337
"eslint": "bin/eslint.js"
···
2385
2392
}
2386
2393
},
2387
2394
"node_modules/eslint-visitor-keys": {
2395
+
"version": "3.4.3",
2396
+
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
2397
+
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
2398
+
"dev": true,
2399
+
"license": "Apache-2.0",
2400
+
"engines": {
2401
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
2402
+
},
2403
+
"funding": {
2404
+
"url": "https://opencollective.com/eslint"
2405
+
}
2406
+
},
2407
+
"node_modules/eslint/node_modules/eslint-visitor-keys": {
2388
2408
"version": "4.2.0",
2389
2409
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
2390
2410
"integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
···
2415
2435
"url": "https://opencollective.com/eslint"
2416
2436
}
2417
2437
},
2438
+
"node_modules/espree/node_modules/eslint-visitor-keys": {
2439
+
"version": "4.2.0",
2440
+
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
2441
+
"integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
2442
+
"dev": true,
2443
+
"license": "Apache-2.0",
2444
+
"engines": {
2445
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
2446
+
},
2447
+
"funding": {
2448
+
"url": "https://opencollective.com/eslint"
2449
+
}
2450
+
},
2418
2451
"node_modules/esquery": {
2419
2452
"version": "1.6.0",
2420
2453
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
···
2461
2494
"node": ">=0.10.0"
2462
2495
}
2463
2496
},
2464
-
"node_modules/etag": {
2465
-
"version": "1.8.1",
2466
-
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
2467
-
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
2468
-
"dev": true,
2469
-
"license": "MIT",
2470
-
"engines": {
2471
-
"node": ">= 0.6"
2472
-
}
2473
-
},
2474
-
"node_modules/eventsource": {
2475
-
"version": "3.0.6",
2476
-
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.6.tgz",
2477
-
"integrity": "sha512-l19WpE2m9hSuyP06+FbuUUf1G+R0SFLrtQfbRb9PRr+oimOfxQhgGCbVaXg5IvZyyTThJsxh6L/srkMiCeBPDA==",
2478
-
"dev": true,
2479
-
"license": "MIT",
2480
-
"dependencies": {
2481
-
"eventsource-parser": "^3.0.1"
2482
-
},
2483
-
"engines": {
2484
-
"node": ">=18.0.0"
2485
-
}
2486
-
},
2487
-
"node_modules/eventsource-parser": {
2488
-
"version": "3.0.1",
2489
-
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.1.tgz",
2490
-
"integrity": "sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA==",
2491
-
"dev": true,
2492
-
"license": "MIT",
2493
-
"engines": {
2494
-
"node": ">=18.0.0"
2495
-
}
2496
-
},
2497
-
"node_modules/express": {
2498
-
"version": "5.1.0",
2499
-
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
2500
-
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
2501
-
"dev": true,
2502
-
"license": "MIT",
2503
-
"dependencies": {
2504
-
"accepts": "^2.0.0",
2505
-
"body-parser": "^2.2.0",
2506
-
"content-disposition": "^1.0.0",
2507
-
"content-type": "^1.0.5",
2508
-
"cookie": "^0.7.1",
2509
-
"cookie-signature": "^1.2.1",
2510
-
"debug": "^4.4.0",
2511
-
"encodeurl": "^2.0.0",
2512
-
"escape-html": "^1.0.3",
2513
-
"etag": "^1.8.1",
2514
-
"finalhandler": "^2.1.0",
2515
-
"fresh": "^2.0.0",
2516
-
"http-errors": "^2.0.0",
2517
-
"merge-descriptors": "^2.0.0",
2518
-
"mime-types": "^3.0.0",
2519
-
"on-finished": "^2.4.1",
2520
-
"once": "^1.4.0",
2521
-
"parseurl": "^1.3.3",
2522
-
"proxy-addr": "^2.0.7",
2523
-
"qs": "^6.14.0",
2524
-
"range-parser": "^1.2.1",
2525
-
"router": "^2.2.0",
2526
-
"send": "^1.1.0",
2527
-
"serve-static": "^2.2.0",
2528
-
"statuses": "^2.0.1",
2529
-
"type-is": "^2.0.1",
2530
-
"vary": "^1.1.2"
2531
-
},
2532
-
"engines": {
2533
-
"node": ">= 18"
2534
-
},
2535
-
"funding": {
2536
-
"type": "opencollective",
2537
-
"url": "https://opencollective.com/express"
2538
-
}
2539
-
},
2540
-
"node_modules/express-rate-limit": {
2541
-
"version": "7.5.0",
2542
-
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz",
2543
-
"integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==",
2544
-
"dev": true,
2545
-
"license": "MIT",
2546
-
"engines": {
2547
-
"node": ">= 16"
2548
-
},
2549
-
"funding": {
2550
-
"url": "https://github.com/sponsors/express-rate-limit"
2551
-
},
2552
-
"peerDependencies": {
2553
-
"express": "^4.11 || 5 || ^5.0.0-beta.1"
2554
-
}
2555
-
},
2556
2497
"node_modules/fast-deep-equal": {
2557
2498
"version": "3.1.3",
2558
2499
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
···
2640
2581
"node": ">=8"
2641
2582
}
2642
2583
},
2643
-
"node_modules/finalhandler": {
2644
-
"version": "2.1.0",
2645
-
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
2646
-
"integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
2647
-
"dev": true,
2648
-
"license": "MIT",
2649
-
"dependencies": {
2650
-
"debug": "^4.4.0",
2651
-
"encodeurl": "^2.0.0",
2652
-
"escape-html": "^1.0.3",
2653
-
"on-finished": "^2.4.1",
2654
-
"parseurl": "^1.3.3",
2655
-
"statuses": "^2.0.1"
2656
-
},
2657
-
"engines": {
2658
-
"node": ">= 0.8"
2659
-
}
2660
-
},
2661
2584
"node_modules/find-up": {
2662
2585
"version": "5.0.0",
2663
2586
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
···
2696
2619
"dev": true,
2697
2620
"license": "ISC"
2698
2621
},
2699
-
"node_modules/forwarded": {
2700
-
"version": "0.2.0",
2701
-
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
2702
-
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
2703
-
"dev": true,
2704
-
"license": "MIT",
2705
-
"engines": {
2706
-
"node": ">= 0.6"
2707
-
}
2708
-
},
2709
-
"node_modules/fresh": {
2710
-
"version": "2.0.0",
2711
-
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
2712
-
"integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
2713
-
"dev": true,
2714
-
"license": "MIT",
2715
-
"engines": {
2716
-
"node": ">= 0.8"
2717
-
}
2718
-
},
2719
2622
"node_modules/fsevents": {
2720
2623
"version": "2.3.3",
2721
2624
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
···
2731
2634
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
2732
2635
}
2733
2636
},
2734
-
"node_modules/function-bind": {
2735
-
"version": "1.1.2",
2736
-
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
2737
-
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
2738
-
"dev": true,
2739
-
"license": "MIT",
2740
-
"funding": {
2741
-
"url": "https://github.com/sponsors/ljharb"
2742
-
}
2743
-
},
2744
2637
"node_modules/gensync": {
2745
2638
"version": "1.0.0-beta.2",
2746
2639
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
···
2751
2644
"node": ">=6.9.0"
2752
2645
}
2753
2646
},
2754
-
"node_modules/get-intrinsic": {
2755
-
"version": "1.3.0",
2756
-
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
2757
-
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
2758
-
"dev": true,
2759
-
"license": "MIT",
2760
-
"dependencies": {
2761
-
"call-bind-apply-helpers": "^1.0.2",
2762
-
"es-define-property": "^1.0.1",
2763
-
"es-errors": "^1.3.0",
2764
-
"es-object-atoms": "^1.1.1",
2765
-
"function-bind": "^1.1.2",
2766
-
"get-proto": "^1.0.1",
2767
-
"gopd": "^1.2.0",
2768
-
"has-symbols": "^1.1.0",
2769
-
"hasown": "^2.0.2",
2770
-
"math-intrinsics": "^1.1.0"
2771
-
},
2772
-
"engines": {
2773
-
"node": ">= 0.4"
2774
-
},
2775
-
"funding": {
2776
-
"url": "https://github.com/sponsors/ljharb"
2777
-
}
2778
-
},
2779
-
"node_modules/get-proto": {
2780
-
"version": "1.0.1",
2781
-
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
2782
-
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
2647
+
"node_modules/get-caller-file": {
2648
+
"version": "2.0.5",
2649
+
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
2650
+
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
2783
2651
"dev": true,
2784
-
"license": "MIT",
2785
-
"dependencies": {
2786
-
"dunder-proto": "^1.0.1",
2787
-
"es-object-atoms": "^1.0.0"
2788
-
},
2652
+
"license": "ISC",
2789
2653
"engines": {
2790
-
"node": ">= 0.4"
2654
+
"node": "6.* || 8.* || >= 10.*"
2791
2655
}
2792
2656
},
2793
2657
"node_modules/glob-parent": {
···
2804
2668
}
2805
2669
},
2806
2670
"node_modules/globals": {
2807
-
"version": "16.0.0",
2808
-
"resolved": "https://registry.npmjs.org/globals/-/globals-16.0.0.tgz",
2809
-
"integrity": "sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==",
2671
+
"version": "16.2.0",
2672
+
"resolved": "https://registry.npmjs.org/globals/-/globals-16.2.0.tgz",
2673
+
"integrity": "sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg==",
2810
2674
"dev": true,
2811
2675
"license": "MIT",
2812
2676
"engines": {
···
2816
2680
"url": "https://github.com/sponsors/sindresorhus"
2817
2681
}
2818
2682
},
2819
-
"node_modules/gopd": {
2820
-
"version": "1.2.0",
2821
-
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
2822
-
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
2823
-
"dev": true,
2824
-
"license": "MIT",
2825
-
"engines": {
2826
-
"node": ">= 0.4"
2827
-
},
2828
-
"funding": {
2829
-
"url": "https://github.com/sponsors/ljharb"
2830
-
}
2831
-
},
2832
2683
"node_modules/graphemer": {
2833
2684
"version": "1.4.0",
2834
2685
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
···
2845
2696
"node": ">=8"
2846
2697
}
2847
2698
},
2848
-
"node_modules/has-symbols": {
2849
-
"version": "1.1.0",
2850
-
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
2851
-
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
2852
-
"dev": true,
2853
-
"license": "MIT",
2854
-
"engines": {
2855
-
"node": ">= 0.4"
2856
-
},
2857
-
"funding": {
2858
-
"url": "https://github.com/sponsors/ljharb"
2859
-
}
2860
-
},
2861
-
"node_modules/hasown": {
2862
-
"version": "2.0.2",
2863
-
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
2864
-
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
2865
-
"dev": true,
2866
-
"license": "MIT",
2867
-
"dependencies": {
2868
-
"function-bind": "^1.1.2"
2869
-
},
2870
-
"engines": {
2871
-
"node": ">= 0.4"
2872
-
}
2873
-
},
2874
-
"node_modules/http-errors": {
2875
-
"version": "2.0.0",
2876
-
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
2877
-
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
2878
-
"dev": true,
2879
-
"license": "MIT",
2880
-
"dependencies": {
2881
-
"depd": "2.0.0",
2882
-
"inherits": "2.0.4",
2883
-
"setprototypeof": "1.2.0",
2884
-
"statuses": "2.0.1",
2885
-
"toidentifier": "1.0.1"
2886
-
},
2887
-
"engines": {
2888
-
"node": ">= 0.8"
2889
-
}
2890
-
},
2891
-
"node_modules/iconv-lite": {
2892
-
"version": "0.6.3",
2893
-
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
2894
-
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
2895
-
"dev": true,
2896
-
"license": "MIT",
2897
-
"dependencies": {
2898
-
"safer-buffer": ">= 2.1.2 < 3.0.0"
2899
-
},
2900
-
"engines": {
2901
-
"node": ">=0.10.0"
2902
-
}
2903
-
},
2904
2699
"node_modules/ignore": {
2905
2700
"version": "5.3.2",
2906
2701
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
···
2938
2733
"node": ">=0.8.19"
2939
2734
}
2940
2735
},
2941
-
"node_modules/inherits": {
2942
-
"version": "2.0.4",
2943
-
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
2944
-
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
2945
-
"dev": true,
2946
-
"license": "ISC"
2947
-
},
2948
-
"node_modules/ipaddr.js": {
2949
-
"version": "1.9.1",
2950
-
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
2951
-
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
2736
+
"node_modules/is-docker": {
2737
+
"version": "2.2.1",
2738
+
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
2739
+
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
2952
2740
"dev": true,
2953
2741
"license": "MIT",
2742
+
"bin": {
2743
+
"is-docker": "cli.js"
2744
+
},
2954
2745
"engines": {
2955
-
"node": ">= 0.10"
2746
+
"node": ">=8"
2747
+
},
2748
+
"funding": {
2749
+
"url": "https://github.com/sponsors/sindresorhus"
2956
2750
}
2957
2751
},
2958
2752
"node_modules/is-extglob": {
···
2965
2759
"node": ">=0.10.0"
2966
2760
}
2967
2761
},
2762
+
"node_modules/is-fullwidth-code-point": {
2763
+
"version": "3.0.0",
2764
+
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
2765
+
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
2766
+
"dev": true,
2767
+
"license": "MIT",
2768
+
"engines": {
2769
+
"node": ">=8"
2770
+
}
2771
+
},
2968
2772
"node_modules/is-glob": {
2969
2773
"version": "4.0.3",
2970
2774
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
···
2988
2792
"node": ">=0.12.0"
2989
2793
}
2990
2794
},
2991
-
"node_modules/is-promise": {
2992
-
"version": "4.0.0",
2993
-
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
2994
-
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
2795
+
"node_modules/is-wsl": {
2796
+
"version": "2.2.0",
2797
+
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
2798
+
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
2995
2799
"dev": true,
2996
-
"license": "MIT"
2800
+
"license": "MIT",
2801
+
"dependencies": {
2802
+
"is-docker": "^2.0.0"
2803
+
},
2804
+
"engines": {
2805
+
"node": ">=8"
2806
+
}
2997
2807
},
2998
2808
"node_modules/isexe": {
2999
2809
"version": "2.0.0",
···
3007
2817
"resolved": "https://registry.npmjs.org/iso-datestring-validator/-/iso-datestring-validator-2.2.2.tgz",
3008
2818
"integrity": "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==",
3009
2819
"license": "MIT"
2820
+
},
2821
+
"node_modules/jiti": {
2822
+
"version": "2.4.2",
2823
+
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz",
2824
+
"integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
2825
+
"dev": true,
2826
+
"license": "MIT",
2827
+
"bin": {
2828
+
"jiti": "lib/jiti-cli.mjs"
2829
+
}
3010
2830
},
3011
2831
"node_modules/js-tokens": {
3012
2832
"version": "4.0.0",
···
3132
2952
"yallist": "^3.0.2"
3133
2953
}
3134
2954
},
3135
-
"node_modules/math-intrinsics": {
3136
-
"version": "1.1.0",
3137
-
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
3138
-
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
3139
-
"dev": true,
3140
-
"license": "MIT",
3141
-
"engines": {
3142
-
"node": ">= 0.4"
3143
-
}
3144
-
},
3145
-
"node_modules/media-typer": {
3146
-
"version": "1.1.0",
3147
-
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
3148
-
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
3149
-
"dev": true,
3150
-
"license": "MIT",
3151
-
"engines": {
3152
-
"node": ">= 0.8"
3153
-
}
3154
-
},
3155
-
"node_modules/merge-descriptors": {
3156
-
"version": "2.0.0",
3157
-
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
3158
-
"integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
3159
-
"dev": true,
3160
-
"license": "MIT",
3161
-
"engines": {
3162
-
"node": ">=18"
3163
-
},
3164
-
"funding": {
3165
-
"url": "https://github.com/sponsors/sindresorhus"
3166
-
}
3167
-
},
3168
2955
"node_modules/merge2": {
3169
2956
"version": "1.4.1",
3170
2957
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
···
3189
2976
"node": ">=8.6"
3190
2977
}
3191
2978
},
3192
-
"node_modules/mime-db": {
3193
-
"version": "1.54.0",
3194
-
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
3195
-
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
3196
-
"dev": true,
3197
-
"license": "MIT",
3198
-
"engines": {
3199
-
"node": ">= 0.6"
3200
-
}
3201
-
},
3202
-
"node_modules/mime-types": {
3203
-
"version": "3.0.1",
3204
-
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
3205
-
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
3206
-
"dev": true,
3207
-
"license": "MIT",
3208
-
"dependencies": {
3209
-
"mime-db": "^1.54.0"
3210
-
},
3211
-
"engines": {
3212
-
"node": ">= 0.6"
3213
-
}
3214
-
},
3215
2979
"node_modules/minimatch": {
3216
2980
"version": "3.1.2",
3217
2981
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
···
3233
2997
"license": "MIT"
3234
2998
},
3235
2999
"node_modules/multiformats": {
3236
-
"version": "9.9.0",
3237
-
"resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz",
3238
-
"integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==",
3239
-
"license": "(Apache-2.0 AND MIT)"
3000
+
"version": "13.3.6",
3001
+
"resolved": "https://registry.npmjs.org/multiformats/-/multiformats-13.3.6.tgz",
3002
+
"integrity": "sha512-yakbt9cPYj8d3vi/8o/XWm61MrOILo7fsTL0qxNx6zS0Nso6K5JqqS2WV7vK/KSuDBvrW3KfCwAdAgarAgOmww==",
3003
+
"license": "Apache-2.0 OR MIT"
3240
3004
},
3241
3005
"node_modules/nanoid": {
3242
3006
"version": "3.3.11",
···
3264
3028
"dev": true,
3265
3029
"license": "MIT"
3266
3030
},
3267
-
"node_modules/negotiator": {
3268
-
"version": "1.0.0",
3269
-
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
3270
-
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
3271
-
"dev": true,
3272
-
"license": "MIT",
3273
-
"engines": {
3274
-
"node": ">= 0.6"
3275
-
}
3276
-
},
3277
3031
"node_modules/node-releases": {
3278
3032
"version": "2.0.19",
3279
3033
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
···
3281
3035
"dev": true,
3282
3036
"license": "MIT"
3283
3037
},
3284
-
"node_modules/object-assign": {
3285
-
"version": "4.1.1",
3286
-
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
3287
-
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
3288
-
"dev": true,
3289
-
"license": "MIT",
3290
-
"engines": {
3291
-
"node": ">=0.10.0"
3292
-
}
3293
-
},
3294
-
"node_modules/object-inspect": {
3295
-
"version": "1.13.4",
3296
-
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
3297
-
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
3038
+
"node_modules/open": {
3039
+
"version": "8.4.2",
3040
+
"resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz",
3041
+
"integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==",
3298
3042
"dev": true,
3299
3043
"license": "MIT",
3044
+
"dependencies": {
3045
+
"define-lazy-prop": "^2.0.0",
3046
+
"is-docker": "^2.1.1",
3047
+
"is-wsl": "^2.2.0"
3048
+
},
3300
3049
"engines": {
3301
-
"node": ">= 0.4"
3050
+
"node": ">=12"
3302
3051
},
3303
3052
"funding": {
3304
-
"url": "https://github.com/sponsors/ljharb"
3305
-
}
3306
-
},
3307
-
"node_modules/on-finished": {
3308
-
"version": "2.4.1",
3309
-
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
3310
-
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
3311
-
"dev": true,
3312
-
"license": "MIT",
3313
-
"dependencies": {
3314
-
"ee-first": "1.1.1"
3315
-
},
3316
-
"engines": {
3317
-
"node": ">= 0.8"
3318
-
}
3319
-
},
3320
-
"node_modules/once": {
3321
-
"version": "1.4.0",
3322
-
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
3323
-
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
3324
-
"dev": true,
3325
-
"license": "ISC",
3326
-
"dependencies": {
3327
-
"wrappy": "1"
3053
+
"url": "https://github.com/sponsors/sindresorhus"
3328
3054
}
3329
3055
},
3330
3056
"node_modules/optionator": {
···
3390
3116
"node": ">=6"
3391
3117
}
3392
3118
},
3393
-
"node_modules/parseurl": {
3394
-
"version": "1.3.3",
3395
-
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
3396
-
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
3397
-
"dev": true,
3398
-
"license": "MIT",
3399
-
"engines": {
3400
-
"node": ">= 0.8"
3401
-
}
3402
-
},
3403
3119
"node_modules/path-exists": {
3404
3120
"version": "4.0.0",
3405
3121
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
···
3420
3136
"node": ">=8"
3421
3137
}
3422
3138
},
3423
-
"node_modules/path-to-regexp": {
3424
-
"version": "8.2.0",
3425
-
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
3426
-
"integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
3427
-
"dev": true,
3428
-
"license": "MIT",
3429
-
"engines": {
3430
-
"node": ">=16"
3431
-
}
3432
-
},
3433
3139
"node_modules/picocolors": {
3434
3140
"version": "1.1.1",
3435
3141
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
···
3448
3154
},
3449
3155
"funding": {
3450
3156
"url": "https://github.com/sponsors/jonschlinkert"
3451
-
}
3452
-
},
3453
-
"node_modules/pkce-challenge": {
3454
-
"version": "5.0.0",
3455
-
"resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz",
3456
-
"integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==",
3457
-
"dev": true,
3458
-
"license": "MIT",
3459
-
"engines": {
3460
-
"node": ">=16.20.0"
3461
3157
}
3462
3158
},
3463
3159
"node_modules/postcss": {
···
3499
3195
"node": ">= 0.8.0"
3500
3196
}
3501
3197
},
3502
-
"node_modules/proxy-addr": {
3503
-
"version": "2.0.7",
3504
-
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
3505
-
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
3506
-
"dev": true,
3507
-
"license": "MIT",
3508
-
"dependencies": {
3509
-
"forwarded": "0.2.0",
3510
-
"ipaddr.js": "1.9.1"
3511
-
},
3512
-
"engines": {
3513
-
"node": ">= 0.10"
3514
-
}
3515
-
},
3516
3198
"node_modules/punycode": {
3517
3199
"version": "2.3.1",
3518
3200
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
···
3523
3205
"node": ">=6"
3524
3206
}
3525
3207
},
3526
-
"node_modules/qs": {
3527
-
"version": "6.14.0",
3528
-
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
3529
-
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
3530
-
"dev": true,
3531
-
"license": "BSD-3-Clause",
3532
-
"dependencies": {
3533
-
"side-channel": "^1.1.0"
3534
-
},
3535
-
"engines": {
3536
-
"node": ">=0.6"
3537
-
},
3538
-
"funding": {
3539
-
"url": "https://github.com/sponsors/ljharb"
3540
-
}
3541
-
},
3542
3208
"node_modules/queue-microtask": {
3543
3209
"version": "1.2.3",
3544
3210
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
···
3560
3226
],
3561
3227
"license": "MIT"
3562
3228
},
3563
-
"node_modules/range-parser": {
3564
-
"version": "1.2.1",
3565
-
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
3566
-
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
3567
-
"dev": true,
3568
-
"license": "MIT",
3569
-
"engines": {
3570
-
"node": ">= 0.6"
3571
-
}
3572
-
},
3573
-
"node_modules/raw-body": {
3574
-
"version": "3.0.0",
3575
-
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
3576
-
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
3577
-
"dev": true,
3578
-
"license": "MIT",
3579
-
"dependencies": {
3580
-
"bytes": "3.1.2",
3581
-
"http-errors": "2.0.0",
3582
-
"iconv-lite": "0.6.3",
3583
-
"unpipe": "1.0.0"
3584
-
},
3585
-
"engines": {
3586
-
"node": ">= 0.8"
3587
-
}
3588
-
},
3589
3229
"node_modules/react": {
3590
3230
"version": "19.1.0",
3591
3231
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
···
3618
3258
}
3619
3259
},
3620
3260
"node_modules/react-router": {
3621
-
"version": "7.5.3",
3622
-
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.5.3.tgz",
3623
-
"integrity": "sha512-3iUDM4/fZCQ89SXlDa+Ph3MevBrozBAI655OAfWQlTm9nBR0IKlrmNwFow5lPHttbwvITZfkeeeZFP6zt3F7pw==",
3261
+
"version": "7.6.1",
3262
+
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.1.tgz",
3263
+
"integrity": "sha512-hPJXXxHJZEsPFNVbtATH7+MMX43UDeOauz+EAU4cgqTn7ojdI9qQORqS8Z0qmDlL1TclO/6jLRYUEtbWidtdHQ==",
3624
3264
"license": "MIT",
3625
3265
"dependencies": {
3626
3266
"cookie": "^1.0.1",
3627
-
"set-cookie-parser": "^2.6.0",
3628
-
"turbo-stream": "2.4.0"
3267
+
"set-cookie-parser": "^2.6.0"
3629
3268
},
3630
3269
"engines": {
3631
3270
"node": ">=20.0.0"
···
3641
3280
}
3642
3281
},
3643
3282
"node_modules/react-router-dom": {
3644
-
"version": "7.5.3",
3645
-
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.5.3.tgz",
3646
-
"integrity": "sha512-cK0jSaTyW4jV9SRKAItMIQfWZ/D6WEZafgHuuCb9g+SjhLolY78qc+De4w/Cz9ybjvLzShAmaIMEXt8iF1Cm+A==",
3283
+
"version": "7.6.1",
3284
+
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.6.1.tgz",
3285
+
"integrity": "sha512-vxU7ei//UfPYQ3iZvHuO1D/5fX3/JOqhNTbRR+WjSBWxf9bIvpWK+ftjmdfJHzPOuMQKe2fiEdG+dZX6E8uUpA==",
3647
3286
"license": "MIT",
3648
3287
"dependencies": {
3649
-
"react-router": "7.5.3"
3288
+
"react-router": "7.6.1"
3650
3289
},
3651
3290
"engines": {
3652
3291
"node": ">=20.0.0"
···
3656
3295
"react-dom": ">=18"
3657
3296
}
3658
3297
},
3659
-
"node_modules/react-router/node_modules/cookie": {
3660
-
"version": "1.0.2",
3661
-
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
3662
-
"integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
3298
+
"node_modules/require-directory": {
3299
+
"version": "2.1.1",
3300
+
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
3301
+
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
3302
+
"dev": true,
3663
3303
"license": "MIT",
3664
3304
"engines": {
3665
-
"node": ">=18"
3305
+
"node": ">=0.10.0"
3666
3306
}
3667
3307
},
3668
3308
"node_modules/resolve-from": {
···
3687
3327
}
3688
3328
},
3689
3329
"node_modules/rollup": {
3690
-
"version": "4.40.1",
3691
-
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.1.tgz",
3692
-
"integrity": "sha512-C5VvvgCCyfyotVITIAv+4efVytl5F7wt+/I2i9q9GZcEXW9BP52YYOXC58igUi+LFZVHukErIIqQSWwv/M3WRw==",
3330
+
"version": "4.41.1",
3331
+
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.1.tgz",
3332
+
"integrity": "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==",
3693
3333
"dev": true,
3694
3334
"license": "MIT",
3695
3335
"dependencies": {
···
3703
3343
"npm": ">=8.0.0"
3704
3344
},
3705
3345
"optionalDependencies": {
3706
-
"@rollup/rollup-android-arm-eabi": "4.40.1",
3707
-
"@rollup/rollup-android-arm64": "4.40.1",
3708
-
"@rollup/rollup-darwin-arm64": "4.40.1",
3709
-
"@rollup/rollup-darwin-x64": "4.40.1",
3710
-
"@rollup/rollup-freebsd-arm64": "4.40.1",
3711
-
"@rollup/rollup-freebsd-x64": "4.40.1",
3712
-
"@rollup/rollup-linux-arm-gnueabihf": "4.40.1",
3713
-
"@rollup/rollup-linux-arm-musleabihf": "4.40.1",
3714
-
"@rollup/rollup-linux-arm64-gnu": "4.40.1",
3715
-
"@rollup/rollup-linux-arm64-musl": "4.40.1",
3716
-
"@rollup/rollup-linux-loongarch64-gnu": "4.40.1",
3717
-
"@rollup/rollup-linux-powerpc64le-gnu": "4.40.1",
3718
-
"@rollup/rollup-linux-riscv64-gnu": "4.40.1",
3719
-
"@rollup/rollup-linux-riscv64-musl": "4.40.1",
3720
-
"@rollup/rollup-linux-s390x-gnu": "4.40.1",
3721
-
"@rollup/rollup-linux-x64-gnu": "4.40.1",
3722
-
"@rollup/rollup-linux-x64-musl": "4.40.1",
3723
-
"@rollup/rollup-win32-arm64-msvc": "4.40.1",
3724
-
"@rollup/rollup-win32-ia32-msvc": "4.40.1",
3725
-
"@rollup/rollup-win32-x64-msvc": "4.40.1",
3346
+
"@rollup/rollup-android-arm-eabi": "4.41.1",
3347
+
"@rollup/rollup-android-arm64": "4.41.1",
3348
+
"@rollup/rollup-darwin-arm64": "4.41.1",
3349
+
"@rollup/rollup-darwin-x64": "4.41.1",
3350
+
"@rollup/rollup-freebsd-arm64": "4.41.1",
3351
+
"@rollup/rollup-freebsd-x64": "4.41.1",
3352
+
"@rollup/rollup-linux-arm-gnueabihf": "4.41.1",
3353
+
"@rollup/rollup-linux-arm-musleabihf": "4.41.1",
3354
+
"@rollup/rollup-linux-arm64-gnu": "4.41.1",
3355
+
"@rollup/rollup-linux-arm64-musl": "4.41.1",
3356
+
"@rollup/rollup-linux-loongarch64-gnu": "4.41.1",
3357
+
"@rollup/rollup-linux-powerpc64le-gnu": "4.41.1",
3358
+
"@rollup/rollup-linux-riscv64-gnu": "4.41.1",
3359
+
"@rollup/rollup-linux-riscv64-musl": "4.41.1",
3360
+
"@rollup/rollup-linux-s390x-gnu": "4.41.1",
3361
+
"@rollup/rollup-linux-x64-gnu": "4.41.1",
3362
+
"@rollup/rollup-linux-x64-musl": "4.41.1",
3363
+
"@rollup/rollup-win32-arm64-msvc": "4.41.1",
3364
+
"@rollup/rollup-win32-ia32-msvc": "4.41.1",
3365
+
"@rollup/rollup-win32-x64-msvc": "4.41.1",
3726
3366
"fsevents": "~2.3.2"
3727
3367
}
3728
3368
},
3729
-
"node_modules/router": {
3730
-
"version": "2.2.0",
3731
-
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
3732
-
"integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
3369
+
"node_modules/rollup-plugin-visualizer": {
3370
+
"version": "6.0.1",
3371
+
"resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-6.0.1.tgz",
3372
+
"integrity": "sha512-NjlGElvLXCSZSAi3gNRZbfX3qlQbQcJ9TW97c5JpqfVwMhttj9YwEdPwcvbKj91RnMX2PWAjonvSEv6UEYtnRQ==",
3733
3373
"dev": true,
3734
3374
"license": "MIT",
3735
3375
"dependencies": {
3736
-
"debug": "^4.4.0",
3737
-
"depd": "^2.0.0",
3738
-
"is-promise": "^4.0.0",
3739
-
"parseurl": "^1.3.3",
3740
-
"path-to-regexp": "^8.0.0"
3376
+
"open": "^8.0.0",
3377
+
"picomatch": "^4.0.2",
3378
+
"source-map": "^0.7.4",
3379
+
"yargs": "^17.5.1"
3380
+
},
3381
+
"bin": {
3382
+
"rollup-plugin-visualizer": "dist/bin/cli.js"
3741
3383
},
3742
3384
"engines": {
3743
-
"node": ">= 18"
3385
+
"node": ">=18"
3386
+
},
3387
+
"peerDependencies": {
3388
+
"rolldown": "1.x",
3389
+
"rollup": "2.x || 3.x || 4.x"
3390
+
},
3391
+
"peerDependenciesMeta": {
3392
+
"rolldown": {
3393
+
"optional": true
3394
+
},
3395
+
"rollup": {
3396
+
"optional": true
3397
+
}
3398
+
}
3399
+
},
3400
+
"node_modules/rollup-plugin-visualizer/node_modules/picomatch": {
3401
+
"version": "4.0.2",
3402
+
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
3403
+
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
3404
+
"dev": true,
3405
+
"license": "MIT",
3406
+
"engines": {
3407
+
"node": ">=12"
3408
+
},
3409
+
"funding": {
3410
+
"url": "https://github.com/sponsors/jonschlinkert"
3744
3411
}
3745
3412
},
3746
3413
"node_modules/run-parallel": {
···
3767
3434
"queue-microtask": "^1.2.2"
3768
3435
}
3769
3436
},
3770
-
"node_modules/safe-buffer": {
3771
-
"version": "5.2.1",
3772
-
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
3773
-
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
3774
-
"dev": true,
3775
-
"funding": [
3776
-
{
3777
-
"type": "github",
3778
-
"url": "https://github.com/sponsors/feross"
3779
-
},
3780
-
{
3781
-
"type": "patreon",
3782
-
"url": "https://www.patreon.com/feross"
3783
-
},
3784
-
{
3785
-
"type": "consulting",
3786
-
"url": "https://feross.org/support"
3787
-
}
3788
-
],
3789
-
"license": "MIT"
3790
-
},
3791
-
"node_modules/safer-buffer": {
3792
-
"version": "2.1.2",
3793
-
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
3794
-
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
3795
-
"dev": true,
3796
-
"license": "MIT"
3797
-
},
3798
3437
"node_modules/scheduler": {
3799
3438
"version": "0.26.0",
3800
3439
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
···
3802
3441
"license": "MIT"
3803
3442
},
3804
3443
"node_modules/semver": {
3805
-
"version": "6.3.1",
3806
-
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
3807
-
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
3444
+
"version": "7.7.2",
3445
+
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
3446
+
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
3808
3447
"dev": true,
3809
3448
"license": "ISC",
3810
3449
"bin": {
3811
3450
"semver": "bin/semver.js"
3812
-
}
3813
-
},
3814
-
"node_modules/send": {
3815
-
"version": "1.2.0",
3816
-
"resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
3817
-
"integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
3818
-
"dev": true,
3819
-
"license": "MIT",
3820
-
"dependencies": {
3821
-
"debug": "^4.3.5",
3822
-
"encodeurl": "^2.0.0",
3823
-
"escape-html": "^1.0.3",
3824
-
"etag": "^1.8.1",
3825
-
"fresh": "^2.0.0",
3826
-
"http-errors": "^2.0.0",
3827
-
"mime-types": "^3.0.1",
3828
-
"ms": "^2.1.3",
3829
-
"on-finished": "^2.4.1",
3830
-
"range-parser": "^1.2.1",
3831
-
"statuses": "^2.0.1"
3832
3451
},
3833
3452
"engines": {
3834
-
"node": ">= 18"
3835
-
}
3836
-
},
3837
-
"node_modules/serve-static": {
3838
-
"version": "2.2.0",
3839
-
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
3840
-
"integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
3841
-
"dev": true,
3842
-
"license": "MIT",
3843
-
"dependencies": {
3844
-
"encodeurl": "^2.0.0",
3845
-
"escape-html": "^1.0.3",
3846
-
"parseurl": "^1.3.3",
3847
-
"send": "^1.2.0"
3848
-
},
3849
-
"engines": {
3850
-
"node": ">= 18"
3453
+
"node": ">=10"
3851
3454
}
3852
3455
},
3853
3456
"node_modules/set-cookie-parser": {
···
3855
3458
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
3856
3459
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
3857
3460
"license": "MIT"
3858
-
},
3859
-
"node_modules/setprototypeof": {
3860
-
"version": "1.2.0",
3861
-
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
3862
-
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
3863
-
"dev": true,
3864
-
"license": "ISC"
3865
3461
},
3866
3462
"node_modules/shebang-command": {
3867
3463
"version": "2.0.0",
···
3886
3482
"node": ">=8"
3887
3483
}
3888
3484
},
3889
-
"node_modules/side-channel": {
3890
-
"version": "1.1.0",
3891
-
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
3892
-
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
3485
+
"node_modules/source-map": {
3486
+
"version": "0.7.4",
3487
+
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
3488
+
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
3893
3489
"dev": true,
3894
-
"license": "MIT",
3895
-
"dependencies": {
3896
-
"es-errors": "^1.3.0",
3897
-
"object-inspect": "^1.13.3",
3898
-
"side-channel-list": "^1.0.0",
3899
-
"side-channel-map": "^1.0.1",
3900
-
"side-channel-weakmap": "^1.0.2"
3901
-
},
3490
+
"license": "BSD-3-Clause",
3902
3491
"engines": {
3903
-
"node": ">= 0.4"
3904
-
},
3905
-
"funding": {
3906
-
"url": "https://github.com/sponsors/ljharb"
3492
+
"node": ">= 8"
3907
3493
}
3908
3494
},
3909
-
"node_modules/side-channel-list": {
3910
-
"version": "1.0.0",
3911
-
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
3912
-
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
3495
+
"node_modules/source-map-js": {
3496
+
"version": "1.2.1",
3497
+
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
3498
+
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
3913
3499
"dev": true,
3914
-
"license": "MIT",
3915
-
"dependencies": {
3916
-
"es-errors": "^1.3.0",
3917
-
"object-inspect": "^1.13.3"
3918
-
},
3500
+
"license": "BSD-3-Clause",
3919
3501
"engines": {
3920
-
"node": ">= 0.4"
3921
-
},
3922
-
"funding": {
3923
-
"url": "https://github.com/sponsors/ljharb"
3502
+
"node": ">=0.10.0"
3924
3503
}
3925
3504
},
3926
-
"node_modules/side-channel-map": {
3927
-
"version": "1.0.1",
3928
-
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
3929
-
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
3505
+
"node_modules/source-map-support": {
3506
+
"version": "0.5.21",
3507
+
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
3508
+
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
3930
3509
"dev": true,
3931
3510
"license": "MIT",
3932
3511
"dependencies": {
3933
-
"call-bound": "^1.0.2",
3934
-
"es-errors": "^1.3.0",
3935
-
"get-intrinsic": "^1.2.5",
3936
-
"object-inspect": "^1.13.3"
3937
-
},
3512
+
"buffer-from": "^1.0.0",
3513
+
"source-map": "^0.6.0"
3514
+
}
3515
+
},
3516
+
"node_modules/source-map-support/node_modules/source-map": {
3517
+
"version": "0.6.1",
3518
+
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
3519
+
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
3520
+
"dev": true,
3521
+
"license": "BSD-3-Clause",
3938
3522
"engines": {
3939
-
"node": ">= 0.4"
3940
-
},
3941
-
"funding": {
3942
-
"url": "https://github.com/sponsors/ljharb"
3523
+
"node": ">=0.10.0"
3943
3524
}
3944
3525
},
3945
-
"node_modules/side-channel-weakmap": {
3946
-
"version": "1.0.2",
3947
-
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
3948
-
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
3526
+
"node_modules/string-width": {
3527
+
"version": "4.2.3",
3528
+
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
3529
+
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
3949
3530
"dev": true,
3950
3531
"license": "MIT",
3951
3532
"dependencies": {
3952
-
"call-bound": "^1.0.2",
3953
-
"es-errors": "^1.3.0",
3954
-
"get-intrinsic": "^1.2.5",
3955
-
"object-inspect": "^1.13.3",
3956
-
"side-channel-map": "^1.0.1"
3533
+
"emoji-regex": "^8.0.0",
3534
+
"is-fullwidth-code-point": "^3.0.0",
3535
+
"strip-ansi": "^6.0.1"
3957
3536
},
3958
3537
"engines": {
3959
-
"node": ">= 0.4"
3960
-
},
3961
-
"funding": {
3962
-
"url": "https://github.com/sponsors/ljharb"
3963
-
}
3964
-
},
3965
-
"node_modules/source-map-js": {
3966
-
"version": "1.2.1",
3967
-
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
3968
-
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
3969
-
"dev": true,
3970
-
"license": "BSD-3-Clause",
3971
-
"engines": {
3972
-
"node": ">=0.10.0"
3538
+
"node": ">=8"
3973
3539
}
3974
3540
},
3975
-
"node_modules/statuses": {
3976
-
"version": "2.0.1",
3977
-
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
3978
-
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
3541
+
"node_modules/strip-ansi": {
3542
+
"version": "6.0.1",
3543
+
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
3544
+
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
3979
3545
"dev": true,
3980
3546
"license": "MIT",
3547
+
"dependencies": {
3548
+
"ansi-regex": "^5.0.1"
3549
+
},
3981
3550
"engines": {
3982
-
"node": ">= 0.8"
3551
+
"node": ">=8"
3983
3552
}
3984
3553
},
3985
3554
"node_modules/strip-json-comments": {
···
4008
3577
"node": ">=8"
4009
3578
}
4010
3579
},
3580
+
"node_modules/terser": {
3581
+
"version": "5.40.0",
3582
+
"resolved": "https://registry.npmjs.org/terser/-/terser-5.40.0.tgz",
3583
+
"integrity": "sha512-cfeKl/jjwSR5ar7d0FGmave9hFGJT8obyo0z+CrQOylLDbk7X81nPU6vq9VORa5jU30SkDnT2FXjLbR8HLP+xA==",
3584
+
"dev": true,
3585
+
"license": "BSD-2-Clause",
3586
+
"dependencies": {
3587
+
"@jridgewell/source-map": "^0.3.3",
3588
+
"acorn": "^8.14.0",
3589
+
"commander": "^2.20.0",
3590
+
"source-map-support": "~0.5.20"
3591
+
},
3592
+
"bin": {
3593
+
"terser": "bin/terser"
3594
+
},
3595
+
"engines": {
3596
+
"node": ">=10"
3597
+
}
3598
+
},
4011
3599
"node_modules/tinyglobby": {
4012
-
"version": "0.2.13",
4013
-
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
4014
-
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
3600
+
"version": "0.2.14",
3601
+
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
3602
+
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
4015
3603
"dev": true,
4016
3604
"license": "MIT",
4017
3605
"dependencies": {
···
4026
3614
}
4027
3615
},
4028
3616
"node_modules/tinyglobby/node_modules/fdir": {
4029
-
"version": "6.4.4",
4030
-
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
4031
-
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
3617
+
"version": "6.4.5",
3618
+
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz",
3619
+
"integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==",
4032
3620
"dev": true,
4033
3621
"license": "MIT",
4034
3622
"peerDependencies": {
···
4054
3642
}
4055
3643
},
4056
3644
"node_modules/tlds": {
4057
-
"version": "1.258.0",
4058
-
"resolved": "https://registry.npmjs.org/tlds/-/tlds-1.258.0.tgz",
4059
-
"integrity": "sha512-XGhStWuOlBA5D8QnyN2xtgB2cUOdJ3ztisne1DYVWMcVH29qh8eQIpRmP3HnuJLdgyzG0HpdGzRMu1lm/Oictw==",
3645
+
"version": "1.259.0",
3646
+
"resolved": "https://registry.npmjs.org/tlds/-/tlds-1.259.0.tgz",
3647
+
"integrity": "sha512-AldGGlDP0PNgwppe2quAvuBl18UcjuNtOnDuUkqhd6ipPqrYYBt3aTxK1QTsBVknk97lS2JcafWMghjGWFtunw==",
4060
3648
"license": "MIT",
4061
3649
"bin": {
4062
3650
"tlds": "bin.js"
···
4073
3661
},
4074
3662
"engines": {
4075
3663
"node": ">=8.0"
4076
-
}
4077
-
},
4078
-
"node_modules/toidentifier": {
4079
-
"version": "1.0.1",
4080
-
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
4081
-
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
4082
-
"dev": true,
4083
-
"license": "MIT",
4084
-
"engines": {
4085
-
"node": ">=0.6"
4086
3664
}
4087
3665
},
4088
3666
"node_modules/ts-api-utils": {
···
4098
3676
"typescript": ">=4.8.4"
4099
3677
}
4100
3678
},
4101
-
"node_modules/turbo-stream": {
4102
-
"version": "2.4.0",
4103
-
"resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz",
4104
-
"integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==",
4105
-
"license": "ISC"
4106
-
},
4107
3679
"node_modules/type-check": {
4108
3680
"version": "0.4.0",
4109
3681
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
···
4117
3689
"node": ">= 0.8.0"
4118
3690
}
4119
3691
},
4120
-
"node_modules/type-is": {
4121
-
"version": "2.0.1",
4122
-
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
4123
-
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
4124
-
"dev": true,
4125
-
"license": "MIT",
4126
-
"dependencies": {
4127
-
"content-type": "^1.0.5",
4128
-
"media-typer": "^1.1.0",
4129
-
"mime-types": "^3.0.0"
4130
-
},
4131
-
"engines": {
4132
-
"node": ">= 0.6"
4133
-
}
4134
-
},
4135
3692
"node_modules/typescript": {
4136
3693
"version": "5.7.3",
4137
3694
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
···
4147
3704
}
4148
3705
},
4149
3706
"node_modules/typescript-eslint": {
4150
-
"version": "8.31.1",
4151
-
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.31.1.tgz",
4152
-
"integrity": "sha512-j6DsEotD/fH39qKzXTQRwYYWlt7D+0HmfpOK+DVhwJOFLcdmn92hq3mBb7HlKJHbjjI/gTOqEcc9d6JfpFf/VA==",
3707
+
"version": "8.33.0",
3708
+
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.33.0.tgz",
3709
+
"integrity": "sha512-5YmNhF24ylCsvdNW2oJwMzTbaeO4bg90KeGtMjUw0AGtHksgEPLRTUil+coHwCfiu4QjVJFnjp94DmU6zV7DhQ==",
4153
3710
"dev": true,
4154
3711
"license": "MIT",
4155
3712
"dependencies": {
4156
-
"@typescript-eslint/eslint-plugin": "8.31.1",
4157
-
"@typescript-eslint/parser": "8.31.1",
4158
-
"@typescript-eslint/utils": "8.31.1"
3713
+
"@typescript-eslint/eslint-plugin": "8.33.0",
3714
+
"@typescript-eslint/parser": "8.33.0",
3715
+
"@typescript-eslint/utils": "8.33.0"
4159
3716
},
4160
3717
"engines": {
4161
3718
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
···
4170
3727
}
4171
3728
},
4172
3729
"node_modules/uint8arrays": {
4173
-
"version": "3.0.0",
4174
-
"resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz",
4175
-
"integrity": "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==",
4176
-
"license": "MIT",
3730
+
"version": "5.1.0",
3731
+
"resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-5.1.0.tgz",
3732
+
"integrity": "sha512-vA6nFepEmlSKkMBnLBaUMVvAC4G3CTmO58C12y4sq6WPDOR7mOFYOi7GlrQ4djeSbP6JG9Pv9tJDM97PedRSww==",
3733
+
"license": "Apache-2.0 OR MIT",
4177
3734
"dependencies": {
4178
-
"multiformats": "^9.4.2"
3735
+
"multiformats": "^13.0.0"
4179
3736
}
4180
3737
},
4181
-
"node_modules/unpipe": {
4182
-
"version": "1.0.0",
4183
-
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
4184
-
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
3738
+
"node_modules/undici-types": {
3739
+
"version": "6.21.0",
3740
+
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
3741
+
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
4185
3742
"dev": true,
4186
-
"license": "MIT",
4187
-
"engines": {
4188
-
"node": ">= 0.8"
4189
-
}
3743
+
"license": "MIT"
4190
3744
},
4191
3745
"node_modules/update-browserslist-db": {
4192
3746
"version": "1.1.3",
···
4229
3783
"punycode": "^2.1.0"
4230
3784
}
4231
3785
},
4232
-
"node_modules/vary": {
4233
-
"version": "1.1.2",
4234
-
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
4235
-
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
4236
-
"dev": true,
4237
-
"license": "MIT",
4238
-
"engines": {
4239
-
"node": ">= 0.8"
4240
-
}
4241
-
},
4242
3786
"node_modules/vite": {
4243
-
"version": "6.3.4",
4244
-
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.4.tgz",
4245
-
"integrity": "sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==",
3787
+
"version": "6.3.5",
3788
+
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
3789
+
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
4246
3790
"dev": true,
4247
3791
"license": "MIT",
4248
3792
"dependencies": {
···
4315
3859
}
4316
3860
},
4317
3861
"node_modules/vite/node_modules/fdir": {
4318
-
"version": "6.4.4",
4319
-
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
4320
-
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
3862
+
"version": "6.4.5",
3863
+
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz",
3864
+
"integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==",
4321
3865
"dev": true,
4322
3866
"license": "MIT",
4323
3867
"peerDependencies": {
···
4368
3912
"node": ">=0.10.0"
4369
3913
}
4370
3914
},
4371
-
"node_modules/wrappy": {
4372
-
"version": "1.0.2",
4373
-
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
4374
-
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
3915
+
"node_modules/wrap-ansi": {
3916
+
"version": "7.0.0",
3917
+
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
3918
+
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
3919
+
"dev": true,
3920
+
"license": "MIT",
3921
+
"dependencies": {
3922
+
"ansi-styles": "^4.0.0",
3923
+
"string-width": "^4.1.0",
3924
+
"strip-ansi": "^6.0.0"
3925
+
},
3926
+
"engines": {
3927
+
"node": ">=10"
3928
+
},
3929
+
"funding": {
3930
+
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
3931
+
}
3932
+
},
3933
+
"node_modules/y18n": {
3934
+
"version": "5.0.8",
3935
+
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
3936
+
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
4375
3937
"dev": true,
4376
-
"license": "ISC"
3938
+
"license": "ISC",
3939
+
"engines": {
3940
+
"node": ">=10"
3941
+
}
4377
3942
},
4378
3943
"node_modules/yallist": {
4379
3944
"version": "3.1.1",
···
4382
3947
"dev": true,
4383
3948
"license": "ISC"
4384
3949
},
3950
+
"node_modules/yargs": {
3951
+
"version": "17.7.2",
3952
+
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
3953
+
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
3954
+
"dev": true,
3955
+
"license": "MIT",
3956
+
"dependencies": {
3957
+
"cliui": "^8.0.1",
3958
+
"escalade": "^3.1.1",
3959
+
"get-caller-file": "^2.0.5",
3960
+
"require-directory": "^2.1.1",
3961
+
"string-width": "^4.2.3",
3962
+
"y18n": "^5.0.5",
3963
+
"yargs-parser": "^21.1.1"
3964
+
},
3965
+
"engines": {
3966
+
"node": ">=12"
3967
+
}
3968
+
},
3969
+
"node_modules/yargs-parser": {
3970
+
"version": "21.1.1",
3971
+
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
3972
+
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
3973
+
"dev": true,
3974
+
"license": "ISC",
3975
+
"engines": {
3976
+
"node": ">=12"
3977
+
}
3978
+
},
4385
3979
"node_modules/yocto-queue": {
4386
3980
"version": "0.1.0",
4387
3981
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
···
4396
3990
}
4397
3991
},
4398
3992
"node_modules/zod": {
4399
-
"version": "3.24.3",
4400
-
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz",
4401
-
"integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==",
3993
+
"version": "3.25.32",
3994
+
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.32.tgz",
3995
+
"integrity": "sha512-OSm2xTIRfW8CV5/QKgngwmQW/8aPfGdaQFlrGoErlgg/Epm7cjb6K6VEyExfe65a3VybUOnu381edLb0dfJl0g==",
4402
3996
"license": "MIT",
4403
3997
"funding": {
4404
3998
"url": "https://github.com/sponsors/colinhacks"
4405
-
}
4406
-
},
4407
-
"node_modules/zod-to-json-schema": {
4408
-
"version": "3.24.5",
4409
-
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz",
4410
-
"integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==",
4411
-
"dev": true,
4412
-
"license": "ISC",
4413
-
"peerDependencies": {
4414
-
"zod": "^3.24.1"
4415
3999
}
4416
4000
}
4417
4001
}
+16
-5
package.json
+16
-5
package.json
···
1
1
{
2
2
"name": "atproto-migration",
3
3
"private": true,
4
-
"version": "0.0.0",
4
+
"version": "0.1.0",
5
5
"type": "module",
6
6
"scripts": {
7
7
"dev": "vite",
8
8
"build": "tsc -b && vite build",
9
-
"lint": "eslint .",
9
+
"lint": "eslint src/",
10
10
"preview": "vite preview"
11
11
},
12
12
"dependencies": {
13
13
"@atproto/api": "^0.15.5",
14
+
"@atproto/crypto": "^0.4.4",
15
+
"multiformats": "^13.3.6",
14
16
"react": "^19.0.0",
15
17
"react-dom": "^19.0.0",
16
-
"react-router-dom": "^7.5.3"
18
+
"react-router-dom": "^7.5.3",
19
+
"uint8arrays": "^5.1.0"
17
20
},
18
21
"devDependencies": {
22
+
"@eslint/eslintrc": "^3.3.1",
19
23
"@eslint/js": "^9.22.0",
20
-
"@types/react": "^19.0.10",
21
-
"@types/react-dom": "^19.0.4",
24
+
"@humanwhocodes/module-importer": "^1.0.1",
25
+
"@types/node": "^22.15.14",
26
+
"@types/react": "^19.1.6",
27
+
"@types/react-dom": "^19.1.5",
28
+
"@typescript-eslint/eslint-plugin": "^8.33.0",
29
+
"@typescript-eslint/parser": "^8.32.0",
22
30
"@vitejs/plugin-react": "^4.3.4",
23
31
"eslint": "^9.22.0",
24
32
"eslint-plugin-react-hooks": "^5.2.0",
25
33
"eslint-plugin-react-refresh": "^0.4.19",
26
34
"globals": "^16.0.0",
35
+
"jiti": "^2.4.2",
36
+
"rollup-plugin-visualizer": "^6.0.1",
37
+
"terser": "^5.40.0",
27
38
"typescript": "~5.7.2",
28
39
"typescript-eslint": "^8.26.1",
29
40
"vite": "^6.3.1"
+37
public/favicon.svg
+37
public/favicon.svg
···
1
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+
<svg
3
+
width="256"
4
+
height="256"
5
+
viewBox="0 0 32 32"
6
+
fill="none"
7
+
version="1.1"
8
+
id="svg5"
9
+
xmlns="http://www.w3.org/2000/svg"
10
+
xmlns:svg="http://www.w3.org/2000/svg">
11
+
<path
12
+
d="M24 8H8C6.89543 8 6 8.89543 6 10V24C6 25.1046 6.89543 26 8 26H24C25.1046 26 26 25.1046 26 24V10C26 8.89543 25.1046 8 24 8Z"
13
+
stroke="#3b82f6"
14
+
stroke-width="2"
15
+
stroke-linecap="round"
16
+
stroke-linejoin="round"
17
+
id="path1" />
18
+
<path
19
+
d="M20 8V6C20 4.89543 19.1046 4 18 4H14C12.8954 4 12 4.89543 12 6V8"
20
+
stroke="#3b82f6"
21
+
stroke-width="2"
22
+
stroke-linecap="round"
23
+
stroke-linejoin="round"
24
+
id="path2" />
25
+
<path
26
+
d="M6 12H26"
27
+
stroke="#3b82f6"
28
+
stroke-width="2"
29
+
stroke-linecap="round"
30
+
stroke-linejoin="round"
31
+
id="path3" />
32
+
<path
33
+
style="font-weight:bold;font-size:12px;font-family:Galvji;-inkscape-font-specification:'Galvji, Bold';fill:#3b82f6;stroke-width:0.250394"
34
+
d="m 15.861771,17.466183 q -0.46875,0 -0.732422,0.351562 -0.257812,0.351563 -0.257812,0.984375 0,0.626953 0.257812,0.984375 0.263672,0.351563 0.726563,0.351563 0.474609,0 0.74414,-0.357422 0.275391,-0.357422 0.275391,-0.978516 0,-0.621094 -0.275391,-0.978515 -0.269531,-0.357422 -0.738281,-0.357422 z m 0.1875,-3.591797 q 1.054688,0 1.921875,0.316406 0.873047,0.316406 1.494141,0.890625 0.626953,0.574219 0.966797,1.376953 0.345703,0.802735 0.345703,1.769531 0,0.697266 -0.164063,1.259766 -0.158203,0.5625 -0.457031,0.955078 -0.298828,0.392578 -0.726562,0.603516 -0.421875,0.210937 -0.955079,0.210937 -0.550781,0 -0.925781,-0.246093 -0.36914,-0.246094 -0.46875,-0.667969 h -0.105469 q -0.375,0.896484 -1.40625,0.896484 -0.457031,0 -0.832031,-0.175781 -0.375,-0.175781 -0.638672,-0.498047 -0.263672,-0.328125 -0.410156,-0.785156 -0.146484,-0.457031 -0.146484,-1.013672 0,-0.533203 0.140625,-0.972656 0.146484,-0.439453 0.404297,-0.75586 0.257812,-0.316406 0.621093,-0.486328 0.363282,-0.175781 0.802735,-0.175781 0.474609,0 0.832031,0.216797 0.363281,0.216797 0.539062,0.609375 h 0.105469 v -0.697266 h 1.183594 v 3.105469 q 0,0.316406 0.146484,0.486328 0.146485,0.169922 0.416016,0.169922 0.222656,0 0.398437,-0.134766 0.181641,-0.134765 0.310547,-0.386718 0.128907,-0.251954 0.19336,-0.609375 0.07031,-0.363282 0.07031,-0.808594 0,-0.796875 -0.263672,-1.441406 -0.263672,-0.650391 -0.75,-1.107422 -0.480468,-0.457032 -1.166015,-0.703125 -0.679688,-0.251953 -1.511719,-0.251953 -0.849609,0 -1.558594,0.292968 -0.703125,0.28711 -1.21289,0.814453 -0.509766,0.521485 -0.791016,1.253907 -0.28125,0.726562 -0.28125,1.605468 0,0.890625 0.28125,1.611329 0.287109,0.714843 0.814453,1.21875 0.533203,0.503906 1.283203,0.773437 0.75586,0.275391 1.6875,0.275391 0.234375,0 0.462891,-0.01758 0.234375,-0.01172 0.445312,-0.03516 0.210938,-0.02344 0.386719,-0.05859 0.175781,-0.03516 0.298828,-0.08203 v 0.955078 q -0.345703,0.09961 -0.779297,0.152344 -0.433593,0.05859 -0.902343,0.05859 -1.142579,0 -2.080079,-0.351563 -0.93164,-0.345703 -1.59375,-0.984375 -0.65625,-0.638671 -1.019531,-1.541015 -0.357422,-0.902344 -0.357422,-2.003906 0,-1.083985 0.357422,-1.980469 0.363281,-0.902344 1.019531,-1.546875 0.65625,-0.644531 1.564454,-1.001953 0.908203,-0.357422 2.009765,-0.357422 z"
35
+
id="text5"
36
+
aria-label="@" />
37
+
</svg>
-1
public/vite.svg
-1
public/vite.svg
···
1
-
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
-172
src/App.tsx
-172
src/App.tsx
···
1
-
import { useState, useEffect } from 'react'
2
-
import { BrowserRouter as Router, Routes, Route, Navigate, useLocation } from 'react-router-dom'
3
-
import { AtpAgent } from '@atproto/api'
4
-
import { AvatarProvider } from './contexts/AvatarContext'
5
-
import { NetworkProvider } from './contexts/NetworkContext'
6
-
import NetworkWarning from './components/common/NetworkWarning'
7
-
import Login from './components/auth/Login'
8
-
import Actions from './components/common/Actions'
9
-
import Migration from './components/common/Migration'
10
-
import MigrationProcess from './components/common/MigrationProcess'
11
-
import RecoveryKey from './components/common/RecoveryKey'
12
-
import RecoveryKeyProcess from './components/common/RecoveryKeyProcess'
13
-
import './styles/App.css'
14
-
15
-
const SESSION_KEY = 'atproto_session';
16
-
const SESSION_EXPIRY = 60 * 60 * 1000; // 1 hour in milliseconds
17
-
18
-
function AppRoutes({ agent, onLogout, handleLogin }: {
19
-
agent: AtpAgent | null;
20
-
onLogout: () => void;
21
-
handleLogin: (agent: AtpAgent) => void;
22
-
}) {
23
-
const location = useLocation();
24
-
25
-
useEffect(() => {
26
-
const checkSession = async () => {
27
-
if (agent) {
28
-
try {
29
-
// Try to make a simple API call to verify the session
30
-
await agent.getProfile({ actor: agent.session?.handle || '' });
31
-
} catch (err) {
32
-
// If the API call fails, the session is likely invalid
33
-
onLogout();
34
-
alert('Your session has expired. Please log in again.');
35
-
}
36
-
}
37
-
};
38
-
39
-
checkSession();
40
-
}, [location.pathname, agent, onLogout]);
41
-
42
-
return (
43
-
<>
44
-
<NetworkWarning />
45
-
<Routes>
46
-
<Route
47
-
path="/"
48
-
element={
49
-
agent ? (
50
-
<Navigate to="/actions" replace />
51
-
) : (
52
-
<Login onLogin={handleLogin} />
53
-
)
54
-
}
55
-
/>
56
-
<Route
57
-
path="/actions"
58
-
element={
59
-
agent ? (
60
-
<Actions agent={agent} onLogout={onLogout} />
61
-
) : (
62
-
<Navigate to="/" replace />
63
-
)
64
-
}
65
-
/>
66
-
<Route
67
-
path="/migration"
68
-
element={
69
-
agent ? (
70
-
<Migration agent={agent} onLogout={onLogout} />
71
-
) : (
72
-
<Navigate to="/" replace />
73
-
)
74
-
}
75
-
/>
76
-
<Route
77
-
path="/migration/process"
78
-
element={
79
-
agent ? (
80
-
<MigrationProcess agent={agent} onLogout={onLogout} />
81
-
) : (
82
-
<Navigate to="/" replace />
83
-
)
84
-
}
85
-
/>
86
-
<Route
87
-
path="/recovery-key"
88
-
element={
89
-
agent ? (
90
-
<RecoveryKey agent={agent} onLogout={onLogout} />
91
-
) : (
92
-
<Navigate to="/" replace />
93
-
)
94
-
}
95
-
/>
96
-
<Route
97
-
path="/recovery-key/process"
98
-
element={
99
-
agent ? (
100
-
<RecoveryKeyProcess agent={agent} onLogout={onLogout} />
101
-
) : (
102
-
<Navigate to="/" replace />
103
-
)
104
-
}
105
-
/>
106
-
</Routes>
107
-
</>
108
-
);
109
-
}
110
-
111
-
function App() {
112
-
const [agent, setAgent] = useState<AtpAgent | null>(null)
113
-
114
-
useEffect(() => {
115
-
// Load session from localStorage on initial load
116
-
const loadSession = async () => {
117
-
const savedSession = localStorage.getItem(SESSION_KEY);
118
-
if (savedSession) {
119
-
const { session, service, timestamp } = JSON.parse(savedSession);
120
-
121
-
// Check if session is expired
122
-
if (Date.now() - timestamp > SESSION_EXPIRY) {
123
-
localStorage.removeItem(SESSION_KEY);
124
-
return;
125
-
}
126
-
127
-
const newAgent = new AtpAgent({ service });
128
-
await newAgent.resumeSession(session);
129
-
setAgent(newAgent);
130
-
}
131
-
};
132
-
133
-
loadSession();
134
-
}, []);
135
-
136
-
const handleLogin = (newAgent: AtpAgent) => {
137
-
setAgent(newAgent);
138
-
// Save session to localStorage
139
-
localStorage.setItem(SESSION_KEY, JSON.stringify({
140
-
session: newAgent.session,
141
-
service: newAgent.service.toString(),
142
-
timestamp: Date.now()
143
-
}));
144
-
};
145
-
146
-
const handleLogout = () => {
147
-
setAgent(null);
148
-
localStorage.removeItem(SESSION_KEY);
149
-
// Clear avatar URL from context
150
-
const avatarContext = document.querySelector('[data-avatar-context]');
151
-
if (avatarContext) {
152
-
const event = new CustomEvent('clearAvatar');
153
-
avatarContext.dispatchEvent(event);
154
-
}
155
-
};
156
-
157
-
return (
158
-
<NetworkProvider>
159
-
<AvatarProvider>
160
-
<Router>
161
-
<AppRoutes
162
-
agent={agent}
163
-
onLogout={handleLogout}
164
-
handleLogin={handleLogin}
165
-
/>
166
-
</Router>
167
-
</AvatarProvider>
168
-
</NetworkProvider>
169
-
)
170
-
}
171
-
172
-
export default App
-189
src/components/auth/Login.tsx
-189
src/components/auth/Login.tsx
···
1
-
import { useState } from 'react';
2
-
import { useNavigate } from 'react-router-dom';
3
-
import { AtpAgent } from '@atproto/api';
4
-
import Footer from '../layout/Footer';
5
-
import '../../styles/App.css';
6
-
7
-
interface LoginProps {
8
-
onLogin: (agent: AtpAgent) => void;
9
-
}
10
-
11
-
interface DidDocument {
12
-
service: Array<{
13
-
id: string;
14
-
type: string;
15
-
serviceEndpoint: string;
16
-
}>;
17
-
}
18
-
19
-
type LoginStep = 'idle' | 'resolving-handle' | 'resolving-did' | 'connecting-pds' | 'authenticating' | 'success';
20
-
21
-
export default function Login({ onLogin }: LoginProps) {
22
-
const [handle, setHandle] = useState('');
23
-
const [password, setPassword] = useState('');
24
-
const [error, setError] = useState('');
25
-
const [appPasswordAttempts, setAppPasswordAttempts] = useState(0);
26
-
const [loginStep, setLoginStep] = useState<LoginStep>('idle');
27
-
const navigate = useNavigate();
28
-
29
-
const isAppPassword = (password: string) => {
30
-
// App passwords are typically in the format xxxx-xxxx-xxxx-xxxx
31
-
return /^[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}$/.test(password);
32
-
};
33
-
34
-
const getStepMessage = (step: LoginStep) => {
35
-
switch (step) {
36
-
case 'resolving-handle':
37
-
return 'Resolving your handle...';
38
-
case 'resolving-did':
39
-
return 'Resolving your DID...';
40
-
case 'connecting-pds':
41
-
return 'Connecting to your Personal Data Server...';
42
-
case 'authenticating':
43
-
return 'Authenticating your credentials...';
44
-
case 'success':
45
-
return 'Login successful! Redirecting...';
46
-
default:
47
-
return '';
48
-
}
49
-
};
50
-
51
-
const handleSubmit = async (e: React.FormEvent) => {
52
-
e.preventDefault();
53
-
setError('');
54
-
setLoginStep('resolving-handle');
55
-
56
-
// app password check and debug method
57
-
if (isAppPassword(password)) {
58
-
if (appPasswordAttempts < 3) {
59
-
setAppPasswordAttempts(appPasswordAttempts + 1);
60
-
setError(`You have entered an app password, which does not allow for you to migrate your account. Please enter your main account password instead.`);
61
-
setLoginStep('idle');
62
-
return;
63
-
}
64
-
}
65
-
66
-
setHandle(handle.trim());
67
-
68
-
try {
69
-
// Create temporary agent to resolve DID
70
-
const tempAgent = new AtpAgent({ service: 'https://public.api.bsky.app' });
71
-
72
-
// Get DID document from handle
73
-
setLoginStep('resolving-handle');
74
-
const didResponse = await tempAgent.com.atproto.identity.resolveHandle({
75
-
handle: handle
76
-
});
77
-
78
-
if (!didResponse.success) {
79
-
// Try did:web resolution first
80
-
const domain = handle.split('.').join(':');
81
-
const webDid = `did:web:${domain}`;
82
-
try {
83
-
const webResponse = await fetch(`https://${handle}/.well-known/did.json`);
84
-
if (webResponse.ok) {
85
-
// If successful, continue with the did:web
86
-
didResponse.data.did = webDid;
87
-
} else {
88
-
throw new Error('Invalid handle');
89
-
}
90
-
} catch {
91
-
throw new Error('Invalid handle');
92
-
}
93
-
}
94
-
95
-
// Get PDS endpoint from DID document
96
-
setLoginStep('resolving-did');
97
-
let didDocResponse;
98
-
const did = didResponse.data.did;
99
-
100
-
if (did.startsWith('did:plc:')) {
101
-
// For PLC DIDs, resolve from plc.directory
102
-
const plcResponse = await fetch(`https://plc.directory/${did}`);
103
-
didDocResponse = { data: await plcResponse.json() };
104
-
} else if (did.startsWith('did:web:')) {
105
-
// For Web DIDs, get from .well-known/did.json
106
-
const domain = did.split(':')[2];
107
-
const webResponse = await fetch(`https://${domain}/.well-known/did.json`);
108
-
didDocResponse = { data: await webResponse.json() };
109
-
} else {
110
-
// Fallback to ATP resolver for other DID types
111
-
didDocResponse = await tempAgent.com.atproto.identity.resolveDid({
112
-
did: did
113
-
});
114
-
}
115
-
116
-
setLoginStep('connecting-pds');
117
-
const pds = ((didDocResponse.data as unknown) as DidDocument).service.find((s) => s.id === '#atproto_pds')?.serviceEndpoint || 'https://bsky.social';
118
-
119
-
const agent = new AtpAgent({ service: pds });
120
-
121
-
setLoginStep('authenticating');
122
-
await agent.login({ identifier: handle, password });
123
-
124
-
setLoginStep('success');
125
-
onLogin(agent);
126
-
navigate('/actions');
127
-
} catch (err) {
128
-
setError(err instanceof Error ? err.message : 'Login failed');
129
-
setLoginStep('idle');
130
-
}
131
-
};
132
-
133
-
return (
134
-
<div>
135
-
<h1 className="login-title">ATproto Migrator</h1>
136
-
<div className="login-container">
137
-
<div className="login-card">
138
-
<h2 className="login-title">Sign in to your account</h2>
139
-
<div className="warning-message">
140
-
โ ๏ธ Please use your main account password, not an app password. All operations are performed locally in your browser.
141
-
</div>
142
-
<div className="warning-message">
143
-
ALSO This tool currently does not do anything, it should be done SOONโข๏ธ
144
-
</div>
145
-
<form className="login-form" onSubmit={handleSubmit}>
146
-
<div className="form-group">
147
-
<input
148
-
type="text"
149
-
required
150
-
className="form-input"
151
-
placeholder="Handle (e.g., example.bsky.social)"
152
-
value={handle}
153
-
onChange={(e) => setHandle(e.target.value)}
154
-
disabled={loginStep !== 'idle'}
155
-
/>
156
-
</div>
157
-
<div className="form-group">
158
-
<input
159
-
type="password"
160
-
required
161
-
className="form-input"
162
-
placeholder="Password"
163
-
value={password}
164
-
onChange={(e) => setPassword(e.target.value)}
165
-
disabled={loginStep !== 'idle'}
166
-
/>
167
-
</div>
168
-
169
-
{error && <div className="error-message">{error}</div>}
170
-
{loginStep !== 'idle' && (
171
-
<div className="loading-message">
172
-
{getStepMessage(loginStep)}
173
-
</div>
174
-
)}
175
-
176
-
<button
177
-
type="submit"
178
-
className="submit-button"
179
-
disabled={loginStep !== 'idle'}
180
-
>
181
-
{loginStep === 'idle' ? 'Sign in' : 'Signing in...'}
182
-
</button>
183
-
</form>
184
-
</div>
185
-
</div>
186
-
<Footer />
187
-
</div>
188
-
);
189
-
}
-131
src/components/common/Actions.tsx
-131
src/components/common/Actions.tsx
···
1
-
import { useEffect, useState } from 'react';
2
-
import { AtpAgent } from '@atproto/api';
3
-
import { useNavigate } from 'react-router-dom';
4
-
import Footer from '../layout/Footer';
5
-
import Header from '../layout/Header';
6
-
import '../../styles/App.css';
7
-
8
-
interface ActionsProps {
9
-
agent: AtpAgent;
10
-
onLogout: () => void;
11
-
}
12
-
13
-
export default function Actions({ agent, onLogout }: ActionsProps) {
14
-
const [didDoc, setDidDoc] = useState<string>('');
15
-
const [loading, setLoading] = useState(true);
16
-
const navigate = useNavigate();
17
-
18
-
const handleLogout = () => {
19
-
onLogout();
20
-
navigate('/');
21
-
};
22
-
23
-
useEffect(() => {
24
-
const fetchProfile = async () => {
25
-
try {
26
-
const did = agent.session?.did;
27
-
if (!did) {
28
-
throw new Error('No DID found in session');
29
-
}
30
-
31
-
let didDocResponse;
32
-
33
-
if (did.startsWith('did:plc:')) {
34
-
// For PLC DIDs, resolve from plc.directory
35
-
const response = await fetch(`https://plc.directory/${did}`);
36
-
didDocResponse = await response.json();
37
-
} else if (did.startsWith('did:web:')) {
38
-
// For Web DIDs, get from .well-known/did.json
39
-
const domain = did.split(':')[2];
40
-
const response = await fetch(`https://${domain}/.well-known/did.json`);
41
-
didDocResponse = await response.json();
42
-
} else {
43
-
throw new Error(`Unsupported DID type: ${did}`);
44
-
}
45
-
46
-
setDidDoc(JSON.stringify(didDocResponse, null, 2));
47
-
} catch (err) {
48
-
console.error('Error fetching DID document:', err);
49
-
setDidDoc(`Error fetching DID document: ${err instanceof Error ? err.message : 'Unknown error'}`);
50
-
} finally {
51
-
setLoading(false);
52
-
}
53
-
};
54
-
55
-
fetchProfile();
56
-
}, [agent]);
57
-
58
-
if (loading) {
59
-
return (
60
-
<div className="loading-container">
61
-
<div className="loading-text">Loading...</div>
62
-
</div>
63
-
);
64
-
}
65
-
66
-
return (
67
-
<div className="actions-page">
68
-
<Header agent={agent} onLogout={handleLogout} />
69
-
70
-
<div className="actions-container">
71
-
<div className="actions-list">
72
-
<button
73
-
className="action-item"
74
-
onClick={() => navigate('/migration')}
75
-
>
76
-
<div className="action-icon">
77
-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
78
-
<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z" />
79
-
</svg>
80
-
</div>
81
-
<div className="action-content">
82
-
<div className="action-title">Migrate account</div>
83
-
<div className="action-subtitle">Move your account to a new data server</div>
84
-
</div>
85
-
</button>
86
-
87
-
<button
88
-
className="action-item"
89
-
onClick={() => navigate('/recovery-key')}
90
-
>
91
-
<div className="action-icon">
92
-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
93
-
<rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
94
-
<path d="M7 11V7a5 5 0 0 1 10 0v4" />
95
-
</svg>
96
-
</div>
97
-
<div className="action-content">
98
-
<div className="action-title">Add recovery key</div>
99
-
<div className="action-subtitle">Create a new recovery key for your account</div>
100
-
</div>
101
-
</button>
102
-
</div>
103
-
104
-
<details className="user-info-section">
105
-
<summary className="user-info-summary">User Information</summary>
106
-
<div className="user-info-content">
107
-
<section>
108
-
<h2>Account Details</h2>
109
-
<dl>
110
-
<dt>DID</dt>
111
-
<dd>{agent.session?.did || 'N/A'}</dd>
112
-
<dt>Handle</dt>
113
-
<dd>@{agent.session?.handle || 'N/A'}</dd>
114
-
<dt>PDS</dt>
115
-
<dd>{agent.serviceUrl.toString() || 'N/A'}</dd>
116
-
</dl>
117
-
</section>
118
-
119
-
<section>
120
-
<h2>DID Document</h2>
121
-
<pre className="did-document">
122
-
<code>{didDoc}</code>
123
-
</pre>
124
-
</section>
125
-
</div>
126
-
</details>
127
-
</div>
128
-
<Footer />
129
-
</div>
130
-
);
131
-
}
+50
src/components/common/Header.tsx
+50
src/components/common/Header.tsx
···
1
+
import { useEffect } from 'react';
2
+
import { AtpAgent } from '@atproto/api';
3
+
import { useAvatar } from '../../contexts/AvatarContext';
4
+
5
+
interface HeaderProps {
6
+
agent: AtpAgent;
7
+
onLogout: () => void;
8
+
}
9
+
10
+
export default function Header({ agent, onLogout }: HeaderProps) {
11
+
const { avatarUrl, setAvatarUrl } = useAvatar();
12
+
13
+
useEffect(() => {
14
+
const fetchProfile = async () => {
15
+
// Only fetch if we don't already have an avatar
16
+
if (!avatarUrl) {
17
+
try {
18
+
const profile = await agent.getProfile({ actor: agent.session?.handle || '' });
19
+
if (profile.data.avatar) {
20
+
setAvatarUrl(profile.data.avatar);
21
+
}
22
+
} catch (err) {
23
+
console.error('Error fetching profile avatar:', err);
24
+
setAvatarUrl('https://placehold.co/400x400');
25
+
}
26
+
}
27
+
};
28
+
29
+
fetchProfile();
30
+
}, [agent, avatarUrl, setAvatarUrl]);
31
+
32
+
return (
33
+
<header className="app-header">
34
+
<h1 className="app-title">ATproto Migrator</h1>
35
+
<div className="user-info">
36
+
{avatarUrl && (
37
+
<img
38
+
src={avatarUrl}
39
+
alt="Profile"
40
+
className="user-avatar"
41
+
/>
42
+
)}
43
+
<span className="user-handle" title={agent.session?.handle}>@{agent.session?.handle}</span>
44
+
<button className="logout-button" onClick={onLogout}>
45
+
Logout
46
+
</button>
47
+
</div>
48
+
</header>
49
+
);
50
+
}
-72
src/components/common/Migration.tsx
-72
src/components/common/Migration.tsx
···
1
-
import { useNavigate } from 'react-router-dom';
2
-
import { AtpAgent } from '@atproto/api';
3
-
import Footer from '../layout/Footer';
4
-
import Header from '../layout/Header';
5
-
import '../../styles/App.css';
6
-
7
-
interface MigrationProps {
8
-
agent: AtpAgent;
9
-
onLogout: () => void;
10
-
}
11
-
12
-
export default function Migration({ agent, onLogout }: MigrationProps) {
13
-
const navigate = useNavigate();
14
-
15
-
return (
16
-
<div className="actions-page">
17
-
<Header agent={agent} onLogout={onLogout} />
18
-
19
-
<div className="actions-container">
20
-
<div className="page-content">
21
-
<h2>Migrate your account</h2>
22
-
<p>This tool allows you to migrate your account to a new Personal Data Server, a data storage service that hosts your account and all of its data.</p>
23
-
<h3>What to expect</h3>
24
-
<p>The migration process is <i>possible</i>, however it is not recommended if you are unsure about what you are doing, especially if you are migrating your primary account.</p>
25
-
<p>You will need the following items to begin the migration process:</p>
26
-
<ul>
27
-
<li>A new PDS to migrate to</li>
28
-
<li>An invite code from the new PDS (if required)</li>
29
-
<li>A PLC operation token to confirm the migration</li>
30
-
<li>A new password for your account <b>(Your password will not be stored by this tool.)</b></li>
31
-
<li>If you are not using a custom domain, you will need a new handle as the default domain (such as alice.bsky.social or bob.example-pds.com) is non-transferable.</li>
32
-
</ul>
33
-
34
-
<div className="warning-section">
35
-
<h3>โ ๏ธ Read Before Continuing โ ๏ธ</h3>
36
-
<ul>
37
-
<li>If you are already on a third-party PDS, it must be able to send emails or you will not be able to get a PLC operation token without direct access to the server.</li>
38
-
<li>Due to performance issues, the main Bluesky data servers do not allow for account data to be imported at this time. <b>You will not be able to migrate back to Bluesky servers.</b></li>
39
-
<li>If your PDS goes down and you do not have access to a recovery key, you will be locked out of your account. <b>Bluesky developers will not be able to help you.</b></li>
40
-
</ul>
41
-
</div>
42
-
43
-
<div className="docs-section">
44
-
<h3>Additional Resources</h3>
45
-
<p>For the technically inclined, here are some additional resources for how the migration process works:</p>
46
-
<ul>
47
-
<li><a href="https://github.com/bluesky-social/pds/blob/main/ACCOUNT_MIGRATION.md" target="_blank" rel="noopener noreferrer">Detailed document on migration for PDS hosters</a></li>
48
-
<li><a href="https://atproto.com/guides/account-migration" target="_blank" rel="noopener noreferrer">AT Protocol's developer documentation on account migration</a></li>
49
-
<li><a href="https://whtwnd.com/bnewbold.net/3l5ii332pf32u">Guide to migrating an account using the command line</a></li>
50
-
</ul>
51
-
</div>
52
-
53
-
<div className="button-container">
54
-
<button
55
-
className="back-button"
56
-
onClick={() => navigate('/actions')}
57
-
>
58
-
โ Go back
59
-
</button>
60
-
<button
61
-
className="continue-button"
62
-
onClick={() => navigate('/migration/process')}
63
-
>
64
-
Continue โ
65
-
</button>
66
-
</div>
67
-
</div>
68
-
</div>
69
-
<Footer />
70
-
</div>
71
-
);
72
-
}
-377
src/components/common/MigrationProcess.tsx
-377
src/components/common/MigrationProcess.tsx
···
1
-
import { useNavigate } from 'react-router-dom';
2
-
import { useState, useEffect, useCallback } from 'react';
3
-
import { AtpAgent } from '@atproto/api';
4
-
import Footer from '../layout/Footer';
5
-
import Header from '../layout/Header';
6
-
import '../../styles/App.css';
7
-
8
-
interface MigrationProcessProps {
9
-
agent: AtpAgent;
10
-
onLogout: () => void;
11
-
}
12
-
13
-
interface PDSInfo {
14
-
exists: boolean;
15
-
requiresInvite: boolean;
16
-
domain: string;
17
-
availableUserDomains: string[];
18
-
}
19
-
20
-
interface AccountDetails {
21
-
handle: string;
22
-
email: string;
23
-
password: string;
24
-
}
25
-
26
-
export default function MigrationProcess({ agent, onLogout }: MigrationProcessProps) {
27
-
const navigate = useNavigate();
28
-
const [pds, setPds] = useState('');
29
-
const [pdsInfo, setPdsInfo] = useState<PDSInfo | null>(null);
30
-
const [isValidating, setIsValidating] = useState(false);
31
-
const [error, setError] = useState('');
32
-
const [inviteCode, setInviteCode] = useState('');
33
-
const [isInviteValid, setIsInviteValid] = useState(false);
34
-
const [showAccountForm, setShowAccountForm] = useState(false);
35
-
const [accountDetails, setAccountDetails] = useState<AccountDetails>({
36
-
handle: '',
37
-
email: '',
38
-
password: ''
39
-
});
40
-
const [isCustomHandle, setIsCustomHandle] = useState(false);
41
-
const [currentHandle, setCurrentHandle] = useState('');
42
-
43
-
// Add warning when trying to close or navigate away and clean up expired data
44
-
useEffect(() => {
45
-
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
46
-
// Clean up expired data
47
-
const savedDetails = localStorage.getItem('migration_details');
48
-
if (savedDetails) {
49
-
const { expiryTime } = JSON.parse(savedDetails);
50
-
if (Date.now() >= expiryTime) {
51
-
localStorage.removeItem('migration_details');
52
-
}
53
-
}
54
-
55
-
e.preventDefault();
56
-
e.returnValue = '';
57
-
return '';
58
-
};
59
-
60
-
window.addEventListener('beforeunload', handleBeforeUnload);
61
-
62
-
return () => {
63
-
window.removeEventListener('beforeunload', handleBeforeUnload);
64
-
// Clean up expired data when component unmounts
65
-
const savedDetails = localStorage.getItem('migration_details');
66
-
if (savedDetails) {
67
-
const { expiryTime } = JSON.parse(savedDetails);
68
-
if (Date.now() >= expiryTime) {
69
-
localStorage.removeItem('migration_details');
70
-
}
71
-
}
72
-
};
73
-
}, []);
74
-
75
-
// Get current user's handle and check if it's a default handle
76
-
useEffect(() => {
77
-
const checkCurrentHandle = async () => {
78
-
try {
79
-
const session = agent.session;
80
-
if (session?.handle) {
81
-
setCurrentHandle(session.handle);
82
-
}
83
-
} catch (err) {
84
-
console.error('Failed to get current handle:', err);
85
-
}
86
-
};
87
-
checkCurrentHandle();
88
-
}, [agent]);
89
-
90
-
// Debounced PDS validation
91
-
const validatePDS = useCallback(async (pdsUrl: string) => {
92
-
if (!pdsUrl) {
93
-
setPdsInfo(null);
94
-
setError('');
95
-
return;
96
-
}
97
-
98
-
setIsValidating(true);
99
-
setError('');
100
-
101
-
try {
102
-
// Ensure the URL has the correct format
103
-
if (!pdsUrl.startsWith('http://') && !pdsUrl.startsWith('https://')) {
104
-
pdsUrl = 'https://' + pdsUrl;
105
-
}
106
-
107
-
// Check if the PDS is a Bluesky PDS
108
-
const hostname = new URL(pdsUrl).hostname;
109
-
if (hostname === 'bsky.social' || hostname === 'bsky.app' || hostname.endsWith('bsky.network')) {
110
-
setPdsInfo({
111
-
exists: false,
112
-
requiresInvite: false,
113
-
domain: hostname,
114
-
availableUserDomains: []
115
-
});
116
-
setError('Bluesky currently does not support migrating accounts to their data servers.');
117
-
return;
118
-
}
119
-
120
-
// Create a temporary agent to check the PDS
121
-
const tempAgent = new AtpAgent({ service: pdsUrl });
122
-
123
-
try {
124
-
// Try to get the server info
125
-
const info = await tempAgent.api.com.atproto.server.describeServer();
126
-
const domain = new URL(pdsUrl).hostname;
127
-
128
-
setPdsInfo({
129
-
exists: true,
130
-
requiresInvite: info.data.inviteCodeRequired || false,
131
-
domain,
132
-
availableUserDomains: info.data.availableUserDomains || []
133
-
});
134
-
} catch (err) {
135
-
setPdsInfo({
136
-
exists: false,
137
-
requiresInvite: false,
138
-
domain: '',
139
-
availableUserDomains: []
140
-
});
141
-
setError('Could not connect to the specified PDS. Please check the URL and try again.');
142
-
}
143
-
} catch (err) {
144
-
setError('Invalid PDS URL format. Please enter a valid URL.');
145
-
} finally {
146
-
setIsValidating(false);
147
-
}
148
-
}, []);
149
-
150
-
// Debounce the validation
151
-
useEffect(() => {
152
-
const timeoutId = setTimeout(() => {
153
-
if (pds) {
154
-
validatePDS(pds);
155
-
}
156
-
}, 500);
157
-
158
-
return () => clearTimeout(timeoutId);
159
-
}, [pds, validatePDS]);
160
-
161
-
// Validate invite code when it changes
162
-
useEffect(() => {
163
-
if (pdsInfo?.requiresInvite && inviteCode) {
164
-
const inviteRegex = /^bsky-noob-quest-[a-zA-Z0-9]{5}-[a-zA-Z0-9]{5}$/;
165
-
setIsInviteValid(inviteRegex.test(inviteCode));
166
-
}
167
-
}, [inviteCode, pdsInfo]);
168
-
169
-
// Check if handle is custom
170
-
useEffect(() => {
171
-
if (accountDetails.handle && pdsInfo?.availableUserDomains?.length) {
172
-
const defaultDomain = pdsInfo.availableUserDomains[0];
173
-
const handleRegex = new RegExp(`^[a-zA-Z0-9._-]+@${defaultDomain}$`);
174
-
const isDefaultHandle = handleRegex.test(accountDetails.handle);
175
-
setIsCustomHandle(!isDefaultHandle);
176
-
}
177
-
}, [accountDetails.handle, pdsInfo]);
178
-
179
-
// Auto-scroll to latest step
180
-
useEffect(() => {
181
-
const formSection = document.querySelector('.form-section:not(.completed)');
182
-
if (formSection) {
183
-
formSection.scrollIntoView({ behavior: 'smooth', block: 'start' });
184
-
}
185
-
}, [showAccountForm]);
186
-
187
-
// Check if current handle is default
188
-
const isCurrentHandleDefault = useCallback(() => {
189
-
if (!currentHandle || !pdsInfo?.availableUserDomains?.length) return false;
190
-
191
-
// If migrating from Bluesky PDS (bsky.network), check if handle is from bsky.social
192
-
if (agent.serviceUrl.host.endsWith('.bsky.network')) {
193
-
return currentHandle.endsWith('.bsky.social');
194
-
}
195
-
196
-
// For third-party PDS, check if handle ends with any of the available user domains
197
-
return pdsInfo.availableUserDomains.some(domain =>
198
-
currentHandle.endsWith(`${domain}`)
199
-
);
200
-
}, [currentHandle, pdsInfo]);
201
-
202
-
const handlePdsBlur = () => {
203
-
if (pds) {
204
-
validatePDS(pds);
205
-
}
206
-
};
207
-
208
-
const handleContinue = () => {
209
-
if (pdsInfo?.exists && (!pdsInfo.requiresInvite || isInviteValid)) {
210
-
setShowAccountForm(true);
211
-
}
212
-
};
213
-
214
-
const handleStartMigration = () => {
215
-
// Clean up any existing expired data first
216
-
const savedDetails = localStorage.getItem('migration_details');
217
-
if (savedDetails) {
218
-
const { expiryTime } = JSON.parse(savedDetails);
219
-
if (Date.now() >= expiryTime) {
220
-
localStorage.removeItem('migration_details');
221
-
}
222
-
}
223
-
224
-
// Save account details to localStorage with 30-minute expiry
225
-
const expiryTime = Date.now() + (30 * 60 * 1000); // 30 minutes in milliseconds
226
-
const migrationDetails = {
227
-
pds: pds,
228
-
inviteCode: inviteCode || null,
229
-
handle: accountDetails.handle + (pdsInfo?.availableUserDomains?.[0] ? `${pdsInfo.availableUserDomains[0]}` : ''),
230
-
email: accountDetails.email,
231
-
password: accountDetails.password,
232
-
expiryTime: expiryTime
233
-
};
234
-
localStorage.setItem('migration_details', JSON.stringify(migrationDetails));
235
-
236
-
// TODO: Implement migration
237
-
};
238
-
239
-
return (
240
-
<div className="actions-page">
241
-
<Header agent={agent} onLogout={onLogout} />
242
-
243
-
<div className="actions-container">
244
-
<div className="page-content">
245
-
<h2>Migrate your account</h2>
246
-
247
-
<div className={`form-section ${showAccountForm ? 'completed' : ''}`}>
248
-
<h3>Select your new PDS</h3>
249
-
<div className="form-group">
250
-
<label htmlFor="pds-input">Personal Data Server (PDS)</label>
251
-
<input
252
-
id="pds-input"
253
-
type="text"
254
-
className="form-input"
255
-
placeholder="Example: example-pds.com"
256
-
value={pds}
257
-
onChange={(e) => setPds(e.target.value)}
258
-
onBlur={handlePdsBlur}
259
-
disabled={isValidating || showAccountForm}
260
-
/>
261
-
{isValidating && (
262
-
<div className="loading-message">Checking PDS availability...</div>
263
-
)}
264
-
{error && (
265
-
<div className="error-message">{error}</div>
266
-
)}
267
-
{pdsInfo?.exists && !pdsInfo.requiresInvite && (
268
-
<div className="success-message">โ This PDS does not require an invite code</div>
269
-
)}
270
-
</div>
271
-
272
-
{pdsInfo?.exists && pdsInfo.requiresInvite && (
273
-
<div className="form-group">
274
-
<label htmlFor="invite-code">Invite Code</label>
275
-
<input
276
-
id="invite-code"
277
-
type="text"
278
-
className="form-input"
279
-
placeholder="Example: bsky-noob-quest-abcde-12345"
280
-
value={inviteCode}
281
-
onChange={(e) => setInviteCode(e.target.value)}
282
-
disabled={showAccountForm}
283
-
/>
284
-
</div>
285
-
)}
286
-
287
-
{!showAccountForm && (
288
-
<div className="button-container">
289
-
<button
290
-
className="back-button"
291
-
onClick={() => navigate('/migration')}
292
-
>
293
-
โ Go back
294
-
</button>
295
-
{pdsInfo?.exists && (!pdsInfo.requiresInvite || isInviteValid) && (
296
-
<button
297
-
className="continue-button"
298
-
onClick={handleContinue}
299
-
>
300
-
Continue โ
301
-
</button>
302
-
)}
303
-
</div>
304
-
)}
305
-
</div>
306
-
307
-
{showAccountForm && (
308
-
<div className="form-section">
309
-
<h3>New account details</h3>
310
-
<div className="form-group">
311
-
<label htmlFor="handle-input">Handle</label>
312
-
<div className="handle-input-container">
313
-
<input
314
-
id="handle-input"
315
-
type="text"
316
-
className="form-input"
317
-
placeholder="username"
318
-
value={accountDetails.handle}
319
-
onChange={(e) => setAccountDetails(prev => ({ ...prev, handle: e.target.value }))}
320
-
/>
321
-
{pdsInfo?.availableUserDomains?.[0] && (
322
-
<span className="handle-domain">{pdsInfo.availableUserDomains[0]}</span>
323
-
)}
324
-
</div>
325
-
{isCustomHandle && !isCurrentHandleDefault() && (
326
-
<div className="info-message">
327
-
During the migration, you'll be assigned a temporary handle. After the migration is completed, we will assign your custom handle automatically.
328
-
</div>
329
-
)}
330
-
</div>
331
-
332
-
<div className="form-group">
333
-
<label htmlFor="email-input">Email</label>
334
-
<input
335
-
id="email-input"
336
-
type="email"
337
-
className="form-input"
338
-
placeholder="Your email address"
339
-
value={accountDetails.email}
340
-
onChange={(e) => setAccountDetails(prev => ({ ...prev, email: e.target.value }))}
341
-
/>
342
-
</div>
343
-
344
-
<div className="form-group">
345
-
<label htmlFor="password-input">Password</label>
346
-
<input
347
-
id="password-input"
348
-
type="password"
349
-
className="form-input"
350
-
placeholder="Your new password"
351
-
value={accountDetails.password}
352
-
onChange={(e) => setAccountDetails(prev => ({ ...prev, password: e.target.value }))}
353
-
/>
354
-
</div>
355
-
<small>We recommend using a different password for your new account. Save all of the above details somewhere before continuing.</small>
356
-
<div className="button-container">
357
-
<button
358
-
className="back-button"
359
-
onClick={() => setShowAccountForm(false)}
360
-
>
361
-
โ Go back
362
-
</button>
363
-
<button
364
-
className="continue-button"
365
-
onClick={handleStartMigration}
366
-
>
367
-
Continue โ
368
-
</button>
369
-
</div>
370
-
</div>
371
-
)}
372
-
</div>
373
-
</div>
374
-
<Footer />
375
-
</div>
376
-
);
377
-
}
-1
src/components/common/NetworkWarning.tsx
-1
src/components/common/NetworkWarning.tsx
-67
src/components/common/RecoveryKey.tsx
-67
src/components/common/RecoveryKey.tsx
···
1
-
import { useNavigate } from 'react-router-dom';
2
-
import { AtpAgent } from '@atproto/api';
3
-
import Footer from '../layout/Footer';
4
-
import Header from '../layout/Header';
5
-
import '../../styles/App.css';
6
-
7
-
interface RecoveryKeyProps {
8
-
agent: AtpAgent;
9
-
onLogout: () => void;
10
-
}
11
-
12
-
export default function RecoveryKey({ agent, onLogout }: RecoveryKeyProps) {
13
-
const navigate = useNavigate();
14
-
15
-
return (
16
-
<div className="actions-page">
17
-
<Header agent={agent} onLogout={onLogout} />
18
-
19
-
<div className="actions-container">
20
-
<div className="page-content">
21
-
<h2>Add a recovery key</h2>
22
-
<p>A recovery key (known as a <b>rotation key</b> in the AT Protocol) is a cryptographic key associated with your account that allows you to modify your account's core identity.</p>
23
-
24
-
<h3>How rotation keys work</h3>
25
-
<p>In the AT Protocol, your account is identified using a DID (<b>Decentralized Identifier</b>), with most accounts on the protocol using a variant of it developed specifically for the protocol. The account's core information (such as your handle and data server on the network) is stored in the account's DID document.</p>
26
-
<p>To change this document, you use a rotation key to confirm that you are the owner of the account and that you are authorized to make the changes. For example, when changing your handle, your data server (also known as a PDS) will use its own rotation key to change it without asking you to manually sign the operation.</p>
27
-
<h3>Why should I add another key?</h3>
28
-
<p>Adding a rotation key allows you to regain control of your account if it is compromised. It also allows you to move your account to a new data server, even if the current server is down.</p>
29
-
<div className="warning-section">
30
-
<h3>โ ๏ธ Read Before Continuing โ ๏ธ</h3>
31
-
<ul>
32
-
<li>You will need a PLC operation token to add a recovery key. Tokens are sent to the email address associated with your account.</li>
33
-
<li>While we do generate a key for you, we will not store it. Please save it in a secure location.</li>
34
-
<li>Keep your recovery key private. Anyone with access to it could potentially take control of your account.</li>
35
-
<li>If you're using a third-party PDS, it must be able to send emails or you will not be able to use this tool to add a recovery key.</li>
36
-
</ul>
37
-
</div>
38
-
<div className="docs-section">
39
-
<h3>Additional Resources</h3>
40
-
<p>For the technically inclined, here are some additional resources for how rotation keys work:</p>
41
-
<ul>
42
-
<li><a href="https://atproto.com/guides/identity" target="_blank" rel="noopener noreferrer">AT Protocol's developer documentation on identity</a></li>
43
-
<li><a href="https://whtwnd.com/did:plc:xz3euvkhf44iadavovbsmqoo/3laimapx6ks2b" target="_blank" rel="noopener noreferrer">Guide to adding a recovery key using the command line</a></li>
44
-
<li><a href="https://whtwnd.com/did:plc:44ybard66vv44zksje25o7dz/3lj7jmt2ct72r" target="_blank" rel="noopener noreferrer">More in-depth guide to adding a recovery key</a></li>
45
-
</ul>
46
-
</div>
47
-
48
-
<div className="button-container">
49
-
<button
50
-
className="back-button"
51
-
onClick={() => navigate('/actions')}
52
-
>
53
-
โ Go back
54
-
</button>
55
-
<button
56
-
className="continue-button"
57
-
onClick={() => navigate('/recovery-key/process')}
58
-
>
59
-
Continue โ
60
-
</button>
61
-
</div>
62
-
</div>
63
-
</div>
64
-
<Footer />
65
-
</div>
66
-
);
67
-
}
-53
src/components/common/RecoveryKeyProcess.tsx
-53
src/components/common/RecoveryKeyProcess.tsx
···
1
-
import { useNavigate } from 'react-router-dom';
2
-
import { useEffect } from 'react';
3
-
import { AtpAgent } from '@atproto/api';
4
-
import Footer from '../layout/Footer';
5
-
import Header from '../layout/Header';
6
-
import '../../styles/App.css';
7
-
8
-
interface RecoveryKeyProcessProps {
9
-
agent: AtpAgent;
10
-
onLogout: () => void;
11
-
}
12
-
13
-
export default function RecoveryKeyProcess({ agent, onLogout }: RecoveryKeyProcessProps) {
14
-
const navigate = useNavigate();
15
-
16
-
// Add warning when trying to close or navigate away
17
-
useEffect(() => {
18
-
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
19
-
e.preventDefault();
20
-
e.returnValue = '';
21
-
return '';
22
-
};
23
-
24
-
window.addEventListener('beforeunload', handleBeforeUnload);
25
-
26
-
return () => {
27
-
window.removeEventListener('beforeunload', handleBeforeUnload);
28
-
};
29
-
}, []);
30
-
31
-
return (
32
-
<div className="actions-page">
33
-
<Header agent={agent} onLogout={onLogout} />
34
-
35
-
<div className="actions-container">
36
-
<div className="page-content">
37
-
<h2>Add Recovery Key</h2>
38
-
<p>This page will guide you through the process of adding a recovery key to your account.</p>
39
-
40
-
<div className="button-container">
41
-
<button
42
-
className="back-button"
43
-
onClick={() => navigate('/recovery-key')}
44
-
>
45
-
โ Go back
46
-
</button>
47
-
</div>
48
-
</div>
49
-
</div>
50
-
<Footer />
51
-
</div>
52
-
);
53
-
}
-50
src/components/layout/Header.tsx
-50
src/components/layout/Header.tsx
···
1
-
import { useEffect } from 'react';
2
-
import { AtpAgent } from '@atproto/api';
3
-
import { useAvatar } from '../../contexts/AvatarContext';
4
-
import '../../styles/App.css';
5
-
6
-
interface HeaderProps {
7
-
agent: AtpAgent;
8
-
onLogout: () => void;
9
-
}
10
-
11
-
export default function Header({ agent, onLogout }: HeaderProps) {
12
-
const { avatarUrl, setAvatarUrl } = useAvatar();
13
-
14
-
useEffect(() => {
15
-
const fetchProfile = async () => {
16
-
// Only fetch if we don't already have an avatar
17
-
if (!avatarUrl) {
18
-
try {
19
-
const profile = await agent.getProfile({ actor: agent.session?.handle || '' });
20
-
if (profile.data.avatar) {
21
-
setAvatarUrl(profile.data.avatar);
22
-
}
23
-
} catch (err) {
24
-
console.error('Error fetching profile:', err);
25
-
}
26
-
}
27
-
};
28
-
29
-
fetchProfile();
30
-
}, [agent, avatarUrl, setAvatarUrl]);
31
-
32
-
return (
33
-
<header className="app-header">
34
-
<h1 className="app-title">ATproto Migrator</h1>
35
-
<div className="user-info">
36
-
{avatarUrl && (
37
-
<img
38
-
src={avatarUrl}
39
-
alt="Profile"
40
-
className="user-avatar"
41
-
/>
42
-
)}
43
-
<span className="user-handle" title={agent.session?.handle}>{agent.session?.handle}</span>
44
-
<button className="logout-button" onClick={onLogout}>
45
-
Logout
46
-
</button>
47
-
</div>
48
-
</header>
49
-
);
50
-
}
+206
src/components/migration/accountDetailsForm.tsx
+206
src/components/migration/accountDetailsForm.tsx
···
1
+
import { useState, useEffect, useCallback } from 'react';
2
+
import { ServerDescription } from '../../lib/migration/serverDescription';
3
+
import { validateHandle } from '../../lib/migration/accountDetailsValidation';
4
+
5
+
interface AccountDetailsFormProps {
6
+
currentHandle: string;
7
+
pds: string;
8
+
inviteCode: string;
9
+
serverDescription: ServerDescription;
10
+
newServerDescription: ServerDescription;
11
+
onBack: () => void;
12
+
onSubmit: (handle: string, email: string, password: string) => void;
13
+
}
14
+
15
+
export default function AccountDetailsForm({
16
+
currentHandle,
17
+
serverDescription,
18
+
newServerDescription,
19
+
onBack,
20
+
onSubmit
21
+
}: AccountDetailsFormProps) {
22
+
const [handle, setHandle] = useState('');
23
+
const [email, setEmail] = useState('');
24
+
const [password, setPassword] = useState('');
25
+
const [isUsingDefaultDomain, setIsUsingDefaultDomain] = useState(false);
26
+
const [emailError, setEmailError] = useState('');
27
+
const [passwordError, setPasswordError] = useState('');
28
+
const [handleError, setHandleError] = useState('');
29
+
30
+
useEffect(() => {
31
+
// Check if current handle is using a default domain
32
+
const availableDomains = serverDescription.getAvailableUserDomains();
33
+
const domainNames = Object.values(availableDomains);
34
+
const { isUsingDefaultDomain, customHandle } = validateHandle(currentHandle, domainNames);
35
+
36
+
37
+
setIsUsingDefaultDomain(isUsingDefaultDomain);
38
+
if (isUsingDefaultDomain) {
39
+
// Set initial handle value from current handle
40
+
setHandle(customHandle);
41
+
} else {
42
+
setHandle(customHandle);
43
+
}
44
+
}, [currentHandle, serverDescription]);
45
+
46
+
const validateEmail = (email: string) => {
47
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
48
+
return emailRegex.test(email);
49
+
};
50
+
51
+
const validatePassword = (password: string) => {
52
+
return password.length >= 8;
53
+
};
54
+
55
+
const validateHandleInput = (handle: string) => {
56
+
if (!isUsingDefaultDomain) {
57
+
return '';
58
+
}
59
+
60
+
if (handle.length < 3) {
61
+
return 'Handle must be at least 3 characters long';
62
+
}
63
+
if (!/^[a-zA-Z0-9-]+$/.test(handle)) {
64
+
return 'Handle can only contain letters, numbers, and hyphens';
65
+
}
66
+
return '';
67
+
};
68
+
69
+
// Debounced validation functions
70
+
const debouncedValidateHandle = useCallback((value: string) => {
71
+
const timeoutId = setTimeout(() => {
72
+
setHandleError(validateHandleInput(value));
73
+
}, 500);
74
+
return () => clearTimeout(timeoutId);
75
+
}, []);
76
+
77
+
const debouncedValidateEmail = useCallback((value: string) => {
78
+
const timeoutId = setTimeout(() => {
79
+
setEmailError(validateEmail(value) ? '' : 'Please enter a valid email address');
80
+
}, 500);
81
+
return () => clearTimeout(timeoutId);
82
+
}, []);
83
+
84
+
const debouncedValidatePassword = useCallback((value: string) => {
85
+
const timeoutId = setTimeout(() => {
86
+
setPasswordError(validatePassword(value) ? '' : 'Password must be at least 8 characters long');
87
+
}, 500);
88
+
return () => clearTimeout(timeoutId);
89
+
}, []);
90
+
91
+
const validateAllFields = () => {
92
+
// Run all validations immediately
93
+
const handleValidationError = validateHandleInput(handle);
94
+
const emailValidationError = !validateEmail(email) ? 'Please enter a valid email address' : '';
95
+
const passwordValidationError = !validatePassword(password) ? 'Password must be at least 8 characters long' : '';
96
+
97
+
// Update all error states
98
+
setHandleError(handleValidationError);
99
+
setEmailError(emailValidationError);
100
+
setPasswordError(passwordValidationError);
101
+
102
+
// Return true if all validations pass
103
+
return !handleValidationError && !emailValidationError && !passwordValidationError;
104
+
};
105
+
106
+
const handleSubmit = (e: React.FormEvent) => {
107
+
e.preventDefault();
108
+
109
+
// Validate all fields and only proceed if all validations pass
110
+
if (validateAllFields()) {
111
+
onSubmit(handle, email, password);
112
+
}
113
+
};
114
+
115
+
// Get the first available domain from the new server description
116
+
const newAvailableDomains = newServerDescription.getAvailableUserDomains();
117
+
const newFirstAvailableDomain = Object.values(newAvailableDomains)[0];
118
+
119
+
return (
120
+
<form onSubmit={handleSubmit}>
121
+
<h3>Create your new account's credentials</h3>
122
+
123
+
{isUsingDefaultDomain && (
124
+
<div>
125
+
<div className="info-message">
126
+
<h3>Default handle detected!</h3>
127
+
We have detected that your current handle is <strong>@{currentHandle}</strong>, which is a default handle provided by your data server. Because of this, we are unable to migrate your current handle to your new data server. We have automatically filled in your previous handle for you, however you can change it to something different if you wish.
128
+
</div>
129
+
130
+
<div className="form-group">
131
+
<label htmlFor="handle">Handle</label>
132
+
<div className="handle-input-container">
133
+
<input
134
+
type="text"
135
+
id="handle"
136
+
value={handle}
137
+
onChange={(e) => {
138
+
const newValue = e.target.value;
139
+
setHandle(newValue);
140
+
debouncedValidateHandle(newValue);
141
+
}}
142
+
placeholder="alice"
143
+
className={`form-input ${handleError ? 'error' : ''}`}
144
+
/>
145
+
<span className="handle-domain">{newFirstAvailableDomain}</span>
146
+
</div>
147
+
{handleError && <div className="error-message">{handleError}</div>}
148
+
</div>
149
+
</div>
150
+
)}
151
+
152
+
<div className="form-group">
153
+
<label htmlFor="email">Email</label>
154
+
<input
155
+
type="text"
156
+
id="email"
157
+
value={email}
158
+
onChange={(e) => {
159
+
const newValue = e.target.value;
160
+
setEmail(newValue);
161
+
debouncedValidateEmail(newValue);
162
+
}}
163
+
placeholder="popbob@example.com"
164
+
required
165
+
className={`form-input ${emailError ? 'error' : ''}`}
166
+
/>
167
+
{emailError && <div className="error-message">{emailError}</div>}
168
+
</div>
169
+
170
+
<div className="form-group">
171
+
<label htmlFor="password">Password</label>
172
+
<input
173
+
type="password"
174
+
id="password"
175
+
value={password}
176
+
onChange={(e) => {
177
+
const newValue = e.target.value;
178
+
setPassword(newValue);
179
+
debouncedValidatePassword(newValue);
180
+
}}
181
+
placeholder="hunter2"
182
+
required
183
+
className={`form-input ${passwordError ? 'error' : ''}`}
184
+
/>
185
+
{passwordError && <div className="error-message">{passwordError}</div>}
186
+
</div>
187
+
<small>We recommend using a new, unique password for your new account.</small>
188
+
189
+
<div className="button-container">
190
+
<button
191
+
type="button"
192
+
className="back-button"
193
+
onClick={onBack}
194
+
>
195
+
โ Go back
196
+
</button>
197
+
<button
198
+
type="submit"
199
+
className="continue-button"
200
+
>
201
+
Continue โ
202
+
</button>
203
+
</div>
204
+
</form>
205
+
);
206
+
}
+165
src/components/migration/confirmationStep.tsx
+165
src/components/migration/confirmationStep.tsx
···
1
+
import { useState } from 'react';
2
+
import '../../css/confirmation.css';
3
+
4
+
interface ConfirmationStepProps {
5
+
handle: string;
6
+
email: string;
7
+
password: string;
8
+
pds: string;
9
+
onBack: () => void;
10
+
onConfirm: () => void;
11
+
currentHandle?: string;
12
+
currentPds?: string;
13
+
}
14
+
15
+
export default function ConfirmationStep({
16
+
handle,
17
+
email,
18
+
password,
19
+
pds,
20
+
onBack,
21
+
onConfirm,
22
+
currentHandle = '',
23
+
currentPds = ''
24
+
}: ConfirmationStepProps) {
25
+
const [showPassword, setShowPassword] = useState(false);
26
+
const [showWarning, setShowWarning] = useState(false);
27
+
const [pdsVerification, setPdsVerification] = useState('');
28
+
29
+
const handleConfirmClick = () => {
30
+
setShowWarning(true);
31
+
};
32
+
33
+
const handleWarningConfirm = () => {
34
+
setShowWarning(false);
35
+
setPdsVerification('');
36
+
onConfirm();
37
+
};
38
+
39
+
const isPdsVerified = pdsVerification.toLowerCase() === pds.toLowerCase();
40
+
41
+
return (
42
+
<div>
43
+
<h3>Migration summary</h3>
44
+
45
+
<div className="comparison-container">
46
+
<div className="comparison-side">
47
+
<h3>Before</h3>
48
+
<div className="detail-group">
49
+
<label>Handle</label>
50
+
<div>@{currentHandle}</div>
51
+
</div>
52
+
<div className="detail-group">
53
+
<label>PDS</label>
54
+
<div>{currentPds}</div>
55
+
</div>
56
+
</div>
57
+
58
+
<div className="comparison-arrow">โ</div>
59
+
60
+
<div className="comparison-side">
61
+
<h3>After</h3>
62
+
<div className="detail-group">
63
+
<label>Handle</label>
64
+
<div>@{handle}</div>
65
+
</div>
66
+
<div className="detail-group">
67
+
<label>PDS</label>
68
+
<div>{pds}</div>
69
+
</div>
70
+
</div>
71
+
</div>
72
+
73
+
<div className="detail-group">
74
+
<label>New Email</label>
75
+
<div className="detail-value">{email}</div>
76
+
</div>
77
+
78
+
<div className="detail-group">
79
+
<label>New Password</label>
80
+
<div className="password-container">
81
+
<div className="detail-value">
82
+
{showPassword ? (
83
+
password
84
+
) : (
85
+
<span className="hidden-password">
86
+
{'โข'.repeat(password.length)}
87
+
</span>
88
+
)}
89
+
</div>
90
+
<button
91
+
type="button"
92
+
className="password-toggle"
93
+
onClick={() => setShowPassword(!showPassword)}
94
+
>
95
+
{showPassword ? 'Hide' : 'Show'}
96
+
</button>
97
+
</div>
98
+
</div>
99
+
100
+
<div className="button-container">
101
+
<button
102
+
type="button"
103
+
className="back-button"
104
+
onClick={onBack}
105
+
>
106
+
โ Go back
107
+
</button>
108
+
<button
109
+
type="button"
110
+
className="confirm-button"
111
+
onClick={handleConfirmClick}
112
+
>
113
+
Confirm Migration โ
114
+
</button>
115
+
</div>
116
+
117
+
{showWarning && (
118
+
<div className="warning-overlay">
119
+
<div className="warning-dialog">
120
+
<h3>Last chance to turn back!</h3>
121
+
<div className="warning-content">
122
+
<p>This tool is currently in beta, as such:</p>
123
+
<ul>
124
+
<li>We do not guarantee that the migration will succeed</li>
125
+
<li>While most errors are recoverable, for the moment we cannot help you recover from them and it will need to be continued manually using command line tools</li>
126
+
<li>We claim no responsibility for any problems that may occur during the process</li>
127
+
</ul>
128
+
<p>If you understand the risks and want to proceed, please type your new PDS <b>({pds})</b> below:</p>
129
+
<div className="pds-verification">
130
+
<input
131
+
type="text"
132
+
value={pdsVerification}
133
+
onChange={(e) => setPdsVerification(e.target.value)}
134
+
placeholder="Type your new PDS here"
135
+
className={pdsVerification && !isPdsVerified ? 'error' : ''}
136
+
/>
137
+
</div>
138
+
</div>
139
+
<div className="warning-buttons">
140
+
<button
141
+
type="button"
142
+
className="back-button"
143
+
onClick={handleWarningConfirm}
144
+
disabled={!isPdsVerified}
145
+
>
146
+
Migrate!
147
+
</button>
148
+
<button
149
+
type="button"
150
+
className="confirm-button"
151
+
onClick={() => {
152
+
setShowWarning(false);
153
+
setPdsVerification('');
154
+
}}
155
+
>
156
+
Cancel
157
+
</button>
158
+
159
+
</div>
160
+
</div>
161
+
</div>
162
+
)}
163
+
</div>
164
+
);
165
+
}
+258
src/components/migration/pdsForm.tsx
+258
src/components/migration/pdsForm.tsx
···
1
+
import { useState, useCallback, useEffect } from 'react';
2
+
import { AtpAgent } from '@atproto/api';
3
+
import { validatePDS, getServerDescription, validateInviteCode } from '../../lib/migration/pdsValidation';
4
+
import { ServerDescription } from '../../lib/migration/serverDescription';
5
+
import { useDebounce } from '../../hooks/useDebounce';
6
+
7
+
interface PdsFormProps {
8
+
agent: AtpAgent;
9
+
onSubmit: (pds: string, inviteCode: string, serverDescription: ServerDescription) => void;
10
+
onBack: () => void;
11
+
}
12
+
13
+
export default function PdsForm({ agent, onSubmit, onBack }: PdsFormProps) {
14
+
const [pds, setPds] = useState('');
15
+
const [inviteCode, setInviteCode] = useState('');
16
+
const [pdsError, setPdsError] = useState<string | null>(null);
17
+
const [inviteCodeError, setInviteCodeError] = useState<string | null>(null);
18
+
const [isValidatingPds, setIsValidatingPds] = useState(false);
19
+
const [isValidatingInviteCode, setIsValidatingInviteCode] = useState(false);
20
+
const [isPdsValid, setIsPdsValid] = useState(false);
21
+
const [isInviteCodeValid, setIsInviteCodeValid] = useState(false);
22
+
const [serverDescription, setServerDescription] = useState<ServerDescription | null>(null);
23
+
const [lastValidatedPds, setLastValidatedPds] = useState('');
24
+
const [isFormReady, setIsFormReady] = useState(false);
25
+
26
+
useEffect(() => {
27
+
const isInviteCodeSectionValid = !serverDescription?.isInviteCodeRequired() ||
28
+
Boolean(inviteCode && !inviteCodeError && !isValidatingInviteCode && isInviteCodeValid);
29
+
30
+
setIsFormReady(
31
+
Boolean(isPdsValid &&
32
+
!pdsError &&
33
+
!isValidatingPds &&
34
+
isInviteCodeSectionValid)
35
+
);
36
+
}, [isPdsValid, pdsError, isValidatingPds, serverDescription, inviteCode, inviteCodeError, isValidatingInviteCode, isInviteCodeValid]);
37
+
38
+
const validatePdsInput = useCallback(async (value: string) => {
39
+
if (value === lastValidatedPds && isPdsValid && !pdsError) {
40
+
return;
41
+
}
42
+
43
+
setIsValidatingPds(true);
44
+
setIsPdsValid(false);
45
+
setServerDescription(null);
46
+
setPdsError(null);
47
+
48
+
try {
49
+
const validationResult = await validatePDS(value, agent);
50
+
if (!validationResult.isValid) {
51
+
console.error('PDS validation failed:', {
52
+
pds: value,
53
+
error: validationResult.error
54
+
});
55
+
setPdsError(validationResult.error);
56
+
setIsValidatingPds(false);
57
+
return;
58
+
}
59
+
60
+
try {
61
+
const description = await getServerDescription(value);
62
+
setServerDescription(description);
63
+
setPdsError(null);
64
+
setIsPdsValid(true);
65
+
setLastValidatedPds(value);
66
+
} catch (e) {
67
+
console.error('Error getting server description:', {
68
+
pds: value,
69
+
error: e instanceof Error ? {
70
+
name: e.name,
71
+
message: e.message,
72
+
stack: e.stack
73
+
} : e
74
+
});
75
+
setPdsError('Failed to get server information');
76
+
}
77
+
} catch (e) {
78
+
console.error('Error validating PDS:', {
79
+
pds: value,
80
+
error: e instanceof Error ? {
81
+
name: e.name,
82
+
message: e.message,
83
+
stack: e.stack
84
+
} : e
85
+
});
86
+
setPdsError('An unexpected error occurred');
87
+
}
88
+
89
+
setIsValidatingPds(false);
90
+
}, [agent, lastValidatedPds, isPdsValid, pdsError]);
91
+
92
+
const validateInviteCodeInput = useCallback(async (pdsValue: string, inviteCodeValue: string) => {
93
+
if (!pdsValue || !inviteCodeValue) {
94
+
setIsInviteCodeValid(false);
95
+
return;
96
+
}
97
+
98
+
setIsValidatingInviteCode(true);
99
+
setInviteCodeError(null);
100
+
setIsInviteCodeValid(false);
101
+
102
+
try {
103
+
const inviteCodeValidationResult = await validateInviteCode(pdsValue, inviteCodeValue);
104
+
if (!inviteCodeValidationResult.isValid) {
105
+
setInviteCodeError(inviteCodeValidationResult.error);
106
+
setIsInviteCodeValid(false);
107
+
} else {
108
+
setIsInviteCodeValid(true);
109
+
}
110
+
} catch (e) {
111
+
console.error('Error validating invite code:', {
112
+
pds: pdsValue,
113
+
inviteCode: inviteCodeValue,
114
+
error: e instanceof Error ? {
115
+
name: e.name,
116
+
message: e.message,
117
+
stack: e.stack
118
+
} : e
119
+
});
120
+
setInviteCodeError('Failed to validate invite code');
121
+
setIsInviteCodeValid(false);
122
+
}
123
+
124
+
setIsValidatingInviteCode(false);
125
+
}, []);
126
+
127
+
const debouncedValidatePds = useDebounce(validatePdsInput, 500);
128
+
const debouncedValidateInviteCode = useDebounce(validateInviteCodeInput, 500);
129
+
130
+
const handleSubmit = async (e: React.FormEvent) => {
131
+
e.preventDefault();
132
+
if (!isFormReady) return;
133
+
134
+
setIsValidatingPds(true);
135
+
setIsValidatingInviteCode(true);
136
+
137
+
try {
138
+
await validatePdsInput(pds);
139
+
140
+
if (serverDescription?.isInviteCodeRequired()) {
141
+
await validateInviteCodeInput(pds, inviteCode);
142
+
}
143
+
144
+
if (!pdsError && !inviteCodeError) {
145
+
onSubmit(pds, inviteCode, serverDescription!);
146
+
}
147
+
} catch (e) {
148
+
console.error('Error during form submission:', {
149
+
pds,
150
+
inviteCode,
151
+
error: e instanceof Error ? {
152
+
name: e.name,
153
+
message: e.message,
154
+
stack: e.stack
155
+
} : e
156
+
});
157
+
setPdsError('An unexpected error occurred');
158
+
}
159
+
160
+
setIsValidatingPds(false);
161
+
setIsValidatingInviteCode(false);
162
+
};
163
+
164
+
const handlePdsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
165
+
const value = e.target.value;
166
+
setPds(value);
167
+
setIsPdsValid(false);
168
+
setServerDescription(null);
169
+
setLastValidatedPds('');
170
+
setPdsError(null);
171
+
debouncedValidatePds(value);
172
+
};
173
+
174
+
const handleInviteCodeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
175
+
const value = e.target.value;
176
+
setInviteCode(value);
177
+
setInviteCodeError(null);
178
+
setIsInviteCodeValid(false);
179
+
if (pds) {
180
+
debouncedValidateInviteCode(pds, value);
181
+
}
182
+
};
183
+
184
+
const handlePdsBlur = () => {
185
+
validatePdsInput(pds);
186
+
};
187
+
188
+
const handleInviteCodeBlur = () => {
189
+
if (pds && inviteCode) {
190
+
validateInviteCodeInput(pds, inviteCode);
191
+
}
192
+
};
193
+
194
+
return (
195
+
<form onSubmit={handleSubmit}>
196
+
<h3>Find your new host</h3>
197
+
<div className="form-group">
198
+
<label htmlFor="pds">Personal Data Server (PDS)</label>
199
+
<input
200
+
type="text"
201
+
id="pds"
202
+
value={pds}
203
+
onChange={handlePdsChange}
204
+
onBlur={handlePdsBlur}
205
+
placeholder="Example: pds.example.com"
206
+
required
207
+
className={`form-input ${pdsError ? 'error' : ''} ${isPdsValid ? 'success' : ''}`}
208
+
disabled={isValidatingPds}
209
+
/>
210
+
{(!isValidatingPds && pdsError && <div className="error-message">{pdsError}</div>) ||
211
+
(isValidatingPds && <div className="info-message" style={{ marginTop: '0.5rem' }}>Checking if the data server is valid...</div>) ||
212
+
(isPdsValid && <div className="success-message" style={{ marginTop: '0.5rem' }}>โ This data server is alive!</div>)}
213
+
214
+
{serverDescription?.isInviteCodeRequired() && (
215
+
<div className="form-group" style={{ marginTop: '1rem' }}>
216
+
<label htmlFor="inviteCode">Invite Code</label>
217
+
<input
218
+
type="text"
219
+
id="inviteCode"
220
+
value={inviteCode}
221
+
onChange={handleInviteCodeChange}
222
+
onBlur={handleInviteCodeBlur}
223
+
placeholder={`Example: ${pds.replace(/\./g, '-')}-XXXXX-XXXXX`}
224
+
required
225
+
className={`form-input ${inviteCodeError ? 'error' : ''} ${isInviteCodeValid ? 'success' : ''}`}
226
+
disabled={isValidatingInviteCode}
227
+
/>
228
+
{(!isValidatingInviteCode && inviteCodeError && <div className="error-message">{inviteCodeError}</div>) ||
229
+
(isValidatingInviteCode && <div className="info-message" style={{ marginTop: '0.5rem' }}>Validating invite code...</div>) ||
230
+
(isInviteCodeValid && <div className="success-message" style={{ marginTop: '0.5rem' }}>โ Invite code is valid</div>)}
231
+
<div className="info-message" style={{ marginTop: '0.5rem' }}>
232
+
This server requires an invite code to register.
233
+
</div>
234
+
</div>
235
+
)}
236
+
237
+
<div className="button-container">
238
+
<button
239
+
type="button"
240
+
className="back-button"
241
+
onClick={onBack}
242
+
disabled={isValidatingPds || isValidatingInviteCode}
243
+
>
244
+
โ Go back
245
+
</button>
246
+
{isFormReady && (
247
+
<button
248
+
type="submit"
249
+
className="continue-button"
250
+
>
251
+
Continue โ
252
+
</button>
253
+
)}
254
+
</div>
255
+
</div>
256
+
</form>
257
+
);
258
+
}
+158
src/css/actions.css
+158
src/css/actions.css
···
1
+
/* Actions page styles */
2
+
.actions-page {
3
+
display: flex;
4
+
flex-direction: column;
5
+
min-height: 100vh;
6
+
max-width: 800px;
7
+
margin: 0 auto;
8
+
padding: 0 20px;
9
+
gap: 1rem;
10
+
}
11
+
12
+
.actions-container {
13
+
padding: 0;
14
+
max-width: 800px;
15
+
width: 100%;
16
+
margin: 0 auto;
17
+
}
18
+
19
+
.actions-list {
20
+
display: flex;
21
+
flex-direction: column;
22
+
gap: 1rem;
23
+
margin-top: 1rem;
24
+
}
25
+
26
+
.action-item {
27
+
display: flex;
28
+
align-items: center;
29
+
gap: 1rem;
30
+
padding: 1rem;
31
+
background-color: var(--white);
32
+
border-radius: 0.5rem;
33
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
34
+
cursor: pointer;
35
+
transition: transform 0.2s, box-shadow 0.2s;
36
+
border: none;
37
+
width: 100%;
38
+
text-align: left;
39
+
}
40
+
41
+
.action-item:hover {
42
+
transform: translateY(-2px);
43
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
44
+
}
45
+
46
+
.action-icon {
47
+
width: 2.5rem;
48
+
height: 2.5rem;
49
+
display: flex;
50
+
align-items: center;
51
+
justify-content: center;
52
+
color: var(--primary-color);
53
+
background-color: var(--bg-color);
54
+
border-radius: 0.5rem;
55
+
flex-shrink: 0;
56
+
}
57
+
58
+
.action-content {
59
+
flex: 1;
60
+
text-align: left;
61
+
}
62
+
63
+
.action-title {
64
+
font-size: 1rem;
65
+
font-weight: 600;
66
+
color: var(--text-color);
67
+
margin-bottom: 0.25rem;
68
+
text-align: left;
69
+
}
70
+
71
+
.action-subtitle {
72
+
font-size: 0.875rem;
73
+
color: var(--text-light);
74
+
text-align: left;
75
+
}
76
+
77
+
/* User info section */
78
+
.user-info-section {
79
+
background-color: var(--white);
80
+
border-radius: 0.5rem;
81
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
82
+
overflow: hidden;
83
+
margin-top: 1rem;
84
+
}
85
+
86
+
.user-info-summary {
87
+
padding: 1rem;
88
+
cursor: pointer;
89
+
display: flex;
90
+
align-items: center;
91
+
justify-content: space-between;
92
+
list-style: none;
93
+
font-weight: 600;
94
+
color: var(--text-color);
95
+
}
96
+
97
+
.user-info-summary::after {
98
+
content: "โผ";
99
+
color: var(--primary-color);
100
+
transition: transform 0.2s;
101
+
}
102
+
103
+
.user-info-section[open] .user-info-summary::after {
104
+
transform: rotate(180deg);
105
+
}
106
+
107
+
.user-info-content {
108
+
padding: 1.5rem;
109
+
border-top: 1px solid var(--border-color);
110
+
}
111
+
112
+
.user-info-content section {
113
+
margin-bottom: 2rem;
114
+
}
115
+
116
+
.user-info-content section:last-child {
117
+
margin-bottom: 0;
118
+
}
119
+
120
+
.user-info-content h2 {
121
+
font-size: 1.125rem;
122
+
font-weight: 600;
123
+
color: var(--text-color);
124
+
margin-bottom: 1rem;
125
+
}
126
+
127
+
.user-info-content dl {
128
+
display: grid;
129
+
grid-template-columns: max-content 1fr;
130
+
gap: 0.75rem 1rem;
131
+
margin: 0;
132
+
}
133
+
134
+
.user-info-content dt {
135
+
font-weight: 500;
136
+
color: var(--text-light);
137
+
}
138
+
139
+
.user-info-content dd {
140
+
margin: 0;
141
+
color: var(--text-color);
142
+
word-break: break-all;
143
+
}
144
+
145
+
.did-document {
146
+
background-color: var(--bg-color);
147
+
padding: 1rem;
148
+
border-radius: 0.375rem;
149
+
overflow-x: auto;
150
+
margin: 0;
151
+
}
152
+
153
+
.did-document code {
154
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
155
+
font-size: 0.875rem;
156
+
color: var(--text-color);
157
+
white-space: pre;
158
+
}
+175
src/css/base.css
+175
src/css/base.css
···
1
+
/* Base styles */
2
+
:root {
3
+
--primary-color: #4338ca;
4
+
--primary-hover: #4f46e5;
5
+
--text-color: #1f2937;
6
+
--text-light: #6b7280;
7
+
--bg-color: #f3f4f6;
8
+
--white: #ffffff;
9
+
--error-color: #ef4444;
10
+
--border-color: #e5e7eb;
11
+
--input-bg: #ffffff;
12
+
}
13
+
14
+
@media (prefers-color-scheme: dark) {
15
+
:root {
16
+
--primary-color: #4f46e5;
17
+
--primary-hover: #6366f1;
18
+
--text-color: #f3f4f6;
19
+
--text-light: #9ca3af;
20
+
--bg-color: #111827;
21
+
--white: #1f2937;
22
+
--error-color: #f87171;
23
+
--border-color: #374151;
24
+
--input-bg: #1f2937;
25
+
}
26
+
}
27
+
28
+
body {
29
+
margin: 0;
30
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
31
+
background-color: var(--bg-color);
32
+
color: var(--text-color);
33
+
min-height: 100vh;
34
+
display: flex;
35
+
flex-direction: column;
36
+
}
37
+
38
+
/* Common utility classes */
39
+
.page-content {
40
+
background-color: var(--white);
41
+
border-radius: 0.5rem;
42
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
43
+
padding: 2rem;
44
+
padding-top: 1rem;
45
+
margin-top: 1rem;
46
+
}
47
+
48
+
.page-content h2 {
49
+
font-size: 1.5rem;
50
+
font-weight: 600;
51
+
color: var(--text-color);
52
+
margin-bottom: 1rem;
53
+
}
54
+
55
+
.page-content p {
56
+
color: var(--text-color);
57
+
margin-bottom: 1rem;
58
+
}
59
+
60
+
.page-content ul {
61
+
list-style-type: disc;
62
+
margin-left: 1.5rem;
63
+
margin-bottom: 1.5rem;
64
+
color: var(--text-color);
65
+
}
66
+
67
+
.page-content li {
68
+
margin-bottom: 0.5rem;
69
+
}
70
+
71
+
.page-content h3 {
72
+
font-size: 1.25rem;
73
+
font-weight: 600;
74
+
color: var(--text-color);
75
+
margin: 1.5rem 0 1rem 0;
76
+
}
77
+
78
+
/* Warning section */
79
+
.warning-section {
80
+
background-color: #fef3c7;
81
+
border: 1px solid #fbbf24;
82
+
border-radius: 0.5rem;
83
+
padding: 1.5rem;
84
+
margin: 1.5rem 0;
85
+
}
86
+
87
+
.warning-section h3 {
88
+
color: #92400e;
89
+
margin-top: 0;
90
+
}
91
+
92
+
.warning-section ul {
93
+
margin-bottom: 0;
94
+
padding-left: 0;
95
+
}
96
+
97
+
.warning-section li {
98
+
color: #92400e;
99
+
}
100
+
101
+
.warning-section b {
102
+
color: #78350f;
103
+
}
104
+
105
+
/* Docs section */
106
+
.docs-section {
107
+
background-color: var(--bg-color);
108
+
border-radius: 0.5rem;
109
+
padding: 1.5rem;
110
+
margin: 1.5rem 0;
111
+
}
112
+
113
+
.docs-section h3 {
114
+
margin-top: 0;
115
+
}
116
+
117
+
.docs-section ul {
118
+
margin-bottom: 0;
119
+
padding-left: 0;
120
+
}
121
+
122
+
.docs-section a {
123
+
color: var(--primary-color);
124
+
text-decoration: none;
125
+
transition: color 0.2s;
126
+
display: inline-flex;
127
+
align-items: center;
128
+
gap: 0.5rem;
129
+
}
130
+
131
+
.docs-section a:hover {
132
+
color: var(--primary-hover);
133
+
}
134
+
135
+
.docs-section a::after {
136
+
content: "โ";
137
+
transition: transform 0.2s;
138
+
}
139
+
140
+
.docs-section a:hover::after {
141
+
transform: translateX(4px);
142
+
}
143
+
144
+
/* Network warning */
145
+
.network-warning {
146
+
position: fixed;
147
+
top: 0;
148
+
left: 0;
149
+
right: 0;
150
+
bottom: 0;
151
+
background-color: rgba(0, 0, 0, 0.8);
152
+
z-index: 1000;
153
+
display: flex;
154
+
align-items: center;
155
+
justify-content: center;
156
+
}
157
+
158
+
.network-warning-content {
159
+
max-width: 800px;
160
+
padding: 2rem;
161
+
display: flex;
162
+
align-items: center;
163
+
gap: 0.75rem;
164
+
color: #92400e;
165
+
font-size: 1.125rem;
166
+
text-align: center;
167
+
background-color: rgba(255, 255, 255, 0.9);
168
+
border-radius: 0.5rem;
169
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
170
+
}
171
+
172
+
.network-warning-icon {
173
+
font-size: 1.5rem;
174
+
flex-shrink: 0;
175
+
}
+216
src/css/confirmation.css
+216
src/css/confirmation.css
···
1
+
.comparison-container {
2
+
display: flex;
3
+
align-items: center;
4
+
gap: 1rem;
5
+
padding: 0 1.5rem 1.5rem 0;
6
+
background-color: var(--white);
7
+
border-radius: 0.5rem;
8
+
flex-wrap: wrap;
9
+
width: 100%;
10
+
}
11
+
12
+
.comparison-side {
13
+
flex: 1;
14
+
min-width: 200px;
15
+
padding: 1rem;
16
+
background-color: var(--bg-color);
17
+
border-radius: 0.375rem;
18
+
}
19
+
20
+
.comparison-side h4 {
21
+
margin: 0 0 0.75rem 0;
22
+
color: var(--text-light);
23
+
font-size: 0.875rem;
24
+
font-weight: 500;
25
+
}
26
+
27
+
.comparison-arrow {
28
+
display: flex;
29
+
align-items: center;
30
+
justify-content: center;
31
+
width: 2.5rem;
32
+
height: 2.5rem;
33
+
background-color: var(--primary-color);
34
+
border-radius: 50%;
35
+
color: white;
36
+
font-size: 1.25rem;
37
+
}
38
+
39
+
.detail-group {
40
+
margin-bottom: 1rem;
41
+
}
42
+
43
+
.detail-group label {
44
+
display: block;
45
+
margin-bottom: 0.5rem;
46
+
color: var(--text-light);
47
+
font-size: 0.875rem;
48
+
}
49
+
50
+
.detail-value {
51
+
padding: 0.75rem;
52
+
background-color: var(--bg-color);
53
+
border-radius: 0.375rem;
54
+
font-size: 0.875rem;
55
+
color: var(--text-color);
56
+
}
57
+
58
+
.password-container {
59
+
position: relative;
60
+
}
61
+
62
+
.password-toggle {
63
+
position: absolute;
64
+
right: 0.75rem;
65
+
top: 50%;
66
+
transform: translateY(-50%);
67
+
background: none;
68
+
border: none;
69
+
color: var(--text-light);
70
+
cursor: pointer;
71
+
padding: 0.25rem;
72
+
font-size: 0.875rem;
73
+
z-index: 1;
74
+
}
75
+
76
+
.password-toggle:hover {
77
+
color: var(--text-color);
78
+
}
79
+
80
+
.hidden-password {
81
+
letter-spacing: 0.25em;
82
+
}
83
+
84
+
.form-section.completed .button-container {
85
+
display: none;
86
+
}
87
+
88
+
.confirm-button {
89
+
background-color: #c72424;
90
+
color: var(--text-color);
91
+
border: none;
92
+
padding: 0.75rem 1.5rem;
93
+
border-radius: 0.375rem;
94
+
font-size: 0.875rem;
95
+
font-weight: 500;
96
+
cursor: pointer;
97
+
transition: background-color 0.2s;
98
+
display: inline-flex;
99
+
align-items: center;
100
+
gap: 0.5rem;
101
+
margin-left: auto;
102
+
}
103
+
104
+
.confirm-button:hover {
105
+
background-color: #f32e2e;
106
+
}
107
+
108
+
@media (max-width: 707px) {
109
+
.comparison-container {
110
+
flex-direction: column;
111
+
align-items: center;
112
+
}
113
+
114
+
.comparison-side {
115
+
margin: 0 0 0 0;
116
+
padding: 1rem 0 0 0;
117
+
}
118
+
119
+
/* UTTER WOKE NONSENSE */
120
+
.comparison-side h3, .comparison-side label, .comparison-side .detail-group div {
121
+
padding: 0 1rem 0 1rem;
122
+
}
123
+
124
+
.comparison-arrow {
125
+
transform: rotate(90deg);
126
+
margin: 0.5rem 0;
127
+
}
128
+
}
129
+
130
+
.warning-overlay {
131
+
position: fixed;
132
+
top: 0;
133
+
left: 0;
134
+
right: 0;
135
+
bottom: 0;
136
+
background-color: rgba(0, 0, 0, 0.5);
137
+
display: flex;
138
+
align-items: center;
139
+
justify-content: center;
140
+
z-index: 1000;
141
+
}
142
+
143
+
.warning-dialog {
144
+
background-color: var(--white);
145
+
border-radius: 0.5rem;
146
+
padding: 1.5rem;
147
+
max-width: 500px;
148
+
width: 90%;
149
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
150
+
}
151
+
152
+
.warning-dialog h3 {
153
+
color: var(--error-color);
154
+
margin: 0 0 1rem 0;
155
+
font-size: 1.25rem;
156
+
}
157
+
158
+
.warning-content {
159
+
margin-bottom: 1.5rem;
160
+
}
161
+
162
+
.warning-content p {
163
+
margin: 0 0 1rem 0;
164
+
color: var(--text-color);
165
+
}
166
+
167
+
.warning-content ul {
168
+
margin: 0 0 1rem 0;
169
+
padding-left: 1.5rem;
170
+
}
171
+
172
+
.warning-content li {
173
+
margin-bottom: 0.5rem;
174
+
color: var(--text-color);
175
+
}
176
+
177
+
.warning-buttons {
178
+
display: flex;
179
+
justify-content: flex-end;
180
+
gap: 1rem;
181
+
}
182
+
183
+
.pds-verification {
184
+
margin: 1rem 0;
185
+
}
186
+
187
+
.pds-verification input {
188
+
width: 98%;
189
+
padding: 0.75rem 0 0.75rem 0.5rem;
190
+
border: 1px solid var(--border-color);
191
+
border-radius: 0.375rem;
192
+
font-size: 0.875rem;
193
+
color: var(--text-color);
194
+
background-color: var(--white);
195
+
transition: border-color 0.2s;
196
+
}
197
+
198
+
.pds-verification input:focus {
199
+
outline: none;
200
+
border-color: var(--primary-color);
201
+
}
202
+
203
+
.pds-verification input.error {
204
+
border-color: var(--error-color);
205
+
}
206
+
207
+
.error-message {
208
+
color: var(--error-color);
209
+
font-size: 0.875rem;
210
+
margin: 0.5rem 0 0 0;
211
+
}
212
+
213
+
.warning-buttons button:disabled {
214
+
opacity: 0.5;
215
+
cursor: not-allowed;
216
+
}
+94
src/css/forms.css
+94
src/css/forms.css
···
1
+
/* Form styles */
2
+
.form-input {
3
+
width: 100%;
4
+
padding: 0.75rem 1rem;
5
+
border: 1px solid var(--border-color);
6
+
border-radius: 0.375rem;
7
+
font-size: 0.875rem;
8
+
color: var(--text-color);
9
+
background-color: var(--input-bg);
10
+
box-sizing: border-box;
11
+
}
12
+
13
+
.form-input:focus {
14
+
outline: none;
15
+
border-color: var(--primary-color);
16
+
box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.1);
17
+
}
18
+
19
+
.form-input::placeholder {
20
+
color: var(--text-light);
21
+
}
22
+
23
+
.form-input.error {
24
+
border-color: var(--error-color);
25
+
}
26
+
27
+
.form-input.error:focus {
28
+
box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.1);
29
+
}
30
+
31
+
.form-input:disabled {
32
+
background-color: var(--bg-color);
33
+
border-color: var(--border-color);
34
+
color: var(--text-light);
35
+
cursor: not-allowed;
36
+
opacity: 0.8;
37
+
}
38
+
39
+
.error-message {
40
+
color: var(--error-color);
41
+
font-size: 0.875rem;
42
+
margin-top: 0.25rem;
43
+
}
44
+
45
+
.success-message {
46
+
color: #059669;
47
+
font-size: 0.875rem;
48
+
margin: 0.5rem 0;
49
+
display: flex;
50
+
align-items: center;
51
+
gap: 0.5rem;
52
+
}
53
+
54
+
.form-section {
55
+
background: var(--white);
56
+
border-radius: 8px;
57
+
padding: 2rem;
58
+
margin-bottom: 2rem;
59
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
60
+
transition: all 0.3s ease;
61
+
border: 1px solid var(--border-color);
62
+
}
63
+
64
+
.form-section.completed {
65
+
opacity: 0.6;
66
+
pointer-events: none;
67
+
background: var(--bg-color);
68
+
}
69
+
70
+
.form-section h3 {
71
+
margin-top: 0;
72
+
margin-bottom: 1.5rem;
73
+
color: var(--text-color);
74
+
font-size: 1.25rem;
75
+
}
76
+
77
+
.info-message {
78
+
color: var(--text-light);
79
+
font-size: 0.875rem;
80
+
margin-top: 0;
81
+
margin-bottom: 1rem;
82
+
padding: 1rem;
83
+
background-color: rgba(76, 67, 249, 0.25);
84
+
border-radius: 0.375rem;
85
+
}
86
+
87
+
.info-message h3 {
88
+
margin-top: 0;
89
+
margin-bottom: 0.5rem;
90
+
}
91
+
92
+
.info-message strong {
93
+
word-break: break-all;
94
+
}
+84
src/css/header.css
+84
src/css/header.css
···
1
+
/* Header styles */
2
+
.app-header {
3
+
display: flex;
4
+
flex-wrap: wrap;
5
+
align-items: center;
6
+
justify-content: space-between;
7
+
gap: 1rem;
8
+
padding: 1rem;
9
+
background-color: var(--white);
10
+
border-radius: 0.5rem;
11
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
12
+
margin: 1rem 0 0 0;
13
+
position: relative;
14
+
z-index: 10;
15
+
}
16
+
17
+
.app-title {
18
+
font-size: 1.25rem;
19
+
font-weight: 700;
20
+
color: var(--text-color);
21
+
margin: 0;
22
+
white-space: nowrap;
23
+
overflow: hidden;
24
+
text-overflow: ellipsis;
25
+
max-width: 200px;
26
+
}
27
+
28
+
.user-info {
29
+
display: flex;
30
+
align-items: center;
31
+
gap: 0.75rem;
32
+
flex-wrap: wrap;
33
+
}
34
+
35
+
.user-avatar {
36
+
width: 2rem;
37
+
height: 2rem;
38
+
border-radius: 50%;
39
+
object-fit: cover;
40
+
flex-shrink: 0;
41
+
}
42
+
43
+
.user-handle {
44
+
font-size: 0.875rem;
45
+
color: var(--text-color);
46
+
white-space: nowrap;
47
+
overflow: hidden;
48
+
text-overflow: ellipsis;
49
+
max-width: 150px;
50
+
}
51
+
52
+
.logout-button {
53
+
background-color: #f44336;
54
+
color: white;
55
+
border: none;
56
+
padding: 8px 16px;
57
+
border-radius: 4px;
58
+
cursor: pointer;
59
+
font-size: 14px;
60
+
transition: background-color 0.2s;
61
+
white-space: nowrap;
62
+
flex-shrink: 0;
63
+
}
64
+
65
+
@media (max-width: 480px) {
66
+
.app-header {
67
+
padding: 0.75rem;
68
+
gap: 0.5rem;
69
+
}
70
+
71
+
.app-title {
72
+
font-size: 1.125rem;
73
+
max-width: 150px;
74
+
}
75
+
76
+
.user-handle {
77
+
max-width: 100px;
78
+
}
79
+
80
+
.logout-button {
81
+
padding: 6px 12px;
82
+
font-size: 13px;
83
+
}
84
+
}
+47
src/css/loading.css
+47
src/css/loading.css
···
1
+
/* Loading state styles */
2
+
.loading-container {
3
+
flex: 1;
4
+
display: flex;
5
+
align-items: center;
6
+
justify-content: center;
7
+
min-height: 100vh;
8
+
padding: 1rem;
9
+
}
10
+
11
+
.loading-text {
12
+
font-size: 1.25rem;
13
+
color: var(--text-color);
14
+
background-color: var(--white);
15
+
padding: 1.5rem 2rem;
16
+
border-radius: 0.5rem;
17
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
18
+
display: flex;
19
+
align-items: center;
20
+
gap: 0.75rem;
21
+
}
22
+
23
+
.loading-text::before {
24
+
content: "";
25
+
width: 1.5rem;
26
+
height: 1.5rem;
27
+
border: 2px solid var(--primary-color);
28
+
border-right-color: transparent;
29
+
border-radius: 50%;
30
+
animation: spin 1s linear infinite;
31
+
}
32
+
33
+
@keyframes spin {
34
+
to {
35
+
transform: rotate(360deg);
36
+
}
37
+
}
38
+
39
+
.loading-message {
40
+
margin: 10px 0;
41
+
padding: 10px;
42
+
background-color: var(--bg-color);
43
+
border-radius: 4px;
44
+
color: var(--text-color);
45
+
font-size: 0.9em;
46
+
text-align: center;
47
+
}
+140
src/css/login.css
+140
src/css/login.css
···
1
+
/* Login page styles */
2
+
.login-container {
3
+
flex: 1;
4
+
display: flex;
5
+
align-items: center;
6
+
justify-content: center;
7
+
padding: 1rem;
8
+
}
9
+
10
+
.login-card {
11
+
max-width: 28rem;
12
+
width: 100%;
13
+
padding: 2rem;
14
+
padding-top: 0;
15
+
background-color: var(--white);
16
+
border-radius: 0.5rem;
17
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
18
+
}
19
+
20
+
.login-title {
21
+
padding-top: 0;
22
+
text-align: center;
23
+
font-size: 1.875rem;
24
+
font-weight: 800;
25
+
color: var(--text-color);
26
+
}
27
+
28
+
.warning-message {
29
+
margin: 1rem 0;
30
+
padding: 0.75rem;
31
+
background-color: #fef3c7;
32
+
border: 1px solid #fbbf24;
33
+
border-radius: 0.375rem;
34
+
color: #92400e;
35
+
font-size: 0.875rem;
36
+
text-align: center;
37
+
}
38
+
39
+
.login-form {
40
+
margin-top: 2rem;
41
+
padding: 0 1rem;
42
+
}
43
+
44
+
.form-group {
45
+
margin-bottom: 1rem;
46
+
}
47
+
48
+
.form-group label {
49
+
display: block;
50
+
margin-bottom: 0.5rem;
51
+
color: var(--text-color);
52
+
font-weight: 500;
53
+
}
54
+
55
+
.handle-input-container {
56
+
display: flex;
57
+
align-items: stretch;
58
+
gap: 0;
59
+
background-color: var(--input-bg);
60
+
border: 1px solid var(--border-color);
61
+
border-radius: 0.375rem;
62
+
}
63
+
64
+
.handle-input-container .form-input {
65
+
border: none;
66
+
padding-right: 0;
67
+
flex: 1;
68
+
border-radius: 0.375rem 0 0 0.375rem;
69
+
}
70
+
71
+
.handle-input-container .form-input:focus {
72
+
box-shadow: none;
73
+
}
74
+
75
+
.handle-domain {
76
+
color: var(--text-light);
77
+
font-size: 0.875rem;
78
+
white-space: nowrap;
79
+
background-color: var(--bg-color);
80
+
padding: 0 0.75rem;
81
+
border-radius: 0 0.375rem 0.375rem 0;
82
+
display: flex;
83
+
align-items: center;
84
+
}
85
+
86
+
/* 2FA Modal styles */
87
+
.modal-overlay {
88
+
position: fixed;
89
+
top: 0;
90
+
left: 0;
91
+
right: 0;
92
+
bottom: 0;
93
+
background-color: rgba(0, 0, 0, 0.75);
94
+
display: flex;
95
+
justify-content: center;
96
+
align-items: center;
97
+
z-index: 1000;
98
+
}
99
+
100
+
.modal-content {
101
+
background-color: var(--white);
102
+
padding: 2rem;
103
+
border-radius: 0.5rem;
104
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
105
+
width: 90%;
106
+
max-width: 28rem;
107
+
}
108
+
109
+
.modal-content h2 {
110
+
margin-top: 0;
111
+
margin-bottom: 1rem;
112
+
color: var(--text-color);
113
+
text-align: center;
114
+
}
115
+
116
+
.modal-content p {
117
+
margin-bottom: 1.5rem;
118
+
color: var(--text-light);
119
+
text-align: center;
120
+
}
121
+
122
+
.two-factor-form {
123
+
margin-top: 2rem;
124
+
}
125
+
126
+
.button-group {
127
+
display: flex;
128
+
gap: 1rem;
129
+
margin-top: 1rem;
130
+
}
131
+
132
+
.button-group .submit-button {
133
+
margin-top: 0;
134
+
flex: 1;
135
+
width: fit-content;
136
+
}
137
+
138
+
.button-group .back-button {
139
+
flex: 1;
140
+
}
+71
src/css/migration.css
+71
src/css/migration.css
···
1
+
/* Migration progress styles */
2
+
.migration-progress {
3
+
margin-top: 2rem;
4
+
padding: 1.5rem;
5
+
background-color: var(--white);
6
+
border-radius: 0.5rem;
7
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
8
+
}
9
+
10
+
.migration-progress h3 {
11
+
margin: 0 0 1rem 0;
12
+
color: var(--text-color);
13
+
}
14
+
15
+
.progress-steps {
16
+
display: flex;
17
+
flex-direction: column;
18
+
gap: 1rem;
19
+
}
20
+
21
+
.progress-step {
22
+
display: flex;
23
+
align-items: center;
24
+
gap: 1rem;
25
+
padding: 0.75rem;
26
+
border-radius: 0.5rem;
27
+
background-color: var(--bg-color);
28
+
transition: background-color 0.2s;
29
+
}
30
+
31
+
.progress-step.active {
32
+
background-color: var(--primary-color);
33
+
color: white;
34
+
}
35
+
36
+
.step-number {
37
+
width: 2rem;
38
+
height: 2rem;
39
+
display: flex;
40
+
align-items: center;
41
+
justify-content: center;
42
+
background-color: var(--white);
43
+
border-radius: 50%;
44
+
font-weight: bold;
45
+
color: var(--text-color);
46
+
}
47
+
48
+
.progress-step.active .step-number {
49
+
background-color: var(--white);
50
+
color: var(--primary-color);
51
+
}
52
+
53
+
.step-text {
54
+
font-size: 0.875rem;
55
+
}
56
+
57
+
@media (max-width: 480px) {
58
+
.migration-progress {
59
+
padding: 1rem;
60
+
}
61
+
62
+
.progress-step {
63
+
padding: 0.5rem;
64
+
}
65
+
66
+
.step-number {
67
+
width: 1.75rem;
68
+
height: 1.75rem;
69
+
font-size: 0.875rem;
70
+
}
71
+
}
+91
src/css/plcToken.css
+91
src/css/plcToken.css
···
1
+
/* PLC token section styles */
2
+
.plc-token-section {
3
+
background: #f8f9fa;
4
+
border-radius: 8px;
5
+
padding: 20px;
6
+
margin: 15px 0;
7
+
}
8
+
9
+
.token-request-container {
10
+
text-align: center;
11
+
}
12
+
13
+
.token-request-container p {
14
+
margin-bottom: 15px;
15
+
color: #666;
16
+
}
17
+
18
+
.token-input-container {
19
+
max-width: 500px;
20
+
margin: 0 auto;
21
+
}
22
+
23
+
.token-input-container p {
24
+
margin-bottom: 10px;
25
+
color: #666;
26
+
}
27
+
28
+
.token-input-group {
29
+
display: flex;
30
+
gap: 10px;
31
+
margin-bottom: 15px;
32
+
}
33
+
34
+
.token-input {
35
+
flex: 1;
36
+
padding: 10px;
37
+
border: 1px solid #ddd;
38
+
border-radius: 4px;
39
+
font-size: 14px;
40
+
}
41
+
42
+
.request-token-button,
43
+
.submit-token-button,
44
+
.request-new-token-button {
45
+
padding: 10px 20px;
46
+
border: none;
47
+
border-radius: 4px;
48
+
font-size: 14px;
49
+
cursor: pointer;
50
+
transition: background-color 0.2s;
51
+
}
52
+
53
+
.request-token-button {
54
+
background-color: #007bff;
55
+
color: white;
56
+
}
57
+
58
+
.request-token-button:hover {
59
+
background-color: #0056b3;
60
+
}
61
+
62
+
.submit-token-button {
63
+
background-color: #28a745;
64
+
color: white;
65
+
}
66
+
67
+
.submit-token-button:hover {
68
+
background-color: #218838;
69
+
}
70
+
71
+
.submit-token-button:disabled {
72
+
background-color: #6c757d;
73
+
cursor: not-allowed;
74
+
}
75
+
76
+
.request-new-token-button {
77
+
background-color: transparent;
78
+
color: #007bff;
79
+
border: 1px solid #007bff;
80
+
width: 100%;
81
+
}
82
+
83
+
.request-new-token-button:hover:not(:disabled) {
84
+
background-color: #f0f7ff;
85
+
}
86
+
87
+
.request-new-token-button:disabled {
88
+
color: #6c757d;
89
+
border-color: #6c757d;
90
+
cursor: not-allowed;
91
+
}
+21
src/hooks/useDebounce.ts
+21
src/hooks/useDebounce.ts
···
1
+
import { useCallback, useRef } from 'react';
2
+
3
+
export function useDebounce<T extends (...args: any[]) => void>(
4
+
callback: T,
5
+
delay: number
6
+
): T {
7
+
const timeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
8
+
9
+
return useCallback(
10
+
(...args: Parameters<T>) => {
11
+
if (timeoutRef.current) {
12
+
clearTimeout(timeoutRef.current);
13
+
}
14
+
15
+
timeoutRef.current = setTimeout(() => {
16
+
callback(...args);
17
+
}, delay);
18
+
},
19
+
[callback, delay]
20
+
) as T;
21
+
}
+24
src/lib/migration/accountDetailsValidation.ts
+24
src/lib/migration/accountDetailsValidation.ts
···
1
+
export const validateHandle = (handle: string, availableUserDomains: string[]): {
2
+
isUsingDefaultDomain: boolean;
3
+
customHandle: string;
4
+
} => {
5
+
// Check if handle ends with any of the available user domains
6
+
const isUsingDefaultDomain = availableUserDomains.some(domain =>
7
+
handle.endsWith(domain)
8
+
);
9
+
10
+
if (!isUsingDefaultDomain) {
11
+
return {
12
+
isUsingDefaultDomain: false,
13
+
customHandle: handle
14
+
};
15
+
}
16
+
17
+
// Extract the custom part of the handle (everything before the domain)
18
+
const customHandle = handle.split('.')[0];
19
+
20
+
return {
21
+
isUsingDefaultDomain: true,
22
+
customHandle: customHandle
23
+
};
24
+
};
+43
src/lib/migration/migrationData.ts
+43
src/lib/migration/migrationData.ts
···
1
+
import { AtpAgent } from "@atproto/api";
2
+
3
+
export class MigrationData {
4
+
private readonly oldPds: String;
5
+
private readonly newPds: String;
6
+
private readonly inviteCode: String | null;
7
+
private readonly handle: String;
8
+
private readonly email: String;
9
+
private readonly password: String;
10
+
11
+
constructor(oldPds: String, newPds: String, inviteCode: String | null, handle: String, email: String, password: String) {
12
+
this.oldPds = oldPds;
13
+
this.newPds = newPds;
14
+
this.inviteCode = inviteCode;
15
+
this.handle = handle;
16
+
this.email = email;
17
+
this.password = password;
18
+
}
19
+
20
+
public getOldPds(): String {
21
+
return this.oldPds;
22
+
}
23
+
24
+
public getNewPds(): String {
25
+
return this.newPds;
26
+
}
27
+
28
+
public getInviteCode(): String | null {
29
+
return this.inviteCode;
30
+
}
31
+
32
+
public getHandle(): String {
33
+
return this.handle;
34
+
}
35
+
36
+
public getEmail(): String {
37
+
return this.email;
38
+
}
39
+
40
+
public getPassword(): String {
41
+
return this.password;
42
+
}
43
+
}
+236
src/lib/migration/pdsValidation.ts
+236
src/lib/migration/pdsValidation.ts
···
1
+
import { AtpAgent } from '@atproto/api';
2
+
import { ServerDescription } from './serverDescription';
3
+
4
+
export interface ValidationResult {
5
+
isValid: boolean;
6
+
error: string | null;
7
+
}
8
+
9
+
const ensureProtocol = (url: string): string => {
10
+
if (url.startsWith('http://') || url.startsWith('https://')) {
11
+
return url;
12
+
}
13
+
return `https://${url}`;
14
+
};
15
+
16
+
const isValidUrl = (url: string): boolean => {
17
+
try {
18
+
new URL(url);
19
+
return true;
20
+
} catch {
21
+
return false;
22
+
}
23
+
};
24
+
25
+
const checkPDSHealth = async (pdsUrl: string): Promise<boolean> => {
26
+
const controller = new AbortController();
27
+
const timeoutId = setTimeout(() => controller.abort(), 15000);
28
+
29
+
try {
30
+
const response = await fetch(`${pdsUrl}/xrpc/_health`, {
31
+
signal: controller.signal
32
+
});
33
+
if (!response.ok) return false;
34
+
35
+
const data = await response.json();
36
+
// Check if response is just a version number
37
+
return typeof data === 'string' || (typeof data === 'object' && Object.keys(data).length === 1 && typeof data.version === 'string');
38
+
} catch (e) {
39
+
if (e instanceof Error && e.name === 'AbortError') {
40
+
throw new Error('Request timed out');
41
+
}
42
+
return false;
43
+
} finally {
44
+
clearTimeout(timeoutId);
45
+
}
46
+
};
47
+
48
+
const getServerDescription = async (pdsUrl: string): Promise<ServerDescription> => {
49
+
const controller = new AbortController();
50
+
const timeoutId = setTimeout(() => controller.abort(), 15000);
51
+
52
+
try {
53
+
const response = await fetch(`https://${pdsUrl}/xrpc/com.atproto.server.describeServer`, {
54
+
signal: controller.signal
55
+
});
56
+
if (!response.ok) {
57
+
const errorData = await response.text();
58
+
console.error('Server description request failed:', {
59
+
status: response.status,
60
+
statusText: response.statusText,
61
+
url: pdsUrl,
62
+
response: errorData
63
+
});
64
+
throw new Error('Failed to get server description');
65
+
}
66
+
67
+
const data = await response.json();
68
+
console.log('Server description response:', {
69
+
url: pdsUrl,
70
+
data
71
+
});
72
+
73
+
return new ServerDescription({
74
+
did: data.did,
75
+
availableUserDomains: data.availableUserDomains ?? {},
76
+
inviteCodeRequired: data.inviteCodeRequired ?? false,
77
+
links: data.links ?? {},
78
+
contact: data.contact ?? {}
79
+
});
80
+
} catch (e) {
81
+
console.error('Error getting server description:', {
82
+
url: pdsUrl,
83
+
error: e instanceof Error ? {
84
+
name: e.name,
85
+
message: e.message,
86
+
stack: e.stack
87
+
} : e
88
+
});
89
+
if (e instanceof Error && e.name === 'AbortError') {
90
+
throw new Error('Request timed out');
91
+
}
92
+
throw e;
93
+
} finally {
94
+
clearTimeout(timeoutId);
95
+
}
96
+
};
97
+
98
+
export const validateInviteCode = async (pdsUrl: string, inviteCode: string): Promise<ValidationResult> => {
99
+
inviteCode = inviteCode.trim();
100
+
101
+
try {
102
+
if (!inviteCode) {
103
+
return {
104
+
isValid: false,
105
+
error: 'This server requires an invite code'
106
+
};
107
+
}
108
+
109
+
const pdsPart = pdsUrl.replace(/\./g, '-');
110
+
const inviteCodeRegex = new RegExp(`^${pdsPart}-[a-zA-Z0-9]{5}-[a-zA-Z0-9]{5}$`);
111
+
const isValid = inviteCodeRegex.test(inviteCode);
112
+
113
+
if (!isValid) {
114
+
console.error('Invalid invite code format:', {
115
+
pds: pdsUrl,
116
+
inviteCode,
117
+
expectedFormat: `${pdsUrl.replace(/\./g, '-')}-XXXXX-XXXXX`
118
+
});
119
+
return {
120
+
isValid: false,
121
+
error: 'Incorrect invite code format'
122
+
};
123
+
}
124
+
125
+
return {
126
+
isValid: true,
127
+
error: null
128
+
};
129
+
} catch (e) {
130
+
console.error('Error validating invite code:', {
131
+
pds: pdsUrl,
132
+
inviteCode,
133
+
error: e instanceof Error ? {
134
+
name: e.name,
135
+
message: e.message,
136
+
stack: e.stack
137
+
} : e
138
+
});
139
+
return {
140
+
isValid: false,
141
+
error: 'Failed to validate invite code'
142
+
};
143
+
}
144
+
};
145
+
146
+
export const validatePDS = async (pdsUrl: string, agent: AtpAgent): Promise<ValidationResult> => {
147
+
if (!pdsUrl.trim()) {
148
+
return {
149
+
isValid: false,
150
+
error: null
151
+
};
152
+
}
153
+
154
+
const urlWithProtocol = ensureProtocol(pdsUrl);
155
+
156
+
if (!isValidUrl(urlWithProtocol)) {
157
+
return {
158
+
isValid: false,
159
+
error: 'Please enter a valid URL'
160
+
};
161
+
}
162
+
163
+
try {
164
+
const url = new URL(urlWithProtocol);
165
+
const hostname = url.hostname;
166
+
167
+
// Check for Bluesky domains
168
+
if (hostname.endsWith('bsky.network') ||
169
+
hostname.endsWith('bsky.social') ||
170
+
hostname.endsWith('bsky.app')) {
171
+
return {
172
+
isValid: false,
173
+
error: 'You cannot migrate to Bluesky data servers at this time.'
174
+
};
175
+
}
176
+
177
+
// Check if trying to migrate to current PDS
178
+
const currentPDS = agent.serviceUrl;
179
+
if (hostname === new URL(currentPDS).hostname) {
180
+
return {
181
+
isValid: false,
182
+
error: 'You cannot migrate to the same PDS that you are currently using.'
183
+
};
184
+
}
185
+
186
+
// Check if PDS is alive and valid
187
+
try {
188
+
const isHealthy = await checkPDSHealth(urlWithProtocol);
189
+
if (!isHealthy) {
190
+
return {
191
+
isValid: false,
192
+
error: 'This PDS is either offline or not a valid server.'
193
+
};
194
+
}
195
+
} catch (e) {
196
+
console.error('Error checking PDS health:', {
197
+
url: urlWithProtocol,
198
+
error: e instanceof Error ? {
199
+
name: e.name,
200
+
message: e.message,
201
+
stack: e.stack
202
+
} : e
203
+
});
204
+
if (e instanceof Error && e.message === 'Request timed out') {
205
+
return {
206
+
isValid: false,
207
+
error: 'The server took too long to respond. Please try again later.'
208
+
};
209
+
}
210
+
return {
211
+
isValid: false,
212
+
error: 'This PDS is either offline or not a valid server.'
213
+
};
214
+
}
215
+
216
+
return {
217
+
isValid: true,
218
+
error: null
219
+
};
220
+
} catch (e) {
221
+
console.error('Error validating PDS:', {
222
+
url: pdsUrl,
223
+
error: e instanceof Error ? {
224
+
name: e.name,
225
+
message: e.message,
226
+
stack: e.stack
227
+
} : e
228
+
});
229
+
return {
230
+
isValid: false,
231
+
error: 'Please enter a valid URL'
232
+
};
233
+
}
234
+
};
235
+
236
+
export { getServerDescription };
+48
src/lib/migration/serverDescription.ts
+48
src/lib/migration/serverDescription.ts
···
1
+
export class ServerDescription {
2
+
private readonly did: string;
3
+
private readonly availableUserDomains: Record<number, string>;
4
+
private readonly inviteCodeRequired: boolean;
5
+
private readonly phoneVerificationRequired: boolean;
6
+
private readonly links: Record<string, string>;
7
+
private readonly contact: Record<string, string>;
8
+
9
+
constructor(data: {
10
+
did: string;
11
+
availableUserDomains: Record<number, string>;
12
+
inviteCodeRequired: boolean;
13
+
phoneVerificationRequired?: boolean;
14
+
links: Record<string, string>;
15
+
contact: Record<string, string>;
16
+
}) {
17
+
this.did = data.did;
18
+
this.availableUserDomains = data.availableUserDomains;
19
+
this.inviteCodeRequired = data.inviteCodeRequired;
20
+
this.phoneVerificationRequired = data.phoneVerificationRequired ?? false;
21
+
this.links = data.links;
22
+
this.contact = data.contact;
23
+
}
24
+
25
+
getDid(): string {
26
+
return this.did;
27
+
}
28
+
29
+
getAvailableUserDomains(): Record<number, string> {
30
+
return this.availableUserDomains;
31
+
}
32
+
33
+
isInviteCodeRequired(): boolean {
34
+
return this.inviteCodeRequired;
35
+
}
36
+
37
+
isPhoneVerificationRequired(): boolean {
38
+
return this.phoneVerificationRequired;
39
+
}
40
+
41
+
getLinks(): Record<string, string> {
42
+
return this.links;
43
+
}
44
+
45
+
getContact(): Record<string, string> {
46
+
return this.contact;
47
+
}
48
+
}
+11
src/main.css
+11
src/main.css
···
1
+
@import 'css/base.css';
2
+
@import 'css/header.css';
3
+
@import 'css/footer.css';
4
+
@import 'css/login.css';
5
+
@import 'css/forms.css';
6
+
@import 'css/buttons.css';
7
+
@import 'css/loading.css';
8
+
@import 'css/migration.css';
9
+
@import 'css/plcToken.css';
10
+
@import 'css/actions.css';
11
+
@import 'css/confirmation.css';
+72
-3
src/main.tsx
+72
-3
src/main.tsx
···
1
-
import { StrictMode } from 'react'
1
+
import { StrictMode, useState, useEffect } from 'react'
2
2
import { createRoot } from 'react-dom/client'
3
-
import './styles/App.css'
4
-
import App from './App.tsx'
3
+
import { BrowserRouter as Router } from 'react-router-dom'
4
+
import { AtpAgent } from '@atproto/api'
5
+
import { AvatarProvider } from './contexts/AvatarContext'
6
+
import { NetworkProvider } from './contexts/NetworkContext'
7
+
import { AppRoutes } from './Routes'
8
+
import './main.css'
9
+
10
+
const SESSION_KEY = 'atproto_session';
11
+
const SESSION_EXPIRY = 60 * 60 * 1000; // 1 hour in milliseconds
12
+
13
+
function App() {
14
+
const [agent, setAgent] = useState<AtpAgent | null>(null)
15
+
16
+
useEffect(() => {
17
+
// Load session from localStorage on initial load
18
+
const loadSession = async () => {
19
+
const savedSession = localStorage.getItem(SESSION_KEY);
20
+
if (savedSession) {
21
+
const { session, service, timestamp } = JSON.parse(savedSession);
22
+
23
+
// Check if session is expired
24
+
if (Date.now() - timestamp > SESSION_EXPIRY) {
25
+
localStorage.removeItem(SESSION_KEY);
26
+
return;
27
+
}
28
+
29
+
const newAgent = new AtpAgent({ service });
30
+
await newAgent.resumeSession(session);
31
+
setAgent(newAgent);
32
+
}
33
+
};
34
+
35
+
loadSession();
36
+
}, []);
37
+
38
+
const handleLogin = (newAgent: AtpAgent) => {
39
+
setAgent(newAgent);
40
+
// Save session to localStorage
41
+
localStorage.setItem(SESSION_KEY, JSON.stringify({
42
+
session: newAgent.session,
43
+
service: newAgent.serviceUrl,
44
+
timestamp: Date.now()
45
+
}));
46
+
};
47
+
48
+
const handleLogout = () => {
49
+
setAgent(null);
50
+
localStorage.removeItem(SESSION_KEY);
51
+
// Clear avatar URL from context
52
+
const avatarContext = document.querySelector('[data-avatar-context]');
53
+
if (avatarContext) {
54
+
const event = new CustomEvent('clearAvatar');
55
+
avatarContext.dispatchEvent(event);
56
+
}
57
+
};
5
58
59
+
return (
60
+
<NetworkProvider>
61
+
<AvatarProvider>
62
+
<Router>
63
+
<AppRoutes
64
+
agent={agent}
65
+
onLogout={handleLogout}
66
+
handleLogin={handleLogin}
67
+
/>
68
+
</Router>
69
+
</AvatarProvider>
70
+
</NetworkProvider>
71
+
)
72
+
}
73
+
74
+
// Initialize the app
6
75
createRoot(document.getElementById('root')!).render(
7
76
<StrictMode>
8
77
<App />
+130
src/pages/Actions.tsx
+130
src/pages/Actions.tsx
···
1
+
import { useEffect, useState } from 'react';
2
+
import { AtpAgent } from '@atproto/api';
3
+
import { useNavigate } from 'react-router-dom';
4
+
import Footer from '../components/common/Footer';
5
+
import Header from '../components/common/Header';
6
+
7
+
interface ActionsProps {
8
+
agent: AtpAgent;
9
+
onLogout: () => void;
10
+
}
11
+
12
+
export default function Actions({ agent, onLogout }: ActionsProps) {
13
+
const [didDoc, setDidDoc] = useState<string>('');
14
+
const [loading, setLoading] = useState(true);
15
+
const navigate = useNavigate();
16
+
17
+
const handleLogout = () => {
18
+
onLogout();
19
+
navigate('/');
20
+
};
21
+
22
+
useEffect(() => {
23
+
const fetchProfile = async () => {
24
+
try {
25
+
const did = agent.session?.did;
26
+
if (!did) {
27
+
throw new Error('No DID found in session');
28
+
}
29
+
30
+
let didDocResponse;
31
+
32
+
if (did.startsWith('did:plc:')) {
33
+
// For PLC DIDs, resolve from plc.directory
34
+
const response = await fetch(`https://plc.directory/${did}`);
35
+
didDocResponse = await response.json();
36
+
} else if (did.startsWith('did:web:')) {
37
+
// For Web DIDs, get from .well-known/did.json
38
+
const domain = did.split(':')[2];
39
+
const response = await fetch(`https://${domain}/.well-known/did.json`);
40
+
didDocResponse = await response.json();
41
+
} else {
42
+
throw new Error(`Unsupported DID type: ${did}`);
43
+
}
44
+
45
+
setDidDoc(JSON.stringify(didDocResponse, null, 2));
46
+
} catch (err) {
47
+
console.error('Error fetching DID document:', err);
48
+
setDidDoc(`Error fetching DID document: ${err instanceof Error ? err.message : 'Unknown error'}`);
49
+
} finally {
50
+
setLoading(false);
51
+
}
52
+
};
53
+
54
+
fetchProfile();
55
+
}, [agent]);
56
+
57
+
if (loading) {
58
+
return (
59
+
<div className="loading-container">
60
+
<div className="loading-text">Loading...</div>
61
+
</div>
62
+
);
63
+
}
64
+
65
+
return (
66
+
<div className="actions-page">
67
+
<Header agent={agent} onLogout={handleLogout} />
68
+
69
+
<div className="actions-container">
70
+
<div className="actions-list">
71
+
<button
72
+
className="action-item"
73
+
onClick={() => navigate('/migration')}
74
+
>
75
+
<div className="action-icon">
76
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
77
+
<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z" />
78
+
</svg>
79
+
</div>
80
+
<div className="action-content">
81
+
<div className="action-title">Migrate account</div>
82
+
<div className="action-subtitle">Move your account to a new data server</div>
83
+
</div>
84
+
</button>
85
+
86
+
<button
87
+
className="action-item"
88
+
onClick={() => navigate('/recovery-key')}
89
+
>
90
+
<div className="action-icon">
91
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
92
+
<rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
93
+
<path d="M7 11V7a5 5 0 0 1 10 0v4" />
94
+
</svg>
95
+
</div>
96
+
<div className="action-content">
97
+
<div className="action-title">Add recovery key</div>
98
+
<div className="action-subtitle">Create a new recovery key for your account</div>
99
+
</div>
100
+
</button>
101
+
</div>
102
+
103
+
<details className="user-info-section">
104
+
<summary className="user-info-summary">User Information</summary>
105
+
<div className="user-info-content">
106
+
<section>
107
+
<h2>Account Details</h2>
108
+
<dl>
109
+
<dt>DID</dt>
110
+
<dd>{agent.session?.did || 'N/A'}</dd>
111
+
<dt>Handle</dt>
112
+
<dd>{agent.session?.handle || 'N/A'}</dd>
113
+
<dt>PDS</dt>
114
+
<dd>{agent.serviceUrl.toString() || 'N/A'}</dd>
115
+
</dl>
116
+
</section>
117
+
118
+
<section>
119
+
<h2>DID Document</h2>
120
+
<pre className="did-document">
121
+
<code>{didDoc}</code>
122
+
</pre>
123
+
</section>
124
+
</div>
125
+
</details>
126
+
</div>
127
+
<Footer />
128
+
</div>
129
+
);
130
+
}
+266
src/pages/Login.tsx
+266
src/pages/Login.tsx
···
1
+
import { useState } from 'react';
2
+
import { useNavigate } from 'react-router-dom';
3
+
import { AtpAgent } from '@atproto/api';
4
+
import Footer from '../components/common/Footer';
5
+
6
+
interface LoginProps {
7
+
onLogin: (agent: AtpAgent) => void;
8
+
}
9
+
10
+
interface DidDocument {
11
+
service: Array<{
12
+
id: string;
13
+
type: string;
14
+
serviceEndpoint: string;
15
+
}>;
16
+
}
17
+
18
+
type LoginStep = 'idle' | 'resolving-handle' | 'resolving-did' | 'connecting-pds' | 'authenticating' | '2fa-required' | 'success';
19
+
20
+
export default function Login({ onLogin }: LoginProps) {
21
+
const [handle, setHandle] = useState('');
22
+
const [password, setPassword] = useState('');
23
+
const [twoFactorCode, setTwoFactorCode] = useState('');
24
+
const [error, setError] = useState('');
25
+
const [appPasswordAttempts, setAppPasswordAttempts] = useState(0);
26
+
const [loginStep, setLoginStep] = useState<LoginStep>('idle');
27
+
const [agent, setAgent] = useState<AtpAgent | null>(null);
28
+
const navigate = useNavigate();
29
+
30
+
const isAppPassword = (password: string) => {
31
+
// App passwords are typically in the format xxxx-xxxx-xxxx-xxxx
32
+
return /^[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}$/.test(password);
33
+
};
34
+
35
+
const getStepMessage = (step: LoginStep) => {
36
+
switch (step) {
37
+
case 'resolving-handle':
38
+
return 'Resolving your handle...';
39
+
case 'resolving-did':
40
+
return 'Resolving your DID...';
41
+
case 'connecting-pds':
42
+
return 'Connecting to your Personal Data Server...';
43
+
case 'authenticating':
44
+
return 'Authenticating your credentials...';
45
+
case '2fa-required':
46
+
return 'Please enter your 2FA code';
47
+
case 'success':
48
+
return 'Login successful! Redirecting...';
49
+
default:
50
+
return '';
51
+
}
52
+
};
53
+
54
+
const handleSubmit = async (e: React.FormEvent) => {
55
+
e.preventDefault();
56
+
setError('');
57
+
58
+
// If we're in 2FA step, handle that separately
59
+
if (loginStep === '2fa-required') {
60
+
if (!agent) {
61
+
setError('Session expired. Please try logging in again.');
62
+
setLoginStep('idle');
63
+
return;
64
+
}
65
+
66
+
try {
67
+
setLoginStep('authenticating');
68
+
await agent.login({ identifier: handle, password, authFactorToken: twoFactorCode });
69
+
setLoginStep('success');
70
+
onLogin(agent);
71
+
navigate('/actions');
72
+
} catch (err) {
73
+
setError(err instanceof Error ? err.message : '2FA verification failed');
74
+
setLoginStep('2fa-required');
75
+
}
76
+
return;
77
+
}
78
+
79
+
setLoginStep('resolving-handle');
80
+
81
+
// app password check and debug method
82
+
if (isAppPassword(password)) {
83
+
if (appPasswordAttempts < 3) {
84
+
setAppPasswordAttempts(appPasswordAttempts + 1);
85
+
setError(`You have entered an app password, which does not allow for you to migrate your account. Please enter your main account password instead.`);
86
+
setLoginStep('idle');
87
+
return;
88
+
}
89
+
}
90
+
91
+
setHandle(handle.trim());
92
+
93
+
try {
94
+
// Create temporary agent to resolve DID
95
+
const tempAgent = new AtpAgent({ service: 'https://public.api.bsky.app' });
96
+
97
+
// Get DID document from handle
98
+
setLoginStep('resolving-handle');
99
+
const didResponse = await tempAgent.com.atproto.identity.resolveHandle({
100
+
handle: handle
101
+
});
102
+
103
+
if (!didResponse.success) {
104
+
// Try did:web resolution first
105
+
const domain = handle.split('.').join(':');
106
+
const webDid = `did:web:${domain}`;
107
+
try {
108
+
const webResponse = await fetch(`https://${handle}/.well-known/did.json`);
109
+
if (webResponse.ok) {
110
+
// If successful, continue with the did:web
111
+
didResponse.data.did = webDid;
112
+
} else {
113
+
throw new Error('Invalid handle');
114
+
}
115
+
} catch {
116
+
throw new Error('Invalid handle');
117
+
}
118
+
}
119
+
120
+
// Get PDS endpoint from DID document
121
+
setLoginStep('resolving-did');
122
+
let didDocResponse;
123
+
const did = didResponse.data.did;
124
+
125
+
if (did.startsWith('did:plc:')) {
126
+
// For PLC DIDs, resolve from plc.directory
127
+
const plcResponse = await fetch(`https://plc.directory/${did}`);
128
+
didDocResponse = { data: await plcResponse.json() };
129
+
} else if (did.startsWith('did:web:')) {
130
+
// For Web DIDs, get from .well-known/did.json
131
+
const domain = did.split(':')[2];
132
+
const webResponse = await fetch(`https://${domain}/.well-known/did.json`);
133
+
didDocResponse = { data: await webResponse.json() };
134
+
} else {
135
+
// Fallback to ATP resolver for other DID types
136
+
didDocResponse = await tempAgent.com.atproto.identity.resolveDid({
137
+
did: did
138
+
});
139
+
}
140
+
141
+
setLoginStep('connecting-pds');
142
+
const pds = ((didDocResponse.data as unknown) as DidDocument).service.find((s) => s.id === '#atproto_pds')?.serviceEndpoint || 'https://bsky.social';
143
+
144
+
const newAgent = new AtpAgent({ service: pds });
145
+
setAgent(newAgent);
146
+
147
+
setLoginStep('authenticating');
148
+
try {
149
+
await newAgent.login({ identifier: handle, password });
150
+
setLoginStep('success');
151
+
onLogin(newAgent);
152
+
navigate('/actions');
153
+
} catch (err) {
154
+
if (err instanceof Error && (
155
+
err.message.includes('AuthFactorTokenRequired') ||
156
+
err.message.includes('A sign in code has been sent to your email address')
157
+
)) {
158
+
setLoginStep('2fa-required');
159
+
return;
160
+
}
161
+
throw err;
162
+
}
163
+
} catch (err) {
164
+
setError(err instanceof Error ? err.message : 'Login failed');
165
+
setLoginStep('idle');
166
+
}
167
+
};
168
+
169
+
return (
170
+
<div>
171
+
<h1 className="login-title">ATproto Migrator</h1>
172
+
<div className="login-container">
173
+
<div className="login-card">
174
+
<h2 className="login-title">Sign in to your account</h2>
175
+
<div className="warning-message">
176
+
โ ๏ธ Please use your main account password, not an app password. All operations are performed locally in your browser.
177
+
</div>
178
+
<form className="login-form" onSubmit={handleSubmit}>
179
+
<div className="form-group">
180
+
<input
181
+
type="text"
182
+
required
183
+
className="form-input"
184
+
placeholder="Handle (e.g., example.bsky.social)"
185
+
value={handle}
186
+
onChange={(e) => setHandle(e.target.value)}
187
+
disabled={loginStep !== 'idle'}
188
+
/>
189
+
</div>
190
+
<div className="form-group">
191
+
<input
192
+
type="password"
193
+
required
194
+
className="form-input"
195
+
placeholder="Password"
196
+
value={password}
197
+
onChange={(e) => setPassword(e.target.value)}
198
+
disabled={loginStep !== 'idle'}
199
+
/>
200
+
</div>
201
+
202
+
{error && <div className="error-message">{error}</div>}
203
+
{loginStep !== 'idle' && loginStep !== '2fa-required' && (
204
+
<div className="loading-message">
205
+
{getStepMessage(loginStep)}
206
+
</div>
207
+
)}
208
+
209
+
<button
210
+
type="submit"
211
+
className="submit-button"
212
+
disabled={loginStep !== 'idle'}
213
+
>
214
+
{loginStep === 'idle' ? 'Sign in' : 'Signing in...'}
215
+
</button>
216
+
</form>
217
+
</div>
218
+
</div>
219
+
220
+
{/* 2FA Modal */}
221
+
{loginStep === '2fa-required' && (
222
+
<div className="modal-overlay">
223
+
<div className="modal-content">
224
+
<h2>Two-Factor Authentication Required</h2>
225
+
<p>A sign in code has been sent to your email address.</p>
226
+
<form onSubmit={handleSubmit} className="two-factor-form">
227
+
<div className="form-group">
228
+
<input
229
+
type="text"
230
+
required
231
+
className="form-input"
232
+
placeholder="Enter 2FA code"
233
+
value={twoFactorCode}
234
+
onChange={(e) => setTwoFactorCode(e.target.value)}
235
+
autoFocus
236
+
/>
237
+
</div>
238
+
{error && <div className="error-message">{error}</div>}
239
+
<div className="button-group">
240
+
<button
241
+
type="button"
242
+
className="back-button"
243
+
onClick={() => {
244
+
setLoginStep('idle');
245
+
setError('');
246
+
setTwoFactorCode('');
247
+
}}
248
+
>
249
+
Cancel
250
+
</button>
251
+
<button
252
+
type="submit"
253
+
className="submit-button"
254
+
>
255
+
Verify
256
+
</button>
257
+
</div>
258
+
</form>
259
+
</div>
260
+
</div>
261
+
)}
262
+
263
+
<Footer />
264
+
</div>
265
+
);
266
+
}
+35
src/pages/error.tsx
+35
src/pages/error.tsx
···
1
+
import React from 'react';
2
+
import { useNavigate } from 'react-router-dom';
3
+
4
+
interface ErrorPageProps {
5
+
statusCode?: number;
6
+
message?: string;
7
+
}
8
+
9
+
const ErrorPage: React.FC<ErrorPageProps> = ({
10
+
statusCode = 404,
11
+
message = "The page you're looking for doesn't exist."
12
+
}) => {
13
+
const navigate = useNavigate();
14
+
15
+
return (
16
+
<div className="page-content" style={{ textAlign: 'center', maxWidth: '600px', margin: '4rem auto' }}>
17
+
<h2 style={{ fontSize: '3rem', marginBottom: '1rem', color: 'var(--error-color)' }}>
18
+
{statusCode}
19
+
</h2>
20
+
<p style={{ fontSize: '1.25rem', marginBottom: '2rem' }}>
21
+
{message}
22
+
</p>
23
+
<div className="button-container" style={{ display: 'inline' }}>
24
+
<button
25
+
className="continue-button"
26
+
onClick={() => navigate('/')}
27
+
>
28
+
Come back home
29
+
</button>
30
+
</div>
31
+
</div>
32
+
);
33
+
};
34
+
35
+
export default ErrorPage;
+72
src/pages/migration/Migration.tsx
+72
src/pages/migration/Migration.tsx
···
1
+
import { useNavigate } from 'react-router-dom';
2
+
import { AtpAgent } from '@atproto/api';
3
+
import Header from '../../components/common/Header';
4
+
import Footer from '../../components/common/Footer';
5
+
6
+
interface MigrationProps {
7
+
agent: AtpAgent;
8
+
onLogout: () => void;
9
+
}
10
+
11
+
export default function Migration({ agent, onLogout }: MigrationProps) {
12
+
const navigate = useNavigate();
13
+
14
+
return (
15
+
<div className="actions-page">
16
+
<Header agent={agent} onLogout={onLogout} />
17
+
18
+
<div className="actions-container">
19
+
<div className="page-content">
20
+
<h2>Migrate your account</h2>
21
+
<p>This tool allows you to migrate your account to a new Personal Data Server, a data server that hosts your account and all of its data.</p>
22
+
<h3>What to expect</h3>
23
+
<p>The migration process is <i>possible</i>, however it is not recommended if you are unsure about what you are doing. We recommend that you migrate a secondary account to your new PDS to make sure that it is able to migrate successfully <i>before</i> migrating your primary account.</p>
24
+
<p>You will need the following items to begin the migration process:</p>
25
+
<ul>
26
+
<li>A new PDS to migrate to.</li>
27
+
<li>An invite code from the new PDS (if required).</li>
28
+
<li>A way to confirm the migration, which is either a code sent to your email or your private rotation key.</li>
29
+
<li>A new password for your account <b>(which will not be stored by this tool)</b>.</li>
30
+
</ul>
31
+
32
+
<div className="warning-section">
33
+
<h3>โ ๏ธ Read Before You Continue โ ๏ธ</h3>
34
+
<ul>
35
+
<li>If you are already on a third-party PDS, it must be able to send emails or you will be unable to get a confirmation code without direct server access. We are currently unable to check this for you, however if you can verify your email address the server supports it.</li>
36
+
<li>If you are not using a custom domain, you will need a new handle as the default domain (such as alice.bsky.social or bob.pds.example.com) is non-transferable.</li>
37
+
<li>If your account is using the did:web method, you will need to modify your DID document manually. If you don't know what this means, you don't need to worry about it.</li>
38
+
<li>Due to performance issues, the main Bluesky data servers have temporarily disabled the ability to import account data. <b>As a result, you cannot migrate back to Bluesky servers for the foreseeable future.</b></li>
39
+
<li>If your PDS goes down and you do not have access to a recovery key, you will be locked out of your account. <b>Bluesky developers will not be able to help you.</b></li>
40
+
</ul>
41
+
</div>
42
+
43
+
<div className="docs-section">
44
+
<h3>Additional Resources</h3>
45
+
<p>For the technically inclined, here are some additional resources for how the migration process works:</p>
46
+
<ul>
47
+
<li><a href="https://github.com/bluesky-social/pds/blob/main/ACCOUNT_MIGRATION.md" target="_blank" rel="noopener noreferrer">Detailed document on migration for PDS hosters</a></li>
48
+
<li><a href="https://atproto.com/guides/account-migration" target="_blank" rel="noopener noreferrer">AT Protocol's developer documentation on account migration</a></li>
49
+
<li><a href="https://whtwnd.com/bnewbold.net/3l5ii332pf32u">Guide to migrating an account using the command line</a></li>
50
+
</ul>
51
+
</div>
52
+
53
+
<div className="button-container">
54
+
<button
55
+
className="back-button"
56
+
onClick={() => navigate('/actions')}
57
+
>
58
+
โ Go back
59
+
</button>
60
+
<button
61
+
className="continue-button"
62
+
onClick={() => navigate('/migration/registration')}
63
+
>
64
+
Continue โ
65
+
</button>
66
+
</div>
67
+
</div>
68
+
</div>
69
+
<Footer />
70
+
</div>
71
+
);
72
+
}
+82
src/pages/migration/MigrationProcess.tsx
+82
src/pages/migration/MigrationProcess.tsx
···
1
+
import { useEffect, useState } from 'react';
2
+
import { useNavigate } from 'react-router-dom';
3
+
import { AtpAgent } from '@atproto/api';
4
+
import Header from '../../components/common/Header';
5
+
import Footer from '../../components/common/Footer';
6
+
import { MigrationData } from '../../lib/migration/migrationData';
7
+
import '../../css/migration.css';
8
+
9
+
interface MigrationProcessProps {
10
+
agent: AtpAgent;
11
+
onLogout: () => void;
12
+
}
13
+
14
+
type MigrationStep = 'account-creation' | 'data-transfer' | 'identity-update' | 'finalization';
15
+
16
+
export default function MigrationProcess({ agent, onLogout }: MigrationProcessProps) {
17
+
let navigate = useNavigate();
18
+
const [migrationData, setMigrationData] = useState<MigrationData | null>(null);
19
+
const [currentStep, setCurrentStep] = useState<MigrationStep>('account-creation');
20
+
21
+
useEffect(() => {
22
+
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
23
+
e.preventDefault();
24
+
return '';
25
+
};
26
+
27
+
window.addEventListener('beforeunload', handleBeforeUnload);
28
+
29
+
return () => {
30
+
window.removeEventListener('beforeunload', handleBeforeUnload);
31
+
};
32
+
}, []);
33
+
34
+
useEffect(() => {
35
+
if (localStorage.getItem('migrationData') == null) {
36
+
navigate('/actions');
37
+
}
38
+
39
+
const migrationData = JSON.parse(localStorage.getItem('migrationData') || '{}');
40
+
setMigrationData(new MigrationData(migrationData.oldPds, migrationData.newPds, migrationData.inviteCode, migrationData.handle, migrationData.email, migrationData.password));
41
+
}, []);
42
+
43
+
return (
44
+
<div className="actions-page">
45
+
<Header agent={agent} onLogout={onLogout} />
46
+
<div className="page-content">
47
+
<h2>Account migration in progress...</h2>
48
+
<div className="progress-steps">
49
+
<div className={`progress-step ${currentStep === 'account-creation' ? 'active' : ''}`}>
50
+
<div className="step-number">1</div>
51
+
<div className="step-text">Creating your new account</div>
52
+
</div>
53
+
<div className={`progress-step ${currentStep === 'data-transfer' ? 'active' : ''}`}>
54
+
<div className="step-number">2</div>
55
+
<div className="step-text">Moving your data</div>
56
+
</div>
57
+
<div className={`progress-step ${currentStep === 'identity-update' ? 'active' : ''}`}>
58
+
<div className="step-number">3</div>
59
+
<div className="step-text">Declaring your new host</div>
60
+
</div>
61
+
<div className={`progress-step ${currentStep === 'finalization' ? 'active' : ''}`}>
62
+
<div className="step-number">4</div>
63
+
<div className="step-text">Finalizing migration</div>
64
+
</div>
65
+
</div><br />
66
+
<div className="info-message">
67
+
<h3>Details</h3>
68
+
<strong>example</strong>
69
+
</div>
70
+
<div className="warning-section">
71
+
<h3>Important!</h3>
72
+
<ul>
73
+
<li>Do not close this window or navigate away</li>
74
+
<li>Keep your browser open until the process is complete</li>
75
+
<li>You will be notified when the migration is finished</li>
76
+
</ul>
77
+
</div>
78
+
</div>
79
+
<Footer />
80
+
</div>
81
+
);
82
+
}
+138
src/pages/migration/migrationForms.tsx
+138
src/pages/migration/migrationForms.tsx
···
1
+
import { useNavigate } from 'react-router-dom';
2
+
import { AtpAgent } from '@atproto/api';
3
+
import { useState, useRef, useEffect } from 'react';
4
+
import Header from '../../components/common/Header';
5
+
import Footer from '../../components/common/Footer';
6
+
import PdsForm from '../../components/migration/PdsForm';
7
+
import AccountDetailsForm from '../../components/migration/AccountDetailsForm';
8
+
import ConfirmationStep from '../../components/migration/ConfirmationStep';
9
+
import { ServerDescription } from '../../lib/migration/serverDescription';
10
+
import { getServerDescription } from '../../lib/migration/pdsValidation';
11
+
import { MigrationData } from '../../lib/migration/migrationData';
12
+
13
+
interface MigrationFormsProps {
14
+
agent: AtpAgent;
15
+
onLogout: () => void;
16
+
}
17
+
18
+
export default function MigrationForms({ agent, onLogout }: MigrationFormsProps) {
19
+
const navigate = useNavigate();
20
+
const [currentStep, setCurrentStep] = useState<'pds' | 'account' | 'confirmation'>('pds');
21
+
const [pdsDetails, setPdsDetails] = useState<{ pds: string; inviteCode: string; serverDescription: ServerDescription } | null>(null);
22
+
const [accountDetails, setAccountDetails] = useState<{ handle: string; email: string; password: string } | null>(null);
23
+
const [currentServerDescription, setCurrentServerDescription] = useState<ServerDescription | null>(null);
24
+
const accountSectionRef = useRef<HTMLDivElement>(null);
25
+
const confirmationSectionRef = useRef<HTMLDivElement>(null);
26
+
27
+
useEffect(() => {
28
+
if (currentStep === 'account' && accountSectionRef.current) {
29
+
accountSectionRef.current.scrollIntoView({ behavior: 'smooth' });
30
+
} else if (currentStep === 'confirmation' && confirmationSectionRef.current) {
31
+
confirmationSectionRef.current.scrollIntoView({ behavior: 'smooth' });
32
+
}
33
+
}, [currentStep]);
34
+
35
+
// Fetch current PDS server description when component mounts
36
+
useEffect(() => {
37
+
const fetchCurrentServerDescription = async () => {
38
+
try {
39
+
const currentPds = new URL(agent.serviceUrl).hostname;
40
+
41
+
// If the current PDS is a Bluesky network server, use bsky.social's description
42
+
if (currentPds.endsWith('.bsky.network')) {
43
+
const description = await getServerDescription('bsky.social');
44
+
setCurrentServerDescription(description);
45
+
} else {
46
+
const description = await getServerDescription(currentPds);
47
+
setCurrentServerDescription(description);
48
+
}
49
+
} catch (e) {
50
+
console.error('Failed to fetch current server description:', e);
51
+
}
52
+
};
53
+
fetchCurrentServerDescription();
54
+
}, [agent.serviceUrl]);
55
+
56
+
const handlePdsSubmit = (pds: string, inviteCode: string, serverDescription: ServerDescription) => {
57
+
setPdsDetails({ pds, inviteCode, serverDescription });
58
+
setCurrentStep('account');
59
+
};
60
+
61
+
const handleAccountSubmit = (handle: string, email: string, password: string) => {
62
+
setAccountDetails({ handle, email, password });
63
+
setCurrentStep('confirmation');
64
+
};
65
+
66
+
const handleBack = () => {
67
+
if (currentStep === 'confirmation') {
68
+
setCurrentStep('account');
69
+
} else if (currentStep === 'account') {
70
+
setCurrentStep('pds');
71
+
} else {
72
+
navigate('/migration');
73
+
}
74
+
};
75
+
76
+
const handleConfirm = () => {
77
+
if (pdsDetails && accountDetails) {
78
+
const migrationData = new MigrationData(agent.serviceUrl.hostname, pdsDetails.pds, pdsDetails.inviteCode, accountDetails.handle, accountDetails.email, accountDetails.password);
79
+
console.log('Migration data:', migrationData);
80
+
localStorage.setItem('migrationData', JSON.stringify(migrationData));
81
+
} else {
82
+
console.error('Migration data is not complete');
83
+
return;
84
+
}
85
+
navigate('/migration/process');
86
+
};
87
+
88
+
return (
89
+
<div className="actions-page">
90
+
<Header agent={agent} onLogout={onLogout} />
91
+
92
+
<div className="actions-container">
93
+
<div className="page-content">
94
+
<h2>Migrate your account</h2>
95
+
96
+
<div className={currentStep !== 'pds' ? 'form-section completed' : 'form-section'}>
97
+
<PdsForm
98
+
agent={agent}
99
+
onSubmit={handlePdsSubmit}
100
+
onBack={handleBack}
101
+
/>
102
+
</div>
103
+
104
+
{currentStep !== 'pds' && pdsDetails && currentServerDescription && (
105
+
<div className={currentStep === 'account' ? 'form-section' : 'form-section completed'} ref={accountSectionRef}>
106
+
<AccountDetailsForm
107
+
currentHandle={agent.session?.handle || ''}
108
+
pds={pdsDetails.pds}
109
+
inviteCode={pdsDetails.inviteCode}
110
+
serverDescription={currentServerDescription}
111
+
newServerDescription={pdsDetails.serverDescription}
112
+
onSubmit={handleAccountSubmit}
113
+
onBack={handleBack}
114
+
/>
115
+
</div>
116
+
)}
117
+
118
+
{currentStep === 'confirmation' && pdsDetails && accountDetails && (
119
+
<div className="form-section" ref={confirmationSectionRef}>
120
+
<ConfirmationStep
121
+
handle={accountDetails.handle}
122
+
email={accountDetails.email}
123
+
password={accountDetails.password}
124
+
pds={pdsDetails.pds}
125
+
currentHandle={agent.session?.handle || ''}
126
+
currentPds={new URL(agent.serviceUrl).hostname}
127
+
onBack={handleBack}
128
+
onConfirm={handleConfirm}
129
+
/>
130
+
</div>
131
+
)}
132
+
</div>
133
+
</div>
134
+
135
+
<Footer />
136
+
</div>
137
+
);
138
+
}
+53
src/pages/recoveryKey/RecoveryKeyProcess.tsx
+53
src/pages/recoveryKey/RecoveryKeyProcess.tsx
···
1
+
import { useNavigate } from 'react-router-dom';
2
+
import { useEffect } from 'react';
3
+
import { AtpAgent } from '@atproto/api';
4
+
import Footer from '../../components/common/Footer';
5
+
import Header from '../../components/common/Header';
6
+
7
+
interface RecoveryKeyProcessProps {
8
+
agent: AtpAgent;
9
+
onLogout: () => void;
10
+
}
11
+
12
+
export default function RecoveryKeyProcess({ agent, onLogout }: RecoveryKeyProcessProps) {
13
+
const navigate = useNavigate();
14
+
15
+
// Add warning when trying to close or navigate away
16
+
useEffect(() => {
17
+
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
18
+
e.preventDefault();
19
+
return '';
20
+
};
21
+
22
+
window.addEventListener('beforeunload', handleBeforeUnload);
23
+
24
+
return () => {
25
+
window.removeEventListener('beforeunload', handleBeforeUnload);
26
+
};
27
+
}, []);
28
+
29
+
return (
30
+
<div className="actions-page">
31
+
<Header agent={agent} onLogout={onLogout} />
32
+
33
+
<div className="actions-container">
34
+
<div className="page-content">
35
+
<h2>Add Recovery Key</h2>
36
+
<p>This page will guide you through the process of adding a recovery key to your account.</p>
37
+
<div className="warning-section">
38
+
<h3>This is not implemented yet!</h3>
39
+
</div>
40
+
<div className="button-container">
41
+
<button
42
+
className="back-button"
43
+
onClick={() => navigate('/recovery-key')}
44
+
>
45
+
โ Go back
46
+
</button>
47
+
</div>
48
+
</div>
49
+
</div>
50
+
<Footer />
51
+
</div>
52
+
);
53
+
}
+66
src/pages/recoveryKey/recovery.tsx
+66
src/pages/recoveryKey/recovery.tsx
···
1
+
import { useNavigate } from 'react-router-dom';
2
+
import { AtpAgent } from '@atproto/api';
3
+
import Footer from '../../components/common/Footer';
4
+
import Header from '../../components/common/Header';
5
+
6
+
interface RecoveryKeyProps {
7
+
agent: AtpAgent;
8
+
onLogout: () => void;
9
+
}
10
+
11
+
export default function RecoveryKey({ agent, onLogout }: RecoveryKeyProps) {
12
+
const navigate = useNavigate();
13
+
14
+
return (
15
+
<div className="actions-page">
16
+
<Header agent={agent} onLogout={onLogout} />
17
+
18
+
<div className="actions-container">
19
+
<div className="page-content">
20
+
<h2>Add a recovery key</h2>
21
+
<p>A recovery key (known as a <b>rotation key</b> in the AT Protocol) is a cryptographic key associated with your account that allows you to modify your account's core identity.</p>
22
+
23
+
<h3>How rotation keys work</h3>
24
+
<p>In the AT Protocol, your account is identified using a DID (<b>Decentralized Identifier</b>), with most accounts on the protocol using a variant of it developed specifically for the protocol named PLC. The account's core information (such as your handle and data server on the network) is stored in the account's DID document.</p>
25
+
<p>To change this document (an event known as a <b>PLC operation</b>), you use a rotation key to confirm that you are the owner of the account and that you are authorized to make the changes. For example, when changing your handle, your data server (also known as a PDS) will use its own rotation key to change the document, allowing the user to change their handle without needing to provide their own key.</p>
26
+
<h3>Why should I add another key?</h3>
27
+
<p>Adding a rotation key allows you to regain control of your account if it is compromised. It also allows you to move your account to a new data server, even if the current server is down.</p>
28
+
<div className="warning-section">
29
+
<h3>โ ๏ธ Read Before You Continue โ ๏ธ</h3>
30
+
<ul>
31
+
<li>You will need a to add a recovery key. Tokens are sent to the email address associated with your account.</li>
32
+
<li>While we do generate a key for you, we will not store it. Please save it in a secure location.</li>
33
+
<li>Keep your recovery key private. Anyone with access to it could potentially take control of your account.</li>
34
+
<li>If you're using a third-party PDS, it must be able to send emails or you will not be able to use this tool to add a recovery key.</li>
35
+
</ul>
36
+
</div>
37
+
<div className="docs-section">
38
+
<h3>Additional Resources</h3>
39
+
<p>For the technically inclined, here are some additional resources for how rotation keys work:</p>
40
+
<ul>
41
+
<li><a href="https://atproto.com/guides/identity" target="_blank" rel="noopener noreferrer">AT Protocol's developer documentation on identity</a></li>
42
+
<li><a href="https://whtwnd.com/did:plc:xz3euvkhf44iadavovbsmqoo/3laimapx6ks2b" target="_blank" rel="noopener noreferrer">Guide to adding a recovery key using the command line</a></li>
43
+
<li><a href="https://whtwnd.com/did:plc:44ybard66vv44zksje25o7dz/3lj7jmt2ct72r" target="_blank" rel="noopener noreferrer">More in-depth guide to adding a recovery key</a></li>
44
+
</ul>
45
+
</div>
46
+
47
+
<div className="button-container">
48
+
<button
49
+
className="back-button"
50
+
onClick={() => navigate('/actions')}
51
+
>
52
+
โ Go back
53
+
</button>
54
+
<button
55
+
className="continue-button"
56
+
onClick={() => navigate('/recovery-key/process')}
57
+
>
58
+
Continue โ
59
+
</button>
60
+
</div>
61
+
</div>
62
+
</div>
63
+
<Footer />
64
+
</div>
65
+
);
66
+
}
+119
src/routes.tsx
+119
src/routes.tsx
···
1
+
import { useEffect } from 'react'
2
+
import { Routes, Route, Navigate, useLocation } from 'react-router-dom'
3
+
import { AtpAgent } from '@atproto/api'
4
+
5
+
import NetworkWarning from './components/common/NetworkWarning'
6
+
import Login from './pages/Login'
7
+
import Actions from './pages/Actions'
8
+
import Migration from './pages/migration/Migration'
9
+
import MigrationForms from './pages/migration/MigrationForms'
10
+
import MigrationProcess from './pages/migration/MigrationProcess'
11
+
import RecoveryKey from './pages/recoveryKey/Recovery'
12
+
import RecoveryKeyProcess from './pages/recoveryKey/RecoveryKeyProcess'
13
+
import ErrorPage from './pages/Error'
14
+
15
+
export function AppRoutes({ agent, onLogout, handleLogin }: {
16
+
agent: AtpAgent | null;
17
+
onLogout: () => void;
18
+
handleLogin: (agent: AtpAgent) => void;
19
+
}) {
20
+
const location = useLocation();
21
+
22
+
useEffect(() => {
23
+
const checkSession = async () => {
24
+
if (agent) {
25
+
try {
26
+
// Try to make a simple API call to verify the session
27
+
await agent.getProfile({ actor: agent.session?.handle || '' });
28
+
} catch (err) {
29
+
// If the API call fails, the session is likely invalid
30
+
console.error('Session check failed: ', err);
31
+
onLogout();
32
+
alert('Your session has expired. Please log in again.');
33
+
}
34
+
}
35
+
};
36
+
37
+
checkSession();
38
+
}, [location.pathname, agent, onLogout]);
39
+
40
+
return (
41
+
<>
42
+
<NetworkWarning />
43
+
<Routes>
44
+
<Route
45
+
path="/"
46
+
element={
47
+
agent ? (
48
+
<Navigate to="/actions" replace />
49
+
) : (
50
+
<Login onLogin={handleLogin} />
51
+
)
52
+
}
53
+
/>
54
+
<Route
55
+
path="/actions"
56
+
element={
57
+
agent ? (
58
+
<Actions agent={agent} onLogout={onLogout} />
59
+
) : (
60
+
<Navigate to="/" replace />
61
+
)
62
+
}
63
+
/>
64
+
<Route
65
+
path="/migration"
66
+
element={
67
+
agent ? (
68
+
<Migration agent={agent} onLogout={onLogout} />
69
+
) : (
70
+
<Navigate to="/" replace />
71
+
)
72
+
}
73
+
/>
74
+
<Route
75
+
path="/migration/registration"
76
+
element={
77
+
agent ? (
78
+
<MigrationForms agent={agent} onLogout={onLogout} />
79
+
) : (
80
+
<Navigate to="/" replace />
81
+
)
82
+
}
83
+
/>
84
+
<Route
85
+
path="/recovery-key"
86
+
element={
87
+
agent ? (
88
+
<RecoveryKey agent={agent} onLogout={onLogout} />
89
+
) : (
90
+
<Navigate to="/" replace />
91
+
)
92
+
}
93
+
/>
94
+
<Route
95
+
path="/recovery-key/process"
96
+
element={
97
+
agent ? (
98
+
<RecoveryKeyProcess agent={agent} onLogout={onLogout} />
99
+
) : (
100
+
<Navigate to="/" replace />
101
+
)
102
+
}
103
+
/>
104
+
<Route
105
+
path="/migration/process"
106
+
element={
107
+
agent ? (
108
+
<MigrationProcess agent={agent} onLogout={onLogout} />
109
+
) : (
110
+
<Navigate to="/" replace />
111
+
)
112
+
}
113
+
/>
114
+
{/* Catch-all route for 404s */}
115
+
<Route path="*" element={<ErrorPage />} />
116
+
</Routes>
117
+
</>
118
+
);
119
+
}
-738
src/styles/App.css
-738
src/styles/App.css
···
1
-
/* Base styles */
2
-
:root {
3
-
--primary-color: #4f46e5;
4
-
--primary-hover: #4338ca;
5
-
--text-color: #1f2937;
6
-
--text-light: #6b7280;
7
-
--bg-color: #f3f4f6;
8
-
--white: #ffffff;
9
-
--error-color: #ef4444;
10
-
--border-color: #e5e7eb;
11
-
--input-bg: #ffffff;
12
-
}
13
-
14
-
@media (prefers-color-scheme: dark) {
15
-
:root {
16
-
--primary-color: #6366f1;
17
-
--primary-hover: #4f46e5;
18
-
--text-color: #f3f4f6;
19
-
--text-light: #9ca3af;
20
-
--bg-color: #111827;
21
-
--white: #1f2937;
22
-
--error-color: #f87171;
23
-
--border-color: #374151;
24
-
--input-bg: #1f2937;
25
-
}
26
-
}
27
-
28
-
body {
29
-
margin: 0;
30
-
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
31
-
background-color: var(--bg-color);
32
-
color: var(--text-color);
33
-
min-height: 100vh;
34
-
display: flex;
35
-
flex-direction: column;
36
-
}
37
-
38
-
/* Login page styles */
39
-
.login-container {
40
-
flex: 1;
41
-
display: flex;
42
-
align-items: center;
43
-
justify-content: center;
44
-
padding: 1rem;
45
-
}
46
-
47
-
.login-card {
48
-
max-width: 28rem;
49
-
width: 100%;
50
-
padding: 2rem;
51
-
padding-top: 0;
52
-
background-color: var(--white);
53
-
border-radius: 0.5rem;
54
-
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
55
-
}
56
-
57
-
.login-title {
58
-
padding-top: 0;
59
-
text-align: center;
60
-
font-size: 1.875rem;
61
-
font-weight: 800;
62
-
color: var(--text-color);
63
-
}
64
-
65
-
.warning-message {
66
-
margin: 1rem 0;
67
-
padding: 0.75rem;
68
-
background-color: #fef3c7;
69
-
border: 1px solid #fbbf24;
70
-
border-radius: 0.375rem;
71
-
color: #92400e;
72
-
font-size: 0.875rem;
73
-
text-align: center;
74
-
}
75
-
76
-
.login-form {
77
-
margin-top: 2rem;
78
-
padding: 0 1rem;
79
-
}
80
-
81
-
.form-group {
82
-
margin-bottom: 1rem;
83
-
}
84
-
85
-
.form-group label {
86
-
display: block;
87
-
margin-bottom: 0.5rem;
88
-
color: var(--text-color);
89
-
font-weight: 500;
90
-
}
91
-
92
-
.handle-input-container {
93
-
display: flex;
94
-
align-items: stretch;
95
-
gap: 0;
96
-
background-color: var(--input-bg);
97
-
border: 1px solid var(--border-color);
98
-
border-radius: 0.375rem;
99
-
}
100
-
101
-
.handle-input-container .form-input {
102
-
border: none;
103
-
padding-right: 0;
104
-
flex: 1;
105
-
border-radius: 0.375rem 0 0 0.375rem;
106
-
}
107
-
108
-
.handle-input-container .form-input:focus {
109
-
box-shadow: none;
110
-
}
111
-
112
-
.handle-domain {
113
-
color: var(--text-light);
114
-
font-size: 0.875rem;
115
-
white-space: nowrap;
116
-
background-color: var(--bg-color);
117
-
padding: 0 0.75rem;
118
-
border-radius: 0 0.375rem 0.375rem 0;
119
-
display: flex;
120
-
align-items: center;
121
-
}
122
-
123
-
.form-input {
124
-
width: 100%;
125
-
padding: 0.75rem 1rem;
126
-
border: 1px solid var(--border-color);
127
-
border-radius: 0.375rem;
128
-
font-size: 0.875rem;
129
-
color: var(--text-color);
130
-
background-color: var(--input-bg);
131
-
box-sizing: border-box;
132
-
}
133
-
134
-
.form-input:focus {
135
-
outline: none;
136
-
border-color: var(--primary-color);
137
-
box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.1);
138
-
}
139
-
140
-
.form-input::placeholder {
141
-
color: var(--text-light);
142
-
}
143
-
144
-
.error-message {
145
-
color: var(--error-color);
146
-
font-size: 0.875rem;
147
-
text-align: center;
148
-
margin: 0.5rem 0;
149
-
}
150
-
151
-
.success-message {
152
-
color: #059669;
153
-
font-size: 0.875rem;
154
-
margin: 0.5rem 0;
155
-
display: flex;
156
-
align-items: center;
157
-
gap: 0.5rem;
158
-
}
159
-
160
-
.submit-button {
161
-
width: 100%;
162
-
padding: 0.75rem;
163
-
background-color: var(--primary-color);
164
-
color: var(--white);
165
-
border: none;
166
-
border-radius: 0.375rem;
167
-
font-size: 0.875rem;
168
-
font-weight: 500;
169
-
cursor: pointer;
170
-
transition: background-color 0.2s;
171
-
margin-top: 1rem;
172
-
}
173
-
174
-
.submit-button:hover {
175
-
background-color: var(--primary-hover);
176
-
}
177
-
178
-
/* Header styles */
179
-
.app-header {
180
-
display: flex;
181
-
flex-wrap: wrap;
182
-
align-items: center;
183
-
justify-content: space-between;
184
-
gap: 1rem;
185
-
padding: 1rem;
186
-
background-color: var(--white);
187
-
border-radius: 0.5rem;
188
-
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
189
-
margin: 1rem 0 0 0;
190
-
position: relative;
191
-
z-index: 10;
192
-
}
193
-
194
-
.app-title {
195
-
font-size: 1.25rem;
196
-
font-weight: 700;
197
-
color: var(--text-color);
198
-
margin: 0;
199
-
white-space: nowrap;
200
-
overflow: hidden;
201
-
text-overflow: ellipsis;
202
-
max-width: 200px;
203
-
}
204
-
205
-
.user-info {
206
-
display: flex;
207
-
align-items: center;
208
-
gap: 0.75rem;
209
-
flex-wrap: wrap;
210
-
}
211
-
212
-
.user-avatar {
213
-
width: 2rem;
214
-
height: 2rem;
215
-
border-radius: 50%;
216
-
object-fit: cover;
217
-
flex-shrink: 0;
218
-
}
219
-
220
-
.user-handle {
221
-
font-size: 0.875rem;
222
-
color: var(--text-color);
223
-
white-space: nowrap;
224
-
overflow: hidden;
225
-
text-overflow: ellipsis;
226
-
max-width: 150px;
227
-
}
228
-
229
-
.logout-button {
230
-
background-color: #f44336;
231
-
color: white;
232
-
border: none;
233
-
padding: 8px 16px;
234
-
border-radius: 4px;
235
-
cursor: pointer;
236
-
font-size: 14px;
237
-
transition: background-color 0.2s;
238
-
white-space: nowrap;
239
-
flex-shrink: 0;
240
-
}
241
-
242
-
@media (max-width: 480px) {
243
-
.app-header {
244
-
padding: 0.75rem;
245
-
gap: 0.5rem;
246
-
}
247
-
248
-
.app-title {
249
-
font-size: 1.125rem;
250
-
max-width: 150px;
251
-
}
252
-
253
-
.user-handle {
254
-
max-width: 100px;
255
-
}
256
-
257
-
.logout-button {
258
-
padding: 6px 12px;
259
-
font-size: 13px;
260
-
}
261
-
}
262
-
263
-
/* Actions page styles */
264
-
.actions-page {
265
-
display: flex;
266
-
flex-direction: column;
267
-
min-height: 100vh;
268
-
max-width: 800px;
269
-
margin: 0 auto;
270
-
padding: 0 20px;
271
-
gap: 1rem;
272
-
}
273
-
274
-
.actions-container {
275
-
padding: 0;
276
-
max-width: 800px;
277
-
width: 100%;
278
-
margin: 0 auto;
279
-
}
280
-
281
-
.actions-list {
282
-
display: flex;
283
-
flex-direction: column;
284
-
gap: 1rem;
285
-
margin-top: 1rem;
286
-
}
287
-
288
-
.action-item {
289
-
display: flex;
290
-
align-items: center;
291
-
gap: 1rem;
292
-
padding: 1rem;
293
-
background-color: var(--white);
294
-
border-radius: 0.5rem;
295
-
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
296
-
cursor: pointer;
297
-
transition: transform 0.2s, box-shadow 0.2s;
298
-
border: none;
299
-
width: 100%;
300
-
text-align: left;
301
-
}
302
-
303
-
.action-item:hover {
304
-
transform: translateY(-2px);
305
-
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
306
-
}
307
-
308
-
.action-icon {
309
-
width: 2.5rem;
310
-
height: 2.5rem;
311
-
display: flex;
312
-
align-items: center;
313
-
justify-content: center;
314
-
color: var(--primary-color);
315
-
background-color: var(--bg-color);
316
-
border-radius: 0.5rem;
317
-
flex-shrink: 0;
318
-
}
319
-
320
-
.action-content {
321
-
flex: 1;
322
-
text-align: left;
323
-
}
324
-
325
-
.action-title {
326
-
font-size: 1rem;
327
-
font-weight: 600;
328
-
color: var(--text-color);
329
-
margin-bottom: 0.25rem;
330
-
text-align: left;
331
-
}
332
-
333
-
.action-subtitle {
334
-
font-size: 0.875rem;
335
-
color: var(--text-light);
336
-
text-align: left;
337
-
}
338
-
339
-
/* User info section */
340
-
.user-info-section {
341
-
background-color: var(--white);
342
-
border-radius: 0.5rem;
343
-
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
344
-
overflow: hidden;
345
-
margin-top: 1rem;
346
-
}
347
-
348
-
.user-info-summary {
349
-
padding: 1rem;
350
-
cursor: pointer;
351
-
display: flex;
352
-
align-items: center;
353
-
justify-content: space-between;
354
-
list-style: none;
355
-
font-weight: 600;
356
-
color: var(--text-color);
357
-
}
358
-
359
-
.user-info-summary::after {
360
-
content: "โผ";
361
-
color: var(--primary-color);
362
-
transition: transform 0.2s;
363
-
}
364
-
365
-
.user-info-section[open] .user-info-summary::after {
366
-
transform: rotate(180deg);
367
-
}
368
-
369
-
.user-info-content {
370
-
padding: 1.5rem;
371
-
border-top: 1px solid var(--border-color);
372
-
}
373
-
374
-
.user-info-content section {
375
-
margin-bottom: 2rem;
376
-
}
377
-
378
-
.user-info-content section:last-child {
379
-
margin-bottom: 0;
380
-
}
381
-
382
-
.user-info-content h2 {
383
-
font-size: 1.125rem;
384
-
font-weight: 600;
385
-
color: var(--text-color);
386
-
margin-bottom: 1rem;
387
-
}
388
-
389
-
.user-info-content dl {
390
-
display: grid;
391
-
grid-template-columns: max-content 1fr;
392
-
gap: 0.75rem 1rem;
393
-
margin: 0;
394
-
}
395
-
396
-
.user-info-content dt {
397
-
font-weight: 500;
398
-
color: var(--text-light);
399
-
}
400
-
401
-
.user-info-content dd {
402
-
margin: 0;
403
-
color: var(--text-color);
404
-
word-break: break-all;
405
-
}
406
-
407
-
.did-document {
408
-
background-color: var(--bg-color);
409
-
padding: 1rem;
410
-
border-radius: 0.375rem;
411
-
overflow-x: auto;
412
-
margin: 0;
413
-
}
414
-
415
-
.did-document code {
416
-
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
417
-
font-size: 0.875rem;
418
-
color: var(--text-color);
419
-
white-space: pre;
420
-
}
421
-
422
-
/* Loading state */
423
-
.loading-container {
424
-
flex: 1;
425
-
display: flex;
426
-
align-items: center;
427
-
justify-content: center;
428
-
min-height: 100vh;
429
-
padding: 1rem;
430
-
}
431
-
432
-
.loading-text {
433
-
font-size: 1.25rem;
434
-
color: var(--text-color);
435
-
background-color: var(--white);
436
-
padding: 1.5rem 2rem;
437
-
border-radius: 0.5rem;
438
-
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
439
-
display: flex;
440
-
align-items: center;
441
-
gap: 0.75rem;
442
-
}
443
-
444
-
.loading-text::before {
445
-
content: "";
446
-
width: 1.5rem;
447
-
height: 1.5rem;
448
-
border: 2px solid var(--primary-color);
449
-
border-right-color: transparent;
450
-
border-radius: 50%;
451
-
animation: spin 1s linear infinite;
452
-
}
453
-
454
-
@keyframes spin {
455
-
to {
456
-
transform: rotate(360deg);
457
-
}
458
-
}
459
-
460
-
/* Footer styles */
461
-
.footer {
462
-
text-align: center;
463
-
padding: 1rem;
464
-
color: var(--text-light);
465
-
font-size: 0.875rem;
466
-
margin: 1rem 0;
467
-
}
468
-
469
-
.footer-link {
470
-
color: var(--primary-color);
471
-
text-decoration: none;
472
-
transition: color 0.2s;
473
-
}
474
-
475
-
.footer-link:hover {
476
-
color: var(--primary-hover);
477
-
}
478
-
479
-
.footer-link strong {
480
-
font-weight: 600;
481
-
}
482
-
483
-
.loading-message {
484
-
margin: 10px 0;
485
-
padding: 10px;
486
-
background-color: var(--bg-color);
487
-
border-radius: 4px;
488
-
color: var(--text-color);
489
-
font-size: 0.9em;
490
-
text-align: center;
491
-
}
492
-
493
-
.form-input:disabled {
494
-
background-color: var(--bg-color);
495
-
border-color: var(--border-color);
496
-
color: var(--text-light);
497
-
cursor: not-allowed;
498
-
opacity: 0.8;
499
-
}
500
-
501
-
.submit-button:disabled {
502
-
background-color: var(--text-light);
503
-
cursor: not-allowed;
504
-
opacity: 0.8;
505
-
}
506
-
507
-
.page-content {
508
-
background-color: var(--white);
509
-
border-radius: 0.5rem;
510
-
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
511
-
padding: 2rem;
512
-
padding-top: 1rem;
513
-
margin-top: 1rem;
514
-
}
515
-
516
-
.page-content h2 {
517
-
font-size: 1.5rem;
518
-
font-weight: 600;
519
-
color: var(--text-color);
520
-
margin-bottom: 1rem;
521
-
}
522
-
523
-
.page-content p {
524
-
color: var(--text-color);
525
-
margin-bottom: 1rem;
526
-
}
527
-
528
-
.page-content ul {
529
-
list-style-type: disc;
530
-
margin-left: 1.5rem;
531
-
margin-bottom: 1.5rem;
532
-
color: var(--text-color);
533
-
}
534
-
535
-
.page-content li {
536
-
margin-bottom: 0.5rem;
537
-
}
538
-
539
-
.back-button {
540
-
background-color: var(--primary-color);
541
-
color: var(--white);
542
-
border: none;
543
-
padding: 0.75rem 1.5rem;
544
-
border-radius: 0.375rem;
545
-
font-size: 0.875rem;
546
-
font-weight: 500;
547
-
cursor: pointer;
548
-
transition: background-color 0.2s;
549
-
display: inline-flex;
550
-
align-items: center;
551
-
gap: 0.5rem;
552
-
}
553
-
554
-
.button-container {
555
-
display: flex;
556
-
justify-content: space-between;
557
-
gap: 1rem;
558
-
margin-top: 2rem;
559
-
}
560
-
561
-
.back-button {
562
-
background-color: var(--text-light);
563
-
color: var(--white);
564
-
border: none;
565
-
padding: 0.75rem 1.5rem;
566
-
border-radius: 0.375rem;
567
-
font-size: 0.875rem;
568
-
font-weight: 500;
569
-
cursor: pointer;
570
-
transition: background-color 0.2s;
571
-
display: inline-flex;
572
-
align-items: center;
573
-
gap: 0.5rem;
574
-
}
575
-
576
-
.back-button:hover {
577
-
background-color: #c7c7c7;
578
-
}
579
-
580
-
.continue-button {
581
-
background-color: var(--primary-color);
582
-
color: var(--white);
583
-
border: none;
584
-
padding: 0.75rem 1.5rem;
585
-
border-radius: 0.375rem;
586
-
font-size: 0.875rem;
587
-
font-weight: 500;
588
-
cursor: pointer;
589
-
transition: background-color 0.2s;
590
-
display: inline-flex;
591
-
align-items: center;
592
-
gap: 0.5rem;
593
-
margin-left: auto;
594
-
}
595
-
596
-
.continue-button:hover {
597
-
background-color: var(--primary-hover);
598
-
}
599
-
600
-
.page-content h3 {
601
-
font-size: 1.25rem;
602
-
font-weight: 600;
603
-
color: var(--text-color);
604
-
margin: 1.5rem 0 1rem 0;
605
-
}
606
-
607
-
.warning-section {
608
-
background-color: #fef3c7;
609
-
border: 1px solid #fbbf24;
610
-
border-radius: 0.5rem;
611
-
padding: 1.5rem;
612
-
margin: 1.5rem 0;
613
-
}
614
-
615
-
.warning-section h3 {
616
-
color: #92400e;
617
-
margin-top: 0;
618
-
}
619
-
620
-
.warning-section ul {
621
-
margin-bottom: 0;
622
-
padding-left: 0;
623
-
}
624
-
625
-
.warning-section li {
626
-
color: #92400e;
627
-
}
628
-
629
-
.warning-section b {
630
-
color: #78350f;
631
-
}
632
-
633
-
.docs-section {
634
-
background-color: var(--bg-color);
635
-
border-radius: 0.5rem;
636
-
padding: 1.5rem;
637
-
margin: 1.5rem 0;
638
-
}
639
-
640
-
.docs-section h3 {
641
-
margin-top: 0;
642
-
}
643
-
644
-
.docs-section ul {
645
-
margin-bottom: 0;
646
-
padding-left: 0;
647
-
}
648
-
649
-
.docs-section a {
650
-
color: var(--primary-color);
651
-
text-decoration: none;
652
-
transition: color 0.2s;
653
-
display: inline-flex;
654
-
align-items: center;
655
-
gap: 0.5rem;
656
-
}
657
-
658
-
.docs-section a:hover {
659
-
color: var(--primary-hover);
660
-
}
661
-
662
-
.docs-section a::after {
663
-
content: "โ";
664
-
transition: transform 0.2s;
665
-
}
666
-
667
-
.docs-section a:hover::after {
668
-
transform: translateX(4px);
669
-
}
670
-
671
-
.network-warning {
672
-
position: fixed;
673
-
top: 0;
674
-
left: 0;
675
-
right: 0;
676
-
bottom: 0;
677
-
background-color: rgba(0, 0, 0, 0.8);
678
-
z-index: 1000;
679
-
display: flex;
680
-
align-items: center;
681
-
justify-content: center;
682
-
}
683
-
684
-
.network-warning-content {
685
-
max-width: 800px;
686
-
padding: 2rem;
687
-
display: flex;
688
-
align-items: center;
689
-
gap: 0.75rem;
690
-
color: #92400e;
691
-
font-size: 1.125rem;
692
-
text-align: center;
693
-
background-color: rgba(255, 255, 255, 0.9);
694
-
border-radius: 0.5rem;
695
-
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
696
-
}
697
-
698
-
.network-warning-icon {
699
-
font-size: 1.5rem;
700
-
flex-shrink: 0;
701
-
}
702
-
703
-
.form-section {
704
-
background: var(--white);
705
-
border-radius: 8px;
706
-
padding: 2rem;
707
-
margin-bottom: 2rem;
708
-
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
709
-
transition: all 0.3s ease;
710
-
border: 1px solid var(--border-color);
711
-
}
712
-
713
-
.form-section.completed {
714
-
opacity: 0.6;
715
-
pointer-events: none;
716
-
background: var(--bg-color);
717
-
}
718
-
719
-
.form-section h3 {
720
-
margin-top: 0;
721
-
margin-bottom: 1.5rem;
722
-
color: var(--text-color);
723
-
font-size: 1.25rem;
724
-
}
725
-
726
-
.info-message {
727
-
color: var(--primary-color);
728
-
font-size: 0.875rem;
729
-
margin-top: 0.5rem;
730
-
display: flex;
731
-
align-items: center;
732
-
gap: 0.5rem;
733
-
}
734
-
735
-
.info-message::before {
736
-
content: "โน๏ธ";
737
-
font-size: 1rem;
738
-
}
+10
-4
tsconfig.app.json
+10
-4
tsconfig.app.json
···
1
1
{
2
2
"compilerOptions": {
3
3
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
4
-
"target": "ES2020",
4
+
"target": "ES2023",
5
5
"useDefineForClassFields": true,
6
-
"lib": ["ES2020", "DOM", "DOM.Iterable"],
6
+
"lib": ["ES2023", "DOM", "DOM.Iterable"],
7
7
"module": "ESNext",
8
8
"skipLibCheck": true,
9
9
10
10
/* Bundler mode */
11
11
"moduleResolution": "bundler",
12
12
"allowImportingTsExtensions": true,
13
+
"resolveJsonModule": true,
13
14
"isolatedModules": true,
14
15
"moduleDetection": "force",
15
16
"noEmit": true,
···
20
21
"noUnusedLocals": true,
21
22
"noUnusedParameters": true,
22
23
"noFallthroughCasesInSwitch": true,
23
-
"noUncheckedSideEffectImports": true
24
+
"noUncheckedSideEffectImports": true,
25
+
"forceConsistentCasingInFileNames": true,
26
+
27
+
/* Types */
28
+
"types": ["react", "react-dom", "node"]
24
29
},
25
-
"include": ["src"]
30
+
"include": ["src/**/*"],
31
+
"references": [{ "path": "./tsconfig.node.json" }]
26
32
}
+5
-5
tsconfig.node.json
+5
-5
tsconfig.node.json
···
1
1
{
2
2
"compilerOptions": {
3
3
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4
-
"target": "ES2022",
4
+
"target": "ES2023",
5
5
"lib": ["ES2023"],
6
6
"module": "ESNext",
7
7
"skipLibCheck": true,
8
8
9
9
/* Bundler mode */
10
-
"moduleResolution": "bundler",
11
-
"allowImportingTsExtensions": true,
10
+
"moduleResolution": "node",
12
11
"isolatedModules": true,
13
12
"moduleDetection": "force",
14
-
"noEmit": true,
13
+
"composite": true,
15
14
16
15
/* Linting */
17
16
"strict": true,
18
17
"noUnusedLocals": true,
19
18
"noUnusedParameters": true,
20
19
"noFallthroughCasesInSwitch": true,
21
-
"noUncheckedSideEffectImports": true
20
+
"noUncheckedSideEffectImports": true,
21
+
"forceConsistentCasingInFileNames": true
22
22
},
23
23
"include": ["vite.config.ts"]
24
24
}
+37
-3
vite.config.ts
+37
-3
vite.config.ts
···
1
-
import { defineConfig } from 'vite'
2
-
import react from '@vitejs/plugin-react'
1
+
import { defineConfig } from 'vite';
2
+
import react from '@vitejs/plugin-react';
3
+
import { visualizer } from 'rollup-plugin-visualizer';
3
4
4
5
// https://vitejs.dev/config/
5
6
export default defineConfig({
6
-
plugins: [react()],
7
+
plugins: [
8
+
react(),
9
+
...(process.env.ANALYZE ? [visualizer({
10
+
open: true,
11
+
filename: 'dist/bundle-analysis.html',
12
+
gzipSize: true
13
+
})] : [])
14
+
],
7
15
build: {
8
16
outDir: 'dist',
17
+
emptyOutDir: true,
9
18
sourcemap: true,
19
+
minify: 'terser',
20
+
terserOptions: {
21
+
compress: {
22
+
drop_console: true,
23
+
drop_debugger: true
24
+
}
25
+
},
26
+
rollupOptions: {
27
+
external: [
28
+
'multiformats',
29
+
'multiformats/basics',
30
+
'multiformats/cid',
31
+
'multiformats/hashes/sha2',
32
+
'multiformats/hashes/sha3',
33
+
'iso-datestring-validator',
34
+
'uint8arrays'
35
+
],
36
+
output: {
37
+
manualChunks: {
38
+
'vendor-react': ['react', 'react-dom', 'react-router-dom', 'scheduler', 'react-dom/client'],
39
+
'vendor-atproto': ['@atproto/api', '@atproto/crypto']
40
+
}
41
+
}
42
+
}
10
43
},
11
44
server: {
12
45
port: 3000,
13
46
},
47
+
publicDir: 'public'
14
48
})