.github/assets/1_home.png
.github/assets/1_home.png
This is a binary file and will not be displayed.
.github/assets/2_words.png
.github/assets/2_words.png
This is a binary file and will not be displayed.
.github/assets/3_offers.png
.github/assets/3_offers.png
This is a binary file and will not be displayed.
.github/assets/icon.png
.github/assets/icon.png
This is a binary file and will not be displayed.
+26
.gitignore
+26
.gitignore
···
···
1
+
# Logs
2
+
logs
3
+
*.log
4
+
npm-debug.log*
5
+
yarn-debug.log*
6
+
yarn-error.log*
7
+
pnpm-debug.log*
8
+
lerna-debug.log*
9
+
10
+
node_modules
11
+
.output
12
+
stats.html
13
+
stats-*.json
14
+
.wxt
15
+
web-ext.config.ts
16
+
17
+
# Editor directories and files
18
+
.vscode/*
19
+
!.vscode/extensions.json
20
+
.idea
21
+
.DS_Store
22
+
*.suo
23
+
*.ntvs*
24
+
*.njsproj
25
+
*.sln
26
+
*.sw?
+16
LICENSE
+16
LICENSE
···
···
1
+
zlib License
2
+
3
+
(C) 2025 Sapphic Angels
4
+
5
+
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable
6
+
for any damages arising from the use of this software.
7
+
8
+
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it
9
+
and redistribute it freely, subject to the following restrictions:
10
+
11
+
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If
12
+
you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not
13
+
required.
14
+
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original
15
+
software.
16
+
3. This notice may not be removed or altered from any source distribution.
+48
README.md
+48
README.md
···
···
1
+
<div align="center">
2
+
3
+
# <img align="top" src="assets/icon.png" alt="tailname icon" width="40" /> tailname
4
+
5
+
[](https://biomejs.dev/)
6
+
[](https://biomejs.dev)
7
+
[](https://github.com/SapphoSys/tailname/blob/master/LICENSE)
8
+
9
+
A browser extension that finds custom tailnet name offers for your Tailscale account using keywords.
10
+
</div>
11
+
12
+
<div style="display: flex; justify-content: center; gap: 1em;">
13
+
<img src=".github/assets/1_home.png" alt="Home page" width="250" />
14
+
<img src=".github/assets/2_words.png" alt="Words screen" width="250" />
15
+
<img src=".github/assets/3_offers.png" alt="Offers screen" width="250" />
16
+
</div>
17
+
18
+
## How to install
19
+
### Chromium-based browsers (Google Chrome, Microsoft Edge, etc)
20
+
*We plan on submitting this extension to the Chrome Web Store soon.*
21
+
22
+
In the meantime, use the following instructions to sideload the extension:
23
+
24
+
0. Get the [latest release for Chromium](link).
25
+
1. Extract the ZIP archive using an archival program of your choice.
26
+
2. Navigate to the Extensions page using [chrome://extensions](chrome://extensions).
27
+
3. Click the "Load unpacked" button.
28
+
4. Select the extracted folder of the extension.
29
+
5. Done!
30
+
31
+
### Gecko-based browsers (Firefox, Zen Browser, etc)
32
+
*The extension submission is currently awaiting a review on the Mozilla Add-on Developer Hub.*
33
+
34
+
In the meantime, use the following instructions to sideload the extension:
35
+
36
+
0. Get the [latest release for Firefox](link).
37
+
1. Navigate to the Extensions page using [about:addons](about:addons).
38
+
2. Click on the settings icon, and select "Install Add-ons From File...".
39
+
3. Select the ZIP archive of the extension.
40
+
4. Done!
41
+
42
+
## License
43
+
44
+
This repository is licensed under the [zlib](LICENSE) license.
45
+
46
+
This extension is not affiliated with Tailscale, Inc.
47
+
48
+
© 2025 Sapphic Angels.
assets/icon.png
assets/icon.png
This is a binary file and will not be displayed.
+48
biome.json
+48
biome.json
···
···
1
+
{
2
+
"$schema": "https://biomejs.dev/schemas/2.2.2/schema.json",
3
+
"vcs": {
4
+
"enabled": false,
5
+
"clientKind": "git",
6
+
"useIgnoreFile": false
7
+
},
8
+
"files": {
9
+
"includes": [
10
+
"entrypoints/**/*",
11
+
"handlers/**/*",
12
+
"helpers/**/*",
13
+
"types/**/*",
14
+
"biome.json",
15
+
"config.ts",
16
+
"tailwind.config.ts",
17
+
"tsconfig.json",
18
+
"wxt.config.ts"
19
+
],
20
+
"ignoreUnknown": false
21
+
},
22
+
"formatter": {
23
+
"enabled": true,
24
+
"indentStyle": "tab"
25
+
},
26
+
"linter": {
27
+
"enabled": true,
28
+
"rules": {
29
+
"recommended": true,
30
+
"suspicious": {
31
+
"noExplicitAny": "error"
32
+
}
33
+
}
34
+
},
35
+
"javascript": {
36
+
"formatter": {
37
+
"quoteStyle": "single"
38
+
}
39
+
},
40
+
"assist": {
41
+
"enabled": true,
42
+
"actions": {
43
+
"source": {
44
+
"organizeImports": "on"
45
+
}
46
+
}
47
+
}
48
+
}
+12
config.ts
+12
config.ts
···
···
1
+
export const badgeColors = {
2
+
active: '#3f5eb3',
3
+
inactive: '#1f1e1e',
4
+
};
5
+
export const badgeUpdateInterval = 30;
6
+
export const defaultCheckOffersInterval = 30;
7
+
export const eligibilityCacheInterval = 60;
8
+
export const tokenValidityInterval = 5 * 60;
9
+
export const tailscaleWordsUrls = [
10
+
'https://raw.githubusercontent.com/tailscale/tailscale/refs/heads/main/words/tails.txt',
11
+
'https://raw.githubusercontent.com/tailscale/tailscale/refs/heads/main/words/scales.txt',
12
+
];
+50
entrypoints/background/alarms.ts
+50
entrypoints/background/alarms.ts
···
···
1
+
import { updateBadgeText } from '$background/badge';
2
+
import { badgeUpdateInterval } from '$config';
3
+
import { log } from '$helpers/logging';
4
+
import { checkTailscaleOffers } from '$helpers/offers';
5
+
import { filterValidTokens } from '$helpers/tokens';
6
+
7
+
export const setupAlarms = () => {
8
+
browser.alarms.create('badgeUpdateAlarm', {
9
+
periodInMinutes: badgeUpdateInterval / 60,
10
+
});
11
+
12
+
browser.alarms.onAlarm.addListener((alarm) => {
13
+
log(`Alarm "${alarm.name}" triggered.`, 'DEBUG', 'Alarms');
14
+
15
+
if (alarm.name === 'checkOffersAlarm') {
16
+
checkTailscaleOffers();
17
+
updateBadgeText();
18
+
}
19
+
20
+
if (alarm.name === 'badgeUpdateAlarm') {
21
+
log(
22
+
'Updating the badge counter and cleaning expired offers...',
23
+
'INFO',
24
+
'Alarms',
25
+
);
26
+
27
+
browser.storage.local.get(['tailscaleTokens'], (result) => {
28
+
const now = Date.now();
29
+
const validTokens = filterValidTokens(
30
+
result.tailscaleTokens || [],
31
+
now,
32
+
);
33
+
34
+
if (validTokens.length !== (result.tailscaleTokens || []).length) {
35
+
browser.storage.local.set({ tailscaleTokens: validTokens }, () => {
36
+
browser.runtime.sendMessage({
37
+
action: 'tokensUpdated',
38
+
tokens: validTokens,
39
+
});
40
+
log('Expired offers removed.', 'INFO', 'Alarms');
41
+
updateBadgeText();
42
+
});
43
+
} else {
44
+
// Update the badges anyway, even if no offers were removed
45
+
updateBadgeText();
46
+
}
47
+
});
48
+
}
49
+
});
50
+
};
+30
entrypoints/background/badge.ts
+30
entrypoints/background/badge.ts
···
···
1
+
import { badgeColors } from '$config';
2
+
import { log } from '$helpers/logging';
3
+
import type { Token } from '$types/tokens';
4
+
5
+
export const updateBadgeText = async () => {
6
+
try {
7
+
const result = await browser.storage.local.get(['tailscaleTokens']);
8
+
const tokens: Token[] = Array.isArray(result.tailscaleTokens)
9
+
? result.tailscaleTokens
10
+
: [];
11
+
const validTokens = tokens.filter((t) => t?.token);
12
+
const count = validTokens.length;
13
+
14
+
const badgeText = count > 0 ? count.toString() : '0';
15
+
const badgeColor = count > 0 ? badgeColors.active : badgeColors.inactive;
16
+
17
+
if (browser.action) {
18
+
await browser.action.setBadgeText({ text: badgeText });
19
+
await browser.action.setBadgeBackgroundColor({ color: badgeColor });
20
+
} else if (browser.browserAction) {
21
+
await browser.browserAction.setBadgeText({ text: badgeText });
22
+
await browser.browserAction.setBadgeBackgroundColor({
23
+
color: badgeColor,
24
+
});
25
+
}
26
+
} catch (error) {
27
+
const errMsg = error instanceof Error ? error.message : 'Unknown error';
28
+
log(`Failed to update badge text: ${errMsg}`);
29
+
}
30
+
};
+23
entrypoints/background/index.ts
+23
entrypoints/background/index.ts
···
···
1
+
import { setupAlarms } from '$background/alarms';
2
+
import { updateBadgeText } from '$background/badge';
3
+
import { setupCookies } from '$background/cookies';
4
+
import { setupMessaging } from '$background/messaging';
5
+
import { setupNotifications } from '$background/notifications';
6
+
import { setupStorage } from '$background/storage';
7
+
import { log } from '$helpers/logging';
8
+
9
+
export default defineBackground(() => {
10
+
log('Setting up alarms and event listeners...', 'DEBUG', 'Setup');
11
+
12
+
// Update badge on startup
13
+
updateBadgeText();
14
+
15
+
// Setup listeners
16
+
setupAlarms();
17
+
setupCookies();
18
+
setupMessaging();
19
+
setupNotifications();
20
+
setupStorage();
21
+
22
+
log('Background setup complete.', 'DEBUG', 'Setup');
23
+
});
+23
entrypoints/background/messaging.ts
+23
entrypoints/background/messaging.ts
···
···
1
+
import handlers from '$handlers';
2
+
import { log } from '$helpers/logging';
3
+
import type { Message } from '$types/messages';
4
+
5
+
export const setupMessaging = () => {
6
+
browser.runtime.onMessage.addListener(
7
+
(message: Message, _sender, sendResponse) => {
8
+
const handler = handlers[message.action];
9
+
if (handler) {
10
+
handler(message, sendResponse);
11
+
return true;
12
+
}
13
+
14
+
log(
15
+
`No handler found for action: ${message.action}`,
16
+
'WARNING',
17
+
'Messaging',
18
+
);
19
+
20
+
return false;
21
+
},
22
+
);
23
+
};
+21
entrypoints/background/notifications.ts
+21
entrypoints/background/notifications.ts
···
···
1
+
import { log } from '$helpers/logging';
2
+
3
+
export const setupNotifications = () => {
4
+
browser.runtime.onInstalled.addListener(() => {
5
+
browser.storage.local.get(['useSystemPush', 'useNtfyPush'], (result) => {
6
+
const updates: Record<string, boolean> = {};
7
+
if (typeof result.useSystemPush !== 'boolean')
8
+
updates.useSystemPush = true;
9
+
if (typeof result.useNtfyPush !== 'boolean') updates.useNtfyPush = false;
10
+
if (Object.keys(updates).length > 0) {
11
+
browser.storage.local.set(updates, () => {
12
+
log(
13
+
'Initialized default notification settings in storage.',
14
+
'DEBUG',
15
+
'Storage',
16
+
);
17
+
});
18
+
}
19
+
});
20
+
});
21
+
};
+9
entrypoints/background/storage.ts
+9
entrypoints/background/storage.ts
+39
entrypoints/content.ts
+39
entrypoints/content.ts
···
···
1
+
export default defineContentScript({
2
+
matches: ['https://login.tailscale.com/admin/dns*'],
3
+
main() {
4
+
browser.runtime.onMessage.addListener((message, _sender, sendResponse) => {
5
+
if (message.action === 'claimToken') {
6
+
const { tcd, token } = message;
7
+
fetch('https://login.tailscale.com/admin/api/tcd', {
8
+
credentials: 'include',
9
+
headers: {
10
+
Accept: 'application/json, text/plain, */*',
11
+
'Accept-Language': 'en-US,en;q=0.5',
12
+
'Content-Type': 'application/json',
13
+
'Sec-Fetch-Site': 'same-origin',
14
+
},
15
+
referrer: 'https://login.tailscale.com/admin/dns',
16
+
body: JSON.stringify({ tcd, token }),
17
+
method: 'POST',
18
+
mode: 'cors',
19
+
})
20
+
.then((res) => res.json())
21
+
.then((result) => {
22
+
if (result.error === 'invalid tailnet name offer token') {
23
+
sendResponse({
24
+
success: false,
25
+
error: 'This tailnet token offer has expired.',
26
+
});
27
+
return;
28
+
} else {
29
+
sendResponse({ success: true, result });
30
+
}
31
+
})
32
+
.catch((err) => {
33
+
sendResponse({ success: false, error: err.toString() });
34
+
});
35
+
return true;
36
+
}
37
+
});
38
+
},
39
+
});
+200
entrypoints/popup/App.tsx
+200
entrypoints/popup/App.tsx
···
···
1
+
import { type FC, useEffect, useState } from 'react';
2
+
3
+
import AlertModal from '$components/AlertModal';
4
+
import EligibilityModal from '$components/EligibilityModal';
5
+
import Footer from '$components/Footer';
6
+
import LoadingOverlay from '$components/LoadingOverlay';
7
+
8
+
import { useEligibility } from '$hooks/useEligibility';
9
+
import { useInputValidation } from '$hooks/useInputValidation';
10
+
import { useModal } from '$hooks/useModal';
11
+
import { useStatus } from '$hooks/useStatus';
12
+
import { useTailnetNames } from '$hooks/useTailnetNames';
13
+
import { useTimer } from '$hooks/useTimer';
14
+
import { useTokens } from '$hooks/useTokens';
15
+
import { useWords } from '$hooks/useWords';
16
+
17
+
import MainScreen from '$screens/MainScreen';
18
+
import TailnetWordsScreen from '$screens/TailnetWords';
19
+
import TokenListScreen from '$screens/TokenList';
20
+
import WordListScreen from '$screens/WordList';
21
+
22
+
const App: FC = () => {
23
+
const [inputValue, setInputValue] = useState('');
24
+
const [error, _setError] = useState<string | null>(null);
25
+
const [showAlert, setShowAlert] = useState(false);
26
+
const [alertMessage, setAlertMessage] = useState('');
27
+
const [claimedToken, setClaimedToken] = useState<string | null>(null);
28
+
const [screen, setScreen] = useState<
29
+
'main' | 'words' | 'tokens' | 'wordlist'
30
+
>('main');
31
+
const [isTransitioning, setIsTransitioning] = useState(false);
32
+
33
+
const handleShowWordsScreen = () => {
34
+
setIsTransitioning(true);
35
+
setScreen('words');
36
+
};
37
+
const handleShowTokensScreen = () => {
38
+
setIsTransitioning(true);
39
+
setScreen('tokens');
40
+
};
41
+
const handleBackToWords = () => {
42
+
setIsTransitioning(true);
43
+
setScreen('words');
44
+
};
45
+
const handleBackToMain = () => {
46
+
setIsTransitioning(true);
47
+
setScreen('main');
48
+
};
49
+
const handleShowWordListScreen = () => {
50
+
setIsTransitioning(true);
51
+
setScreen('wordlist');
52
+
};
53
+
54
+
const { status, setStatus, timer, loading, setLoading } = useTimer();
55
+
const { handleStart, handleStop } = useStatus(setStatus);
56
+
const { tails, scales } = useWords();
57
+
const {
58
+
tailnetNames,
59
+
setTailnetNames,
60
+
handleAddTailnet,
61
+
handleRemoveTailnet,
62
+
} = useTailnetNames(tails, scales, setStatus, inputValue, setInputValue);
63
+
const [
64
+
tokens,
65
+
setTokens,
66
+
{ handleClaimToken, handleRemoveToken, error: claimTokenError },
67
+
] = useTokens({
68
+
setLoading,
69
+
setAlertMessage,
70
+
setShowAlert,
71
+
setClaimedToken,
72
+
});
73
+
74
+
const { eligibility, eligibilityLoading } = useEligibility();
75
+
const { alertModalRef, alertCloseBtnRef, handleAlertCloseModal } = useModal(
76
+
showAlert,
77
+
setShowAlert,
78
+
setAlertMessage,
79
+
claimedToken,
80
+
setClaimedToken,
81
+
setTokens,
82
+
setTailnetNames,
83
+
);
84
+
const isInputValid = useInputValidation(
85
+
inputValue,
86
+
tails,
87
+
scales,
88
+
tailnetNames,
89
+
);
90
+
91
+
useEffect(() => {
92
+
if (isTransitioning) {
93
+
const timer = setTimeout(() => setIsTransitioning(false), 300);
94
+
return () => clearTimeout(timer);
95
+
}
96
+
}, [isTransitioning]);
97
+
98
+
return (
99
+
<div className="flex flex-col min-w-[600px] max-w-lg min-h-[600px] bg-background overflow-hidden mx-auto h-full">
100
+
<div className="flex-1 relative flex flex-col h-full">
101
+
<div className="flex-1 relative overflow-x-hidden overflow-y-auto [&::-webkit-scrollbar]:hidden">
102
+
<div
103
+
className={`absolute inset-0 transition-all duration-500 bg-background ${
104
+
screen === 'main'
105
+
? 'translate-x-0 opacity-100 pointer-events-auto'
106
+
: 'translate-x-[-100%] opacity-0 pointer-events-none'
107
+
}`}
108
+
>
109
+
<MainScreen
110
+
onShowWords={handleShowWordsScreen}
111
+
onShowTokens={handleShowTokensScreen}
112
+
status={status}
113
+
timer={timer}
114
+
handleStart={handleStart}
115
+
handleStop={handleStop}
116
+
tailnetNames={tailnetNames}
117
+
tokens={tokens}
118
+
/>
119
+
</div>
120
+
121
+
<div
122
+
className={`absolute inset-0 transition-all duration-500 bg-background ${
123
+
screen === 'words'
124
+
? 'translate-x-0 opacity-100 pointer-events-auto'
125
+
: screen === 'main'
126
+
? 'translate-x-[100%] opacity-0 pointer-events-none'
127
+
: 'translate-x-[-100%] opacity-0 pointer-events-none'
128
+
}`}
129
+
>
130
+
<TailnetWordsScreen
131
+
tailnetNames={tailnetNames}
132
+
handleAddTailnet={handleAddTailnet}
133
+
handleRemoveTailnet={handleRemoveTailnet}
134
+
inputValue={inputValue}
135
+
setInputValue={setInputValue}
136
+
isInputValid={!!isInputValid}
137
+
loading={loading}
138
+
onBack={handleBackToMain}
139
+
error={error}
140
+
tails={tails}
141
+
scales={scales}
142
+
onShowWordList={handleShowWordListScreen}
143
+
/>
144
+
</div>
145
+
146
+
<div
147
+
className={`absolute inset-0 transition-all duration-500 bg-background ${
148
+
screen === 'tokens'
149
+
? 'translate-x-0 opacity-100 pointer-events-auto'
150
+
: 'translate-x-[100%] opacity-0 pointer-events-none'
151
+
}`}
152
+
>
153
+
<TokenListScreen
154
+
tokens={tokens}
155
+
handleClaimToken={handleClaimToken}
156
+
handleRemoveToken={handleRemoveToken}
157
+
loading={loading}
158
+
error={claimTokenError}
159
+
onBack={handleBackToMain}
160
+
/>
161
+
</div>
162
+
163
+
<div
164
+
className={`absolute inset-0 transition-all duration-500 bg-background ${
165
+
screen === 'wordlist'
166
+
? 'translate-x-0 opacity-100 pointer-events-auto'
167
+
: 'translate-x-[100%] opacity-0 pointer-events-none'
168
+
}`}
169
+
>
170
+
<WordListScreen
171
+
tails={tails}
172
+
scales={scales}
173
+
onBack={handleBackToWords}
174
+
/>
175
+
</div>
176
+
177
+
<LoadingOverlay
178
+
eligibilityLoading={eligibilityLoading}
179
+
loading={loading}
180
+
/>
181
+
<EligibilityModal
182
+
eligibility={eligibility}
183
+
eligibilityLoading={eligibilityLoading}
184
+
/>
185
+
<AlertModal
186
+
alertCloseBtnRef={alertCloseBtnRef}
187
+
alertMessage={alertMessage}
188
+
alertModalRef={alertModalRef}
189
+
handleAlertCloseModal={handleAlertCloseModal}
190
+
showAlert={showAlert}
191
+
/>
192
+
</div>
193
+
194
+
<Footer />
195
+
</div>
196
+
</div>
197
+
);
198
+
};
199
+
200
+
export default App;
+27
entrypoints/popup/components/ActionCard.tsx
+27
entrypoints/popup/components/ActionCard.tsx
···
···
1
+
import type { FC, ReactNode } from 'react';
2
+
3
+
interface ActionCardProps {
4
+
icon: ReactNode;
5
+
title: string;
6
+
subtitle: string;
7
+
onClick: () => void;
8
+
}
9
+
10
+
const ActionCard: FC<ActionCardProps> = ({
11
+
icon,
12
+
title,
13
+
subtitle,
14
+
onClick,
15
+
}) => (
16
+
<button
17
+
className="bg-header shadow-lg rounded-xl p-6 flex flex-col items-center justify-center hover:scale-105 transition-transform border border-border w-full mx-auto"
18
+
type="button"
19
+
onClick={onClick}
20
+
>
21
+
<span className="text-2xl mb-2">{icon}</span>
22
+
<span className="text-base font-semibold mb-1 text-text">{title}</span>
23
+
<span className="text-subtext text-sm">{subtitle}</span>
24
+
</button>
25
+
);
26
+
27
+
export default ActionCard;
+57
entrypoints/popup/components/AlertModal.tsx
+57
entrypoints/popup/components/AlertModal.tsx
···
···
1
+
import type { FC, RefObject } from 'react';
2
+
import { useId } from 'react';
3
+
4
+
interface AlertModalProps {
5
+
showAlert: boolean;
6
+
alertMessage: string;
7
+
alertModalRef: RefObject<HTMLDivElement | null>;
8
+
alertCloseBtnRef: RefObject<HTMLButtonElement | null>;
9
+
handleAlertCloseModal: () => void;
10
+
}
11
+
const AlertModal: FC<AlertModalProps> = ({
12
+
showAlert,
13
+
alertMessage,
14
+
alertModalRef,
15
+
alertCloseBtnRef,
16
+
handleAlertCloseModal,
17
+
}) => {
18
+
const alertMessageId = useId();
19
+
20
+
return (
21
+
<div
22
+
ref={alertModalRef}
23
+
tabIndex={-1}
24
+
className={`fixed inset-0 z-50 flex items-center justify-center bg-background bg-opacity-40 backdrop-blur-sm transition-opacity duration-300 ${
25
+
showAlert ? 'opacity-100' : 'opacity-0 pointer-events-none'
26
+
}`}
27
+
role="dialog"
28
+
aria-modal="true"
29
+
>
30
+
<div
31
+
className={`rounded-md shadow-2xl p-6 bg-header max-w-md mx-auto flex flex-col border text-text border-border transform transition-all duration-300 ${
32
+
showAlert ? 'scale-100 opacity-100' : 'scale-95 opacity-0'
33
+
}`}
34
+
tabIndex={-1}
35
+
>
36
+
<span id={alertMessageId} className="block mb-4 text-lg text-gray-800">
37
+
{alertMessage.split('\n').map((line) => (
38
+
<span key={line} className="block mb-3">
39
+
{line}
40
+
</span>
41
+
))}
42
+
</span>
43
+
44
+
<button
45
+
ref={alertCloseBtnRef as RefObject<HTMLButtonElement>}
46
+
className="px-4 py-2 bg-accent text-text rounded-md hover:opacity-80 transition-opacity focus:outline-none"
47
+
type="button"
48
+
onClick={handleAlertCloseModal}
49
+
>
50
+
OK
51
+
</button>
52
+
</div>
53
+
</div>
54
+
);
55
+
};
56
+
57
+
export default AlertModal;
+60
entrypoints/popup/components/ConfirmLogoutModal.tsx
+60
entrypoints/popup/components/ConfirmLogoutModal.tsx
···
···
1
+
import type { FC } from 'react';
2
+
3
+
interface ConfirmLogoutModalProps {
4
+
show: boolean;
5
+
onConfirm: () => void;
6
+
onCancel: () => void;
7
+
}
8
+
9
+
const ConfirmLogoutModal: FC<ConfirmLogoutModalProps> = ({
10
+
show,
11
+
onConfirm,
12
+
onCancel,
13
+
}) => {
14
+
// Animate modal in/out
15
+
return (
16
+
<div
17
+
className={`fixed inset-0 flex items-center text-text justify-center bg-background bg-opacity-40 backdrop-blur-sm z-50 transition-opacity duration-300 ${
18
+
show ? 'opacity-100' : 'opacity-0 pointer-events-none'
19
+
}`}
20
+
>
21
+
<div
22
+
className={`rounded-md shadow-2xl p-6 bg-header max-w-md mx-auto flex flex-col border border-border transform transition-all duration-300 ${
23
+
show ? 'scale-100 opacity-100' : 'scale-95 opacity-0'
24
+
}`}
25
+
>
26
+
<h2 className="text-2xl font-bold mb-2">Log out</h2>
27
+
<div className="flex flex-col gap-2 pb-3">
28
+
<p className="text-base">
29
+
This will log you out from your account. Are you sure?
30
+
</p>
31
+
32
+
<p className="text-sm text-subtext">
33
+
You will need to log in again with a different Tailscale account to
34
+
use the extension.
35
+
</p>
36
+
</div>
37
+
38
+
<div className="flex gap-2">
39
+
<button
40
+
className="px-4 py-2 rounded-lg bg-danger text-text font-medium hover:opacity-80 transition-opacity"
41
+
onClick={onConfirm}
42
+
type="button"
43
+
>
44
+
Log out
45
+
</button>
46
+
47
+
<button
48
+
className="px-4 py-2 rounded-lg bg-item text-text font-medium hover:opacity-80 transition-opacity"
49
+
onClick={onCancel}
50
+
type="button"
51
+
>
52
+
Cancel
53
+
</button>
54
+
</div>
55
+
</div>
56
+
</div>
57
+
);
58
+
};
59
+
60
+
export default ConfirmLogoutModal;
+125
entrypoints/popup/components/EligibilityModal.tsx
+125
entrypoints/popup/components/EligibilityModal.tsx
···
···
1
+
import { type FC, useEffect, useState } from 'react';
2
+
import ConfirmLogoutModal from '$components/ConfirmLogoutModal';
3
+
import type { Eligibility } from '$types/eligibility';
4
+
5
+
interface EligibilityModalProps {
6
+
eligibility: Eligibility | null;
7
+
eligibilityLoading: boolean;
8
+
}
9
+
10
+
const EligibilityModal: FC<EligibilityModalProps> = ({
11
+
eligibility,
12
+
eligibilityLoading,
13
+
}) => {
14
+
const [showConfirmLogout, setShowConfirmLogout] = useState(false);
15
+
const [visible, setVisible] = useState(false);
16
+
const [shouldRender, setShouldRender] = useState(false);
17
+
18
+
// Animate modal in/out
19
+
useEffect(() => {
20
+
if (!eligibilityLoading && eligibility && !eligibility.eligible) {
21
+
setShouldRender(true);
22
+
setTimeout(() => setVisible(true), 10); // pop in
23
+
} else if (visible) {
24
+
setVisible(false);
25
+
setTimeout(() => setShouldRender(false), 300); // fade out
26
+
} else {
27
+
setShouldRender(false);
28
+
}
29
+
}, [eligibilityLoading, eligibility, visible]);
30
+
31
+
const handleLogoutClick = (e: React.MouseEvent) => {
32
+
e.preventDefault();
33
+
setShowConfirmLogout(true);
34
+
};
35
+
const handleConfirmLogout = () => {
36
+
setShowConfirmLogout(false);
37
+
window.open('https://login.tailscale.com/logout', '_blank');
38
+
browser.storage.local.remove('eligibility');
39
+
};
40
+
const handleCancelLogout = () => {
41
+
setShowConfirmLogout(false);
42
+
};
43
+
if (!shouldRender || !eligibility) return null;
44
+
return (
45
+
<div
46
+
className={`fixed inset-0 flex items-center justify-center bg-background bg-opacity-40 backdrop-blur-sm shadow-[#000] transition-opacity duration-300 ${
47
+
visible ? 'opacity-100' : 'opacity-0 pointer-events-none'
48
+
}`}
49
+
aria-modal="true"
50
+
role="dialog"
51
+
>
52
+
<div
53
+
className={`text-text shadow-[#000] rounded-md shadow-2xl p-6 bg-header max-w-lg mx-auto flex flex-col border border-border transform transition-all duration-300 ${
54
+
visible ? 'scale-100 opacity-100' : 'scale-95 opacity-0'
55
+
}`}
56
+
>
57
+
<div className="flex flex-col flex-wrap gap-x-2 mb-2">
58
+
<h2 className="text-2xl font-bold">
59
+
{eligibility.id === 'not-logged-in'
60
+
? 'Not logged in'
61
+
: eligibility.id === 'api-error'
62
+
? 'API Error'
63
+
: 'Not Eligible'}
64
+
</h2>
65
+
<p className="text-base text-subtext">({eligibility.id})</p>
66
+
</div>
67
+
<div className="text-base text-danger mb-2">
68
+
{eligibility.id === 'not-logged-in'
69
+
? 'You must be logged in to Tailscale to use this extension.'
70
+
: eligibility.reason}
71
+
</div>
72
+
<div className="text-sm text-subtext">
73
+
{eligibility.id === 'not-logged-in'
74
+
? 'When you login, the extension will refresh.'
75
+
: 'Please log out and try another Tailscale account.'}
76
+
</div>
77
+
<div className="login-btn-row flex gap-2 mt-4">
78
+
{eligibility.id === 'not-logged-in' ? (
79
+
<a
80
+
href="https://login.tailscale.com"
81
+
target="_blank"
82
+
rel="noopener noreferrer"
83
+
>
84
+
<button
85
+
className="login-btn px-4 py-2 rounded-lg bg-accent text-text font-medium hover:opacity-80 transition-opacity"
86
+
type="button"
87
+
>
88
+
Log in
89
+
</button>
90
+
</a>
91
+
) : (
92
+
<button
93
+
className="login-btn px-4 py-2 rounded-lg bg-danger text-text font-medium hover:opacity-80 transition-opacity"
94
+
type="button"
95
+
onClick={handleLogoutClick}
96
+
>
97
+
Log out
98
+
</button>
99
+
)}
100
+
{eligibility.id === 'custom-tailnet' && (
101
+
<a
102
+
href="https://tailscale.com/kb/1217/tailnet-name"
103
+
target="_blank"
104
+
rel="noopener noreferrer"
105
+
>
106
+
<button
107
+
className="login-btn learn-more px-4 py-2 rounded-lg bg-accent text-text font-medium hover:opacity-80 transition-opacity"
108
+
type="button"
109
+
>
110
+
Learn more
111
+
</button>
112
+
</a>
113
+
)}
114
+
</div>
115
+
</div>
116
+
<ConfirmLogoutModal
117
+
show={showConfirmLogout}
118
+
onConfirm={handleConfirmLogout}
119
+
onCancel={handleCancelLogout}
120
+
/>
121
+
</div>
122
+
);
123
+
};
124
+
125
+
export default EligibilityModal;
+103
entrypoints/popup/components/FAQModal.tsx
+103
entrypoints/popup/components/FAQModal.tsx
···
···
1
+
import type { FC } from 'react';
2
+
3
+
interface FAQModalProps {
4
+
show: boolean;
5
+
onClose: () => void;
6
+
}
7
+
8
+
const FAQModal: FC<FAQModalProps> = ({ show, onClose }) => {
9
+
return (
10
+
<div
11
+
className={`fixed inset-0 flex items-center justify-center bg-background bg-opacity-40 backdrop-blur-sm z-50 transition-opacity duration-300 ${
12
+
show ? 'opacity-100' : 'opacity-0 pointer-events-none'
13
+
}`}
14
+
aria-modal="true"
15
+
role="dialog"
16
+
onClick={(e) => {
17
+
if (e.target === e.currentTarget) onClose();
18
+
}}
19
+
onKeyDown={(e) => {
20
+
if (e.key === 'Escape') onClose();
21
+
}}
22
+
tabIndex={-1}
23
+
>
24
+
<div
25
+
className={`rounded-md shadow-2xl p-6 bg-header max-w-md mx-auto flex flex-col border border-border transform transition-all duration-300 ${
26
+
show ? 'scale-100 opacity-100' : 'scale-95 opacity-0'
27
+
}`}
28
+
>
29
+
<h2 className="text-2xl font-bold text-subtext mb-2">FAQ</h2>
30
+
<div className="flex flex-col gap-3 pb-3 text-text mb-2">
31
+
<div className="flex flex-col gap-1">
32
+
<h3 className="text-lg font-semibold">What is tailname?</h3>
33
+
34
+
<p className="text-sm">
35
+
tailname is a browser extension that finds custom tailnet name
36
+
offers for your Tailscale account.
37
+
</p>
38
+
</div>
39
+
40
+
<div className="flex flex-col gap-1">
41
+
<h3 className="text-lg font-semibold">
42
+
How do I use the extension?
43
+
</h3>
44
+
45
+
<p className="text-sm">
46
+
You'll need to add at least one keyword in the "Manage tailnet
47
+
words" screen. After that, the extension will begin the checking
48
+
offers process.
49
+
</p>
50
+
51
+
<p className="text-sm">
52
+
The extension will notify you if it finds a tailnet name offer
53
+
that matches one of your keywords or combos.
54
+
</p>
55
+
</div>
56
+
57
+
<div className="flex flex-col gap-1">
58
+
<h3 className="text-lg font-semibold">
59
+
Why can't I find a tailnet name offer?
60
+
</h3>
61
+
62
+
<p className="text-sm">
63
+
It's recommended you add multiple words to find offers quicker.
64
+
All this extension does is request the Tailscale offers API, and
65
+
then filter the results using your keywords and combos.
66
+
</p>
67
+
</div>
68
+
69
+
<div className="flex flex-col gap-1">
70
+
<h3 className="text-lg font-semibold">
71
+
I found a bug! Where can I report it?
72
+
</h3>
73
+
74
+
<p className="text-sm">
75
+
Please visit{' '}
76
+
<a
77
+
href="https://github.com/SapphoSys/tailname"
78
+
className="text-pink hover:opacity-80 hover:underline transition-opacity"
79
+
target="_blank"
80
+
rel="noopener"
81
+
>
82
+
the GitHub repository
83
+
</a>{' '}
84
+
and file a bug report.
85
+
</p>
86
+
</div>
87
+
</div>
88
+
89
+
<div className="flex gap-2">
90
+
<button
91
+
className="px-4 py-2 rounded-lg bg-accent text-text font-medium hover:opacity-80 transition-opacity"
92
+
type="button"
93
+
onClick={onClose}
94
+
>
95
+
Close
96
+
</button>
97
+
</div>
98
+
</div>
99
+
</div>
100
+
);
101
+
};
102
+
103
+
export default FAQModal;
+14
entrypoints/popup/components/HeaderSection.tsx
+14
entrypoints/popup/components/HeaderSection.tsx
···
···
1
+
import type { FC } from 'react';
2
+
3
+
const HeaderSection: FC = () => (
4
+
<section className="header text-2xl font-semibold text-center mb-2 text-text select-none">
5
+
<img
6
+
src={browser.runtime.getURL('/icons/128.png')}
7
+
alt="Tailscale Logo"
8
+
className="inline-block w-8 h-8 mr-2"
9
+
/>
10
+
tailname
11
+
</section>
12
+
);
13
+
14
+
export default HeaderSection;
+21
entrypoints/popup/components/HelperText.tsx
+21
entrypoints/popup/components/HelperText.tsx
···
···
1
+
import type { FC } from 'react';
2
+
import { BiTable } from 'react-icons/bi';
3
+
4
+
interface HelperTextProps {
5
+
onShowWordList: () => void;
6
+
}
7
+
8
+
const HelperText: FC<HelperTextProps> = ({ onShowWordList }) => {
9
+
return (
10
+
<button
11
+
type="button"
12
+
className="w-full text-center border border-accent flex flex-row gap-2 justify-center text-text bg-accent text-sm rounded-md mt-3 py-3 items-center font-medium hover:opacity-80 transition-opacity"
13
+
onClick={onShowWordList}
14
+
>
15
+
<BiTable className="inline-block" size={20} aria-hidden={true} />
16
+
View all available tailnet words
17
+
</button>
18
+
);
19
+
};
20
+
21
+
export default HelperText;
+49
entrypoints/popup/components/LoadingOverlay.tsx
+49
entrypoints/popup/components/LoadingOverlay.tsx
···
···
1
+
import type { FC } from 'react';
2
+
import { BiLoaderAlt } from 'react-icons/bi';
3
+
4
+
interface LoadingOverlayProps {
5
+
loading: boolean;
6
+
eligibilityLoading: boolean;
7
+
}
8
+
9
+
const LoadingOverlay: FC<LoadingOverlayProps> = ({
10
+
loading,
11
+
eligibilityLoading,
12
+
}) => {
13
+
if (!(eligibilityLoading || loading)) return null;
14
+
15
+
// Determine which loading state to show
16
+
const isEligibility = eligibilityLoading && !loading;
17
+
const isRegular = loading && !eligibilityLoading;
18
+
const isBoth = eligibilityLoading && loading;
19
+
20
+
let loadingText = 'Loading...';
21
+
if (isEligibility && !isBoth) {
22
+
loadingText = 'Checking eligibility...';
23
+
}
24
+
25
+
if (isRegular && !isBoth) {
26
+
loadingText = 'Processing...';
27
+
}
28
+
29
+
if (isBoth) {
30
+
loadingText = 'Checking eligibility and processing...';
31
+
}
32
+
33
+
return (
34
+
<div className="fixed inset-0 flex items-center justify-center bg-background bg-opacity-80 z-50 backdrop-blur-sm">
35
+
<div className="text-text rounded-2xl shadow-lg p-6 bg-header max-w-lg mx-auto flex flex-col items-center">
36
+
<BiLoaderAlt
37
+
className="loading-icon text-4xl mb-4 animate-spin-fast"
38
+
aria-hidden={true}
39
+
/>
40
+
41
+
<span className="loading-text text-blue-600 text-lg font-medium">
42
+
{loadingText}
43
+
</span>
44
+
</div>
45
+
</div>
46
+
);
47
+
};
48
+
49
+
export default LoadingOverlay;
+441
entrypoints/popup/components/SettingsModal.tsx
+441
entrypoints/popup/components/SettingsModal.tsx
···
···
1
+
import { type FC, useEffect, useId, useRef, useState } from 'react';
2
+
import AlertModal from './AlertModal';
3
+
4
+
interface SettingsModalProps {
5
+
visible: boolean;
6
+
shouldRender: boolean;
7
+
interval: number;
8
+
setInterval: (val: number) => void;
9
+
onClose: () => void;
10
+
}
11
+
12
+
const SettingsModal: FC<SettingsModalProps> = ({
13
+
visible,
14
+
shouldRender,
15
+
interval,
16
+
setInterval,
17
+
onClose,
18
+
}) => {
19
+
const [inputValue, setInputValue] = useState(String(interval));
20
+
const [isClosing, setIsClosing] = useState(false);
21
+
const [ntfyUrl, setNtfyUrl] = useState('');
22
+
const [ntfyTopic, setNtfyTopic] = useState('');
23
+
const [ntfyToken, setNtfyToken] = useState('');
24
+
const [activeTab, setActiveTab] = useState<'general' | 'notifications'>(
25
+
'general',
26
+
);
27
+
const [useSystemPush, setUseSystemPush] = useState(true);
28
+
const [useNtfyPush, setUseNtfyPush] = useState(false);
29
+
const [showAlert, setShowAlert] = useState(false);
30
+
const [alertMessage, setAlertMessage] = useState('');
31
+
const [ntfyLoading, setNtfyLoading] = useState(false);
32
+
const [tabTransitioning, setTabTransitioning] = useState(false);
33
+
34
+
const alertModalRef = useRef<HTMLDivElement | null>(null);
35
+
const alertCloseBtnRef = useRef<HTMLButtonElement | null>(null);
36
+
37
+
const ntfyUrlId = useId();
38
+
const ntfyTopicId = useId();
39
+
const ntfyTokenId = useId();
40
+
const systemPushId = useId();
41
+
const ntfyPushId = useId();
42
+
43
+
useEffect(() => {
44
+
setInputValue(String(interval));
45
+
}, [interval]);
46
+
47
+
useEffect(() => {
48
+
if (isClosing) {
49
+
const timer = setTimeout(() => {
50
+
setIsClosing(false);
51
+
onClose();
52
+
}, 300);
53
+
return () => clearTimeout(timer);
54
+
}
55
+
}, [isClosing, onClose]);
56
+
57
+
useEffect(() => {
58
+
// Load notification preferences from storage
59
+
browser.storage.local.get(
60
+
['ntfyUrl', 'ntfyTopic', 'ntfyToken', 'useSystemPush', 'useNtfyPush'],
61
+
(result) => {
62
+
if (result.ntfyUrl) setNtfyUrl(result.ntfyUrl);
63
+
if (result.ntfyTopic) setNtfyTopic(result.ntfyTopic);
64
+
if (result.ntfyToken) setNtfyToken(result.ntfyToken);
65
+
if (typeof result.useSystemPush === 'boolean')
66
+
setUseSystemPush(result.useSystemPush);
67
+
if (typeof result.useNtfyPush === 'boolean')
68
+
setUseNtfyPush(result.useNtfyPush);
69
+
},
70
+
);
71
+
}, []);
72
+
73
+
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
74
+
setInputValue(e.target.value);
75
+
};
76
+
77
+
const handleClose = () => {
78
+
let val = Number(inputValue);
79
+
if (Number.isNaN(val) || inputValue.trim() === '') val = interval;
80
+
if (val < 5) val = 5;
81
+
setInputValue(String(val));
82
+
if (val !== interval) {
83
+
setInterval(val);
84
+
}
85
+
setIsClosing(true);
86
+
};
87
+
88
+
const handleBackdropClick = (e: React.MouseEvent<HTMLDivElement>) => {
89
+
if (e.target === e.currentTarget) {
90
+
setIsClosing(true);
91
+
}
92
+
};
93
+
94
+
const handleBackdropKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
95
+
if (e.key === 'Escape') {
96
+
setIsClosing(true);
97
+
}
98
+
};
99
+
100
+
const handleAlertCloseModal = () => {
101
+
setShowAlert(false);
102
+
};
103
+
104
+
const handleNtfyTest = async () => {
105
+
if (!ntfyUrl || !ntfyTopic) {
106
+
setAlertMessage('Please enter both server URL and topic.');
107
+
setShowAlert(true);
108
+
return;
109
+
}
110
+
111
+
setNtfyLoading(true);
112
+
113
+
try {
114
+
const url = `${ntfyUrl.replace(/\/$/, '')}/${ntfyTopic}`;
115
+
const headers: Record<string, string> = { 'Content-Type': 'text/plain' };
116
+
if (ntfyToken) headers.Authorization = `Bearer ${ntfyToken}`;
117
+
118
+
const res = await fetch(url, {
119
+
method: 'POST',
120
+
headers,
121
+
body: 'Test notification from Tailscale WXT',
122
+
});
123
+
124
+
if (res.ok) {
125
+
setAlertMessage('Test notification sent!');
126
+
setShowAlert(true);
127
+
} else if (res.status === 403) {
128
+
setAlertMessage(
129
+
'Server returned a 403 error. This likely means an auth token is required.',
130
+
);
131
+
setShowAlert(true);
132
+
} else {
133
+
setAlertMessage(`Failed: ${res.status}`);
134
+
setShowAlert(true);
135
+
}
136
+
} catch (err) {
137
+
if (err instanceof Error && err.message === 'Failed to fetch') {
138
+
setAlertMessage(
139
+
'Failed to reach the server. Perhaps the URL is incorrect?',
140
+
);
141
+
} else {
142
+
setAlertMessage(
143
+
`Error: ${err instanceof Error ? err.message : String(err)}`,
144
+
);
145
+
}
146
+
setShowAlert(true);
147
+
}
148
+
setNtfyLoading(false);
149
+
};
150
+
151
+
const handleStorageChange =
152
+
<T extends string>(setter: (v: T) => void, key: string) =>
153
+
(e: React.ChangeEvent<HTMLInputElement>) => {
154
+
const value = e.target.value as T;
155
+
setter(value);
156
+
browser.storage.local.set({ [key]: value });
157
+
};
158
+
const handleCheckboxChange =
159
+
(setter: (v: boolean) => void, key: string) =>
160
+
(e: React.ChangeEvent<HTMLInputElement>) => {
161
+
const checked = e.target.checked;
162
+
setter(checked);
163
+
browser.storage.local.set({ [key]: checked });
164
+
};
165
+
166
+
const handleSystemPushChange = handleCheckboxChange(
167
+
setUseSystemPush,
168
+
'useSystemPush',
169
+
);
170
+
const handleNtfyPushChange = handleCheckboxChange(
171
+
setUseNtfyPush,
172
+
'useNtfyPush',
173
+
);
174
+
const handleNtfyUrlChange = handleStorageChange(setNtfyUrl, 'ntfyUrl');
175
+
const handleNtfyTopicChange = handleStorageChange(setNtfyTopic, 'ntfyTopic');
176
+
const handleNtfyTokenChange = handleStorageChange(setNtfyToken, 'ntfyToken');
177
+
178
+
const handleTabSwitch = (tab: 'general' | 'notifications') => {
179
+
if (activeTab !== tab) {
180
+
setTabTransitioning(true);
181
+
setTimeout(() => {
182
+
setTabTransitioning(false);
183
+
setActiveTab(tab);
184
+
}, 250);
185
+
}
186
+
};
187
+
188
+
if (!shouldRender && !isClosing) return null;
189
+
return (
190
+
<>
191
+
<div
192
+
className={`fixed inset-0 flex items-center justify-center bg-background bg-opacity-40 backdrop-blur-sm z-50 transition-opacity duration-300 ${
193
+
visible && !isClosing ? 'opacity-100' : 'opacity-0'
194
+
}`}
195
+
aria-modal="true"
196
+
role="dialog"
197
+
onClick={handleBackdropClick}
198
+
onKeyDown={handleBackdropKeyDown}
199
+
>
200
+
<div
201
+
className={`text-text shadow-[#000] rounded-md shadow-2xl p-6 bg-header max-w-md mx-auto flex flex-col border border-border transform transition-all duration-300 ${
202
+
visible && !isClosing
203
+
? 'scale-100 opacity-100'
204
+
: 'scale-95 opacity-0'
205
+
}`}
206
+
>
207
+
<h2 className="text-2xl font-bold mb-2">Settings</h2>
208
+
<div className="flex flex-row gap-2 mb-4">
209
+
<button
210
+
className={`px-4 py-2 rounded-lg font-medium text-sm transition-colors duration-150 hover:outline-double hover:outline-2 ${
211
+
activeTab === 'general'
212
+
? 'bg-accent text-text font-bold shadow'
213
+
: 'bg-item text-text hover:bg-accent/10 hover:text-accent'
214
+
}`}
215
+
onClick={() => handleTabSwitch('general')}
216
+
type="button"
217
+
aria-selected={activeTab === 'general'}
218
+
aria-controls="general-tab"
219
+
role="tab"
220
+
>
221
+
General
222
+
</button>
223
+
<button
224
+
className={`px-4 py-2 rounded-lg font-medium text-sm transition-colors duration-150 hover:outline-double hover:outline-2 ${
225
+
activeTab === 'notifications'
226
+
? 'bg-accent text-text font-bold shadow'
227
+
: 'bg-item text-text hover:bg-accent/10 hover:text-accent'
228
+
}`}
229
+
onClick={() => handleTabSwitch('notifications')}
230
+
type="button"
231
+
aria-selected={activeTab === 'notifications'}
232
+
aria-controls="notifications-tab"
233
+
role="tab"
234
+
>
235
+
Notifications
236
+
</button>
237
+
</div>
238
+
239
+
<div className="min-w-[380px]">
240
+
<div
241
+
className={`transition-all duration-300 ${
242
+
activeTab === 'general'
243
+
? tabTransitioning
244
+
? 'opacity-0 translate-x-4 pointer-events-none'
245
+
: 'opacity-100 translate-x-0'
246
+
: 'opacity-0 pointer-events-none'
247
+
} bg-header`}
248
+
aria-labelledby="general-tab"
249
+
role="tabpanel"
250
+
>
251
+
{activeTab === 'general' && (
252
+
<div className="flex flex-col gap-2">
253
+
<label className="text-base font-medium flex flex-col">
254
+
<span className="text-sm font-medium mb-1">
255
+
Check interval (seconds):
256
+
</span>
257
+
<input
258
+
type="number"
259
+
min={0}
260
+
value={inputValue}
261
+
onChange={handleInputChange}
262
+
className="px-2 py-1 border rounded-md bg-item text-text w-full"
263
+
/>
264
+
</label>
265
+
266
+
<div className="gap-0.5 flex flex-col">
267
+
<p className="text-sm text-subtext">
268
+
How often to check Tailscale for new tailnet name offers.
269
+
</p>
270
+
<p className="text-sm text-subtext">
271
+
The minimum value is 5 seconds.
272
+
</p>
273
+
</div>
274
+
</div>
275
+
)}
276
+
</div>
277
+
278
+
<div
279
+
className={`transition-all duration-300 ${
280
+
activeTab === 'notifications'
281
+
? tabTransitioning
282
+
? 'opacity-0 -translate-x-4 pointer-events-none'
283
+
: 'opacity-100 translate-x-0'
284
+
: 'opacity-0 pointer-events-none'
285
+
} bg-header`}
286
+
aria-labelledby="notifications-tab"
287
+
role="tabpanel"
288
+
>
289
+
{activeTab === 'notifications' && (
290
+
<div className="flex flex-col gap-3">
291
+
<div className="flex items-center gap-2">
292
+
<input
293
+
id={systemPushId}
294
+
type="checkbox"
295
+
checked={useSystemPush}
296
+
onChange={handleSystemPushChange}
297
+
className="accent-accent w-4 h-4 cursor-pointer"
298
+
/>
299
+
<label
300
+
htmlFor={systemPushId}
301
+
className="text-sm text-text cursor-pointer"
302
+
>
303
+
Use system push notifications
304
+
</label>
305
+
</div>
306
+
307
+
<div className="flex items-center gap-2">
308
+
<input
309
+
id={ntfyPushId}
310
+
type="checkbox"
311
+
checked={useNtfyPush}
312
+
onChange={handleNtfyPushChange}
313
+
className="accent-accent w-4 h-4 cursor-pointer"
314
+
/>
315
+
<label
316
+
htmlFor={ntfyPushId}
317
+
className="text-sm text-text cursor-pointer"
318
+
>
319
+
Use ntfy push notifications
320
+
</label>
321
+
</div>
322
+
323
+
<p className="text-xs text-subtext">
324
+
You will {!useSystemPush && !useNtfyPush ? 'not ' : ''}
325
+
receive notifications{' '}
326
+
{useSystemPush && useNtfyPush
327
+
? 'via system & ntfy'
328
+
: useSystemPush
329
+
? 'via system'
330
+
: useNtfyPush
331
+
? 'via ntfy'
332
+
: ''}{' '}
333
+
for new tailnet offers.
334
+
</p>
335
+
336
+
<div className="flex flex-col gap-2">
337
+
<label
338
+
htmlFor={ntfyUrlId}
339
+
className={`text-sm font-medium ${
340
+
!useNtfyPush ? 'text-subtext' : ''
341
+
}`}
342
+
>
343
+
ntfy server URL
344
+
</label>
345
+
<input
346
+
id={ntfyUrlId}
347
+
type="text"
348
+
value={ntfyUrl}
349
+
onChange={handleNtfyUrlChange}
350
+
disabled={!useNtfyPush}
351
+
placeholder="https://ntfy.sh"
352
+
className={`px-2 py-1 border rounded-md bg-item text-text w-full transition-opacity ${
353
+
!useNtfyPush ? 'opacity-50 cursor-not-allowed' : ''
354
+
}`}
355
+
/>
356
+
<label
357
+
htmlFor={ntfyTopicId}
358
+
className={`text-sm font-medium ${
359
+
!useNtfyPush ? 'text-subtext' : ''
360
+
}`}
361
+
>
362
+
ntfy topic
363
+
</label>
364
+
<input
365
+
id={ntfyTopicId}
366
+
type="text"
367
+
value={ntfyTopic}
368
+
onChange={handleNtfyTopicChange}
369
+
disabled={!useNtfyPush}
370
+
placeholder="tailscale"
371
+
className={`px-2 py-1 border rounded-md bg-item text-text w-full transition-opacity ${
372
+
!useNtfyPush ? 'opacity-50 cursor-not-allowed' : ''
373
+
}`}
374
+
/>
375
+
<label
376
+
htmlFor={ntfyTokenId}
377
+
className={`text-sm font-medium ${
378
+
!useNtfyPush ? 'text-subtext' : ''
379
+
}`}
380
+
>
381
+
ntfy auth token (optional)
382
+
</label>
383
+
<input
384
+
id={ntfyTokenId}
385
+
type="password"
386
+
value={ntfyToken}
387
+
onChange={handleNtfyTokenChange}
388
+
disabled={!useNtfyPush}
389
+
placeholder="tk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
390
+
className={`px-2 py-1 border rounded-md bg-item text-text w-full transition-opacity ${
391
+
!useNtfyPush ? 'opacity-50 cursor-not-allowed' : ''
392
+
} [&::-ms-reveal]:invert`}
393
+
/>
394
+
<button
395
+
type="button"
396
+
disabled={
397
+
!useNtfyPush || !(ntfyUrl && ntfyTopic) || ntfyLoading
398
+
}
399
+
className={`px-3 py-2 rounded-lg text-xs bg-accent text-text font-medium mt-2 transition-opacity flex items-center justify-center gap-2 ${
400
+
!useNtfyPush || !(ntfyUrl && ntfyTopic) || ntfyLoading
401
+
? 'opacity-50 cursor-not-allowed'
402
+
: 'hover:opacity-80'
403
+
}`}
404
+
onClick={handleNtfyTest}
405
+
>
406
+
{ntfyLoading ? (
407
+
<div className="w-4 h-4 text-text border-2 border-gray-300 border-t-accent rounded-full animate-spin"></div>
408
+
) : (
409
+
'Send test notification'
410
+
)}
411
+
</button>
412
+
</div>
413
+
</div>
414
+
)}
415
+
</div>
416
+
</div>
417
+
418
+
<div className="flex gap-2 mt-4">
419
+
<button
420
+
className="px-4 py-2 rounded-lg bg-accent text-text font-medium hover:opacity-80 transition-opacity"
421
+
type="button"
422
+
onClick={handleClose}
423
+
>
424
+
Close
425
+
</button>
426
+
</div>
427
+
</div>
428
+
</div>
429
+
430
+
<AlertModal
431
+
showAlert={showAlert}
432
+
alertMessage={alertMessage}
433
+
alertModalRef={alertModalRef}
434
+
alertCloseBtnRef={alertCloseBtnRef}
435
+
handleAlertCloseModal={handleAlertCloseModal}
436
+
/>
437
+
</>
438
+
);
439
+
};
440
+
441
+
export default SettingsModal;
+181
entrypoints/popup/components/StatusControls.tsx
+181
entrypoints/popup/components/StatusControls.tsx
···
···
1
+
import { type FC, useEffect, useState } from 'react';
2
+
import { BiLoaderAlt, BiPlay, BiSolidCog, BiStop } from 'react-icons/bi';
3
+
import { MdOutlineWarning } from 'react-icons/md';
4
+
import SettingsModal from '$components/SettingsModal';
5
+
import { useNotificationSettings } from '$hooks/useNotificationSettings';
6
+
import { useSettings } from '$hooks/useSettings';
7
+
8
+
interface StatusControlsProps {
9
+
status: string;
10
+
timer: number | null;
11
+
handleStart: () => void;
12
+
handleStop: () => void;
13
+
tailnetNames: string[];
14
+
redirectToTailnetList: () => void;
15
+
}
16
+
17
+
const StatusControls: FC<StatusControlsProps> = ({
18
+
status,
19
+
timer,
20
+
handleStart,
21
+
handleStop,
22
+
tailnetNames,
23
+
redirectToTailnetList,
24
+
}) => {
25
+
const settings = useSettings(30);
26
+
const [showMessage, setShowMessage] = useState(false);
27
+
const [shouldRender, setShouldRender] = useState(false);
28
+
const [hasBeenToggled, setHasBeenToggled] = useState(false);
29
+
const [pendingAction, setPendingAction] = useState<'start' | null>(null);
30
+
const notificationsEnabled = useNotificationSettings();
31
+
32
+
// Only show loading if pendingAction is 'start' and status is not yet updated
33
+
const isLoading = pendingAction === 'start' && status === 'Stopped';
34
+
35
+
useEffect(() => {
36
+
let timer: NodeJS.Timeout;
37
+
38
+
if (status !== 'Stopped') {
39
+
setShouldRender(true);
40
+
timer = setTimeout(
41
+
() => {
42
+
setShowMessage(true);
43
+
},
44
+
hasBeenToggled ? 50 : 0,
45
+
);
46
+
// Clear pendingAction when UI is ready (status is running and message is shown)
47
+
if (pendingAction === 'start' && showMessage) setPendingAction(null);
48
+
} else {
49
+
setShowMessage(false);
50
+
timer = setTimeout(
51
+
() => {
52
+
setShouldRender(false);
53
+
},
54
+
hasBeenToggled ? 200 : 0,
55
+
);
56
+
// No pendingAction logic for stop
57
+
}
58
+
59
+
return () => clearTimeout(timer);
60
+
}, [status, hasBeenToggled, pendingAction, showMessage]);
61
+
62
+
const handleStartClick = async () => {
63
+
setHasBeenToggled(true);
64
+
setPendingAction('start');
65
+
await Promise.resolve(handleStart());
66
+
};
67
+
68
+
const handleStopClick = async () => {
69
+
await Promise.resolve(handleStop());
70
+
};
71
+
72
+
return (
73
+
<>
74
+
<div className="flex gap-3 pb-4 justify-center items-center">
75
+
{status === 'Stopped' ? (
76
+
<button
77
+
className="px-4 py-2 rounded-lg bg-accent flex flex-row items-center gap-1 text-text text-sm font-medium disabled:opacity-50 hover:opacity-80 transition-opacity disabled:cursor-not-allowed"
78
+
type="button"
79
+
onClick={handleStartClick}
80
+
disabled={tailnetNames.length === 0 || isLoading}
81
+
>
82
+
{isLoading ? (
83
+
<BiLoaderAlt
84
+
className="inline-block animate-spin"
85
+
size={20}
86
+
aria-hidden={true}
87
+
/>
88
+
) : (
89
+
<BiPlay className="inline-block" size={20} aria-hidden={true} />
90
+
)}
91
+
Start
92
+
</button>
93
+
) : (
94
+
<button
95
+
className="px-4 py-2 rounded-lg bg-danger flex flex-row items-center gap-1 text-text text-sm font-medium disabled:opacity-50 hover:opacity-80 transition-opacity disabled:cursor-not-allowed"
96
+
type="button"
97
+
onClick={handleStopClick}
98
+
>
99
+
<BiStop className="inline-block" size={20} aria-hidden={true} />
100
+
Stop
101
+
</button>
102
+
)}
103
+
<button
104
+
className="px-3 py-2 rounded-lg bg-item flex flex-row items-center gap-2 border border-border text-text text-sm font-medium hover:opacity-80 transition-opacity ml-2"
105
+
type="button"
106
+
aria-label="Settings"
107
+
onClick={settings.open}
108
+
>
109
+
<BiSolidCog className="inline-block" size={20} aria-hidden={true} />
110
+
Settings
111
+
</button>
112
+
</div>
113
+
114
+
<SettingsModal
115
+
visible={settings.visible}
116
+
shouldRender={settings.shouldRender}
117
+
interval={settings.interval}
118
+
setInterval={settings.setInterval}
119
+
onClose={settings.close}
120
+
/>
121
+
122
+
{tailnetNames.length === 0 && (
123
+
<div className="flex">
124
+
<button
125
+
className="w-full text-sm text-center border border-border text-text bg-item mx-4 rounded-md mb-4 py-4 items-center cursor-pointer hover:scale-105 transition-transform"
126
+
onClick={redirectToTailnetList}
127
+
aria-label="Go to Tailnet List"
128
+
type="button"
129
+
>
130
+
<p>
131
+
<MdOutlineWarning
132
+
className="inline-block text-warning mr-1"
133
+
size={20}
134
+
aria-hidden={true}
135
+
/>
136
+
You'll need to add at least one tailnet word in order to start
137
+
searching.
138
+
</p>
139
+
140
+
<p className="font-medium">Click here to start adding words.</p>
141
+
</button>
142
+
</div>
143
+
)}
144
+
145
+
{tailnetNames.length !== 0 && shouldRender && (
146
+
<div
147
+
className={`flex flex-col pb-4 ${
148
+
hasBeenToggled ? 'transition-opacity duration-500' : ''
149
+
} ${showMessage ? 'opacity-100' : 'opacity-0'}`}
150
+
>
151
+
<div className="flex flex-col gap-0.5 border text-sm border-border text-text bg-item mx-4 rounded-md p-4">
152
+
<p>
153
+
You can now leave this extension running in the background.{' '}
154
+
{notificationsEnabled ? (
155
+
"You'll receive a notification when a tailnet offer is found that matches one of your words."
156
+
) : (
157
+
<span className="text-warning font-semibold">
158
+
Notifications are currently disabled. You won't receive alerts
159
+
for new tailnet offers.
160
+
</span>
161
+
)}
162
+
</p>
163
+
164
+
<p className="text-subtext text-xs">
165
+
Next check in{' '}
166
+
{isLoading
167
+
? `${settings.interval} second${settings.interval === 1 ? '' : 's'}`
168
+
: timer !== null
169
+
? `${timer} second${timer === 1 ? '' : 's'}`
170
+
: `a few second${timer === 1 ? '' : 's'}`}
171
+
.
172
+
</p>
173
+
</div>
174
+
</div>
175
+
)}
176
+
177
+
</>
178
+
);
179
+
};
180
+
181
+
export default StatusControls;
+127
entrypoints/popup/components/TailnetInput.tsx
+127
entrypoints/popup/components/TailnetInput.tsx
···
···
1
+
import { type FC, useId } from 'react';
2
+
import { BiPlus } from 'react-icons/bi';
3
+
4
+
interface TailnetInputProps {
5
+
inputValue: string;
6
+
setInputValue: (val: string) => void;
7
+
isInputValid: string | boolean;
8
+
handleAddTailnet: () => void;
9
+
tails: string[];
10
+
scales: string[];
11
+
tailnetNames: string[];
12
+
loading: boolean;
13
+
}
14
+
15
+
const TailnetInput: FC<TailnetInputProps> = ({
16
+
inputValue,
17
+
setInputValue,
18
+
isInputValid,
19
+
handleAddTailnet,
20
+
tails,
21
+
scales,
22
+
tailnetNames,
23
+
loading,
24
+
}) => {
25
+
const datalistId = useId();
26
+
27
+
const words = [...tails, ...scales];
28
+
29
+
let comboSuggestions: string[] = [];
30
+
if (inputValue.includes('-')) {
31
+
const [tailPrefix, scalePrefix] = inputValue.split('-');
32
+
comboSuggestions = tails
33
+
.filter((tail) => tail.startsWith(tailPrefix))
34
+
.flatMap((tail) =>
35
+
scales
36
+
.filter((scale) => scale.startsWith(scalePrefix || ''))
37
+
.map((scale) => `${tail}-${scale}`),
38
+
)
39
+
.filter((combo) => !tailnetNames.includes(combo));
40
+
}
41
+
42
+
const isWordUsed = (word: string) =>
43
+
tailnetNames.some(
44
+
(name) =>
45
+
name === word ||
46
+
name.split('-')[0] === word ||
47
+
name.split('-')[1] === word,
48
+
);
49
+
50
+
const singleWordSuggestions =
51
+
!inputValue.includes('-') && inputValue.length > 0
52
+
? Array.from(
53
+
new Set(
54
+
words.filter(
55
+
(word) => !isWordUsed(word) && word.startsWith(inputValue),
56
+
),
57
+
),
58
+
)
59
+
: [];
60
+
61
+
return (
62
+
<div className="flex flex-col mb-2">
63
+
<div className="flex flex-row w-full gap-2">
64
+
<input
65
+
type="text"
66
+
className="w-full border border-border rounded-lg px-3 py-1.5 bg-input text-base text-text focus:border-accent focus:outline-none disabled:cursor-not-allowed"
67
+
placeholder="Enter a tailnet word (bunny) or combo (miku-kitchen)..."
68
+
value={inputValue}
69
+
onChange={(e) => setInputValue(e.target.value)}
70
+
onKeyDown={(e) => {
71
+
if (e.key === 'Enter' && !!isInputValid) {
72
+
handleAddTailnet();
73
+
}
74
+
}}
75
+
list={datalistId}
76
+
disabled={loading}
77
+
/>
78
+
79
+
<button
80
+
className="flex flex-row items-center px-3 gap-1 rounded-lg bg-accent text-text font-medium disabled:opacity-50 hover:opacity-80 disabled:cursor-not-allowed"
81
+
type="button"
82
+
onClick={handleAddTailnet}
83
+
disabled={!isInputValid || isInputValid === ''}
84
+
>
85
+
<BiPlus className="inline-block" size={20} aria-hidden={true} />
86
+
Add
87
+
</button>
88
+
</div>
89
+
90
+
<datalist id={datalistId}>
91
+
{singleWordSuggestions.map((word: string) => (
92
+
<option key={word} value={word} />
93
+
))}
94
+
95
+
{comboSuggestions.map((combo) => (
96
+
<option key={combo} value={combo} />
97
+
))}
98
+
</datalist>
99
+
100
+
{inputValue.includes('-') && comboSuggestions.length === 0 && (
101
+
<div className="w-full text-center border border-border text-text bg-item rounded-md mt-3 py-4 items-center font-medium">
102
+
<p>⚠️ No valid combos found for your input.</p>
103
+
104
+
<p>
105
+
The combo must match the format of <tail>-<scale> (e.g.
106
+
miku-kitchen).
107
+
</p>
108
+
</div>
109
+
)}
110
+
111
+
{!inputValue.includes('-') &&
112
+
inputValue.length > 0 &&
113
+
singleWordSuggestions.length === 0 && (
114
+
<div className="w-full text-center border border-border text-text bg-item rounded-md mt-3 py-4 items-center font-medium ">
115
+
<p>⚠️ No valid single words were found for your input.</p>
116
+
117
+
<p>
118
+
Either the matching words have already been added or they do not
119
+
exist.
120
+
</p>
121
+
</div>
122
+
)}
123
+
</div>
124
+
);
125
+
};
126
+
127
+
export default TailnetInput;
+38
entrypoints/popup/components/TailnetList.tsx
+38
entrypoints/popup/components/TailnetList.tsx
···
···
1
+
import type { FC } from 'react';
2
+
import { BiX } from 'react-icons/bi';
3
+
4
+
interface TailnetListProps {
5
+
tailnetNames: string[];
6
+
handleRemoveTailnet: (name: string) => void;
7
+
}
8
+
9
+
const TailnetList: FC<TailnetListProps> = ({
10
+
tailnetNames,
11
+
handleRemoveTailnet,
12
+
}) => {
13
+
if (tailnetNames.length === 0) return null;
14
+
return (
15
+
<div className="bg-header border border-border rounded-lg p-4 mt-4 max-h-[23em] overflow-y-auto [&::-webkit-scrollbar]:[width:0.5em] [&::-webkit-scrollbar-thumb]:bg-subtext">
16
+
<div className="flex flex-wrap gap-2">
17
+
{tailnetNames.map((name: string) => (
18
+
<span
19
+
key={name}
20
+
className="flex items-center gap-2.5 bg-item text-text rounded-md px-3 py-1 text-base font-medium shadow-sm border border-border"
21
+
>
22
+
{name}
23
+
<button
24
+
className="text-lg text-subtext transition-colors hover:rounded-lg hover:bg-danger hover:text-text rounded-full flex"
25
+
type="button"
26
+
onClick={() => handleRemoveTailnet(name)}
27
+
aria-label={`Remove ${name}`}
28
+
>
29
+
<BiX size={20} />
30
+
</button>
31
+
</span>
32
+
))}
33
+
</div>
34
+
</div>
35
+
);
36
+
};
37
+
38
+
export default TailnetList;
+115
entrypoints/popup/components/TokenList.tsx
+115
entrypoints/popup/components/TokenList.tsx
···
···
1
+
import type { FC } from 'react';
2
+
import { MdCheck, MdDelete, MdOutlineWarning } from 'react-icons/md';
3
+
import { extractTailnetNameFromToken } from '$helpers/tokens';
4
+
import type { Token } from '$types/tokens';
5
+
6
+
interface TokenListProps {
7
+
tokens: Token[];
8
+
handleClaimToken: (tokenObj: Token) => Promise<void>;
9
+
handleRemoveToken: (tokenObj: Token) => void;
10
+
loading: boolean;
11
+
error: string | null;
12
+
}
13
+
14
+
const TokenList: FC<TokenListProps> = ({
15
+
tokens,
16
+
handleClaimToken,
17
+
handleRemoveToken,
18
+
loading,
19
+
error,
20
+
}) => {
21
+
return (
22
+
<div>
23
+
<div className="bg-item flex flex-row items-center gap-1 text-sm border-border border px-3 py-3 rounded-lg text-text mb-3">
24
+
<MdOutlineWarning
25
+
className="text-warning"
26
+
size={20}
27
+
aria-hidden={true}
28
+
/>
29
+
Each tailnet offer only last 5 minutes. Make sure to use them quickly!
30
+
</div>
31
+
32
+
<div className="bg-header border border-border text-text rounded-lg p-4 max-h-[25rem] overflow-y-auto [&::-webkit-scrollbar]:[width:0.5em] [&::-webkit-scrollbar-thumb]:bg-subtext">
33
+
<ul className="flex flex-col gap-4">
34
+
{tokens.length > 0 ? (
35
+
tokens.map((tokenObj, idx) => {
36
+
const expiresAt = tokenObj.timestamp
37
+
? tokenObj.timestamp + 5 * 60 * 1000
38
+
: null;
39
+
const timeLeft = expiresAt ? expiresAt - Date.now() : null;
40
+
let expireColor = 'bg-header border-border';
41
+
if (timeLeft !== null) {
42
+
if (timeLeft < 60 * 1000)
43
+
expireColor = 'bg-danger border-danger';
44
+
else if (timeLeft < 2 * 60 * 1000)
45
+
expireColor = 'bg-warning text-header border-warning';
46
+
}
47
+
return (
48
+
<li
49
+
key={`${tokenObj.token}-${idx}`}
50
+
className="flex flex-row items-center justify-between gap-4 px-3 py-2.5 rounded-lg border border-border bg-item"
51
+
>
52
+
<div className="flex flex-col gap-1">
53
+
<span className="font-semibold text-base text-text truncate">
54
+
{extractTailnetNameFromToken(tokenObj.token)}
55
+
</span>
56
+
57
+
{expiresAt &&
58
+
(Date.now() > expiresAt ? (
59
+
<span className="px-2 py-1 rounded-lg text-xs font-medium bg-danger text-text border border-danger w-fit">
60
+
Token expired
61
+
</span>
62
+
) : (
63
+
<span
64
+
className={`px-2 py-1 rounded-lg text-xs font-medium ${expireColor} border w-fit`}
65
+
>
66
+
Expires at{' '}
67
+
{new Date(expiresAt).toLocaleTimeString([], {
68
+
hour: '2-digit',
69
+
minute: '2-digit',
70
+
second: '2-digit',
71
+
})}
72
+
</span>
73
+
))}
74
+
</div>
75
+
<div className="flex flex-row gap-2 items-center">
76
+
<button
77
+
className={`px-3 py-2 rounded-lg bg-accent text-text text-sm font-semibold transition flex items-center gap-1 ${loading || Boolean(expiresAt && Date.now() > expiresAt) ? 'opacity-50 cursor-not-allowed' : 'hover:opacity-80 transition-opacity'}`}
78
+
type="button"
79
+
onClick={() => handleClaimToken(tokenObj)}
80
+
disabled={
81
+
loading || Boolean(expiresAt && Date.now() > expiresAt)
82
+
}
83
+
aria-label="Claim token"
84
+
>
85
+
<MdCheck size={20} />
86
+
<span>Claim</span>
87
+
</button>
88
+
<button
89
+
className="px-3 py-2 rounded-lg bg-danger text-text text-sm font-semibold hover:opacity-80 transition-opacity flex items-center gap-1"
90
+
type="button"
91
+
onClick={() => handleRemoveToken(tokenObj)}
92
+
disabled={loading}
93
+
aria-label="Remove token"
94
+
>
95
+
<MdDelete size={20} />
96
+
<span>Remove</span>
97
+
</button>
98
+
</div>
99
+
</li>
100
+
);
101
+
})
102
+
) : (
103
+
<li className="text-subtext text-center py-4">
104
+
No matching offers found yet.
105
+
</li>
106
+
)}
107
+
</ul>
108
+
</div>
109
+
110
+
{error && <div className="text-danger mt-2">{error}</div>}
111
+
</div>
112
+
);
113
+
};
114
+
115
+
export default TokenList;
+37
entrypoints/popup/hooks/useEligibility.ts
+37
entrypoints/popup/hooks/useEligibility.ts
···
···
1
+
import { useEffect, useState } from 'react';
2
+
import { checkEligibility } from '$helpers/eligibility';
3
+
import type { Message } from '$types/messages';
4
+
5
+
export const useEligibility = () => {
6
+
const [eligibility, setEligibility] = useState<{
7
+
eligible: boolean;
8
+
reason: string;
9
+
id?: string;
10
+
} | null>(null);
11
+
const [eligibilityLoading, setEligibilityLoading] = useState(true);
12
+
const [loading, setLoading] = useState(true);
13
+
14
+
useEffect(() => {
15
+
setEligibilityLoading(true);
16
+
checkEligibility().then((result) => {
17
+
setEligibility(result);
18
+
setEligibilityLoading(false);
19
+
setLoading(false);
20
+
});
21
+
22
+
const listener = (message: Message) => {
23
+
if (message.action === 'refreshEligibility') {
24
+
setEligibilityLoading(true);
25
+
checkEligibility().then((result) => {
26
+
setEligibility(result);
27
+
setEligibilityLoading(false);
28
+
setLoading(false);
29
+
});
30
+
}
31
+
};
32
+
browser.runtime.onMessage.addListener(listener);
33
+
return () => browser.runtime.onMessage.removeListener(listener);
34
+
}, []);
35
+
36
+
return { eligibility, eligibilityLoading, loading, setLoading };
37
+
};
+25
entrypoints/popup/hooks/useInputValidation.ts
+25
entrypoints/popup/hooks/useInputValidation.ts
···
···
1
+
import { useMemo } from 'react';
2
+
3
+
const isCombo = (val: string, tails: string[], scales: string[]) => {
4
+
const parts = val.split('-');
5
+
return (
6
+
parts.length === 2 && tails.includes(parts[0]) && scales.includes(parts[1])
7
+
);
8
+
};
9
+
10
+
export const useInputValidation = (
11
+
inputValue: string,
12
+
tails: string[],
13
+
scales: string[],
14
+
tailnetNames: string[],
15
+
) => {
16
+
return useMemo(
17
+
() =>
18
+
inputValue &&
19
+
(tails.includes(inputValue) ||
20
+
scales.includes(inputValue) ||
21
+
isCombo(inputValue, tails, scales)) &&
22
+
!tailnetNames.includes(inputValue),
23
+
[inputValue, tails, scales, tailnetNames],
24
+
);
25
+
};
+86
entrypoints/popup/hooks/useModal.ts
+86
entrypoints/popup/hooks/useModal.ts
···
···
1
+
import { useCallback, useEffect, useRef } from 'react';
2
+
import type { RuntimeMessage } from '$types/messages';
3
+
import type { Token } from '$types/tokens';
4
+
5
+
export const useModal = (
6
+
showAlert: boolean,
7
+
setShowAlert: (v: boolean) => void,
8
+
setAlertMessage: (v: string) => void,
9
+
claimedToken: string | null,
10
+
setClaimedToken: (v: string | null) => void,
11
+
setTokens: (fn: (prevTokens: Token[]) => Token[]) => void,
12
+
setTailnetNames: (v: string[]) => void,
13
+
) => {
14
+
const alertModalRef = useRef<HTMLDivElement>(null);
15
+
const alertCloseBtnRef = useRef<HTMLButtonElement>(null);
16
+
17
+
const handleAlertCloseModal = useCallback(() => {
18
+
setShowAlert(false);
19
+
setTimeout(() => {
20
+
setAlertMessage('');
21
+
}, 300);
22
+
browser.runtime.sendMessage({ action: 'userAcknowledgedRedirect' });
23
+
if (claimedToken) {
24
+
setTokens((prevTokens: Token[]) =>
25
+
prevTokens.filter((t) => t.token !== claimedToken),
26
+
);
27
+
browser.storage.local.get(['tailscaleTokens'], (result) => {
28
+
const updatedTokens = (result.tailscaleTokens || []).filter(
29
+
(t: Token) => t.token !== claimedToken,
30
+
);
31
+
browser.storage.local.set({ tailscaleTokens: updatedTokens }, () => {
32
+
browser.runtime.sendMessage({ action: 'updateBadge' });
33
+
});
34
+
});
35
+
browser.storage.local.get(['tailnetNames'], (result) => {
36
+
const tailnetName = claimedToken.split('.')[0];
37
+
const updatedNames = (result.tailnetNames || []).filter(
38
+
(n: string) => n !== tailnetName,
39
+
);
40
+
setTailnetNames(updatedNames);
41
+
browser.storage.local.set({ tailnetNames: updatedNames });
42
+
});
43
+
setClaimedToken(null);
44
+
}
45
+
}, [
46
+
setShowAlert,
47
+
setAlertMessage,
48
+
claimedToken,
49
+
setTokens,
50
+
setTailnetNames,
51
+
setClaimedToken,
52
+
]);
53
+
54
+
useEffect(() => {
55
+
if (showAlert && alertModalRef.current && alertCloseBtnRef.current) {
56
+
const focusableEls = [alertCloseBtnRef.current];
57
+
const handleKeyDown = (e: KeyboardEvent) => {
58
+
if (e.key === 'Tab') {
59
+
e.preventDefault();
60
+
focusableEls[0].focus();
61
+
}
62
+
if (e.key === 'Escape') {
63
+
handleAlertCloseModal();
64
+
}
65
+
};
66
+
alertModalRef.current.focus();
67
+
alertModalRef.current.addEventListener('keydown', handleKeyDown);
68
+
return () => {
69
+
alertModalRef.current?.removeEventListener('keydown', handleKeyDown);
70
+
};
71
+
}
72
+
}, [showAlert, handleAlertCloseModal]);
73
+
74
+
useEffect(() => {
75
+
const listener = (message: RuntimeMessage<{ message?: string }>) => {
76
+
if (message.action === 'showCustomAlert' && message.message) {
77
+
setAlertMessage(message.message);
78
+
setShowAlert(true);
79
+
}
80
+
};
81
+
browser.runtime.onMessage.addListener(listener);
82
+
return () => browser.runtime.onMessage.removeListener(listener);
83
+
}, [setAlertMessage, setShowAlert]);
84
+
85
+
return { alertModalRef, alertCloseBtnRef, handleAlertCloseModal };
86
+
};
+39
entrypoints/popup/hooks/useNotificationSettings.ts
+39
entrypoints/popup/hooks/useNotificationSettings.ts
···
···
1
+
import { useCallback, useEffect, useState } from 'react';
2
+
3
+
export const useNotificationSettings = () => {
4
+
const [notificationsEnabled, setNotificationsEnabled] = useState<
5
+
boolean | null
6
+
>(null);
7
+
8
+
const fetchNotificationSettings = useCallback(() => {
9
+
browser.storage.local.get(['useSystemPush', 'useNtfyPush'], (result) => {
10
+
const enabled =
11
+
Boolean(result.useSystemPush) || Boolean(result.useNtfyPush);
12
+
setNotificationsEnabled(enabled);
13
+
});
14
+
}, []);
15
+
16
+
useEffect(() => {
17
+
fetchNotificationSettings();
18
+
}, [fetchNotificationSettings]);
19
+
20
+
useEffect(() => {
21
+
const handleStorageChange = (
22
+
changes: Record<string, Browser.storage.StorageChange>,
23
+
areaName: string,
24
+
) => {
25
+
if (
26
+
areaName === 'local' &&
27
+
('useSystemPush' in changes || 'useNtfyPush' in changes)
28
+
) {
29
+
fetchNotificationSettings();
30
+
}
31
+
};
32
+
browser.storage.onChanged.addListener(handleStorageChange);
33
+
return () => {
34
+
browser.storage.onChanged.removeListener(handleStorageChange);
35
+
};
36
+
}, [fetchNotificationSettings]);
37
+
38
+
return notificationsEnabled;
39
+
};
+71
entrypoints/popup/hooks/useSettings.ts
+71
entrypoints/popup/hooks/useSettings.ts
···
···
1
+
import { useEffect, useRef, useState } from 'react';
2
+
3
+
export const useSettings = (defaultInterval: number) => {
4
+
const [show, setShow] = useState(false);
5
+
const [visible, setVisible] = useState(false);
6
+
const [shouldRender, setShouldRender] = useState(false);
7
+
const [interval, setInterval] = useState(defaultInterval);
8
+
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
9
+
10
+
useEffect(() => {
11
+
browser.storage.local.get(['checkInterval'], (result) => {
12
+
if (result.checkInterval) setInterval(result.checkInterval);
13
+
});
14
+
}, []);
15
+
16
+
useEffect(() => {
17
+
if (timeoutRef.current) {
18
+
clearTimeout(timeoutRef.current);
19
+
timeoutRef.current = null;
20
+
}
21
+
if (show) {
22
+
setShouldRender(true);
23
+
timeoutRef.current = setTimeout(() => setVisible(true), 10);
24
+
} else if (visible) {
25
+
setVisible(false);
26
+
timeoutRef.current = setTimeout(() => setShouldRender(false), 300);
27
+
} else {
28
+
setShouldRender(false);
29
+
}
30
+
return () => {
31
+
if (timeoutRef.current) {
32
+
clearTimeout(timeoutRef.current);
33
+
timeoutRef.current = null;
34
+
}
35
+
};
36
+
}, [show, visible]);
37
+
38
+
const open = () => {
39
+
if (timeoutRef.current) {
40
+
clearTimeout(timeoutRef.current);
41
+
timeoutRef.current = null;
42
+
}
43
+
setShow(true);
44
+
};
45
+
const close = () => {
46
+
if (timeoutRef.current) {
47
+
clearTimeout(timeoutRef.current);
48
+
timeoutRef.current = null;
49
+
}
50
+
setShow(false);
51
+
};
52
+
const handleIntervalChange = (val: number) => {
53
+
const safeVal = Math.max(5, val);
54
+
setInterval(safeVal);
55
+
browser.storage.local.set({ checkInterval: safeVal });
56
+
browser.runtime.sendMessage({
57
+
action: 'setAlarmInterval',
58
+
interval: safeVal,
59
+
});
60
+
};
61
+
62
+
return {
63
+
show,
64
+
visible,
65
+
shouldRender,
66
+
interval,
67
+
setInterval: handleIntervalChange,
68
+
open,
69
+
close,
70
+
};
71
+
};
+19
entrypoints/popup/hooks/useStatus.ts
+19
entrypoints/popup/hooks/useStatus.ts
···
···
1
+
import { useCallback } from 'react';
2
+
3
+
export const useStatus = (
4
+
setStatus: (status: 'Running' | 'Stopped') => void,
5
+
) => {
6
+
const handleStart = useCallback(() => {
7
+
browser.runtime.sendMessage({ action: 'startCheck' }, (response) => {
8
+
setStatus(response?.status || 'Running');
9
+
});
10
+
}, [setStatus]);
11
+
12
+
const handleStop = useCallback(() => {
13
+
browser.runtime.sendMessage({ action: 'stopCheck' }, (response) => {
14
+
setStatus(response?.status || 'Stopped');
15
+
});
16
+
}, [setStatus]);
17
+
18
+
return { handleStart, handleStop };
19
+
};
+68
entrypoints/popup/hooks/useTailnetNames.ts
+68
entrypoints/popup/hooks/useTailnetNames.ts
···
···
1
+
import { useCallback, useEffect, useState } from 'react';
2
+
import { getTailnetNames } from '$helpers/tailnet';
3
+
4
+
const isCombo = (val: string, tails: string[], scales: string[]) => {
5
+
const parts = val.split('-');
6
+
return (
7
+
parts.length === 2 && tails.includes(parts[0]) && scales.includes(parts[1])
8
+
);
9
+
};
10
+
11
+
export const useTailnetNames = (
12
+
tails: string[],
13
+
scales: string[],
14
+
setStatus: (status: 'Running' | 'Stopped') => void,
15
+
inputValue: string,
16
+
setInputValue: (v: string) => void,
17
+
) => {
18
+
const [tailnetNames, setTailnetNames] = useState<string[]>([]);
19
+
20
+
useEffect(() => {
21
+
getTailnetNames().then(setTailnetNames);
22
+
}, []);
23
+
24
+
const isValid = useCallback(
25
+
(val: string) => {
26
+
return (
27
+
(val && tails.includes(val)) ||
28
+
(val && scales.includes(val)) ||
29
+
isCombo(val, tails, scales)
30
+
);
31
+
},
32
+
[tails, scales],
33
+
);
34
+
35
+
const handleAddTailnet = useCallback(() => {
36
+
if (
37
+
inputValue &&
38
+
isValid(inputValue) &&
39
+
!tailnetNames.includes(inputValue)
40
+
) {
41
+
const updated = [...tailnetNames, inputValue];
42
+
setTailnetNames(updated);
43
+
browser.storage.local.set({ tailnetNames: updated });
44
+
setInputValue('');
45
+
}
46
+
}, [inputValue, tailnetNames, setInputValue, isValid]);
47
+
48
+
const handleRemoveTailnet = useCallback(
49
+
(name: string) => {
50
+
const updated = tailnetNames.filter((n) => n !== name);
51
+
setTailnetNames(updated);
52
+
browser.storage.local.set({ tailnetNames: updated });
53
+
if (updated.length === 0) {
54
+
browser.runtime.sendMessage({ action: 'stopCheck' }, (response) => {
55
+
setStatus(response?.status || 'Stopped');
56
+
});
57
+
}
58
+
},
59
+
[tailnetNames, setStatus],
60
+
);
61
+
62
+
return {
63
+
tailnetNames,
64
+
setTailnetNames,
65
+
handleAddTailnet,
66
+
handleRemoveTailnet,
67
+
};
68
+
};
+35
entrypoints/popup/hooks/useTimer.ts
+35
entrypoints/popup/hooks/useTimer.ts
···
···
1
+
import { useCallback, useEffect, useState } from 'react';
2
+
3
+
export const useTimer = () => {
4
+
const [status, setStatus] = useState<'Running' | 'Stopped'>('Stopped');
5
+
const [timer, setTimer] = useState<number | null>(null);
6
+
const [loading, setLoading] = useState(true);
7
+
8
+
const fetchAndSetTimer = useCallback(() => {
9
+
browser.runtime.sendMessage({ action: 'getNextAlarm' }, (resp) => {
10
+
setTimer(resp?.timeRemaining ?? null);
11
+
});
12
+
}, []);
13
+
14
+
useEffect(() => {
15
+
browser.runtime.sendMessage({ action: 'getStatus' }, (response) => {
16
+
setStatus(response?.status || 'Stopped');
17
+
setLoading(false);
18
+
if (response?.status === 'Running') {
19
+
fetchAndSetTimer();
20
+
}
21
+
});
22
+
}, [fetchAndSetTimer]);
23
+
24
+
useEffect(() => {
25
+
if (status === 'Running') {
26
+
fetchAndSetTimer(); // Ensure timer is set immediately on status change
27
+
const interval = setInterval(fetchAndSetTimer, 1000);
28
+
return () => clearInterval(interval);
29
+
} else {
30
+
setTimer(null);
31
+
}
32
+
}, [status, fetchAndSetTimer]);
33
+
34
+
return { status, setStatus, timer, loading, setLoading };
35
+
};
+123
entrypoints/popup/hooks/useTokens.ts
+123
entrypoints/popup/hooks/useTokens.ts
···
···
1
+
import { useEffect, useState } from 'react';
2
+
import {
3
+
extractTailnetNameFromToken,
4
+
filterValidTokens,
5
+
} from '$helpers/tokens';
6
+
import type { RuntimeMessage } from '$types/messages';
7
+
import type { Token } from '$types/tokens';
8
+
9
+
interface UseTokensOptions {
10
+
setLoading?: (v: boolean) => void;
11
+
setAlertMessage?: (v: string) => void;
12
+
setShowAlert?: (v: boolean) => void;
13
+
setClaimedToken?: (v: string) => void;
14
+
}
15
+
16
+
// Custom hook for managing tokens and claiming tokens
17
+
export const useTokens = ({
18
+
setLoading,
19
+
setAlertMessage,
20
+
setShowAlert,
21
+
setClaimedToken,
22
+
}: UseTokensOptions = {}) => {
23
+
const [tokens, setTokens] = useState<Token[]>([]);
24
+
const [error, setError] = useState<string | null>(null);
25
+
26
+
// Load and check for expired tokens once on mount
27
+
useEffect(() => {
28
+
const checkExpiredTokens = () => {
29
+
browser.storage.local.get(['tailscaleTokens'], (result) => {
30
+
const now = Date.now();
31
+
32
+
const validTokens: Token[] = filterValidTokens(
33
+
result.tailscaleTokens || [],
34
+
now,
35
+
);
36
+
37
+
if (validTokens.length !== (result.tailscaleTokens || []).length) {
38
+
browser.storage.local.set({ tailscaleTokens: validTokens });
39
+
}
40
+
41
+
setTokens(validTokens);
42
+
});
43
+
};
44
+
45
+
// Check immediately on mount
46
+
checkExpiredTokens();
47
+
}, []);
48
+
49
+
useEffect(() => {
50
+
const listener = (message: RuntimeMessage<{ tokens?: Token[] }>) => {
51
+
if (message.action === 'tokensUpdated') {
52
+
const validTokens: Token[] = filterValidTokens(message.tokens || []);
53
+
setTokens(validTokens);
54
+
}
55
+
};
56
+
browser.runtime.onMessage.addListener(listener);
57
+
return () => browser.runtime.onMessage.removeListener(listener);
58
+
}, []);
59
+
60
+
// Claim token logic
61
+
const handleClaimToken = async (tokenObj: Token) => {
62
+
if (setLoading) setLoading(true);
63
+
setError(null);
64
+
65
+
try {
66
+
const response = await browser.runtime.sendMessage({
67
+
action: 'claimToken',
68
+
tcd: tokenObj.tcd || '',
69
+
token: tokenObj.token,
70
+
});
71
+
if (response?.redirected) {
72
+
// DNS page opened, do not show error or alert
73
+
return;
74
+
}
75
+
76
+
if (response?.error) {
77
+
console.error(`[useTokens] Error claiming token:`, response.error);
78
+
if (setAlertMessage && setShowAlert) {
79
+
setAlertMessage(response.error);
80
+
setShowAlert(true);
81
+
} else {
82
+
setError(response.error);
83
+
}
84
+
return;
85
+
}
86
+
87
+
if (setAlertMessage && setShowAlert && setClaimedToken) {
88
+
setAlertMessage(
89
+
`Offer claimed! Your tailnet name is now ${extractTailnetNameFromToken(
90
+
tokenObj.token,
91
+
)}.\nYou may need to refresh Tailscale to see the changes take effect.`,
92
+
);
93
+
setShowAlert(true);
94
+
setClaimedToken(tokenObj.token);
95
+
return;
96
+
}
97
+
} catch (e) {
98
+
const errorMsg = e instanceof Error ? e.message : 'Unknown error';
99
+
100
+
if (setAlertMessage && setShowAlert) {
101
+
setAlertMessage(`Error claiming token: ${errorMsg}`);
102
+
setShowAlert(true);
103
+
} else {
104
+
setError(errorMsg);
105
+
}
106
+
} finally {
107
+
if (setLoading) setLoading(false);
108
+
}
109
+
};
110
+
111
+
// Remove token logic
112
+
const handleRemoveToken = (tokenObj: Token) => {
113
+
const updatedTokens = tokens.filter((t) => t.token !== tokenObj.token);
114
+
setTokens(updatedTokens);
115
+
browser.storage.local.set({ tailscaleTokens: updatedTokens });
116
+
};
117
+
118
+
return [
119
+
tokens,
120
+
setTokens,
121
+
{ handleClaimToken, handleRemoveToken, error },
122
+
] as const;
123
+
};
+16
entrypoints/popup/hooks/useWords.ts
+16
entrypoints/popup/hooks/useWords.ts
···
···
1
+
import { useEffect, useState } from 'react';
2
+
import { fetchAndCacheWords } from '$helpers/words';
3
+
4
+
// Custom hook for fetching and caching tailnet words
5
+
export const useWords = () => {
6
+
const [tails, setTails] = useState<string[]>([]);
7
+
const [scales, setScales] = useState<string[]>([]);
8
+
useEffect(() => {
9
+
fetchAndCacheWords().then(({ tails, scales }) => {
10
+
setTails(tails);
11
+
setScales(scales);
12
+
});
13
+
}, []);
14
+
15
+
return { tails, scales };
16
+
};
+3
entrypoints/popup/index.css
+3
entrypoints/popup/index.css
+13
entrypoints/popup/index.html
+13
entrypoints/popup/index.html
···
···
1
+
<!doctype html>
2
+
<html lang="en">
3
+
<head>
4
+
<meta charset="UTF-8" />
5
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+
<title>tailname</title>
7
+
<meta name="manifest.type" content="browser_action" />
8
+
</head>
9
+
<body>
10
+
<div id="root"></div>
11
+
<script type="module" src="./main.tsx"></script>
12
+
</body>
13
+
</html>
+11
entrypoints/popup/main.tsx
+11
entrypoints/popup/main.tsx
···
···
1
+
import { StrictMode } from 'react';
2
+
import { createRoot } from 'react-dom/client';
3
+
import App from './App';
4
+
import './index.css';
5
+
6
+
// biome-ignore lint/style/noNonNullAssertion: Known non-null.
7
+
createRoot(document.getElementById('root')!).render(
8
+
<StrictMode>
9
+
<App />
10
+
</StrictMode>,
11
+
);
+65
entrypoints/popup/screens/MainScreen.tsx
+65
entrypoints/popup/screens/MainScreen.tsx
···
···
1
+
import type { FC } from 'react';
2
+
import { BiListPlus, BiSolidKey } from 'react-icons/bi';
3
+
import ActionCard from '$components/ActionCard';
4
+
import HeaderSection from '$components/HeaderSection';
5
+
import StatusControls from '$components/StatusControls';
6
+
import type { Token } from '$types/tokens';
7
+
8
+
interface MainScreenProps {
9
+
onShowWords: () => void;
10
+
onShowTokens: () => void;
11
+
status: string;
12
+
timer: number | null;
13
+
handleStart: () => void;
14
+
handleStop: () => void;
15
+
tailnetNames: string[];
16
+
tokens: Token[];
17
+
}
18
+
19
+
const MainScreen: FC<MainScreenProps> = ({
20
+
onShowWords,
21
+
onShowTokens,
22
+
status,
23
+
timer,
24
+
handleStart,
25
+
handleStop,
26
+
tailnetNames,
27
+
tokens,
28
+
}) => (
29
+
<main className="rounded-none shadow-lg p-6 w-full h-full">
30
+
<HeaderSection />
31
+
32
+
<StatusControls
33
+
handleStart={handleStart}
34
+
handleStop={handleStop}
35
+
status={status}
36
+
tailnetNames={tailnetNames}
37
+
timer={timer}
38
+
redirectToTailnetList={onShowWords}
39
+
/>
40
+
41
+
<div className="flex flex-col items-center justify-center gap-6">
42
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 w-full px-4">
43
+
<ActionCard
44
+
icon={
45
+
<BiListPlus className="text-text" size={40} aria-hidden={true} />
46
+
}
47
+
title="Manage tailnet words"
48
+
subtitle="Search for specific tailnet names with keywords"
49
+
onClick={onShowWords}
50
+
/>
51
+
52
+
<ActionCard
53
+
icon={
54
+
<BiSolidKey className="text-text" size={40} aria-hidden={true} />
55
+
}
56
+
title={`View tailnet offers ${tokens && tokens.length > 0 ? `(${tokens.length})` : ''}`}
57
+
subtitle="Claim offers to use as custom tailnet names"
58
+
onClick={onShowTokens}
59
+
/>
60
+
</div>
61
+
</div>
62
+
</main>
63
+
);
64
+
65
+
export default MainScreen;
+83
entrypoints/popup/screens/TailnetWords.tsx
+83
entrypoints/popup/screens/TailnetWords.tsx
···
···
1
+
import type { FC } from 'react';
2
+
import { BiArrowBack, BiInfoCircle, BiListPlus } from 'react-icons/bi';
3
+
import HelperText from '$components/HelperText';
4
+
import TailnetInput from '$components/TailnetInput';
5
+
import TailnetList from '$components/TailnetList';
6
+
7
+
interface TailnetWordsScreenProps {
8
+
tailnetNames: string[];
9
+
handleAddTailnet: () => void;
10
+
handleRemoveTailnet: (name: string) => void;
11
+
inputValue: string;
12
+
setInputValue: (val: string) => void;
13
+
isInputValid: boolean;
14
+
loading: boolean;
15
+
onBack: () => void;
16
+
error: string | null;
17
+
tails: string[];
18
+
scales: string[];
19
+
onShowWordList: () => void;
20
+
}
21
+
22
+
const TailnetWordsScreen: FC<TailnetWordsScreenProps> = ({
23
+
tailnetNames,
24
+
handleAddTailnet,
25
+
handleRemoveTailnet,
26
+
inputValue,
27
+
setInputValue,
28
+
isInputValid,
29
+
loading,
30
+
onBack,
31
+
error,
32
+
tails,
33
+
scales,
34
+
onShowWordList,
35
+
}) => (
36
+
<main className="rounded-none shadow-lg p-6 w-full h-full">
37
+
<div className="flex justify-between mb-4 items-center">
38
+
<button
39
+
className="text-sm px-3 py-2 rounded-lg flex flex-row items-center bg-item border border-border text-text font-medium hover:bg-accent transition-colors hover:border-accent"
40
+
type="button"
41
+
onClick={onBack}
42
+
>
43
+
<BiArrowBack
44
+
className="inline-block mr-1"
45
+
size={20}
46
+
aria-hidden={true}
47
+
/>
48
+
Back
49
+
</button>
50
+
51
+
<h2 className="text-2xl flex flex-row gap-2 font-semibold text-center text-text">
52
+
<BiListPlus className="text-text" size={35} aria-hidden={true} />
53
+
Manage tailnet words
54
+
</h2>
55
+
</div>
56
+
57
+
<div className="pb-4">
58
+
<TailnetInput
59
+
handleAddTailnet={handleAddTailnet}
60
+
inputValue={inputValue}
61
+
isInputValid={isInputValid}
62
+
loading={loading}
63
+
setInputValue={setInputValue}
64
+
tailnetNames={tailnetNames}
65
+
tails={tails}
66
+
scales={scales}
67
+
/>
68
+
<div className="w-full text-center border border-border flex flex-row gap-2 justify-center text-text bg-item text-sm rounded-md mt-3 py-3 items-center">
69
+
<BiInfoCircle className="inline-block" size={20} aria-hidden={true} />
70
+
It's recommended to add multiple tailnet words to get tailnet offers
71
+
faster.
72
+
</div>
73
+
<HelperText onShowWordList={onShowWordList} />
74
+
<TailnetList
75
+
handleRemoveTailnet={handleRemoveTailnet}
76
+
tailnetNames={tailnetNames}
77
+
/>
78
+
{error && <div className="text-danger mt-2">{error}</div>}
79
+
</div>
80
+
</main>
81
+
);
82
+
83
+
export default TailnetWordsScreen;
+54
entrypoints/popup/screens/TokenList.tsx
+54
entrypoints/popup/screens/TokenList.tsx
···
···
1
+
import type { FC } from 'react';
2
+
import { BiArrowBack, BiSolidKey } from 'react-icons/bi';
3
+
import TokenList from '$components/TokenList';
4
+
import type { Token } from '$types/tokens';
5
+
6
+
interface TokenListScreenProps {
7
+
tokens: Token[];
8
+
handleClaimToken: (tokenObj: Token) => Promise<void>;
9
+
handleRemoveToken: (tokenObj: Token) => void;
10
+
loading: boolean;
11
+
error: string | null;
12
+
onBack: () => void;
13
+
}
14
+
15
+
const TokenListScreen: FC<TokenListScreenProps> = ({
16
+
tokens,
17
+
handleClaimToken,
18
+
handleRemoveToken,
19
+
loading,
20
+
error,
21
+
onBack,
22
+
}) => (
23
+
<main className="rounded-none shadow-lg p-6 w-full h-full">
24
+
<div className="flex justify-between items-center mb-4">
25
+
<button
26
+
className="text-sm px-3 py-2 rounded-lg flex flex-row items-center bg-item border border-border text-text font-medium hover:bg-accent transition-colors hover:border-accent"
27
+
type="button"
28
+
onClick={onBack}
29
+
>
30
+
<BiArrowBack
31
+
className="inline-block mr-1"
32
+
size={20}
33
+
aria-hidden={true}
34
+
/>
35
+
Back
36
+
</button>
37
+
38
+
<h2 className="text-2xl font-semibold gap-2 flex flex-row text-text">
39
+
<BiSolidKey className="text-text" size={35} aria-hidden={true} />
40
+
Tailnet name offers
41
+
</h2>
42
+
</div>
43
+
44
+
<TokenList
45
+
error={error}
46
+
handleClaimToken={handleClaimToken}
47
+
handleRemoveToken={handleRemoveToken}
48
+
loading={loading}
49
+
tokens={tokens}
50
+
/>
51
+
</main>
52
+
);
53
+
54
+
export default TokenListScreen;
+97
entrypoints/popup/screens/WordList.tsx
+97
entrypoints/popup/screens/WordList.tsx
···
···
1
+
import { type FC, useState } from 'react';
2
+
import { BiArrowBack, BiTable } from 'react-icons/bi';
3
+
4
+
interface WordListScreenProps {
5
+
tails: string[];
6
+
scales: string[];
7
+
onBack: () => void;
8
+
}
9
+
10
+
const WordListScreen: FC<WordListScreenProps> = ({ tails, scales, onBack }) => {
11
+
const [search, setSearch] = useState('');
12
+
13
+
const sortedTails = [...tails].sort((a, b) => a.localeCompare(b));
14
+
const sortedScales = [...scales].sort((a, b) => a.localeCompare(b));
15
+
16
+
const filteredTails = sortedTails.filter((word) =>
17
+
word.toLowerCase().includes(search.toLowerCase()),
18
+
);
19
+
const filteredScales = sortedScales.filter((word) =>
20
+
word.toLowerCase().includes(search.toLowerCase()),
21
+
);
22
+
23
+
return (
24
+
<main className="rounded-none shadow-lg p-6 w-full h-full flex flex-col">
25
+
<div className="flex justify-between items-center mb-3">
26
+
<button
27
+
className="text-sm px-3 py-2 rounded-lg flex flex-row items-center bg-item border border-border text-text font-medium hover:bg-accent transition-colors hover:border-accent"
28
+
type="button"
29
+
onClick={onBack}
30
+
>
31
+
<BiArrowBack
32
+
className="inline-block mr-1"
33
+
size={20}
34
+
aria-hidden={true}
35
+
/>
36
+
Back
37
+
</button>
38
+
39
+
<h2 className="text-2xl flex flex-row gap-2 font-semibold text-center text-text">
40
+
<BiTable size={35} aria-hidden={true} />
41
+
Tailnet name word list
42
+
</h2>
43
+
</div>
44
+
45
+
<input
46
+
type="text"
47
+
className="w-full border border-border rounded-lg px-3 py-1.5 mb-4 bg-input text-base text-text focus:border-accent focus:outline-none transition-colors"
48
+
placeholder="Search for a word..."
49
+
value={search}
50
+
onChange={(e) => setSearch(e.target.value)}
51
+
/>
52
+
53
+
<div className="flex-1 overflow-y-auto border border-border rounded-lg bg-header p-4 [&::-webkit-scrollbar]:[width:0.5em] [&::-webkit-scrollbar-thumb]:bg-subtext">
54
+
<div className="flex gap-6">
55
+
<div className="flex-1">
56
+
<h3 className="text-lg font-bold mb-2 text-text">Tails</h3>
57
+
{filteredTails.length > 0 ? (
58
+
<ul className="grid grid-cols-2 md:grid-cols-3 gap-2 mb-4">
59
+
{filteredTails.map((word) => (
60
+
<li
61
+
key={word}
62
+
className="text-text bg-item rounded px-2 py-1 text-base font-medium border border-border transition-colors"
63
+
>
64
+
{word}
65
+
</li>
66
+
))}
67
+
</ul>
68
+
) : (
69
+
<div className="text-subtext text-sm mb-4">
70
+
No tails words found.
71
+
</div>
72
+
)}
73
+
</div>
74
+
<div className="flex-1">
75
+
<h3 className="text-lg font-bold mb-2 text-text">Scales</h3>
76
+
{filteredScales.length > 0 ? (
77
+
<ul className="grid grid-cols-2 md:grid-cols-3 gap-2">
78
+
{filteredScales.map((word) => (
79
+
<li
80
+
key={word}
81
+
className="text-text bg-item rounded px-2 py-1 text-base font-medium border border-border transition-colors"
82
+
>
83
+
{word}
84
+
</li>
85
+
))}
86
+
</ul>
87
+
) : (
88
+
<div className="text-subtext text-sm">No scales words found.</div>
89
+
)}
90
+
</div>
91
+
</div>
92
+
</div>
93
+
</main>
94
+
);
95
+
};
96
+
97
+
export default WordListScreen;
+46
handlers/alarm.ts
+46
handlers/alarm.ts
···
···
1
+
import { updateBadgeText } from '$background/badge';
2
+
import { log } from '$helpers/logging';
3
+
import type { Message } from '$types/messages';
4
+
5
+
export const handleGetNextAlarm = async (
6
+
_message: Message,
7
+
sendResponse: (resp: unknown) => void,
8
+
) => {
9
+
const alarm = await browser.alarms.get('checkOffersAlarm');
10
+
if (alarm?.scheduledTime) {
11
+
const now = Date.now();
12
+
const timeRemaining = Math.max(
13
+
0,
14
+
Math.round((alarm.scheduledTime - now) / 1000),
15
+
);
16
+
sendResponse({ timeRemaining });
17
+
} else {
18
+
sendResponse({ timeRemaining: null });
19
+
}
20
+
};
21
+
22
+
export const handleSetAlarmInterval = async (
23
+
message: Message,
24
+
sendResponse: (resp: unknown) => void,
25
+
) => {
26
+
const interval = message.interval as number;
27
+
const alarm = await browser.alarms.get('checkOffersAlarm');
28
+
29
+
if (alarm) {
30
+
await browser.alarms.clear('checkOffersAlarm');
31
+
await browser.alarms.create('checkOffersAlarm', {
32
+
periodInMinutes: interval / 60,
33
+
});
34
+
35
+
log(
36
+
`Alarm 'checkOffersAlarm' interval updated to ${interval} seconds (restarted).`,
37
+
'INFO',
38
+
'Alarms',
39
+
);
40
+
41
+
updateBadgeText();
42
+
sendResponse({ success: true });
43
+
} else {
44
+
sendResponse({ success: false, reason: 'Alarm not running.' });
45
+
}
46
+
};
+44
handlers/alert.ts
+44
handlers/alert.ts
···
···
1
+
import { log } from '$helpers/logging';
2
+
import { openDnsTabWithNotification } from '$helpers/notifications';
3
+
import type { Message } from '$types/messages';
4
+
5
+
const showCustomAlertWithAck = async (
6
+
_sendResponse: (resp: unknown) => void,
7
+
) => {
8
+
browser.runtime.sendMessage({
9
+
action: 'showCustomAlert',
10
+
message:
11
+
"This extension will now redirect you to the Tailscale DNS page. This is sadly a limitation of what we can do with Tailscale's claim offers endpoint.\nAfter you're redirected, please re-open the extension window to finish the claim process.",
12
+
});
13
+
14
+
const ackListener = async (msg: Message) => {
15
+
if (msg.action === 'userAcknowledgedRedirect') {
16
+
await openDnsTabWithNotification();
17
+
browser.runtime.onMessage.removeListener(ackListener);
18
+
}
19
+
};
20
+
21
+
browser.runtime.onMessage.addListener(ackListener);
22
+
};
23
+
24
+
export const showDnsRedirectAlert = async (
25
+
sendResponse: (resp: unknown) => void,
26
+
) => {
27
+
const activeTabs = await browser.tabs.query({
28
+
active: true,
29
+
currentWindow: true,
30
+
});
31
+
if (activeTabs && activeTabs.length > 0) {
32
+
showCustomAlertWithAck(sendResponse);
33
+
} else {
34
+
await openDnsTabWithNotification();
35
+
}
36
+
37
+
log(
38
+
'No Tailscale admin tab found. Now attempting to open https://login.tailscale.com/admin/dns in a new browser tab.',
39
+
'WARNING',
40
+
'Tokens',
41
+
);
42
+
43
+
sendResponse({ success: true, redirected: true });
44
+
};
+10
handlers/eligibility.ts
+10
handlers/eligibility.ts
···
···
1
+
import { checkEligibility } from '$helpers/eligibility';
2
+
import type { Message } from '$types/messages';
3
+
4
+
export const handleCheckEligibility = async (
5
+
_message: Message,
6
+
sendResponse: (resp: unknown) => void,
7
+
) => {
8
+
const result = await checkEligibility();
9
+
sendResponse({ eligibility: result });
10
+
};
+24
handlers/index.ts
+24
handlers/index.ts
···
···
1
+
import { handleGetNextAlarm, handleSetAlarmInterval } from '$handlers/alarm';
2
+
import { handleCheckEligibility } from '$handlers/eligibility';
3
+
import {
4
+
handleGetStatus,
5
+
handleStartCheck,
6
+
handleStopCheck,
7
+
} from '$handlers/status';
8
+
import { handleClaimToken } from '$handlers/token';
9
+
import type { Message } from '$types/messages';
10
+
11
+
const handlers: Record<
12
+
string,
13
+
(message: Message, sendResponse: (resp: unknown) => void) => void
14
+
> = {
15
+
checkEligibility: handleCheckEligibility,
16
+
claimToken: handleClaimToken,
17
+
getNextAlarm: handleGetNextAlarm,
18
+
getStatus: handleGetStatus,
19
+
setAlarmInterval: handleSetAlarmInterval,
20
+
startCheck: handleStartCheck,
21
+
stopCheck: handleStopCheck,
22
+
};
23
+
24
+
export default handlers;
+40
handlers/status.ts
+40
handlers/status.ts
···
···
1
+
import { updateBadgeText } from '$background/badge';
2
+
import { defaultCheckOffersInterval } from '$config';
3
+
import { log } from '$helpers/logging';
4
+
import { checkTailscaleOffers } from '$helpers/offers';
5
+
import type { Message } from '$types/messages';
6
+
7
+
export const handleStartCheck = async (
8
+
_message: Message,
9
+
sendResponse: (resp: unknown) => void,
10
+
) => {
11
+
const result = await browser.storage.local.get(['checkInterval']);
12
+
const interval = result.checkInterval || defaultCheckOffersInterval; // default 60 seconds
13
+
browser.alarms.create('checkOffersAlarm', { periodInMinutes: interval / 60 });
14
+
log(
15
+
`Checking offers process initialized. Interval set to check every ${interval} seconds.`,
16
+
'INFO',
17
+
'Alarms',
18
+
);
19
+
await checkTailscaleOffers();
20
+
updateBadgeText();
21
+
sendResponse({ status: 'Running' });
22
+
};
23
+
24
+
export const handleStopCheck = async (
25
+
_message: Message,
26
+
sendResponse: (resp: unknown) => void,
27
+
) => {
28
+
browser.alarms.clear('checkOffersAlarm');
29
+
log('Checking offers process manually stopped by user.', 'INFO', 'Alarms');
30
+
updateBadgeText();
31
+
sendResponse({ status: 'Stopped' });
32
+
};
33
+
34
+
export const handleGetStatus = async (
35
+
_message: Message,
36
+
sendResponse: (resp: unknown) => void,
37
+
) => {
38
+
const alarm = await browser.alarms.get('checkOffersAlarm');
39
+
sendResponse({ status: alarm ? 'Running' : 'Stopped' });
40
+
};
+68
handlers/token.ts
+68
handlers/token.ts
···
···
1
+
import { updateBadgeText } from '$background/badge';
2
+
import { showDnsRedirectAlert } from '$handlers/alert';
3
+
import { log } from '$helpers/logging';
4
+
import type { Message } from '$types/messages';
5
+
6
+
const injectAndClaimToken = async (
7
+
tabId: number,
8
+
tcd: unknown,
9
+
token: unknown,
10
+
sendResponse: (resp: unknown) => void,
11
+
) => {
12
+
await browser.scripting.executeScript({
13
+
target: { tabId },
14
+
files: ['content-scripts/content.js'],
15
+
});
16
+
17
+
try {
18
+
const response = await browser.tabs.sendMessage(tabId, {
19
+
action: 'claimToken',
20
+
tcd,
21
+
token,
22
+
});
23
+
24
+
log(
25
+
`Content script claim response: ${JSON.stringify(response)}`,
26
+
'DEBUG',
27
+
'Messaging',
28
+
);
29
+
30
+
updateBadgeText();
31
+
sendResponse(response);
32
+
} catch (err) {
33
+
log(
34
+
`Error while sending the claimToken alarm: ${err}`,
35
+
'ERROR',
36
+
'Messaging',
37
+
);
38
+
updateBadgeText();
39
+
40
+
const errorMsg = err instanceof Error ? err.message : 'Unknown error';
41
+
sendResponse({ success: false, error: errorMsg });
42
+
}
43
+
};
44
+
45
+
export const handleClaimToken = async (
46
+
message: Message,
47
+
sendResponse: (resp: unknown) => void,
48
+
) => {
49
+
const { tcd, token } = message;
50
+
51
+
log(
52
+
'Forwarding claimToken request to content script. Awaiting response...',
53
+
'INFO',
54
+
'Messaging',
55
+
);
56
+
57
+
const tabs = await browser.tabs.query({
58
+
url: 'https://login.tailscale.com/admin/dns',
59
+
});
60
+
if (!tabs || tabs.length === 0) {
61
+
await showDnsRedirectAlert(sendResponse);
62
+
return;
63
+
}
64
+
65
+
// biome-ignore lint/style/noNonNullAssertion: We're force injecting here just in case the user already has the DNS page open.
66
+
const tabId = tabs[0].id!;
67
+
await injectAndClaimToken(tabId, tcd, token, sendResponse);
68
+
};
+126
helpers/eligibility.ts
+126
helpers/eligibility.ts
···
···
1
+
import { eligibilityCacheInterval } from '$config';
2
+
import { log } from '$helpers/logging';
3
+
import { fetchTailscaleOffers, getTailcontrolCookie } from '$helpers/offers';
4
+
import type { Eligibility, EligibilityApiResponse } from '$types/eligibility';
5
+
6
+
const eligibilityMap: Record<string, Eligibility> = {
7
+
apiError: {
8
+
eligible: false,
9
+
reason: 'API error occurred.',
10
+
id: 'api-error',
11
+
error: 'API error',
12
+
},
13
+
eligible: {
14
+
eligible: true,
15
+
reason: 'Eligible for new offers!',
16
+
id: 'eligible',
17
+
offerType: 'reoffer',
18
+
},
19
+
'forbidden - not admin': {
20
+
eligible: false,
21
+
reason:
22
+
'User does not have the Admin role in Tailscale, and thus has insufficient privileges.',
23
+
id: 'not-admin',
24
+
error: 'forbidden - not admin',
25
+
},
26
+
'forbidden - not logged in': {
27
+
eligible: false,
28
+
reason: 'User is not logged in to Tailscale.',
29
+
id: 'not-logged-in',
30
+
error: 'forbidden - not logged in',
31
+
},
32
+
locked: {
33
+
eligible: false,
34
+
reason: 'User already used their tailnet name for an HTTPS certificate.',
35
+
id: 'custom-tailnet',
36
+
offerType: 'locked',
37
+
},
38
+
unknown: {
39
+
eligible: false,
40
+
reason: 'Unknown eligibility state.',
41
+
id: 'unknown',
42
+
},
43
+
};
44
+
45
+
export const parseEligibilityResponse = (
46
+
body: EligibilityApiResponse,
47
+
): Eligibility => {
48
+
if (body.error) {
49
+
return {
50
+
...eligibilityMap.apiError,
51
+
reason: `API error: ${body.error}`,
52
+
error: body.error,
53
+
};
54
+
}
55
+
56
+
if (body.error && eligibilityMap[body.error])
57
+
return eligibilityMap[body.error];
58
+
59
+
if (body.data?.offerType === 'locked') return eligibilityMap.locked;
60
+
if (body.data?.offerType === 'reoffer') return eligibilityMap.eligible;
61
+
62
+
return {
63
+
...eligibilityMap.unknown,
64
+
offerType: body.data?.offerType,
65
+
};
66
+
};
67
+
68
+
export const handleEligibilityResult = (eligibility: Eligibility) => {
69
+
eligibility.checkedAt = Date.now();
70
+
browser.storage.local.set({ eligibility });
71
+
};
72
+
73
+
export const checkEligibility = async (): Promise<Eligibility> => {
74
+
log(
75
+
'Checking if the user is eligible for Tailscale offers...',
76
+
'INFO',
77
+
'Eligibility',
78
+
);
79
+
let eligibility: Eligibility = eligibilityMap.unknown;
80
+
81
+
const cacheDurationMs = eligibilityCacheInterval * 1000;
82
+
const cached = await browser.storage.local.get('eligibility');
83
+
if (cached.eligibility?.checkedAt) {
84
+
const now = Date.now();
85
+
if (now - cached.eligibility.checkedAt < cacheDurationMs) {
86
+
log('Using cached eligibility response.', 'INFO', 'Eligibility');
87
+
return cached.eligibility;
88
+
}
89
+
}
90
+
91
+
try {
92
+
const cookie = await getTailcontrolCookie();
93
+
if (!cookie) {
94
+
eligibility = eligibilityMap['forbidden - not logged in'];
95
+
handleEligibilityResult(eligibility);
96
+
return eligibility;
97
+
}
98
+
99
+
const cookieValue = cookie.value;
100
+
const response = await fetchTailscaleOffers(cookieValue);
101
+
const body = await response.json();
102
+
console.log(body);
103
+
eligibility = parseEligibilityResponse(body);
104
+
handleEligibilityResult(eligibility);
105
+
106
+
return eligibility;
107
+
} catch (e) {
108
+
const errorMsg = e instanceof Error ? e.message : 'Unknown error';
109
+
110
+
eligibility = {
111
+
...eligibilityMap.unknown,
112
+
reason: errorMsg,
113
+
error: errorMsg,
114
+
};
115
+
116
+
handleEligibilityResult(eligibility);
117
+
118
+
return eligibility;
119
+
} finally {
120
+
log(
121
+
`Eligibility check result: ${eligibility.eligible} (${eligibility.reason})`,
122
+
'INFO',
123
+
'Eligibility',
124
+
);
125
+
}
126
+
};
+31
helpers/logging.ts
+31
helpers/logging.ts
···
···
1
+
type LogSeverity = 'INFO' | 'WARNING' | 'DEBUG' | 'ERROR';
2
+
type LogCategory =
3
+
| 'Alarms'
4
+
| 'Notifications'
5
+
| 'Cookies'
6
+
| 'Tokens'
7
+
| 'Eligibility'
8
+
| 'Offers'
9
+
| 'Storage'
10
+
| 'Messaging'
11
+
| 'Setup'
12
+
| 'General';
13
+
14
+
const severityEmoji: Record<LogSeverity, string> = {
15
+
INFO: 'ℹ️',
16
+
WARNING: '⚠️',
17
+
DEBUG: '🐛',
18
+
ERROR: '❌',
19
+
};
20
+
21
+
export const log = (
22
+
message: string,
23
+
severity: LogSeverity = 'INFO',
24
+
category: LogCategory = 'General',
25
+
) => {
26
+
const timestamp = new Date().toLocaleTimeString();
27
+
const emoji = severityEmoji[severity] || '';
28
+
console.log(
29
+
`${emoji} [${timestamp}] [${severity}] [${category}]: ${message}`,
30
+
);
31
+
};
+76
helpers/notifications.ts
+76
helpers/notifications.ts
···
···
1
+
import { log } from '$helpers/logging';
2
+
import type { DefaultStorageOptions } from '$types/storage';
3
+
4
+
export const openDnsTabWithNotification = async () => {
5
+
await browser.tabs.create({ url: 'https://login.tailscale.com/admin/dns' });
6
+
browser.notifications.create({
7
+
type: 'basic',
8
+
iconUrl: browser.runtime.getURL('/icons/128.png'),
9
+
title: 'Tailscale DNS Page Opened',
10
+
message:
11
+
'Please re-open the extension window to continue the claim process.',
12
+
});
13
+
};
14
+
15
+
export const buildSystemNotificationOptions = (
16
+
message: string,
17
+
): Browser.notifications.NotificationCreateOptions => ({
18
+
type: 'basic',
19
+
iconUrl: browser.runtime.getURL('/icons/128.png'),
20
+
title: 'Tailnet name offer found!',
21
+
message,
22
+
});
23
+
24
+
export const buildNtfyNotificationOptions = (
25
+
token: string,
26
+
): Record<string, string> => ({
27
+
Title: `Tailnet name offer found!`,
28
+
Priority: '3',
29
+
Tags: 'key',
30
+
...(token ? { Authorization: `Bearer ${token}` } : {}),
31
+
});
32
+
33
+
export const notifyNewTokens = async (
34
+
newTokens: string[],
35
+
result: DefaultStorageOptions,
36
+
) => {
37
+
const tailnetNamesOnly = newTokens.map((token) => token.split('/')[0]);
38
+
const messageText = `New offer${
39
+
newTokens.length > 1 ? 's' : ''
40
+
} found: ${tailnetNamesOnly.join(', ')}. Hurry, it expires in 5 minutes!`;
41
+
42
+
if (result.useSystemPush) {
43
+
try {
44
+
await browser.notifications.create(
45
+
buildSystemNotificationOptions(messageText),
46
+
);
47
+
} catch (err) {
48
+
log(
49
+
`Failed to create notification for new offers: ${err}`,
50
+
'ERROR',
51
+
'Notifications',
52
+
);
53
+
}
54
+
}
55
+
56
+
if (result.useNtfyPush) {
57
+
const ntfyUrl = (result.ntfyUrl || '').replace(/\/$/, '');
58
+
const ntfyTopic = result.ntfyTopic || '';
59
+
const ntfyToken = result.ntfyToken || '';
60
+
if (ntfyUrl && ntfyTopic) {
61
+
try {
62
+
await fetch(`${ntfyUrl}/${ntfyTopic}`, {
63
+
method: 'POST',
64
+
headers: buildNtfyNotificationOptions(ntfyToken),
65
+
body: messageText,
66
+
});
67
+
} catch (err) {
68
+
log(
69
+
`Failed to send ntfy notification for new offers: ${err}`,
70
+
'ERROR',
71
+
'Notifications',
72
+
);
73
+
}
74
+
}
75
+
}
76
+
};
+103
helpers/offers.ts
+103
helpers/offers.ts
···
···
1
+
import { log } from '$helpers/logging';
2
+
import { getTailnetNames, validateTailnetNames } from '$helpers/tailnet';
3
+
import { extractMatchingTokens, storeTokens } from '$helpers/tokens';
4
+
import type { Offer } from '$types/tokens';
5
+
6
+
export const getTailcontrolCookie = async (): Promise<{
7
+
value: string;
8
+
} | null> => {
9
+
return await browser.cookies.get({
10
+
url: 'https://login.tailscale.com',
11
+
name: 'tailcontrol',
12
+
});
13
+
};
14
+
15
+
export const fetchTailscaleOffers = async (cookieValue: string) => {
16
+
return await fetch('https://login.tailscale.com/admin/api/tcd/offers', {
17
+
headers: {
18
+
accept: 'application/json, text/plain, */*',
19
+
'accept-language': 'en-US,en;q=0.9',
20
+
'cache-control': 'no-cache',
21
+
pragma: 'no-cache',
22
+
cookie: `tailcontrol=${cookieValue}; tailwww-ui=true`,
23
+
},
24
+
referrer: 'https://login.tailscale.com/admin/dns',
25
+
referrerPolicy: 'strict-origin-when-cross-origin',
26
+
method: 'GET',
27
+
mode: 'cors',
28
+
});
29
+
};
30
+
31
+
const handleCheckOffersError = (errMsg: string) => {
32
+
log(errMsg, 'ERROR', 'Offers');
33
+
log(
34
+
'Checking offers process finished due to an error. See line above.',
35
+
'ERROR',
36
+
'Offers',
37
+
);
38
+
39
+
browser.alarms.clear('checkOffersAlarm');
40
+
};
41
+
42
+
export const checkTailscaleOffers = async () => {
43
+
log(
44
+
'Checking offers process started. Searching Tailscale for tailnet name offers...',
45
+
'INFO',
46
+
'Offers',
47
+
);
48
+
let tailnetNames: string[] = [];
49
+
50
+
try {
51
+
tailnetNames = await getTailnetNames();
52
+
} catch {
53
+
log(
54
+
'Failed to get tailnetNames from storage. Defaulting to empty array.',
55
+
'WARNING',
56
+
'Offers',
57
+
);
58
+
}
59
+
60
+
if (!validateTailnetNames(tailnetNames)) {
61
+
handleCheckOffersError(
62
+
'No tailnet keywords set. User needs to add at least one tailnet keyword.',
63
+
);
64
+
return;
65
+
}
66
+
67
+
try {
68
+
const cookie = await getTailcontrolCookie();
69
+
if (!cookie) {
70
+
handleCheckOffersError(
71
+
'Tailcontrol cookie not found. User needs to log in to Tailscale.',
72
+
);
73
+
return;
74
+
}
75
+
76
+
const cookieValue = cookie.value;
77
+
const response = await fetchTailscaleOffers(cookieValue);
78
+
const body: { data?: Offer } = await response.json();
79
+
80
+
if (body.data?.tcds) {
81
+
const tokens = extractMatchingTokens(body.data.tcds, tailnetNames);
82
+
83
+
if (tokens.length > 0) {
84
+
log(
85
+
`Found matching tokens: ${JSON.stringify(tokens)}`,
86
+
'INFO',
87
+
'Offers',
88
+
);
89
+
90
+
storeTokens(tokens);
91
+
} else {
92
+
log(
93
+
"No matching tailnet name offers found that match the user's defined keywords.",
94
+
'INFO',
95
+
'Offers',
96
+
);
97
+
}
98
+
}
99
+
} catch (e) {
100
+
const errorMsg = e instanceof Error ? e.message : 'Unknown error';
101
+
handleCheckOffersError(errorMsg || 'Unknown error');
102
+
}
103
+
};
+11
helpers/tailnet.ts
+11
helpers/tailnet.ts
···
···
1
+
export const getTailnetNames = async (): Promise<string[]> => {
2
+
return await new Promise((resolve) => {
3
+
browser.storage.local.get(['tailnetNames'], (result) => {
4
+
resolve(Array.isArray(result.tailnetNames) ? result.tailnetNames : []);
5
+
});
6
+
});
7
+
};
8
+
9
+
export const validateTailnetNames = (tailnetNames: string[]) => {
10
+
return Array.isArray(tailnetNames) && tailnetNames.length > 0;
11
+
};
+100
helpers/tokens.ts
+100
helpers/tokens.ts
···
···
1
+
import { tokenValidityInterval } from '$config';
2
+
import { log } from '$helpers/logging';
3
+
import { notifyNewTokens } from '$helpers/notifications';
4
+
5
+
import type { StoreTokensResult, Token } from '$types/tokens';
6
+
7
+
export const extractMatchingTokens = (
8
+
tcds: Token[],
9
+
tailnetNames: string[],
10
+
) => {
11
+
return tcds
12
+
.filter(({ tcd }) => {
13
+
const tcdName = tcd.replace(/\.ts\.net$/, '');
14
+
return tailnetNames.some((name) => {
15
+
if (name.includes('-')) {
16
+
// Combo: must match exactly
17
+
return tcdName === name;
18
+
}
19
+
20
+
// Single word: match any part
21
+
return tcdName.split('-').includes(name);
22
+
});
23
+
})
24
+
.map(({ token }) => token);
25
+
};
26
+
27
+
export const storeTokens = async (tokens: string[]) => {
28
+
try {
29
+
const result: StoreTokensResult = await browser.storage.local.get([
30
+
'tailscaleTokens',
31
+
'useSystemPush',
32
+
'useNtfyPush',
33
+
'ntfyUrl',
34
+
'ntfyTopic',
35
+
'ntfyToken',
36
+
]);
37
+
38
+
const now = Date.now();
39
+
const existingTokens = Array.isArray(result.tailscaleTokens)
40
+
? result.tailscaleTokens
41
+
: [];
42
+
const validTokens = filterValidTokens(existingTokens, now);
43
+
44
+
if (validTokens.length !== existingTokens.length) {
45
+
try {
46
+
await browser.storage.local.set({ tailscaleTokens: validTokens });
47
+
} catch (err) {
48
+
log(
49
+
`Failed to update valid tokens in storage: ${err}`,
50
+
'ERROR',
51
+
'Tokens',
52
+
);
53
+
}
54
+
}
55
+
56
+
const newTokens = tokens.filter(
57
+
(token) => !validTokens.some((t: Token) => t.token === token),
58
+
);
59
+
const updatedTokens = validTokens.concat(
60
+
newTokens.map((token) => ({ token, timestamp: now }) as Token),
61
+
);
62
+
63
+
try {
64
+
await browser.storage.local.set({ tailscaleTokens: updatedTokens });
65
+
log('Tokens appended to storage with timestamp.', 'INFO', 'Tokens');
66
+
await browser.runtime.sendMessage({
67
+
action: 'tokensUpdated',
68
+
tokens: updatedTokens,
69
+
});
70
+
} catch (err) {
71
+
log(
72
+
`Failed to update tokens in storage or send message: ${err}`,
73
+
'ERROR',
74
+
'Tokens',
75
+
);
76
+
}
77
+
78
+
if (newTokens.length > 0) {
79
+
await notifyNewTokens(newTokens, result);
80
+
}
81
+
} catch (err) {
82
+
log(`Failed to store tokens: ${err}`, 'ERROR', 'Tokens');
83
+
}
84
+
};
85
+
86
+
export const extractTailnetNameFromToken = (token: string) => {
87
+
const match = token.match(/^([^.]+(?:-[^.]+)?)\.ts\.net/);
88
+
return match ? match[0] : token;
89
+
};
90
+
91
+
export const filterValidTokens = (
92
+
tokens: Token[],
93
+
now: number = Date.now(),
94
+
): Token[] => {
95
+
return (tokens || []).filter(
96
+
(t) =>
97
+
typeof t.timestamp === 'number' &&
98
+
now - t.timestamp < tokenValidityInterval * 1000,
99
+
);
100
+
};
+55
helpers/words.ts
+55
helpers/words.ts
···
···
1
+
import { tailscaleWordsUrls } from '$config';
2
+
3
+
export const fetchAndCacheWords = (): Promise<{
4
+
tails: string[];
5
+
scales: string[];
6
+
}> => {
7
+
return new Promise((resolve) => {
8
+
browser.storage.local.get(
9
+
['tailnetTails', 'tailnetScales'],
10
+
async (result) => {
11
+
if (
12
+
Array.isArray(result.tailnetTails) &&
13
+
result.tailnetTails.length > 0 &&
14
+
Array.isArray(result.tailnetScales) &&
15
+
result.tailnetScales.length > 0
16
+
) {
17
+
resolve({ tails: result.tailnetTails, scales: result.tailnetScales });
18
+
return;
19
+
}
20
+
21
+
try {
22
+
const responses = await Promise.all(
23
+
tailscaleWordsUrls.map((url) => fetch(url)),
24
+
);
25
+
const texts = await Promise.all(responses.map((r) => r.text()));
26
+
const tails = Array.from(
27
+
new Set(
28
+
texts[0]
29
+
.split(/\r?\n/)
30
+
.map((w) => w.trim())
31
+
.filter((w) => w.length > 0),
32
+
),
33
+
);
34
+
const scales = Array.from(
35
+
new Set(
36
+
texts[1]
37
+
.split(/\r?\n/)
38
+
.map((w) => w.trim())
39
+
.filter((w) => w.length > 0),
40
+
),
41
+
);
42
+
43
+
browser.storage.local.set(
44
+
{ tailnetTails: tails, tailnetScales: scales },
45
+
() => {
46
+
resolve({ tails, scales });
47
+
},
48
+
);
49
+
} catch {
50
+
resolve({ tails: [], scales: [] });
51
+
}
52
+
},
53
+
);
54
+
});
55
+
};
+36
package.json
+36
package.json
···
···
1
+
{
2
+
"name": "tailname",
3
+
"description": "Search for custom tailnet name offers with keywords.",
4
+
"author": "Chloe Arciniega <chloe@sapphic.moe>",
5
+
"private": true,
6
+
"version": "1.0.0",
7
+
"type": "module",
8
+
"license": "zlib",
9
+
"scripts": {
10
+
"dev": "wxt",
11
+
"dev:firefox": "wxt -b firefox",
12
+
"build": "wxt build",
13
+
"build:firefox": "wxt build -b firefox",
14
+
"zip": "wxt zip",
15
+
"zip:firefox": "wxt zip -b firefox",
16
+
"compile": "tsc --noEmit",
17
+
"postinstall": "wxt prepare"
18
+
},
19
+
"dependencies": {
20
+
"react": "^19.1.0",
21
+
"react-dom": "^19.1.0",
22
+
"react-icons": "^5.5.0"
23
+
},
24
+
"devDependencies": {
25
+
"@biomejs/biome": "2.2.4",
26
+
"@types/react": "^19.1.13",
27
+
"@types/react-dom": "^19.1.9",
28
+
"@wxt-dev/auto-icons": "^1.1.0",
29
+
"@wxt-dev/module-react": "^1.1.5",
30
+
"autoprefixer": "^10.4.21",
31
+
"postcss": "^8.5.6",
32
+
"tailwindcss": "3.4.1",
33
+
"typescript": "^5.8.3",
34
+
"wxt": "^0.20.11"
35
+
}
36
+
}
+4839
pnpm-lock.yaml
+4839
pnpm-lock.yaml
···
···
1
+
lockfileVersion: '9.0'
2
+
3
+
settings:
4
+
autoInstallPeers: true
5
+
excludeLinksFromLockfile: false
6
+
7
+
importers:
8
+
9
+
.:
10
+
dependencies:
11
+
react:
12
+
specifier: ^19.1.0
13
+
version: 19.1.1
14
+
react-dom:
15
+
specifier: ^19.1.0
16
+
version: 19.1.1(react@19.1.1)
17
+
react-icons:
18
+
specifier: ^5.5.0
19
+
version: 5.5.0(react@19.1.1)
20
+
devDependencies:
21
+
'@biomejs/biome':
22
+
specifier: 2.2.4
23
+
version: 2.2.4
24
+
'@types/react':
25
+
specifier: ^19.1.13
26
+
version: 19.1.13
27
+
'@types/react-dom':
28
+
specifier: ^19.1.9
29
+
version: 19.1.9(@types/react@19.1.13)
30
+
'@wxt-dev/auto-icons':
31
+
specifier: ^1.1.0
32
+
version: 1.1.0(wxt@0.20.11(@types/node@24.3.2)(jiti@2.5.1)(rollup@4.50.1)(yaml@2.8.1))
33
+
'@wxt-dev/module-react':
34
+
specifier: ^1.1.5
35
+
version: 1.1.5(vite@7.1.5(@types/node@24.3.2)(jiti@2.5.1)(yaml@2.8.1))(wxt@0.20.11(@types/node@24.3.2)(jiti@2.5.1)(rollup@4.50.1)(yaml@2.8.1))
36
+
autoprefixer:
37
+
specifier: ^10.4.21
38
+
version: 10.4.21(postcss@8.5.6)
39
+
postcss:
40
+
specifier: ^8.5.6
41
+
version: 8.5.6
42
+
tailwindcss:
43
+
specifier: 3.4.1
44
+
version: 3.4.1
45
+
typescript:
46
+
specifier: ^5.8.3
47
+
version: 5.9.2
48
+
wxt:
49
+
specifier: ^0.20.11
50
+
version: 0.20.11(@types/node@24.3.2)(jiti@2.5.1)(rollup@4.50.1)(yaml@2.8.1)
51
+
52
+
packages:
53
+
54
+
'@1natsu/wait-element@4.1.2':
55
+
resolution: {integrity: sha512-qWxSJD+Q5b8bKOvESFifvfZ92DuMsY+03SBNjTO34ipJLP6mZ9yK4bQz/vlh48aEQXoJfaZBqUwKL5BdI5iiWw==}
56
+
57
+
'@aklinker1/rollup-plugin-visualizer@5.12.0':
58
+
resolution: {integrity: sha512-X24LvEGw6UFmy0lpGJDmXsMyBD58XmX1bbwsaMLhNoM+UMQfQ3b2RtC+nz4b/NoRK5r6QJSKJHBNVeUdwqybaQ==}
59
+
engines: {node: '>=14'}
60
+
hasBin: true
61
+
peerDependencies:
62
+
rollup: 2.x || 3.x || 4.x
63
+
peerDependenciesMeta:
64
+
rollup:
65
+
optional: true
66
+
67
+
'@alloc/quick-lru@5.2.0':
68
+
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
69
+
engines: {node: '>=10'}
70
+
71
+
'@babel/code-frame@7.27.1':
72
+
resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
73
+
engines: {node: '>=6.9.0'}
74
+
75
+
'@babel/compat-data@7.28.4':
76
+
resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==}
77
+
engines: {node: '>=6.9.0'}
78
+
79
+
'@babel/core@7.28.4':
80
+
resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==}
81
+
engines: {node: '>=6.9.0'}
82
+
83
+
'@babel/generator@7.28.3':
84
+
resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==}
85
+
engines: {node: '>=6.9.0'}
86
+
87
+
'@babel/helper-compilation-targets@7.27.2':
88
+
resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==}
89
+
engines: {node: '>=6.9.0'}
90
+
91
+
'@babel/helper-globals@7.28.0':
92
+
resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
93
+
engines: {node: '>=6.9.0'}
94
+
95
+
'@babel/helper-module-imports@7.27.1':
96
+
resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==}
97
+
engines: {node: '>=6.9.0'}
98
+
99
+
'@babel/helper-module-transforms@7.28.3':
100
+
resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==}
101
+
engines: {node: '>=6.9.0'}
102
+
peerDependencies:
103
+
'@babel/core': ^7.0.0
104
+
105
+
'@babel/helper-plugin-utils@7.27.1':
106
+
resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==}
107
+
engines: {node: '>=6.9.0'}
108
+
109
+
'@babel/helper-string-parser@7.27.1':
110
+
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
111
+
engines: {node: '>=6.9.0'}
112
+
113
+
'@babel/helper-validator-identifier@7.27.1':
114
+
resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
115
+
engines: {node: '>=6.9.0'}
116
+
117
+
'@babel/helper-validator-option@7.27.1':
118
+
resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
119
+
engines: {node: '>=6.9.0'}
120
+
121
+
'@babel/helpers@7.28.4':
122
+
resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==}
123
+
engines: {node: '>=6.9.0'}
124
+
125
+
'@babel/parser@7.28.4':
126
+
resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==}
127
+
engines: {node: '>=6.0.0'}
128
+
hasBin: true
129
+
130
+
'@babel/plugin-transform-react-jsx-self@7.27.1':
131
+
resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==}
132
+
engines: {node: '>=6.9.0'}
133
+
peerDependencies:
134
+
'@babel/core': ^7.0.0-0
135
+
136
+
'@babel/plugin-transform-react-jsx-source@7.27.1':
137
+
resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==}
138
+
engines: {node: '>=6.9.0'}
139
+
peerDependencies:
140
+
'@babel/core': ^7.0.0-0
141
+
142
+
'@babel/runtime@7.28.2':
143
+
resolution: {integrity: sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==}
144
+
engines: {node: '>=6.9.0'}
145
+
146
+
'@babel/template@7.27.2':
147
+
resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
148
+
engines: {node: '>=6.9.0'}
149
+
150
+
'@babel/traverse@7.28.4':
151
+
resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==}
152
+
engines: {node: '>=6.9.0'}
153
+
154
+
'@babel/types@7.28.4':
155
+
resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==}
156
+
engines: {node: '>=6.9.0'}
157
+
158
+
'@biomejs/biome@2.2.4':
159
+
resolution: {integrity: sha512-TBHU5bUy/Ok6m8c0y3pZiuO/BZoY/OcGxoLlrfQof5s8ISVwbVBdFINPQZyFfKwil8XibYWb7JMwnT8wT4WVPg==}
160
+
engines: {node: '>=14.21.3'}
161
+
hasBin: true
162
+
163
+
'@biomejs/cli-darwin-arm64@2.2.4':
164
+
resolution: {integrity: sha512-RJe2uiyaloN4hne4d2+qVj3d3gFJFbmrr5PYtkkjei1O9c+BjGXgpUPVbi8Pl8syumhzJjFsSIYkcLt2VlVLMA==}
165
+
engines: {node: '>=14.21.3'}
166
+
cpu: [arm64]
167
+
os: [darwin]
168
+
169
+
'@biomejs/cli-darwin-x64@2.2.4':
170
+
resolution: {integrity: sha512-cFsdB4ePanVWfTnPVaUX+yr8qV8ifxjBKMkZwN7gKb20qXPxd/PmwqUH8mY5wnM9+U0QwM76CxFyBRJhC9tQwg==}
171
+
engines: {node: '>=14.21.3'}
172
+
cpu: [x64]
173
+
os: [darwin]
174
+
175
+
'@biomejs/cli-linux-arm64-musl@2.2.4':
176
+
resolution: {integrity: sha512-7TNPkMQEWfjvJDaZRSkDCPT/2r5ESFPKx+TEev+I2BXDGIjfCZk2+b88FOhnJNHtksbOZv8ZWnxrA5gyTYhSsQ==}
177
+
engines: {node: '>=14.21.3'}
178
+
cpu: [arm64]
179
+
os: [linux]
180
+
181
+
'@biomejs/cli-linux-arm64@2.2.4':
182
+
resolution: {integrity: sha512-M/Iz48p4NAzMXOuH+tsn5BvG/Jb07KOMTdSVwJpicmhN309BeEyRyQX+n1XDF0JVSlu28+hiTQ2L4rZPvu7nMw==}
183
+
engines: {node: '>=14.21.3'}
184
+
cpu: [arm64]
185
+
os: [linux]
186
+
187
+
'@biomejs/cli-linux-x64-musl@2.2.4':
188
+
resolution: {integrity: sha512-m41nFDS0ksXK2gwXL6W6yZTYPMH0LughqbsxInSKetoH6morVj43szqKx79Iudkp8WRT5SxSh7qVb8KCUiewGg==}
189
+
engines: {node: '>=14.21.3'}
190
+
cpu: [x64]
191
+
os: [linux]
192
+
193
+
'@biomejs/cli-linux-x64@2.2.4':
194
+
resolution: {integrity: sha512-orr3nnf2Dpb2ssl6aihQtvcKtLySLta4E2UcXdp7+RTa7mfJjBgIsbS0B9GC8gVu0hjOu021aU8b3/I1tn+pVQ==}
195
+
engines: {node: '>=14.21.3'}
196
+
cpu: [x64]
197
+
os: [linux]
198
+
199
+
'@biomejs/cli-win32-arm64@2.2.4':
200
+
resolution: {integrity: sha512-NXnfTeKHDFUWfxAefa57DiGmu9VyKi0cDqFpdI+1hJWQjGJhJutHPX0b5m+eXvTKOaf+brU+P0JrQAZMb5yYaQ==}
201
+
engines: {node: '>=14.21.3'}
202
+
cpu: [arm64]
203
+
os: [win32]
204
+
205
+
'@biomejs/cli-win32-x64@2.2.4':
206
+
resolution: {integrity: sha512-3Y4V4zVRarVh/B/eSHczR4LYoSVyv3Dfuvm3cWs5w/HScccS0+Wt/lHOcDTRYeHjQmMYVC3rIRWqyN2EI52+zg==}
207
+
engines: {node: '>=14.21.3'}
208
+
cpu: [x64]
209
+
os: [win32]
210
+
211
+
'@devicefarmer/adbkit-logcat@2.1.3':
212
+
resolution: {integrity: sha512-yeaGFjNBc/6+svbDeul1tNHtNChw6h8pSHAt5D+JsedUrMTN7tla7B15WLDyekxsuS2XlZHRxpuC6m92wiwCNw==}
213
+
engines: {node: '>= 4'}
214
+
215
+
'@devicefarmer/adbkit-monkey@1.2.1':
216
+
resolution: {integrity: sha512-ZzZY/b66W2Jd6NHbAhLyDWOEIBWC11VizGFk7Wx7M61JZRz7HR9Cq5P+65RKWUU7u6wgsE8Lmh9nE4Mz+U2eTg==}
217
+
engines: {node: '>= 0.10.4'}
218
+
219
+
'@devicefarmer/adbkit@3.3.8':
220
+
resolution: {integrity: sha512-7rBLLzWQnBwutH2WZ0EWUkQdihqrnLYCUMaB44hSol9e0/cdIhuNFcqZO0xNheAU6qqHVA8sMiLofkYTgb+lmw==}
221
+
engines: {node: '>= 0.10.4'}
222
+
hasBin: true
223
+
224
+
'@emnapi/runtime@1.4.5':
225
+
resolution: {integrity: sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==}
226
+
227
+
'@esbuild/aix-ppc64@0.25.9':
228
+
resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==}
229
+
engines: {node: '>=18'}
230
+
cpu: [ppc64]
231
+
os: [aix]
232
+
233
+
'@esbuild/android-arm64@0.25.9':
234
+
resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==}
235
+
engines: {node: '>=18'}
236
+
cpu: [arm64]
237
+
os: [android]
238
+
239
+
'@esbuild/android-arm@0.25.9':
240
+
resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==}
241
+
engines: {node: '>=18'}
242
+
cpu: [arm]
243
+
os: [android]
244
+
245
+
'@esbuild/android-x64@0.25.9':
246
+
resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==}
247
+
engines: {node: '>=18'}
248
+
cpu: [x64]
249
+
os: [android]
250
+
251
+
'@esbuild/darwin-arm64@0.25.9':
252
+
resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==}
253
+
engines: {node: '>=18'}
254
+
cpu: [arm64]
255
+
os: [darwin]
256
+
257
+
'@esbuild/darwin-x64@0.25.9':
258
+
resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==}
259
+
engines: {node: '>=18'}
260
+
cpu: [x64]
261
+
os: [darwin]
262
+
263
+
'@esbuild/freebsd-arm64@0.25.9':
264
+
resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==}
265
+
engines: {node: '>=18'}
266
+
cpu: [arm64]
267
+
os: [freebsd]
268
+
269
+
'@esbuild/freebsd-x64@0.25.9':
270
+
resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==}
271
+
engines: {node: '>=18'}
272
+
cpu: [x64]
273
+
os: [freebsd]
274
+
275
+
'@esbuild/linux-arm64@0.25.9':
276
+
resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==}
277
+
engines: {node: '>=18'}
278
+
cpu: [arm64]
279
+
os: [linux]
280
+
281
+
'@esbuild/linux-arm@0.25.9':
282
+
resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==}
283
+
engines: {node: '>=18'}
284
+
cpu: [arm]
285
+
os: [linux]
286
+
287
+
'@esbuild/linux-ia32@0.25.9':
288
+
resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==}
289
+
engines: {node: '>=18'}
290
+
cpu: [ia32]
291
+
os: [linux]
292
+
293
+
'@esbuild/linux-loong64@0.25.9':
294
+
resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==}
295
+
engines: {node: '>=18'}
296
+
cpu: [loong64]
297
+
os: [linux]
298
+
299
+
'@esbuild/linux-mips64el@0.25.9':
300
+
resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==}
301
+
engines: {node: '>=18'}
302
+
cpu: [mips64el]
303
+
os: [linux]
304
+
305
+
'@esbuild/linux-ppc64@0.25.9':
306
+
resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==}
307
+
engines: {node: '>=18'}
308
+
cpu: [ppc64]
309
+
os: [linux]
310
+
311
+
'@esbuild/linux-riscv64@0.25.9':
312
+
resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==}
313
+
engines: {node: '>=18'}
314
+
cpu: [riscv64]
315
+
os: [linux]
316
+
317
+
'@esbuild/linux-s390x@0.25.9':
318
+
resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==}
319
+
engines: {node: '>=18'}
320
+
cpu: [s390x]
321
+
os: [linux]
322
+
323
+
'@esbuild/linux-x64@0.25.9':
324
+
resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==}
325
+
engines: {node: '>=18'}
326
+
cpu: [x64]
327
+
os: [linux]
328
+
329
+
'@esbuild/netbsd-arm64@0.25.9':
330
+
resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==}
331
+
engines: {node: '>=18'}
332
+
cpu: [arm64]
333
+
os: [netbsd]
334
+
335
+
'@esbuild/netbsd-x64@0.25.9':
336
+
resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==}
337
+
engines: {node: '>=18'}
338
+
cpu: [x64]
339
+
os: [netbsd]
340
+
341
+
'@esbuild/openbsd-arm64@0.25.9':
342
+
resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==}
343
+
engines: {node: '>=18'}
344
+
cpu: [arm64]
345
+
os: [openbsd]
346
+
347
+
'@esbuild/openbsd-x64@0.25.9':
348
+
resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==}
349
+
engines: {node: '>=18'}
350
+
cpu: [x64]
351
+
os: [openbsd]
352
+
353
+
'@esbuild/openharmony-arm64@0.25.9':
354
+
resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==}
355
+
engines: {node: '>=18'}
356
+
cpu: [arm64]
357
+
os: [openharmony]
358
+
359
+
'@esbuild/sunos-x64@0.25.9':
360
+
resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==}
361
+
engines: {node: '>=18'}
362
+
cpu: [x64]
363
+
os: [sunos]
364
+
365
+
'@esbuild/win32-arm64@0.25.9':
366
+
resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==}
367
+
engines: {node: '>=18'}
368
+
cpu: [arm64]
369
+
os: [win32]
370
+
371
+
'@esbuild/win32-ia32@0.25.9':
372
+
resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==}
373
+
engines: {node: '>=18'}
374
+
cpu: [ia32]
375
+
os: [win32]
376
+
377
+
'@esbuild/win32-x64@0.25.9':
378
+
resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==}
379
+
engines: {node: '>=18'}
380
+
cpu: [x64]
381
+
os: [win32]
382
+
383
+
'@img/sharp-darwin-arm64@0.34.3':
384
+
resolution: {integrity: sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==}
385
+
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
386
+
cpu: [arm64]
387
+
os: [darwin]
388
+
389
+
'@img/sharp-darwin-x64@0.34.3':
390
+
resolution: {integrity: sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==}
391
+
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
392
+
cpu: [x64]
393
+
os: [darwin]
394
+
395
+
'@img/sharp-libvips-darwin-arm64@1.2.0':
396
+
resolution: {integrity: sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==}
397
+
cpu: [arm64]
398
+
os: [darwin]
399
+
400
+
'@img/sharp-libvips-darwin-x64@1.2.0':
401
+
resolution: {integrity: sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==}
402
+
cpu: [x64]
403
+
os: [darwin]
404
+
405
+
'@img/sharp-libvips-linux-arm64@1.2.0':
406
+
resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==}
407
+
cpu: [arm64]
408
+
os: [linux]
409
+
410
+
'@img/sharp-libvips-linux-arm@1.2.0':
411
+
resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==}
412
+
cpu: [arm]
413
+
os: [linux]
414
+
415
+
'@img/sharp-libvips-linux-ppc64@1.2.0':
416
+
resolution: {integrity: sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==}
417
+
cpu: [ppc64]
418
+
os: [linux]
419
+
420
+
'@img/sharp-libvips-linux-s390x@1.2.0':
421
+
resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==}
422
+
cpu: [s390x]
423
+
os: [linux]
424
+
425
+
'@img/sharp-libvips-linux-x64@1.2.0':
426
+
resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==}
427
+
cpu: [x64]
428
+
os: [linux]
429
+
430
+
'@img/sharp-libvips-linuxmusl-arm64@1.2.0':
431
+
resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==}
432
+
cpu: [arm64]
433
+
os: [linux]
434
+
435
+
'@img/sharp-libvips-linuxmusl-x64@1.2.0':
436
+
resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==}
437
+
cpu: [x64]
438
+
os: [linux]
439
+
440
+
'@img/sharp-linux-arm64@0.34.3':
441
+
resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==}
442
+
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
443
+
cpu: [arm64]
444
+
os: [linux]
445
+
446
+
'@img/sharp-linux-arm@0.34.3':
447
+
resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==}
448
+
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
449
+
cpu: [arm]
450
+
os: [linux]
451
+
452
+
'@img/sharp-linux-ppc64@0.34.3':
453
+
resolution: {integrity: sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==}
454
+
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
455
+
cpu: [ppc64]
456
+
os: [linux]
457
+
458
+
'@img/sharp-linux-s390x@0.34.3':
459
+
resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==}
460
+
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
461
+
cpu: [s390x]
462
+
os: [linux]
463
+
464
+
'@img/sharp-linux-x64@0.34.3':
465
+
resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==}
466
+
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
467
+
cpu: [x64]
468
+
os: [linux]
469
+
470
+
'@img/sharp-linuxmusl-arm64@0.34.3':
471
+
resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==}
472
+
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
473
+
cpu: [arm64]
474
+
os: [linux]
475
+
476
+
'@img/sharp-linuxmusl-x64@0.34.3':
477
+
resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==}
478
+
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
479
+
cpu: [x64]
480
+
os: [linux]
481
+
482
+
'@img/sharp-wasm32@0.34.3':
483
+
resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==}
484
+
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
485
+
cpu: [wasm32]
486
+
487
+
'@img/sharp-win32-arm64@0.34.3':
488
+
resolution: {integrity: sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==}
489
+
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
490
+
cpu: [arm64]
491
+
os: [win32]
492
+
493
+
'@img/sharp-win32-ia32@0.34.3':
494
+
resolution: {integrity: sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==}
495
+
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
496
+
cpu: [ia32]
497
+
os: [win32]
498
+
499
+
'@img/sharp-win32-x64@0.34.3':
500
+
resolution: {integrity: sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==}
501
+
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
502
+
cpu: [x64]
503
+
os: [win32]
504
+
505
+
'@isaacs/balanced-match@4.0.1':
506
+
resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==}
507
+
engines: {node: 20 || >=22}
508
+
509
+
'@isaacs/brace-expansion@5.0.0':
510
+
resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==}
511
+
engines: {node: 20 || >=22}
512
+
513
+
'@isaacs/cliui@8.0.2':
514
+
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
515
+
engines: {node: '>=12'}
516
+
517
+
'@jridgewell/gen-mapping@0.3.13':
518
+
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
519
+
520
+
'@jridgewell/remapping@2.3.5':
521
+
resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
522
+
523
+
'@jridgewell/resolve-uri@3.1.2':
524
+
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
525
+
engines: {node: '>=6.0.0'}
526
+
527
+
'@jridgewell/sourcemap-codec@1.5.5':
528
+
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
529
+
530
+
'@jridgewell/trace-mapping@0.3.30':
531
+
resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==}
532
+
533
+
'@jridgewell/trace-mapping@0.3.31':
534
+
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
535
+
536
+
'@nodelib/fs.scandir@2.1.5':
537
+
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
538
+
engines: {node: '>= 8'}
539
+
540
+
'@nodelib/fs.stat@2.0.5':
541
+
resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
542
+
engines: {node: '>= 8'}
543
+
544
+
'@nodelib/fs.walk@1.2.8':
545
+
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
546
+
engines: {node: '>= 8'}
547
+
548
+
'@pkgjs/parseargs@0.11.0':
549
+
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
550
+
engines: {node: '>=14'}
551
+
552
+
'@pnpm/config.env-replace@1.1.0':
553
+
resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==}
554
+
engines: {node: '>=12.22.0'}
555
+
556
+
'@pnpm/network.ca-file@1.0.2':
557
+
resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==}
558
+
engines: {node: '>=12.22.0'}
559
+
560
+
'@pnpm/npm-conf@2.3.1':
561
+
resolution: {integrity: sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==}
562
+
engines: {node: '>=12'}
563
+
564
+
'@rolldown/pluginutils@1.0.0-beta.34':
565
+
resolution: {integrity: sha512-LyAREkZHP5pMom7c24meKmJCdhf2hEyvam2q0unr3or9ydwDL+DJ8chTF6Av/RFPb3rH8UFBdMzO5MxTZW97oA==}
566
+
567
+
'@rollup/rollup-android-arm-eabi@4.50.1':
568
+
resolution: {integrity: sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==}
569
+
cpu: [arm]
570
+
os: [android]
571
+
572
+
'@rollup/rollup-android-arm64@4.50.1':
573
+
resolution: {integrity: sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==}
574
+
cpu: [arm64]
575
+
os: [android]
576
+
577
+
'@rollup/rollup-darwin-arm64@4.50.1':
578
+
resolution: {integrity: sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==}
579
+
cpu: [arm64]
580
+
os: [darwin]
581
+
582
+
'@rollup/rollup-darwin-x64@4.50.1':
583
+
resolution: {integrity: sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==}
584
+
cpu: [x64]
585
+
os: [darwin]
586
+
587
+
'@rollup/rollup-freebsd-arm64@4.50.1':
588
+
resolution: {integrity: sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==}
589
+
cpu: [arm64]
590
+
os: [freebsd]
591
+
592
+
'@rollup/rollup-freebsd-x64@4.50.1':
593
+
resolution: {integrity: sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==}
594
+
cpu: [x64]
595
+
os: [freebsd]
596
+
597
+
'@rollup/rollup-linux-arm-gnueabihf@4.50.1':
598
+
resolution: {integrity: sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==}
599
+
cpu: [arm]
600
+
os: [linux]
601
+
602
+
'@rollup/rollup-linux-arm-musleabihf@4.50.1':
603
+
resolution: {integrity: sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==}
604
+
cpu: [arm]
605
+
os: [linux]
606
+
607
+
'@rollup/rollup-linux-arm64-gnu@4.50.1':
608
+
resolution: {integrity: sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==}
609
+
cpu: [arm64]
610
+
os: [linux]
611
+
612
+
'@rollup/rollup-linux-arm64-musl@4.50.1':
613
+
resolution: {integrity: sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==}
614
+
cpu: [arm64]
615
+
os: [linux]
616
+
617
+
'@rollup/rollup-linux-loongarch64-gnu@4.50.1':
618
+
resolution: {integrity: sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==}
619
+
cpu: [loong64]
620
+
os: [linux]
621
+
622
+
'@rollup/rollup-linux-ppc64-gnu@4.50.1':
623
+
resolution: {integrity: sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==}
624
+
cpu: [ppc64]
625
+
os: [linux]
626
+
627
+
'@rollup/rollup-linux-riscv64-gnu@4.50.1':
628
+
resolution: {integrity: sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==}
629
+
cpu: [riscv64]
630
+
os: [linux]
631
+
632
+
'@rollup/rollup-linux-riscv64-musl@4.50.1':
633
+
resolution: {integrity: sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==}
634
+
cpu: [riscv64]
635
+
os: [linux]
636
+
637
+
'@rollup/rollup-linux-s390x-gnu@4.50.1':
638
+
resolution: {integrity: sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==}
639
+
cpu: [s390x]
640
+
os: [linux]
641
+
642
+
'@rollup/rollup-linux-x64-gnu@4.50.1':
643
+
resolution: {integrity: sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==}
644
+
cpu: [x64]
645
+
os: [linux]
646
+
647
+
'@rollup/rollup-linux-x64-musl@4.50.1':
648
+
resolution: {integrity: sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==}
649
+
cpu: [x64]
650
+
os: [linux]
651
+
652
+
'@rollup/rollup-openharmony-arm64@4.50.1':
653
+
resolution: {integrity: sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==}
654
+
cpu: [arm64]
655
+
os: [openharmony]
656
+
657
+
'@rollup/rollup-win32-arm64-msvc@4.50.1':
658
+
resolution: {integrity: sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==}
659
+
cpu: [arm64]
660
+
os: [win32]
661
+
662
+
'@rollup/rollup-win32-ia32-msvc@4.50.1':
663
+
resolution: {integrity: sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==}
664
+
cpu: [ia32]
665
+
os: [win32]
666
+
667
+
'@rollup/rollup-win32-x64-msvc@4.50.1':
668
+
resolution: {integrity: sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==}
669
+
cpu: [x64]
670
+
os: [win32]
671
+
672
+
'@types/babel__core@7.20.5':
673
+
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
674
+
675
+
'@types/babel__generator@7.27.0':
676
+
resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==}
677
+
678
+
'@types/babel__template@7.4.4':
679
+
resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
680
+
681
+
'@types/babel__traverse@7.28.0':
682
+
resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
683
+
684
+
'@types/estree@1.0.8':
685
+
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
686
+
687
+
'@types/filesystem@0.0.36':
688
+
resolution: {integrity: sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==}
689
+
690
+
'@types/filewriter@0.0.33':
691
+
resolution: {integrity: sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==}
692
+
693
+
'@types/har-format@1.2.16':
694
+
resolution: {integrity: sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==}
695
+
696
+
'@types/minimatch@3.0.5':
697
+
resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==}
698
+
699
+
'@types/node@24.3.2':
700
+
resolution: {integrity: sha512-6L8PkB+m1SSb2kaGGFk3iXENxl8lrs7cyVl7AXH6pgdMfulDfM6yUrVdjtxdnGrLrGzzuav8fFnZMY+rcscqcA==}
701
+
702
+
'@types/react-dom@19.1.9':
703
+
resolution: {integrity: sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==}
704
+
peerDependencies:
705
+
'@types/react': ^19.0.0
706
+
707
+
'@types/react@19.1.13':
708
+
resolution: {integrity: sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==}
709
+
710
+
'@types/yauzl@2.10.3':
711
+
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
712
+
713
+
'@vitejs/plugin-react@5.0.2':
714
+
resolution: {integrity: sha512-tmyFgixPZCx2+e6VO9TNITWcCQl8+Nl/E8YbAyPVv85QCc7/A3JrdfG2A8gIzvVhWuzMOVrFW1aReaNxrI6tbw==}
715
+
engines: {node: ^20.19.0 || >=22.12.0}
716
+
peerDependencies:
717
+
vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
718
+
719
+
'@webext-core/fake-browser@1.3.2':
720
+
resolution: {integrity: sha512-jFyPWWz+VkHAC9DRIiIPOyu6X/KlC8dYqSKweHz6tsDb86QawtVgZSpYcM+GOQBlZc5DHFo92jJ7cIq4uBnU0A==}
721
+
722
+
'@webext-core/isolated-element@1.1.2':
723
+
resolution: {integrity: sha512-CNHYhsIR8TPkPb+4yqTIuzaGnVn/Fshev5fyoPW+/8Cyc93tJbCjP9PC1XSK6fDWu+xASdPHLZaoa2nWAYoxeQ==}
724
+
725
+
'@webext-core/match-patterns@1.0.3':
726
+
resolution: {integrity: sha512-NY39ACqCxdKBmHgw361M9pfJma8e4AZo20w9AY+5ZjIj1W2dvXC8J31G5fjfOGbulW9w4WKpT8fPooi0mLkn9A==}
727
+
728
+
'@wxt-dev/auto-icons@1.1.0':
729
+
resolution: {integrity: sha512-lDFZjDbrY5gDaapUuUOYTPudE88oB3Z7rTdg0N7iq2WIWga1h0bhzCJDaqNqMvPN2DCYvHFfA0cnqA12vEJjiA==}
730
+
peerDependencies:
731
+
wxt: '>=0.19.0'
732
+
733
+
'@wxt-dev/browser@0.1.4':
734
+
resolution: {integrity: sha512-9x03I15i79XU8qYwjv4le0K2HdMl/Yga2wUBSoUbcrCnamv8P3nvuYxREQ9C5QY/qPAfeEVdAtaTrS3KWak71g==}
735
+
736
+
'@wxt-dev/module-react@1.1.5':
737
+
resolution: {integrity: sha512-KgsUrsgH5rBT8MwiipnDEOHBXmLvTIdFICrI7KjngqSf9DpVRn92HsKmToxY0AYpkP19hHWta2oNYFTzmmm++g==}
738
+
peerDependencies:
739
+
wxt: '>=0.19.16'
740
+
741
+
'@wxt-dev/storage@1.2.0':
742
+
resolution: {integrity: sha512-4A44zCpwl5GZdmUdSJvUWJ6ekZZ+Fz5ttYqTGPIRJSsyosKX8X8Yl7D2Loy1ZlqIg6oJHysaiFXALtTE+pFjpw==}
743
+
744
+
acorn@8.15.0:
745
+
resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
746
+
engines: {node: '>=0.4.0'}
747
+
hasBin: true
748
+
749
+
adm-zip@0.5.16:
750
+
resolution: {integrity: sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==}
751
+
engines: {node: '>=12.0'}
752
+
753
+
ansi-align@3.0.1:
754
+
resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==}
755
+
756
+
ansi-escapes@7.1.0:
757
+
resolution: {integrity: sha512-YdhtCd19sKRKfAAUsrcC1wzm4JuzJoiX4pOJqIoW2qmKj5WzG/dL8uUJ0361zaXtHqK7gEhOwtAtz7t3Yq3X5g==}
758
+
engines: {node: '>=18'}
759
+
760
+
ansi-regex@5.0.1:
761
+
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
762
+
engines: {node: '>=8'}
763
+
764
+
ansi-regex@6.2.0:
765
+
resolution: {integrity: sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==}
766
+
engines: {node: '>=12'}
767
+
768
+
ansi-regex@6.2.2:
769
+
resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==}
770
+
engines: {node: '>=12'}
771
+
772
+
ansi-styles@4.3.0:
773
+
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
774
+
engines: {node: '>=8'}
775
+
776
+
ansi-styles@6.2.1:
777
+
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
778
+
engines: {node: '>=12'}
779
+
780
+
ansi-styles@6.2.3:
781
+
resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
782
+
engines: {node: '>=12'}
783
+
784
+
any-promise@1.3.0:
785
+
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
786
+
787
+
anymatch@3.1.3:
788
+
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
789
+
engines: {node: '>= 8'}
790
+
791
+
arg@5.0.2:
792
+
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
793
+
794
+
array-differ@4.0.0:
795
+
resolution: {integrity: sha512-Q6VPTLMsmXZ47ENG3V+wQyZS1ZxXMxFyYzA+Z/GMrJ6yIutAIEf9wTyroTzmGjNfox9/h3GdGBCVh43GVFx4Uw==}
796
+
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
797
+
798
+
array-union@3.0.1:
799
+
resolution: {integrity: sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==}
800
+
engines: {node: '>=12'}
801
+
802
+
async-mutex@0.5.0:
803
+
resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==}
804
+
805
+
async@3.2.6:
806
+
resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
807
+
808
+
atomic-sleep@1.0.0:
809
+
resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==}
810
+
engines: {node: '>=8.0.0'}
811
+
812
+
atomically@2.0.3:
813
+
resolution: {integrity: sha512-kU6FmrwZ3Lx7/7y3hPS5QnbJfaohcIul5fGqf7ok+4KklIEk9tJ0C2IQPdacSbVUWv6zVHXEBWoWd6NrVMT7Cw==}
814
+
815
+
autoprefixer@10.4.21:
816
+
resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==}
817
+
engines: {node: ^10 || ^12 || >=14}
818
+
hasBin: true
819
+
peerDependencies:
820
+
postcss: ^8.1.0
821
+
822
+
balanced-match@1.0.2:
823
+
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
824
+
825
+
base64-js@1.5.1:
826
+
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
827
+
828
+
baseline-browser-mapping@2.8.2:
829
+
resolution: {integrity: sha512-NvcIedLxrs9llVpX7wI+Jz4Hn9vJQkCPKrTaHIE0sW/Rj1iq6Fzby4NbyTZjQJNoypBXNaG7tEHkTgONZpwgxQ==}
830
+
hasBin: true
831
+
832
+
binary-extensions@2.3.0:
833
+
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
834
+
engines: {node: '>=8'}
835
+
836
+
bl@5.1.0:
837
+
resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==}
838
+
839
+
bluebird@3.7.2:
840
+
resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
841
+
842
+
boolbase@1.0.0:
843
+
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
844
+
845
+
boxen@8.0.1:
846
+
resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==}
847
+
engines: {node: '>=18'}
848
+
849
+
brace-expansion@1.1.12:
850
+
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
851
+
852
+
brace-expansion@2.0.2:
853
+
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
854
+
855
+
braces@3.0.3:
856
+
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
857
+
engines: {node: '>=8'}
858
+
859
+
browserslist@4.25.3:
860
+
resolution: {integrity: sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==}
861
+
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
862
+
hasBin: true
863
+
864
+
browserslist@4.26.0:
865
+
resolution: {integrity: sha512-P9go2WrP9FiPwLv3zqRD/Uoxo0RSHjzFCiQz7d4vbmwNqQFo9T9WCeP/Qn5EbcKQY6DBbkxEXNcpJOmncNrb7A==}
866
+
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
867
+
hasBin: true
868
+
869
+
buffer-crc32@0.2.13:
870
+
resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
871
+
872
+
buffer-from@1.1.2:
873
+
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
874
+
875
+
buffer@6.0.3:
876
+
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
877
+
878
+
bundle-name@4.1.0:
879
+
resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
880
+
engines: {node: '>=18'}
881
+
882
+
c12@3.2.0:
883
+
resolution: {integrity: sha512-ixkEtbYafL56E6HiFuonMm1ZjoKtIo7TH68/uiEq4DAwv9NcUX2nJ95F8TrbMeNjqIkZpruo3ojXQJ+MGG5gcQ==}
884
+
peerDependencies:
885
+
magicast: ^0.3.5
886
+
peerDependenciesMeta:
887
+
magicast:
888
+
optional: true
889
+
890
+
cac@6.7.14:
891
+
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
892
+
engines: {node: '>=8'}
893
+
894
+
camelcase-css@2.0.1:
895
+
resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
896
+
engines: {node: '>= 6'}
897
+
898
+
camelcase@8.0.0:
899
+
resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==}
900
+
engines: {node: '>=16'}
901
+
902
+
caniuse-lite@1.0.30001735:
903
+
resolution: {integrity: sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==}
904
+
905
+
caniuse-lite@1.0.30001741:
906
+
resolution: {integrity: sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==}
907
+
908
+
chalk@4.1.2:
909
+
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
910
+
engines: {node: '>=10'}
911
+
912
+
chalk@5.6.2:
913
+
resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==}
914
+
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
915
+
916
+
chokidar@3.6.0:
917
+
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
918
+
engines: {node: '>= 8.10.0'}
919
+
920
+
chokidar@4.0.3:
921
+
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
922
+
engines: {node: '>= 14.16.0'}
923
+
924
+
chrome-launcher@1.2.0:
925
+
resolution: {integrity: sha512-JbuGuBNss258bvGil7FT4HKdC3SC2K7UAEUqiPy3ACS3Yxo3hAW6bvFpCu2HsIJLgTqxgEX6BkujvzZfLpUD0Q==}
926
+
engines: {node: '>=12.13.0'}
927
+
hasBin: true
928
+
929
+
ci-info@4.3.0:
930
+
resolution: {integrity: sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==}
931
+
engines: {node: '>=8'}
932
+
933
+
citty@0.1.6:
934
+
resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==}
935
+
936
+
cli-boxes@3.0.0:
937
+
resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==}
938
+
engines: {node: '>=10'}
939
+
940
+
cli-cursor@4.0.0:
941
+
resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==}
942
+
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
943
+
944
+
cli-cursor@5.0.0:
945
+
resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==}
946
+
engines: {node: '>=18'}
947
+
948
+
cli-highlight@2.1.11:
949
+
resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==}
950
+
engines: {node: '>=8.0.0', npm: '>=5.0.0'}
951
+
hasBin: true
952
+
953
+
cli-spinners@2.9.2:
954
+
resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==}
955
+
engines: {node: '>=6'}
956
+
957
+
cli-truncate@4.0.0:
958
+
resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==}
959
+
engines: {node: '>=18'}
960
+
961
+
cliui@7.0.4:
962
+
resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
963
+
964
+
cliui@8.0.1:
965
+
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
966
+
engines: {node: '>=12'}
967
+
968
+
clone@1.0.4:
969
+
resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
970
+
engines: {node: '>=0.8'}
971
+
972
+
color-convert@2.0.1:
973
+
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
974
+
engines: {node: '>=7.0.0'}
975
+
976
+
color-name@1.1.4:
977
+
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
978
+
979
+
color-string@1.9.1:
980
+
resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
981
+
982
+
color@4.2.3:
983
+
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
984
+
engines: {node: '>=12.5.0'}
985
+
986
+
colorette@2.0.20:
987
+
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
988
+
989
+
commander@2.9.0:
990
+
resolution: {integrity: sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==}
991
+
engines: {node: '>= 0.6.x'}
992
+
993
+
commander@4.1.1:
994
+
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
995
+
engines: {node: '>= 6'}
996
+
997
+
commander@9.5.0:
998
+
resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
999
+
engines: {node: ^12.20.0 || >=14}
1000
+
1001
+
concat-map@0.0.1:
1002
+
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
1003
+
1004
+
concat-stream@1.6.2:
1005
+
resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==}
1006
+
engines: {'0': node >= 0.8}
1007
+
1008
+
confbox@0.1.8:
1009
+
resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
1010
+
1011
+
confbox@0.2.2:
1012
+
resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==}
1013
+
1014
+
config-chain@1.1.13:
1015
+
resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==}
1016
+
1017
+
configstore@7.0.0:
1018
+
resolution: {integrity: sha512-yk7/5PN5im4qwz0WFZW3PXnzHgPu9mX29Y8uZ3aefe2lBPC1FYttWZRcaW9fKkT0pBCJyuQ2HfbmPVaODi9jcQ==}
1019
+
engines: {node: '>=18'}
1020
+
1021
+
consola@3.4.2:
1022
+
resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==}
1023
+
engines: {node: ^14.18.0 || >=16.10.0}
1024
+
1025
+
convert-source-map@2.0.0:
1026
+
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
1027
+
1028
+
core-util-is@1.0.3:
1029
+
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
1030
+
1031
+
cross-spawn@7.0.6:
1032
+
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
1033
+
engines: {node: '>= 8'}
1034
+
1035
+
css-select@5.2.2:
1036
+
resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==}
1037
+
1038
+
css-what@6.2.2:
1039
+
resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==}
1040
+
engines: {node: '>= 6'}
1041
+
1042
+
cssesc@3.0.0:
1043
+
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
1044
+
engines: {node: '>=4'}
1045
+
hasBin: true
1046
+
1047
+
cssom@0.5.0:
1048
+
resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==}
1049
+
1050
+
csstype@3.1.3:
1051
+
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
1052
+
1053
+
debounce@1.2.1:
1054
+
resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==}
1055
+
1056
+
debug@4.3.7:
1057
+
resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
1058
+
engines: {node: '>=6.0'}
1059
+
peerDependencies:
1060
+
supports-color: '*'
1061
+
peerDependenciesMeta:
1062
+
supports-color:
1063
+
optional: true
1064
+
1065
+
debug@4.4.1:
1066
+
resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
1067
+
engines: {node: '>=6.0'}
1068
+
peerDependencies:
1069
+
supports-color: '*'
1070
+
peerDependenciesMeta:
1071
+
supports-color:
1072
+
optional: true
1073
+
1074
+
deep-extend@0.6.0:
1075
+
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
1076
+
engines: {node: '>=4.0.0'}
1077
+
1078
+
default-browser-id@5.0.0:
1079
+
resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==}
1080
+
engines: {node: '>=18'}
1081
+
1082
+
default-browser@5.2.1:
1083
+
resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==}
1084
+
engines: {node: '>=18'}
1085
+
1086
+
defaults@1.0.4:
1087
+
resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
1088
+
1089
+
define-lazy-prop@2.0.0:
1090
+
resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==}
1091
+
engines: {node: '>=8'}
1092
+
1093
+
define-lazy-prop@3.0.0:
1094
+
resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
1095
+
engines: {node: '>=12'}
1096
+
1097
+
defu@6.1.4:
1098
+
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
1099
+
1100
+
dequal@2.0.3:
1101
+
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
1102
+
engines: {node: '>=6'}
1103
+
1104
+
destr@2.0.5:
1105
+
resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==}
1106
+
1107
+
detect-libc@2.0.4:
1108
+
resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
1109
+
engines: {node: '>=8'}
1110
+
1111
+
didyoumean@1.2.2:
1112
+
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
1113
+
1114
+
dlv@1.1.3:
1115
+
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
1116
+
1117
+
dom-serializer@2.0.0:
1118
+
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
1119
+
1120
+
domelementtype@2.3.0:
1121
+
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
1122
+
1123
+
domhandler@5.0.3:
1124
+
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
1125
+
engines: {node: '>= 4'}
1126
+
1127
+
domutils@3.2.2:
1128
+
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
1129
+
1130
+
dot-prop@9.0.0:
1131
+
resolution: {integrity: sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==}
1132
+
engines: {node: '>=18'}
1133
+
1134
+
dotenv-expand@12.0.3:
1135
+
resolution: {integrity: sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==}
1136
+
engines: {node: '>=12'}
1137
+
1138
+
dotenv@16.6.1:
1139
+
resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==}
1140
+
engines: {node: '>=12'}
1141
+
1142
+
dotenv@17.2.2:
1143
+
resolution: {integrity: sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==}
1144
+
engines: {node: '>=12'}
1145
+
1146
+
eastasianwidth@0.2.0:
1147
+
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
1148
+
1149
+
electron-to-chromium@1.5.207:
1150
+
resolution: {integrity: sha512-mryFrrL/GXDTmAtIVMVf+eIXM09BBPlO5IQ7lUyKmK8d+A4VpRGG+M3ofoVef6qyF8s60rJei8ymlJxjUA8Faw==}
1151
+
1152
+
electron-to-chromium@1.5.218:
1153
+
resolution: {integrity: sha512-uwwdN0TUHs8u6iRgN8vKeWZMRll4gBkz+QMqdS7DDe49uiK68/UX92lFb61oiFPrpYZNeZIqa4bA7O6Aiasnzg==}
1154
+
1155
+
emoji-regex@10.5.0:
1156
+
resolution: {integrity: sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==}
1157
+
1158
+
emoji-regex@8.0.0:
1159
+
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
1160
+
1161
+
emoji-regex@9.2.2:
1162
+
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
1163
+
1164
+
end-of-stream@1.4.5:
1165
+
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
1166
+
1167
+
entities@4.5.0:
1168
+
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
1169
+
engines: {node: '>=0.12'}
1170
+
1171
+
entities@6.0.1:
1172
+
resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
1173
+
engines: {node: '>=0.12'}
1174
+
1175
+
environment@1.1.0:
1176
+
resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
1177
+
engines: {node: '>=18'}
1178
+
1179
+
error-ex@1.3.2:
1180
+
resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
1181
+
1182
+
es-module-lexer@1.7.0:
1183
+
resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
1184
+
1185
+
es6-error@4.1.1:
1186
+
resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==}
1187
+
1188
+
esbuild@0.25.9:
1189
+
resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==}
1190
+
engines: {node: '>=18'}
1191
+
hasBin: true
1192
+
1193
+
escalade@3.2.0:
1194
+
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
1195
+
engines: {node: '>=6'}
1196
+
1197
+
escape-goat@4.0.0:
1198
+
resolution: {integrity: sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==}
1199
+
engines: {node: '>=12'}
1200
+
1201
+
escape-string-regexp@4.0.0:
1202
+
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
1203
+
engines: {node: '>=10'}
1204
+
1205
+
escape-string-regexp@5.0.0:
1206
+
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
1207
+
engines: {node: '>=12'}
1208
+
1209
+
estree-walker@3.0.3:
1210
+
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
1211
+
1212
+
eventemitter3@5.0.1:
1213
+
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
1214
+
1215
+
exsolve@1.0.7:
1216
+
resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==}
1217
+
1218
+
extract-zip@2.0.1:
1219
+
resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==}
1220
+
engines: {node: '>= 10.17.0'}
1221
+
hasBin: true
1222
+
1223
+
fast-glob@3.3.3:
1224
+
resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
1225
+
engines: {node: '>=8.6.0'}
1226
+
1227
+
fast-redact@3.5.0:
1228
+
resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==}
1229
+
engines: {node: '>=6'}
1230
+
1231
+
fastq@1.19.1:
1232
+
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
1233
+
1234
+
fd-slicer@1.1.0:
1235
+
resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
1236
+
1237
+
fdir@6.5.0:
1238
+
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
1239
+
engines: {node: '>=12.0.0'}
1240
+
peerDependencies:
1241
+
picomatch: ^3 || ^4
1242
+
peerDependenciesMeta:
1243
+
picomatch:
1244
+
optional: true
1245
+
1246
+
filesize@11.0.2:
1247
+
resolution: {integrity: sha512-s/iAeeWLk5BschUIpmdrF8RA8lhFZ/xDZgKw1Tan72oGws1/dFGB06nYEiyyssWUfjKNQTNRlrwMVjO9/hvXDw==}
1248
+
engines: {node: '>= 10.4.0'}
1249
+
1250
+
fill-range@7.1.1:
1251
+
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
1252
+
engines: {node: '>=8'}
1253
+
1254
+
firefox-profile@4.7.0:
1255
+
resolution: {integrity: sha512-aGApEu5bfCNbA4PGUZiRJAIU6jKmghV2UVdklXAofnNtiDjqYw0czLS46W7IfFqVKgKhFB8Ao2YoNGHY4BoIMQ==}
1256
+
engines: {node: '>=18'}
1257
+
hasBin: true
1258
+
1259
+
foreground-child@3.3.1:
1260
+
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
1261
+
engines: {node: '>=14'}
1262
+
1263
+
formdata-node@6.0.3:
1264
+
resolution: {integrity: sha512-8e1++BCiTzUno9v5IZ2J6bv4RU+3UKDmqWUQD0MIMVCd9AdhWkO1gw57oo1mNEX1dMq2EGI+FbWz4B92pscSQg==}
1265
+
engines: {node: '>= 18'}
1266
+
1267
+
fraction.js@4.3.7:
1268
+
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
1269
+
1270
+
fs-extra@11.3.1:
1271
+
resolution: {integrity: sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==}
1272
+
engines: {node: '>=14.14'}
1273
+
1274
+
fsevents@2.3.3:
1275
+
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
1276
+
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
1277
+
os: [darwin]
1278
+
1279
+
function-bind@1.1.2:
1280
+
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
1281
+
1282
+
fx-runner@1.4.0:
1283
+
resolution: {integrity: sha512-rci1g6U0rdTg6bAaBboP7XdRu01dzTAaKXxFf+PUqGuCv6Xu7o8NZdY1D5MvKGIjb6EdS1g3VlXOgksir1uGkg==}
1284
+
hasBin: true
1285
+
1286
+
gensync@1.0.0-beta.2:
1287
+
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
1288
+
engines: {node: '>=6.9.0'}
1289
+
1290
+
get-caller-file@2.0.5:
1291
+
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
1292
+
engines: {node: 6.* || 8.* || >= 10.*}
1293
+
1294
+
get-east-asian-width@1.4.0:
1295
+
resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==}
1296
+
engines: {node: '>=18'}
1297
+
1298
+
get-port-please@3.2.0:
1299
+
resolution: {integrity: sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==}
1300
+
1301
+
get-stream@5.2.0:
1302
+
resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
1303
+
engines: {node: '>=8'}
1304
+
1305
+
giget@2.0.0:
1306
+
resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==}
1307
+
hasBin: true
1308
+
1309
+
glob-parent@5.1.2:
1310
+
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
1311
+
engines: {node: '>= 6'}
1312
+
1313
+
glob-parent@6.0.2:
1314
+
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
1315
+
engines: {node: '>=10.13.0'}
1316
+
1317
+
glob-to-regexp@0.4.1:
1318
+
resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==}
1319
+
1320
+
glob@10.4.5:
1321
+
resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
1322
+
hasBin: true
1323
+
1324
+
global-directory@4.0.1:
1325
+
resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==}
1326
+
engines: {node: '>=18'}
1327
+
1328
+
graceful-fs@4.2.10:
1329
+
resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
1330
+
1331
+
graceful-fs@4.2.11:
1332
+
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
1333
+
1334
+
graceful-readlink@1.0.1:
1335
+
resolution: {integrity: sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==}
1336
+
1337
+
growly@1.3.0:
1338
+
resolution: {integrity: sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==}
1339
+
1340
+
has-flag@4.0.0:
1341
+
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
1342
+
engines: {node: '>=8'}
1343
+
1344
+
hasown@2.0.2:
1345
+
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
1346
+
engines: {node: '>= 0.4'}
1347
+
1348
+
highlight.js@10.7.3:
1349
+
resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==}
1350
+
1351
+
hookable@5.5.3:
1352
+
resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
1353
+
1354
+
html-escaper@3.0.3:
1355
+
resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==}
1356
+
1357
+
htmlparser2@10.0.0:
1358
+
resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==}
1359
+
1360
+
ieee754@1.2.1:
1361
+
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
1362
+
1363
+
immediate@3.0.6:
1364
+
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
1365
+
1366
+
import-meta-resolve@4.2.0:
1367
+
resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==}
1368
+
1369
+
inherits@2.0.4:
1370
+
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
1371
+
1372
+
ini@1.3.8:
1373
+
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
1374
+
1375
+
ini@4.1.1:
1376
+
resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==}
1377
+
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
1378
+
1379
+
ini@4.1.3:
1380
+
resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==}
1381
+
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
1382
+
1383
+
is-absolute@0.1.7:
1384
+
resolution: {integrity: sha512-Xi9/ZSn4NFapG8RP98iNPMOeaV3mXPisxKxzKtHVqr3g56j/fBn+yZmnxSVAA8lmZbl2J9b/a4kJvfU3hqQYgA==}
1385
+
engines: {node: '>=0.10.0'}
1386
+
1387
+
is-arrayish@0.2.1:
1388
+
resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
1389
+
1390
+
is-arrayish@0.3.2:
1391
+
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
1392
+
1393
+
is-binary-path@2.1.0:
1394
+
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
1395
+
engines: {node: '>=8'}
1396
+
1397
+
is-core-module@2.16.1:
1398
+
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
1399
+
engines: {node: '>= 0.4'}
1400
+
1401
+
is-docker@2.2.1:
1402
+
resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
1403
+
engines: {node: '>=8'}
1404
+
hasBin: true
1405
+
1406
+
is-docker@3.0.0:
1407
+
resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==}
1408
+
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
1409
+
hasBin: true
1410
+
1411
+
is-extglob@2.1.1:
1412
+
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
1413
+
engines: {node: '>=0.10.0'}
1414
+
1415
+
is-fullwidth-code-point@3.0.0:
1416
+
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
1417
+
engines: {node: '>=8'}
1418
+
1419
+
is-fullwidth-code-point@4.0.0:
1420
+
resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==}
1421
+
engines: {node: '>=12'}
1422
+
1423
+
is-fullwidth-code-point@5.1.0:
1424
+
resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==}
1425
+
engines: {node: '>=18'}
1426
+
1427
+
is-glob@4.0.3:
1428
+
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
1429
+
engines: {node: '>=0.10.0'}
1430
+
1431
+
is-in-ci@1.0.0:
1432
+
resolution: {integrity: sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==}
1433
+
engines: {node: '>=18'}
1434
+
hasBin: true
1435
+
1436
+
is-inside-container@1.0.0:
1437
+
resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==}
1438
+
engines: {node: '>=14.16'}
1439
+
hasBin: true
1440
+
1441
+
is-installed-globally@1.0.0:
1442
+
resolution: {integrity: sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==}
1443
+
engines: {node: '>=18'}
1444
+
1445
+
is-interactive@2.0.0:
1446
+
resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==}
1447
+
engines: {node: '>=12'}
1448
+
1449
+
is-npm@6.1.0:
1450
+
resolution: {integrity: sha512-O2z4/kNgyjhQwVR1Wpkbfc19JIhggF97NZNCpWTnjH7kVcZMUrnut9XSN7txI7VdyIYk5ZatOq3zvSuWpU8hoA==}
1451
+
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
1452
+
1453
+
is-number@7.0.0:
1454
+
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
1455
+
engines: {node: '>=0.12.0'}
1456
+
1457
+
is-path-inside@4.0.0:
1458
+
resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==}
1459
+
engines: {node: '>=12'}
1460
+
1461
+
is-plain-object@2.0.4:
1462
+
resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==}
1463
+
engines: {node: '>=0.10.0'}
1464
+
1465
+
is-potential-custom-element-name@1.0.1:
1466
+
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
1467
+
1468
+
is-primitive@3.0.1:
1469
+
resolution: {integrity: sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w==}
1470
+
engines: {node: '>=0.10.0'}
1471
+
1472
+
is-relative@0.1.3:
1473
+
resolution: {integrity: sha512-wBOr+rNM4gkAZqoLRJI4myw5WzzIdQosFAAbnvfXP5z1LyzgAI3ivOKehC5KfqlQJZoihVhirgtCBj378Eg8GA==}
1474
+
engines: {node: '>=0.10.0'}
1475
+
1476
+
is-unicode-supported@1.3.0:
1477
+
resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==}
1478
+
engines: {node: '>=12'}
1479
+
1480
+
is-unicode-supported@2.1.0:
1481
+
resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==}
1482
+
engines: {node: '>=18'}
1483
+
1484
+
is-wsl@2.2.0:
1485
+
resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
1486
+
engines: {node: '>=8'}
1487
+
1488
+
is-wsl@3.1.0:
1489
+
resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==}
1490
+
engines: {node: '>=16'}
1491
+
1492
+
isarray@1.0.0:
1493
+
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
1494
+
1495
+
isexe@1.1.2:
1496
+
resolution: {integrity: sha512-d2eJzK691yZwPHcv1LbeAOa91yMJ9QmfTgSO1oXB65ezVhXQsxBac2vEB4bMVms9cGzaA99n6V2viHMq82VLDw==}
1497
+
1498
+
isexe@2.0.0:
1499
+
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
1500
+
1501
+
isobject@3.0.1:
1502
+
resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==}
1503
+
engines: {node: '>=0.10.0'}
1504
+
1505
+
jackspeak@3.4.3:
1506
+
resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
1507
+
1508
+
jiti@1.21.7:
1509
+
resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==}
1510
+
hasBin: true
1511
+
1512
+
jiti@2.5.1:
1513
+
resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==}
1514
+
hasBin: true
1515
+
1516
+
js-tokens@4.0.0:
1517
+
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
1518
+
1519
+
js-tokens@9.0.1:
1520
+
resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
1521
+
1522
+
jsesc@3.1.0:
1523
+
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
1524
+
engines: {node: '>=6'}
1525
+
hasBin: true
1526
+
1527
+
json-parse-even-better-errors@3.0.2:
1528
+
resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==}
1529
+
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
1530
+
1531
+
json5@2.2.3:
1532
+
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
1533
+
engines: {node: '>=6'}
1534
+
hasBin: true
1535
+
1536
+
jsonfile@6.2.0:
1537
+
resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==}
1538
+
1539
+
jszip@3.10.1:
1540
+
resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
1541
+
1542
+
kleur@3.0.3:
1543
+
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
1544
+
engines: {node: '>=6'}
1545
+
1546
+
ky@1.10.0:
1547
+
resolution: {integrity: sha512-YRPCzHEWZffbfvmRrfwa+5nwBHwZuYiTrfDX0wuhGBPV0pA/zCqcOq93MDssON/baIkpYbvehIX5aLpMxrRhaA==}
1548
+
engines: {node: '>=18'}
1549
+
1550
+
latest-version@9.0.0:
1551
+
resolution: {integrity: sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA==}
1552
+
engines: {node: '>=18'}
1553
+
1554
+
lie@3.3.0:
1555
+
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
1556
+
1557
+
lighthouse-logger@2.0.2:
1558
+
resolution: {integrity: sha512-vWl2+u5jgOQuZR55Z1WM0XDdrJT6mzMP8zHUct7xTlWhuQs+eV0g+QL0RQdFjT54zVmbhLCP8vIVpy1wGn/gCg==}
1559
+
1560
+
lilconfig@2.1.0:
1561
+
resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
1562
+
engines: {node: '>=10'}
1563
+
1564
+
lilconfig@3.1.3:
1565
+
resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
1566
+
engines: {node: '>=14'}
1567
+
1568
+
lines-and-columns@1.2.4:
1569
+
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
1570
+
1571
+
lines-and-columns@2.0.4:
1572
+
resolution: {integrity: sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==}
1573
+
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
1574
+
1575
+
linkedom@0.18.12:
1576
+
resolution: {integrity: sha512-jalJsOwIKuQJSeTvsgzPe9iJzyfVaEJiEXl+25EkKevsULHvMJzpNqwvj1jOESWdmgKDiXObyjOYwlUqG7wo1Q==}
1577
+
engines: {node: '>=16'}
1578
+
peerDependencies:
1579
+
canvas: '>= 2'
1580
+
peerDependenciesMeta:
1581
+
canvas:
1582
+
optional: true
1583
+
1584
+
listr2@8.3.3:
1585
+
resolution: {integrity: sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==}
1586
+
engines: {node: '>=18.0.0'}
1587
+
1588
+
local-pkg@1.1.2:
1589
+
resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==}
1590
+
engines: {node: '>=14'}
1591
+
1592
+
lodash.camelcase@4.3.0:
1593
+
resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
1594
+
1595
+
lodash.kebabcase@4.1.1:
1596
+
resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==}
1597
+
1598
+
lodash.merge@4.6.2:
1599
+
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
1600
+
1601
+
lodash.snakecase@4.1.1:
1602
+
resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==}
1603
+
1604
+
log-symbols@5.1.0:
1605
+
resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==}
1606
+
engines: {node: '>=12'}
1607
+
1608
+
log-symbols@6.0.0:
1609
+
resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==}
1610
+
engines: {node: '>=18'}
1611
+
1612
+
log-update@6.1.0:
1613
+
resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==}
1614
+
engines: {node: '>=18'}
1615
+
1616
+
lru-cache@10.4.3:
1617
+
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
1618
+
1619
+
lru-cache@5.1.1:
1620
+
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
1621
+
1622
+
magic-string@0.30.19:
1623
+
resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==}
1624
+
1625
+
magicast@0.3.5:
1626
+
resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==}
1627
+
1628
+
make-error@1.3.6:
1629
+
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
1630
+
1631
+
many-keys-map@2.0.1:
1632
+
resolution: {integrity: sha512-DHnZAD4phTbZ+qnJdjoNEVU1NecYoSdbOOoVmTDH46AuxDkEVh3MxTVpXq10GtcTC6mndN9dkv1rNfpjRcLnOw==}
1633
+
1634
+
marky@1.3.0:
1635
+
resolution: {integrity: sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==}
1636
+
1637
+
merge2@1.4.1:
1638
+
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
1639
+
engines: {node: '>= 8'}
1640
+
1641
+
micromatch@4.0.8:
1642
+
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
1643
+
engines: {node: '>=8.6'}
1644
+
1645
+
mimic-fn@2.1.0:
1646
+
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
1647
+
engines: {node: '>=6'}
1648
+
1649
+
mimic-function@5.0.1:
1650
+
resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
1651
+
engines: {node: '>=18'}
1652
+
1653
+
minimatch@10.0.3:
1654
+
resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==}
1655
+
engines: {node: 20 || >=22}
1656
+
1657
+
minimatch@3.1.2:
1658
+
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
1659
+
1660
+
minimatch@9.0.5:
1661
+
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
1662
+
engines: {node: '>=16 || 14 >=14.17'}
1663
+
1664
+
minimist@1.2.8:
1665
+
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
1666
+
1667
+
minipass@7.1.2:
1668
+
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
1669
+
engines: {node: '>=16 || 14 >=14.17'}
1670
+
1671
+
mlly@1.8.0:
1672
+
resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==}
1673
+
1674
+
ms@2.1.3:
1675
+
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
1676
+
1677
+
multimatch@6.0.0:
1678
+
resolution: {integrity: sha512-I7tSVxHGPlmPN/enE3mS1aOSo6bWBfls+3HmuEeCUBCE7gWnm3cBXCBkpurzFjVRwC6Kld8lLaZ1Iv5vOcjvcQ==}
1679
+
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
1680
+
1681
+
mz@2.7.0:
1682
+
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
1683
+
1684
+
nano-spawn@1.0.3:
1685
+
resolution: {integrity: sha512-jtpsQDetTnvS2Ts1fiRdci5rx0VYws5jGyC+4IYOTnIQ/wwdf6JdomlHBwqC3bJYOvaKu0C2GSZ1A60anrYpaA==}
1686
+
engines: {node: '>=20.17'}
1687
+
1688
+
nanoid@3.3.11:
1689
+
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
1690
+
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
1691
+
hasBin: true
1692
+
1693
+
node-fetch-native@1.6.7:
1694
+
resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
1695
+
1696
+
node-forge@1.3.1:
1697
+
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
1698
+
engines: {node: '>= 6.13.0'}
1699
+
1700
+
node-notifier@10.0.1:
1701
+
resolution: {integrity: sha512-YX7TSyDukOZ0g+gmzjB6abKu+hTGvO8+8+gIFDsRCU2t8fLV/P2unmt+LGFaIa4y64aX98Qksa97rgz4vMNeLQ==}
1702
+
1703
+
node-releases@2.0.19:
1704
+
resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
1705
+
1706
+
node-releases@2.0.21:
1707
+
resolution: {integrity: sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==}
1708
+
1709
+
normalize-path@3.0.0:
1710
+
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
1711
+
engines: {node: '>=0.10.0'}
1712
+
1713
+
normalize-range@0.1.2:
1714
+
resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
1715
+
engines: {node: '>=0.10.0'}
1716
+
1717
+
nth-check@2.1.1:
1718
+
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
1719
+
1720
+
nypm@0.6.1:
1721
+
resolution: {integrity: sha512-hlacBiRiv1k9hZFiphPUkfSQ/ZfQzZDzC+8z0wL3lvDAOUu/2NnChkKuMoMjNur/9OpKuz2QsIeiPVN0xM5Q0w==}
1722
+
engines: {node: ^14.16.0 || >=16.10.0}
1723
+
hasBin: true
1724
+
1725
+
object-assign@4.1.1:
1726
+
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
1727
+
engines: {node: '>=0.10.0'}
1728
+
1729
+
object-hash@3.0.0:
1730
+
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
1731
+
engines: {node: '>= 6'}
1732
+
1733
+
ofetch@1.4.1:
1734
+
resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==}
1735
+
1736
+
ohash@2.0.11:
1737
+
resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==}
1738
+
1739
+
on-exit-leak-free@2.1.2:
1740
+
resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==}
1741
+
engines: {node: '>=14.0.0'}
1742
+
1743
+
once@1.4.0:
1744
+
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
1745
+
1746
+
onetime@5.1.2:
1747
+
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
1748
+
engines: {node: '>=6'}
1749
+
1750
+
onetime@7.0.0:
1751
+
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
1752
+
engines: {node: '>=18'}
1753
+
1754
+
open@10.2.0:
1755
+
resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==}
1756
+
engines: {node: '>=18'}
1757
+
1758
+
open@8.4.2:
1759
+
resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
1760
+
engines: {node: '>=12'}
1761
+
1762
+
ora@6.3.1:
1763
+
resolution: {integrity: sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ==}
1764
+
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
1765
+
1766
+
ora@8.2.0:
1767
+
resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==}
1768
+
engines: {node: '>=18'}
1769
+
1770
+
os-shim@0.1.3:
1771
+
resolution: {integrity: sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A==}
1772
+
engines: {node: '>= 0.4.0'}
1773
+
1774
+
package-json-from-dist@1.0.1:
1775
+
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
1776
+
1777
+
package-json@10.0.1:
1778
+
resolution: {integrity: sha512-ua1L4OgXSBdsu1FPb7F3tYH0F48a6kxvod4pLUlGY9COeJAJQNX/sNH2IiEmsxw7lqYiAwrdHMjz1FctOsyDQg==}
1779
+
engines: {node: '>=18'}
1780
+
1781
+
pako@1.0.11:
1782
+
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
1783
+
1784
+
parse-json@7.1.1:
1785
+
resolution: {integrity: sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==}
1786
+
engines: {node: '>=16'}
1787
+
1788
+
parse5-htmlparser2-tree-adapter@6.0.1:
1789
+
resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==}
1790
+
1791
+
parse5@5.1.1:
1792
+
resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==}
1793
+
1794
+
parse5@6.0.1:
1795
+
resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
1796
+
1797
+
path-key@3.1.1:
1798
+
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
1799
+
engines: {node: '>=8'}
1800
+
1801
+
path-parse@1.0.7:
1802
+
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
1803
+
1804
+
path-scurry@1.11.1:
1805
+
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
1806
+
engines: {node: '>=16 || 14 >=14.18'}
1807
+
1808
+
pathe@2.0.3:
1809
+
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
1810
+
1811
+
pend@1.2.0:
1812
+
resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
1813
+
1814
+
perfect-debounce@1.0.0:
1815
+
resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
1816
+
1817
+
perfect-debounce@2.0.0:
1818
+
resolution: {integrity: sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==}
1819
+
1820
+
picocolors@1.1.1:
1821
+
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
1822
+
1823
+
picomatch@2.3.1:
1824
+
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
1825
+
engines: {node: '>=8.6'}
1826
+
1827
+
picomatch@4.0.3:
1828
+
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
1829
+
engines: {node: '>=12'}
1830
+
1831
+
pify@2.3.0:
1832
+
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
1833
+
engines: {node: '>=0.10.0'}
1834
+
1835
+
pino-abstract-transport@2.0.0:
1836
+
resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==}
1837
+
1838
+
pino-std-serializers@7.0.0:
1839
+
resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==}
1840
+
1841
+
pino@9.7.0:
1842
+
resolution: {integrity: sha512-vnMCM6xZTb1WDmLvtG2lE/2p+t9hDEIvTWJsu6FejkE62vB7gDhvzrpFR4Cw2to+9JNQxVnkAKVPA1KPB98vWg==}
1843
+
hasBin: true
1844
+
1845
+
pirates@4.0.7:
1846
+
resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
1847
+
engines: {node: '>= 6'}
1848
+
1849
+
pkg-types@1.3.1:
1850
+
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
1851
+
1852
+
pkg-types@2.3.0:
1853
+
resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
1854
+
1855
+
postcss-import@15.1.0:
1856
+
resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
1857
+
engines: {node: '>=14.0.0'}
1858
+
peerDependencies:
1859
+
postcss: ^8.0.0
1860
+
1861
+
postcss-js@4.0.1:
1862
+
resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
1863
+
engines: {node: ^12 || ^14 || >= 16}
1864
+
peerDependencies:
1865
+
postcss: ^8.4.21
1866
+
1867
+
postcss-load-config@4.0.2:
1868
+
resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
1869
+
engines: {node: '>= 14'}
1870
+
peerDependencies:
1871
+
postcss: '>=8.0.9'
1872
+
ts-node: '>=9.0.0'
1873
+
peerDependenciesMeta:
1874
+
postcss:
1875
+
optional: true
1876
+
ts-node:
1877
+
optional: true
1878
+
1879
+
postcss-nested@6.2.0:
1880
+
resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==}
1881
+
engines: {node: '>=12.0'}
1882
+
peerDependencies:
1883
+
postcss: ^8.2.14
1884
+
1885
+
postcss-selector-parser@6.1.2:
1886
+
resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
1887
+
engines: {node: '>=4'}
1888
+
1889
+
postcss-value-parser@4.2.0:
1890
+
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
1891
+
1892
+
postcss@8.5.6:
1893
+
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
1894
+
engines: {node: ^10 || ^12 || >=14}
1895
+
1896
+
process-nextick-args@2.0.1:
1897
+
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
1898
+
1899
+
process-warning@5.0.0:
1900
+
resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==}
1901
+
1902
+
promise-toolbox@0.21.0:
1903
+
resolution: {integrity: sha512-NV8aTmpwrZv+Iys54sSFOBx3tuVaOBvvrft5PNppnxy9xpU/akHbaWIril22AB22zaPgrgwKdD0KsrM0ptUtpg==}
1904
+
engines: {node: '>=6'}
1905
+
1906
+
prompts@2.4.2:
1907
+
resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
1908
+
engines: {node: '>= 6'}
1909
+
1910
+
proto-list@1.2.4:
1911
+
resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
1912
+
1913
+
publish-browser-extension@3.0.2:
1914
+
resolution: {integrity: sha512-yZLPF/WyyaKYUHmurDcSMYpgZLqpUkx/4482bLpelHyRlyghjo3951pJXw/KunMnO6pdwWEZGr0AJnvlls2H8g==}
1915
+
engines: {node: ^18.0.0 || >=20.0.0}
1916
+
hasBin: true
1917
+
1918
+
pump@3.0.3:
1919
+
resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==}
1920
+
1921
+
pupa@3.1.0:
1922
+
resolution: {integrity: sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==}
1923
+
engines: {node: '>=12.20'}
1924
+
1925
+
quansync@0.2.11:
1926
+
resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==}
1927
+
1928
+
queue-microtask@1.2.3:
1929
+
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
1930
+
1931
+
quick-format-unescaped@4.0.4:
1932
+
resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==}
1933
+
1934
+
rc9@2.1.2:
1935
+
resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==}
1936
+
1937
+
rc@1.2.8:
1938
+
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
1939
+
hasBin: true
1940
+
1941
+
react-dom@19.1.1:
1942
+
resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==}
1943
+
peerDependencies:
1944
+
react: ^19.1.1
1945
+
1946
+
react-icons@5.5.0:
1947
+
resolution: {integrity: sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==}
1948
+
peerDependencies:
1949
+
react: '*'
1950
+
1951
+
react-refresh@0.17.0:
1952
+
resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
1953
+
engines: {node: '>=0.10.0'}
1954
+
1955
+
react@19.1.1:
1956
+
resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==}
1957
+
engines: {node: '>=0.10.0'}
1958
+
1959
+
read-cache@1.0.0:
1960
+
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
1961
+
1962
+
readable-stream@2.3.8:
1963
+
resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
1964
+
1965
+
readable-stream@3.6.2:
1966
+
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
1967
+
engines: {node: '>= 6'}
1968
+
1969
+
readdirp@3.6.0:
1970
+
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
1971
+
engines: {node: '>=8.10.0'}
1972
+
1973
+
readdirp@4.1.2:
1974
+
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
1975
+
engines: {node: '>= 14.18.0'}
1976
+
1977
+
real-require@0.2.0:
1978
+
resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
1979
+
engines: {node: '>= 12.13.0'}
1980
+
1981
+
registry-auth-token@5.1.0:
1982
+
resolution: {integrity: sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==}
1983
+
engines: {node: '>=14'}
1984
+
1985
+
registry-url@6.0.1:
1986
+
resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==}
1987
+
engines: {node: '>=12'}
1988
+
1989
+
require-directory@2.1.1:
1990
+
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
1991
+
engines: {node: '>=0.10.0'}
1992
+
1993
+
resolve@1.22.10:
1994
+
resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
1995
+
engines: {node: '>= 0.4'}
1996
+
hasBin: true
1997
+
1998
+
restore-cursor@4.0.0:
1999
+
resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==}
2000
+
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
2001
+
2002
+
restore-cursor@5.1.0:
2003
+
resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
2004
+
engines: {node: '>=18'}
2005
+
2006
+
reusify@1.1.0:
2007
+
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
2008
+
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
2009
+
2010
+
rfdc@1.4.1:
2011
+
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
2012
+
2013
+
rollup@4.50.1:
2014
+
resolution: {integrity: sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==}
2015
+
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
2016
+
hasBin: true
2017
+
2018
+
run-applescript@7.1.0:
2019
+
resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==}
2020
+
engines: {node: '>=18'}
2021
+
2022
+
run-parallel@1.2.0:
2023
+
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
2024
+
2025
+
safe-buffer@5.1.2:
2026
+
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
2027
+
2028
+
safe-buffer@5.2.1:
2029
+
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
2030
+
2031
+
safe-stable-stringify@2.5.0:
2032
+
resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
2033
+
engines: {node: '>=10'}
2034
+
2035
+
sax@1.4.1:
2036
+
resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
2037
+
2038
+
scheduler@0.26.0:
2039
+
resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==}
2040
+
2041
+
scule@1.3.0:
2042
+
resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==}
2043
+
2044
+
semver@6.3.1:
2045
+
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
2046
+
hasBin: true
2047
+
2048
+
semver@7.7.2:
2049
+
resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
2050
+
engines: {node: '>=10'}
2051
+
hasBin: true
2052
+
2053
+
set-value@4.1.0:
2054
+
resolution: {integrity: sha512-zTEg4HL0RwVrqcWs3ztF+x1vkxfm0lP+MQQFPiMJTKVceBwEV0A569Ou8l9IYQG8jOZdMVI1hGsc0tmeD2o/Lw==}
2055
+
engines: {node: '>=11.0'}
2056
+
2057
+
setimmediate@1.0.5:
2058
+
resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
2059
+
2060
+
sharp@0.34.3:
2061
+
resolution: {integrity: sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==}
2062
+
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
2063
+
2064
+
shebang-command@2.0.0:
2065
+
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
2066
+
engines: {node: '>=8'}
2067
+
2068
+
shebang-regex@3.0.0:
2069
+
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
2070
+
engines: {node: '>=8'}
2071
+
2072
+
shell-quote@1.7.3:
2073
+
resolution: {integrity: sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==}
2074
+
2075
+
shellwords@0.1.1:
2076
+
resolution: {integrity: sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==}
2077
+
2078
+
signal-exit@3.0.7:
2079
+
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
2080
+
2081
+
signal-exit@4.1.0:
2082
+
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
2083
+
engines: {node: '>=14'}
2084
+
2085
+
simple-swizzle@0.2.2:
2086
+
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
2087
+
2088
+
sisteransi@1.0.5:
2089
+
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
2090
+
2091
+
slice-ansi@5.0.0:
2092
+
resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==}
2093
+
engines: {node: '>=12'}
2094
+
2095
+
slice-ansi@7.1.2:
2096
+
resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==}
2097
+
engines: {node: '>=18'}
2098
+
2099
+
sonic-boom@4.2.0:
2100
+
resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==}
2101
+
2102
+
source-map-js@1.2.1:
2103
+
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
2104
+
engines: {node: '>=0.10.0'}
2105
+
2106
+
source-map-support@0.5.21:
2107
+
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
2108
+
2109
+
source-map@0.6.1:
2110
+
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
2111
+
engines: {node: '>=0.10.0'}
2112
+
2113
+
source-map@0.7.6:
2114
+
resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==}
2115
+
engines: {node: '>= 12'}
2116
+
2117
+
spawn-sync@1.0.15:
2118
+
resolution: {integrity: sha512-9DWBgrgYZzNghseho0JOuh+5fg9u6QWhAWa51QC7+U5rCheZ/j1DrEZnyE0RBBRqZ9uEXGPgSSM0nky6burpVw==}
2119
+
2120
+
split2@4.2.0:
2121
+
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
2122
+
engines: {node: '>= 10.x'}
2123
+
2124
+
split@1.0.1:
2125
+
resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==}
2126
+
2127
+
stdin-discarder@0.1.0:
2128
+
resolution: {integrity: sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==}
2129
+
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
2130
+
2131
+
stdin-discarder@0.2.2:
2132
+
resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==}
2133
+
engines: {node: '>=18'}
2134
+
2135
+
string-width@4.2.3:
2136
+
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
2137
+
engines: {node: '>=8'}
2138
+
2139
+
string-width@5.1.2:
2140
+
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
2141
+
engines: {node: '>=12'}
2142
+
2143
+
string-width@7.2.0:
2144
+
resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
2145
+
engines: {node: '>=18'}
2146
+
2147
+
string_decoder@1.1.1:
2148
+
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
2149
+
2150
+
string_decoder@1.3.0:
2151
+
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
2152
+
2153
+
strip-ansi@6.0.1:
2154
+
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
2155
+
engines: {node: '>=8'}
2156
+
2157
+
strip-ansi@7.1.0:
2158
+
resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
2159
+
engines: {node: '>=12'}
2160
+
2161
+
strip-ansi@7.1.2:
2162
+
resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==}
2163
+
engines: {node: '>=12'}
2164
+
2165
+
strip-bom@5.0.0:
2166
+
resolution: {integrity: sha512-p+byADHF7SzEcVnLvc/r3uognM1hUhObuHXxJcgLCfD194XAkaLbjq3Wzb0N5G2tgIjH0dgT708Z51QxMeu60A==}
2167
+
engines: {node: '>=12'}
2168
+
2169
+
strip-json-comments@2.0.1:
2170
+
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
2171
+
engines: {node: '>=0.10.0'}
2172
+
2173
+
strip-json-comments@5.0.2:
2174
+
resolution: {integrity: sha512-4X2FR3UwhNUE9G49aIsJW5hRRR3GXGTBTZRMfv568O60ojM8HcWjV/VxAxCDW3SUND33O6ZY66ZuRcdkj73q2g==}
2175
+
engines: {node: '>=14.16'}
2176
+
2177
+
strip-literal@3.0.0:
2178
+
resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==}
2179
+
2180
+
stubborn-fs@1.2.5:
2181
+
resolution: {integrity: sha512-H2N9c26eXjzL/S/K+i/RHHcFanE74dptvvjM8iwzwbVcWY/zjBbgRqF3K0DY4+OD+uTTASTBvDoxPDaPN02D7g==}
2182
+
2183
+
sucrase@3.35.0:
2184
+
resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
2185
+
engines: {node: '>=16 || 14 >=14.17'}
2186
+
hasBin: true
2187
+
2188
+
supports-color@7.2.0:
2189
+
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
2190
+
engines: {node: '>=8'}
2191
+
2192
+
supports-preserve-symlinks-flag@1.0.0:
2193
+
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
2194
+
engines: {node: '>= 0.4'}
2195
+
2196
+
tailwindcss@3.4.1:
2197
+
resolution: {integrity: sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==}
2198
+
engines: {node: '>=14.0.0'}
2199
+
hasBin: true
2200
+
2201
+
thenify-all@1.6.0:
2202
+
resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
2203
+
engines: {node: '>=0.8'}
2204
+
2205
+
thenify@3.3.1:
2206
+
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
2207
+
2208
+
thread-stream@3.1.0:
2209
+
resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==}
2210
+
2211
+
through@2.3.8:
2212
+
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
2213
+
2214
+
tinyexec@1.0.1:
2215
+
resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==}
2216
+
2217
+
tinyglobby@0.2.15:
2218
+
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
2219
+
engines: {node: '>=12.0.0'}
2220
+
2221
+
tmp@0.2.5:
2222
+
resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==}
2223
+
engines: {node: '>=14.14'}
2224
+
2225
+
to-regex-range@5.0.1:
2226
+
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
2227
+
engines: {node: '>=8.0'}
2228
+
2229
+
ts-interface-checker@0.1.13:
2230
+
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
2231
+
2232
+
tslib@2.8.1:
2233
+
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
2234
+
2235
+
type-fest@3.13.1:
2236
+
resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==}
2237
+
engines: {node: '>=14.16'}
2238
+
2239
+
type-fest@4.41.0:
2240
+
resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
2241
+
engines: {node: '>=16'}
2242
+
2243
+
typedarray@0.0.6:
2244
+
resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
2245
+
2246
+
typescript@5.9.2:
2247
+
resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==}
2248
+
engines: {node: '>=14.17'}
2249
+
hasBin: true
2250
+
2251
+
ufo@1.6.1:
2252
+
resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==}
2253
+
2254
+
uhyphen@0.2.0:
2255
+
resolution: {integrity: sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==}
2256
+
2257
+
undici-types@7.10.0:
2258
+
resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==}
2259
+
2260
+
unimport@5.2.0:
2261
+
resolution: {integrity: sha512-bTuAMMOOqIAyjV4i4UH7P07pO+EsVxmhOzQ2YJ290J6mkLUdozNhb5I/YoOEheeNADC03ent3Qj07X0fWfUpmw==}
2262
+
engines: {node: '>=18.12.0'}
2263
+
2264
+
universalify@2.0.1:
2265
+
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
2266
+
engines: {node: '>= 10.0.0'}
2267
+
2268
+
unplugin-utils@0.2.5:
2269
+
resolution: {integrity: sha512-gwXJnPRewT4rT7sBi/IvxKTjsms7jX7QIDLOClApuZwR49SXbrB1z2NLUZ+vDHyqCj/n58OzRRqaW+B8OZi8vg==}
2270
+
engines: {node: '>=18.12.0'}
2271
+
2272
+
unplugin@2.3.10:
2273
+
resolution: {integrity: sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==}
2274
+
engines: {node: '>=18.12.0'}
2275
+
2276
+
update-browserslist-db@1.1.3:
2277
+
resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
2278
+
hasBin: true
2279
+
peerDependencies:
2280
+
browserslist: '>= 4.21.0'
2281
+
2282
+
update-notifier@7.3.1:
2283
+
resolution: {integrity: sha512-+dwUY4L35XFYEzE+OAL3sarJdUioVovq+8f7lcIJ7wnmnYQV5UD1Y/lcwaMSyaQ6Bj3JMj1XSTjZbNLHn/19yA==}
2284
+
engines: {node: '>=18'}
2285
+
2286
+
util-deprecate@1.0.2:
2287
+
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
2288
+
2289
+
uuid@8.3.2:
2290
+
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
2291
+
hasBin: true
2292
+
2293
+
vite-node@3.2.4:
2294
+
resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==}
2295
+
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
2296
+
hasBin: true
2297
+
2298
+
vite@7.1.5:
2299
+
resolution: {integrity: sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==}
2300
+
engines: {node: ^20.19.0 || >=22.12.0}
2301
+
hasBin: true
2302
+
peerDependencies:
2303
+
'@types/node': ^20.19.0 || >=22.12.0
2304
+
jiti: '>=1.21.0'
2305
+
less: ^4.0.0
2306
+
lightningcss: ^1.21.0
2307
+
sass: ^1.70.0
2308
+
sass-embedded: ^1.70.0
2309
+
stylus: '>=0.54.8'
2310
+
sugarss: ^5.0.0
2311
+
terser: ^5.16.0
2312
+
tsx: ^4.8.1
2313
+
yaml: ^2.4.2
2314
+
peerDependenciesMeta:
2315
+
'@types/node':
2316
+
optional: true
2317
+
jiti:
2318
+
optional: true
2319
+
less:
2320
+
optional: true
2321
+
lightningcss:
2322
+
optional: true
2323
+
sass:
2324
+
optional: true
2325
+
sass-embedded:
2326
+
optional: true
2327
+
stylus:
2328
+
optional: true
2329
+
sugarss:
2330
+
optional: true
2331
+
terser:
2332
+
optional: true
2333
+
tsx:
2334
+
optional: true
2335
+
yaml:
2336
+
optional: true
2337
+
2338
+
watchpack@2.4.4:
2339
+
resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==}
2340
+
engines: {node: '>=10.13.0'}
2341
+
2342
+
wcwidth@1.0.1:
2343
+
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
2344
+
2345
+
web-ext-run@0.2.4:
2346
+
resolution: {integrity: sha512-rQicL7OwuqWdQWI33JkSXKcp7cuv1mJG8u3jRQwx/8aDsmhbTHs9ZRmNYOL+LX0wX8edIEQX8jj4bB60GoXtKA==}
2347
+
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
2348
+
2349
+
webpack-virtual-modules@0.6.2:
2350
+
resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
2351
+
2352
+
when-exit@2.1.4:
2353
+
resolution: {integrity: sha512-4rnvd3A1t16PWzrBUcSDZqcAmsUIy4minDXT/CZ8F2mVDgd65i4Aalimgz1aQkRGU0iH5eT5+6Rx2TK8o443Pg==}
2354
+
2355
+
when@3.7.7:
2356
+
resolution: {integrity: sha512-9lFZp/KHoqH6bPKjbWqa+3Dg/K/r2v0X/3/G2x4DBGchVS2QX2VXL3cZV994WQVnTM1/PD71Az25nAzryEUugw==}
2357
+
2358
+
which@1.2.4:
2359
+
resolution: {integrity: sha512-zDRAqDSBudazdfM9zpiI30Fu9ve47htYXcGi3ln0wfKu2a7SmrT6F3VDoYONu//48V8Vz4TdCRNPjtvyRO3yBA==}
2360
+
hasBin: true
2361
+
2362
+
which@2.0.2:
2363
+
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
2364
+
engines: {node: '>= 8'}
2365
+
hasBin: true
2366
+
2367
+
widest-line@5.0.0:
2368
+
resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==}
2369
+
engines: {node: '>=18'}
2370
+
2371
+
winreg@0.0.12:
2372
+
resolution: {integrity: sha512-typ/+JRmi7RqP1NanzFULK36vczznSNN8kWVA9vIqXyv8GhghUlwhGp1Xj3Nms1FsPcNnsQrJOR10N58/nQ9hQ==}
2373
+
2374
+
wrap-ansi@7.0.0:
2375
+
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
2376
+
engines: {node: '>=10'}
2377
+
2378
+
wrap-ansi@8.1.0:
2379
+
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
2380
+
engines: {node: '>=12'}
2381
+
2382
+
wrap-ansi@9.0.2:
2383
+
resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==}
2384
+
engines: {node: '>=18'}
2385
+
2386
+
wrappy@1.0.2:
2387
+
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
2388
+
2389
+
wsl-utils@0.1.0:
2390
+
resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==}
2391
+
engines: {node: '>=18'}
2392
+
2393
+
wxt@0.20.11:
2394
+
resolution: {integrity: sha512-DqqHc/5COs8GR21ii99bANXf/mu6zuDpiXFV1YKNsqO5/PvkrCx5arY0aVPL5IATsuacAnNzdj4eMc1qbzS53Q==}
2395
+
hasBin: true
2396
+
2397
+
xdg-basedir@5.1.0:
2398
+
resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==}
2399
+
engines: {node: '>=12'}
2400
+
2401
+
xml2js@0.6.2:
2402
+
resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==}
2403
+
engines: {node: '>=4.0.0'}
2404
+
2405
+
xmlbuilder@11.0.1:
2406
+
resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==}
2407
+
engines: {node: '>=4.0'}
2408
+
2409
+
y18n@5.0.8:
2410
+
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
2411
+
engines: {node: '>=10'}
2412
+
2413
+
yallist@3.1.1:
2414
+
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
2415
+
2416
+
yaml@2.8.1:
2417
+
resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==}
2418
+
engines: {node: '>= 14.6'}
2419
+
hasBin: true
2420
+
2421
+
yargs-parser@20.2.9:
2422
+
resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
2423
+
engines: {node: '>=10'}
2424
+
2425
+
yargs-parser@21.1.1:
2426
+
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
2427
+
engines: {node: '>=12'}
2428
+
2429
+
yargs@16.2.0:
2430
+
resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
2431
+
engines: {node: '>=10'}
2432
+
2433
+
yargs@17.7.2:
2434
+
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
2435
+
engines: {node: '>=12'}
2436
+
2437
+
yauzl@2.10.0:
2438
+
resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==}
2439
+
2440
+
zip-dir@2.0.0:
2441
+
resolution: {integrity: sha512-uhlsJZWz26FLYXOD6WVuq+fIcZ3aBPGo/cFdiLlv3KNwpa52IF3ISV8fLhQLiqVu5No3VhlqlgthN6gehil1Dg==}
2442
+
2443
+
zod@3.25.76:
2444
+
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
2445
+
2446
+
snapshots:
2447
+
2448
+
'@1natsu/wait-element@4.1.2':
2449
+
dependencies:
2450
+
defu: 6.1.4
2451
+
many-keys-map: 2.0.1
2452
+
2453
+
'@aklinker1/rollup-plugin-visualizer@5.12.0(rollup@4.50.1)':
2454
+
dependencies:
2455
+
open: 8.4.2
2456
+
picomatch: 2.3.1
2457
+
source-map: 0.7.6
2458
+
yargs: 17.7.2
2459
+
optionalDependencies:
2460
+
rollup: 4.50.1
2461
+
2462
+
'@alloc/quick-lru@5.2.0': {}
2463
+
2464
+
'@babel/code-frame@7.27.1':
2465
+
dependencies:
2466
+
'@babel/helper-validator-identifier': 7.27.1
2467
+
js-tokens: 4.0.0
2468
+
picocolors: 1.1.1
2469
+
2470
+
'@babel/compat-data@7.28.4': {}
2471
+
2472
+
'@babel/core@7.28.4':
2473
+
dependencies:
2474
+
'@babel/code-frame': 7.27.1
2475
+
'@babel/generator': 7.28.3
2476
+
'@babel/helper-compilation-targets': 7.27.2
2477
+
'@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4)
2478
+
'@babel/helpers': 7.28.4
2479
+
'@babel/parser': 7.28.4
2480
+
'@babel/template': 7.27.2
2481
+
'@babel/traverse': 7.28.4
2482
+
'@babel/types': 7.28.4
2483
+
'@jridgewell/remapping': 2.3.5
2484
+
convert-source-map: 2.0.0
2485
+
debug: 4.4.1
2486
+
gensync: 1.0.0-beta.2
2487
+
json5: 2.2.3
2488
+
semver: 6.3.1
2489
+
transitivePeerDependencies:
2490
+
- supports-color
2491
+
2492
+
'@babel/generator@7.28.3':
2493
+
dependencies:
2494
+
'@babel/parser': 7.28.4
2495
+
'@babel/types': 7.28.4
2496
+
'@jridgewell/gen-mapping': 0.3.13
2497
+
'@jridgewell/trace-mapping': 0.3.31
2498
+
jsesc: 3.1.0
2499
+
2500
+
'@babel/helper-compilation-targets@7.27.2':
2501
+
dependencies:
2502
+
'@babel/compat-data': 7.28.4
2503
+
'@babel/helper-validator-option': 7.27.1
2504
+
browserslist: 4.26.0
2505
+
lru-cache: 5.1.1
2506
+
semver: 6.3.1
2507
+
2508
+
'@babel/helper-globals@7.28.0': {}
2509
+
2510
+
'@babel/helper-module-imports@7.27.1':
2511
+
dependencies:
2512
+
'@babel/traverse': 7.28.4
2513
+
'@babel/types': 7.28.4
2514
+
transitivePeerDependencies:
2515
+
- supports-color
2516
+
2517
+
'@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)':
2518
+
dependencies:
2519
+
'@babel/core': 7.28.4
2520
+
'@babel/helper-module-imports': 7.27.1
2521
+
'@babel/helper-validator-identifier': 7.27.1
2522
+
'@babel/traverse': 7.28.4
2523
+
transitivePeerDependencies:
2524
+
- supports-color
2525
+
2526
+
'@babel/helper-plugin-utils@7.27.1': {}
2527
+
2528
+
'@babel/helper-string-parser@7.27.1': {}
2529
+
2530
+
'@babel/helper-validator-identifier@7.27.1': {}
2531
+
2532
+
'@babel/helper-validator-option@7.27.1': {}
2533
+
2534
+
'@babel/helpers@7.28.4':
2535
+
dependencies:
2536
+
'@babel/template': 7.27.2
2537
+
'@babel/types': 7.28.4
2538
+
2539
+
'@babel/parser@7.28.4':
2540
+
dependencies:
2541
+
'@babel/types': 7.28.4
2542
+
2543
+
'@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.4)':
2544
+
dependencies:
2545
+
'@babel/core': 7.28.4
2546
+
'@babel/helper-plugin-utils': 7.27.1
2547
+
2548
+
'@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.4)':
2549
+
dependencies:
2550
+
'@babel/core': 7.28.4
2551
+
'@babel/helper-plugin-utils': 7.27.1
2552
+
2553
+
'@babel/runtime@7.28.2': {}
2554
+
2555
+
'@babel/template@7.27.2':
2556
+
dependencies:
2557
+
'@babel/code-frame': 7.27.1
2558
+
'@babel/parser': 7.28.4
2559
+
'@babel/types': 7.28.4
2560
+
2561
+
'@babel/traverse@7.28.4':
2562
+
dependencies:
2563
+
'@babel/code-frame': 7.27.1
2564
+
'@babel/generator': 7.28.3
2565
+
'@babel/helper-globals': 7.28.0
2566
+
'@babel/parser': 7.28.4
2567
+
'@babel/template': 7.27.2
2568
+
'@babel/types': 7.28.4
2569
+
debug: 4.4.1
2570
+
transitivePeerDependencies:
2571
+
- supports-color
2572
+
2573
+
'@babel/types@7.28.4':
2574
+
dependencies:
2575
+
'@babel/helper-string-parser': 7.27.1
2576
+
'@babel/helper-validator-identifier': 7.27.1
2577
+
2578
+
'@biomejs/biome@2.2.4':
2579
+
optionalDependencies:
2580
+
'@biomejs/cli-darwin-arm64': 2.2.4
2581
+
'@biomejs/cli-darwin-x64': 2.2.4
2582
+
'@biomejs/cli-linux-arm64': 2.2.4
2583
+
'@biomejs/cli-linux-arm64-musl': 2.2.4
2584
+
'@biomejs/cli-linux-x64': 2.2.4
2585
+
'@biomejs/cli-linux-x64-musl': 2.2.4
2586
+
'@biomejs/cli-win32-arm64': 2.2.4
2587
+
'@biomejs/cli-win32-x64': 2.2.4
2588
+
2589
+
'@biomejs/cli-darwin-arm64@2.2.4':
2590
+
optional: true
2591
+
2592
+
'@biomejs/cli-darwin-x64@2.2.4':
2593
+
optional: true
2594
+
2595
+
'@biomejs/cli-linux-arm64-musl@2.2.4':
2596
+
optional: true
2597
+
2598
+
'@biomejs/cli-linux-arm64@2.2.4':
2599
+
optional: true
2600
+
2601
+
'@biomejs/cli-linux-x64-musl@2.2.4':
2602
+
optional: true
2603
+
2604
+
'@biomejs/cli-linux-x64@2.2.4':
2605
+
optional: true
2606
+
2607
+
'@biomejs/cli-win32-arm64@2.2.4':
2608
+
optional: true
2609
+
2610
+
'@biomejs/cli-win32-x64@2.2.4':
2611
+
optional: true
2612
+
2613
+
'@devicefarmer/adbkit-logcat@2.1.3': {}
2614
+
2615
+
'@devicefarmer/adbkit-monkey@1.2.1': {}
2616
+
2617
+
'@devicefarmer/adbkit@3.3.8':
2618
+
dependencies:
2619
+
'@devicefarmer/adbkit-logcat': 2.1.3
2620
+
'@devicefarmer/adbkit-monkey': 1.2.1
2621
+
bluebird: 3.7.2
2622
+
commander: 9.5.0
2623
+
debug: 4.3.7
2624
+
node-forge: 1.3.1
2625
+
split: 1.0.1
2626
+
transitivePeerDependencies:
2627
+
- supports-color
2628
+
2629
+
'@emnapi/runtime@1.4.5':
2630
+
dependencies:
2631
+
tslib: 2.8.1
2632
+
optional: true
2633
+
2634
+
'@esbuild/aix-ppc64@0.25.9':
2635
+
optional: true
2636
+
2637
+
'@esbuild/android-arm64@0.25.9':
2638
+
optional: true
2639
+
2640
+
'@esbuild/android-arm@0.25.9':
2641
+
optional: true
2642
+
2643
+
'@esbuild/android-x64@0.25.9':
2644
+
optional: true
2645
+
2646
+
'@esbuild/darwin-arm64@0.25.9':
2647
+
optional: true
2648
+
2649
+
'@esbuild/darwin-x64@0.25.9':
2650
+
optional: true
2651
+
2652
+
'@esbuild/freebsd-arm64@0.25.9':
2653
+
optional: true
2654
+
2655
+
'@esbuild/freebsd-x64@0.25.9':
2656
+
optional: true
2657
+
2658
+
'@esbuild/linux-arm64@0.25.9':
2659
+
optional: true
2660
+
2661
+
'@esbuild/linux-arm@0.25.9':
2662
+
optional: true
2663
+
2664
+
'@esbuild/linux-ia32@0.25.9':
2665
+
optional: true
2666
+
2667
+
'@esbuild/linux-loong64@0.25.9':
2668
+
optional: true
2669
+
2670
+
'@esbuild/linux-mips64el@0.25.9':
2671
+
optional: true
2672
+
2673
+
'@esbuild/linux-ppc64@0.25.9':
2674
+
optional: true
2675
+
2676
+
'@esbuild/linux-riscv64@0.25.9':
2677
+
optional: true
2678
+
2679
+
'@esbuild/linux-s390x@0.25.9':
2680
+
optional: true
2681
+
2682
+
'@esbuild/linux-x64@0.25.9':
2683
+
optional: true
2684
+
2685
+
'@esbuild/netbsd-arm64@0.25.9':
2686
+
optional: true
2687
+
2688
+
'@esbuild/netbsd-x64@0.25.9':
2689
+
optional: true
2690
+
2691
+
'@esbuild/openbsd-arm64@0.25.9':
2692
+
optional: true
2693
+
2694
+
'@esbuild/openbsd-x64@0.25.9':
2695
+
optional: true
2696
+
2697
+
'@esbuild/openharmony-arm64@0.25.9':
2698
+
optional: true
2699
+
2700
+
'@esbuild/sunos-x64@0.25.9':
2701
+
optional: true
2702
+
2703
+
'@esbuild/win32-arm64@0.25.9':
2704
+
optional: true
2705
+
2706
+
'@esbuild/win32-ia32@0.25.9':
2707
+
optional: true
2708
+
2709
+
'@esbuild/win32-x64@0.25.9':
2710
+
optional: true
2711
+
2712
+
'@img/sharp-darwin-arm64@0.34.3':
2713
+
optionalDependencies:
2714
+
'@img/sharp-libvips-darwin-arm64': 1.2.0
2715
+
optional: true
2716
+
2717
+
'@img/sharp-darwin-x64@0.34.3':
2718
+
optionalDependencies:
2719
+
'@img/sharp-libvips-darwin-x64': 1.2.0
2720
+
optional: true
2721
+
2722
+
'@img/sharp-libvips-darwin-arm64@1.2.0':
2723
+
optional: true
2724
+
2725
+
'@img/sharp-libvips-darwin-x64@1.2.0':
2726
+
optional: true
2727
+
2728
+
'@img/sharp-libvips-linux-arm64@1.2.0':
2729
+
optional: true
2730
+
2731
+
'@img/sharp-libvips-linux-arm@1.2.0':
2732
+
optional: true
2733
+
2734
+
'@img/sharp-libvips-linux-ppc64@1.2.0':
2735
+
optional: true
2736
+
2737
+
'@img/sharp-libvips-linux-s390x@1.2.0':
2738
+
optional: true
2739
+
2740
+
'@img/sharp-libvips-linux-x64@1.2.0':
2741
+
optional: true
2742
+
2743
+
'@img/sharp-libvips-linuxmusl-arm64@1.2.0':
2744
+
optional: true
2745
+
2746
+
'@img/sharp-libvips-linuxmusl-x64@1.2.0':
2747
+
optional: true
2748
+
2749
+
'@img/sharp-linux-arm64@0.34.3':
2750
+
optionalDependencies:
2751
+
'@img/sharp-libvips-linux-arm64': 1.2.0
2752
+
optional: true
2753
+
2754
+
'@img/sharp-linux-arm@0.34.3':
2755
+
optionalDependencies:
2756
+
'@img/sharp-libvips-linux-arm': 1.2.0
2757
+
optional: true
2758
+
2759
+
'@img/sharp-linux-ppc64@0.34.3':
2760
+
optionalDependencies:
2761
+
'@img/sharp-libvips-linux-ppc64': 1.2.0
2762
+
optional: true
2763
+
2764
+
'@img/sharp-linux-s390x@0.34.3':
2765
+
optionalDependencies:
2766
+
'@img/sharp-libvips-linux-s390x': 1.2.0
2767
+
optional: true
2768
+
2769
+
'@img/sharp-linux-x64@0.34.3':
2770
+
optionalDependencies:
2771
+
'@img/sharp-libvips-linux-x64': 1.2.0
2772
+
optional: true
2773
+
2774
+
'@img/sharp-linuxmusl-arm64@0.34.3':
2775
+
optionalDependencies:
2776
+
'@img/sharp-libvips-linuxmusl-arm64': 1.2.0
2777
+
optional: true
2778
+
2779
+
'@img/sharp-linuxmusl-x64@0.34.3':
2780
+
optionalDependencies:
2781
+
'@img/sharp-libvips-linuxmusl-x64': 1.2.0
2782
+
optional: true
2783
+
2784
+
'@img/sharp-wasm32@0.34.3':
2785
+
dependencies:
2786
+
'@emnapi/runtime': 1.4.5
2787
+
optional: true
2788
+
2789
+
'@img/sharp-win32-arm64@0.34.3':
2790
+
optional: true
2791
+
2792
+
'@img/sharp-win32-ia32@0.34.3':
2793
+
optional: true
2794
+
2795
+
'@img/sharp-win32-x64@0.34.3':
2796
+
optional: true
2797
+
2798
+
'@isaacs/balanced-match@4.0.1': {}
2799
+
2800
+
'@isaacs/brace-expansion@5.0.0':
2801
+
dependencies:
2802
+
'@isaacs/balanced-match': 4.0.1
2803
+
2804
+
'@isaacs/cliui@8.0.2':
2805
+
dependencies:
2806
+
string-width: 5.1.2
2807
+
string-width-cjs: string-width@4.2.3
2808
+
strip-ansi: 7.1.0
2809
+
strip-ansi-cjs: strip-ansi@6.0.1
2810
+
wrap-ansi: 8.1.0
2811
+
wrap-ansi-cjs: wrap-ansi@7.0.0
2812
+
2813
+
'@jridgewell/gen-mapping@0.3.13':
2814
+
dependencies:
2815
+
'@jridgewell/sourcemap-codec': 1.5.5
2816
+
'@jridgewell/trace-mapping': 0.3.30
2817
+
2818
+
'@jridgewell/remapping@2.3.5':
2819
+
dependencies:
2820
+
'@jridgewell/gen-mapping': 0.3.13
2821
+
'@jridgewell/trace-mapping': 0.3.31
2822
+
2823
+
'@jridgewell/resolve-uri@3.1.2': {}
2824
+
2825
+
'@jridgewell/sourcemap-codec@1.5.5': {}
2826
+
2827
+
'@jridgewell/trace-mapping@0.3.30':
2828
+
dependencies:
2829
+
'@jridgewell/resolve-uri': 3.1.2
2830
+
'@jridgewell/sourcemap-codec': 1.5.5
2831
+
2832
+
'@jridgewell/trace-mapping@0.3.31':
2833
+
dependencies:
2834
+
'@jridgewell/resolve-uri': 3.1.2
2835
+
'@jridgewell/sourcemap-codec': 1.5.5
2836
+
2837
+
'@nodelib/fs.scandir@2.1.5':
2838
+
dependencies:
2839
+
'@nodelib/fs.stat': 2.0.5
2840
+
run-parallel: 1.2.0
2841
+
2842
+
'@nodelib/fs.stat@2.0.5': {}
2843
+
2844
+
'@nodelib/fs.walk@1.2.8':
2845
+
dependencies:
2846
+
'@nodelib/fs.scandir': 2.1.5
2847
+
fastq: 1.19.1
2848
+
2849
+
'@pkgjs/parseargs@0.11.0':
2850
+
optional: true
2851
+
2852
+
'@pnpm/config.env-replace@1.1.0': {}
2853
+
2854
+
'@pnpm/network.ca-file@1.0.2':
2855
+
dependencies:
2856
+
graceful-fs: 4.2.10
2857
+
2858
+
'@pnpm/npm-conf@2.3.1':
2859
+
dependencies:
2860
+
'@pnpm/config.env-replace': 1.1.0
2861
+
'@pnpm/network.ca-file': 1.0.2
2862
+
config-chain: 1.1.13
2863
+
2864
+
'@rolldown/pluginutils@1.0.0-beta.34': {}
2865
+
2866
+
'@rollup/rollup-android-arm-eabi@4.50.1':
2867
+
optional: true
2868
+
2869
+
'@rollup/rollup-android-arm64@4.50.1':
2870
+
optional: true
2871
+
2872
+
'@rollup/rollup-darwin-arm64@4.50.1':
2873
+
optional: true
2874
+
2875
+
'@rollup/rollup-darwin-x64@4.50.1':
2876
+
optional: true
2877
+
2878
+
'@rollup/rollup-freebsd-arm64@4.50.1':
2879
+
optional: true
2880
+
2881
+
'@rollup/rollup-freebsd-x64@4.50.1':
2882
+
optional: true
2883
+
2884
+
'@rollup/rollup-linux-arm-gnueabihf@4.50.1':
2885
+
optional: true
2886
+
2887
+
'@rollup/rollup-linux-arm-musleabihf@4.50.1':
2888
+
optional: true
2889
+
2890
+
'@rollup/rollup-linux-arm64-gnu@4.50.1':
2891
+
optional: true
2892
+
2893
+
'@rollup/rollup-linux-arm64-musl@4.50.1':
2894
+
optional: true
2895
+
2896
+
'@rollup/rollup-linux-loongarch64-gnu@4.50.1':
2897
+
optional: true
2898
+
2899
+
'@rollup/rollup-linux-ppc64-gnu@4.50.1':
2900
+
optional: true
2901
+
2902
+
'@rollup/rollup-linux-riscv64-gnu@4.50.1':
2903
+
optional: true
2904
+
2905
+
'@rollup/rollup-linux-riscv64-musl@4.50.1':
2906
+
optional: true
2907
+
2908
+
'@rollup/rollup-linux-s390x-gnu@4.50.1':
2909
+
optional: true
2910
+
2911
+
'@rollup/rollup-linux-x64-gnu@4.50.1':
2912
+
optional: true
2913
+
2914
+
'@rollup/rollup-linux-x64-musl@4.50.1':
2915
+
optional: true
2916
+
2917
+
'@rollup/rollup-openharmony-arm64@4.50.1':
2918
+
optional: true
2919
+
2920
+
'@rollup/rollup-win32-arm64-msvc@4.50.1':
2921
+
optional: true
2922
+
2923
+
'@rollup/rollup-win32-ia32-msvc@4.50.1':
2924
+
optional: true
2925
+
2926
+
'@rollup/rollup-win32-x64-msvc@4.50.1':
2927
+
optional: true
2928
+
2929
+
'@types/babel__core@7.20.5':
2930
+
dependencies:
2931
+
'@babel/parser': 7.28.4
2932
+
'@babel/types': 7.28.4
2933
+
'@types/babel__generator': 7.27.0
2934
+
'@types/babel__template': 7.4.4
2935
+
'@types/babel__traverse': 7.28.0
2936
+
2937
+
'@types/babel__generator@7.27.0':
2938
+
dependencies:
2939
+
'@babel/types': 7.28.4
2940
+
2941
+
'@types/babel__template@7.4.4':
2942
+
dependencies:
2943
+
'@babel/parser': 7.28.4
2944
+
'@babel/types': 7.28.4
2945
+
2946
+
'@types/babel__traverse@7.28.0':
2947
+
dependencies:
2948
+
'@babel/types': 7.28.4
2949
+
2950
+
'@types/estree@1.0.8': {}
2951
+
2952
+
'@types/filesystem@0.0.36':
2953
+
dependencies:
2954
+
'@types/filewriter': 0.0.33
2955
+
2956
+
'@types/filewriter@0.0.33': {}
2957
+
2958
+
'@types/har-format@1.2.16': {}
2959
+
2960
+
'@types/minimatch@3.0.5': {}
2961
+
2962
+
'@types/node@24.3.2':
2963
+
dependencies:
2964
+
undici-types: 7.10.0
2965
+
2966
+
'@types/react-dom@19.1.9(@types/react@19.1.13)':
2967
+
dependencies:
2968
+
'@types/react': 19.1.13
2969
+
2970
+
'@types/react@19.1.13':
2971
+
dependencies:
2972
+
csstype: 3.1.3
2973
+
2974
+
'@types/yauzl@2.10.3':
2975
+
dependencies:
2976
+
'@types/node': 24.3.2
2977
+
optional: true
2978
+
2979
+
'@vitejs/plugin-react@5.0.2(vite@7.1.5(@types/node@24.3.2)(jiti@2.5.1)(yaml@2.8.1))':
2980
+
dependencies:
2981
+
'@babel/core': 7.28.4
2982
+
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4)
2983
+
'@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.4)
2984
+
'@rolldown/pluginutils': 1.0.0-beta.34
2985
+
'@types/babel__core': 7.20.5
2986
+
react-refresh: 0.17.0
2987
+
vite: 7.1.5(@types/node@24.3.2)(jiti@2.5.1)(yaml@2.8.1)
2988
+
transitivePeerDependencies:
2989
+
- supports-color
2990
+
2991
+
'@webext-core/fake-browser@1.3.2':
2992
+
dependencies:
2993
+
lodash.merge: 4.6.2
2994
+
2995
+
'@webext-core/isolated-element@1.1.2':
2996
+
dependencies:
2997
+
is-potential-custom-element-name: 1.0.1
2998
+
2999
+
'@webext-core/match-patterns@1.0.3': {}
3000
+
3001
+
'@wxt-dev/auto-icons@1.1.0(wxt@0.20.11(@types/node@24.3.2)(jiti@2.5.1)(rollup@4.50.1)(yaml@2.8.1))':
3002
+
dependencies:
3003
+
defu: 6.1.4
3004
+
fs-extra: 11.3.1
3005
+
sharp: 0.34.3
3006
+
wxt: 0.20.11(@types/node@24.3.2)(jiti@2.5.1)(rollup@4.50.1)(yaml@2.8.1)
3007
+
3008
+
'@wxt-dev/browser@0.1.4':
3009
+
dependencies:
3010
+
'@types/filesystem': 0.0.36
3011
+
'@types/har-format': 1.2.16
3012
+
3013
+
'@wxt-dev/module-react@1.1.5(vite@7.1.5(@types/node@24.3.2)(jiti@2.5.1)(yaml@2.8.1))(wxt@0.20.11(@types/node@24.3.2)(jiti@2.5.1)(rollup@4.50.1)(yaml@2.8.1))':
3014
+
dependencies:
3015
+
'@vitejs/plugin-react': 5.0.2(vite@7.1.5(@types/node@24.3.2)(jiti@2.5.1)(yaml@2.8.1))
3016
+
wxt: 0.20.11(@types/node@24.3.2)(jiti@2.5.1)(rollup@4.50.1)(yaml@2.8.1)
3017
+
transitivePeerDependencies:
3018
+
- supports-color
3019
+
- vite
3020
+
3021
+
'@wxt-dev/storage@1.2.0':
3022
+
dependencies:
3023
+
'@wxt-dev/browser': 0.1.4
3024
+
async-mutex: 0.5.0
3025
+
dequal: 2.0.3
3026
+
3027
+
acorn@8.15.0: {}
3028
+
3029
+
adm-zip@0.5.16: {}
3030
+
3031
+
ansi-align@3.0.1:
3032
+
dependencies:
3033
+
string-width: 4.2.3
3034
+
3035
+
ansi-escapes@7.1.0:
3036
+
dependencies:
3037
+
environment: 1.1.0
3038
+
3039
+
ansi-regex@5.0.1: {}
3040
+
3041
+
ansi-regex@6.2.0: {}
3042
+
3043
+
ansi-regex@6.2.2: {}
3044
+
3045
+
ansi-styles@4.3.0:
3046
+
dependencies:
3047
+
color-convert: 2.0.1
3048
+
3049
+
ansi-styles@6.2.1: {}
3050
+
3051
+
ansi-styles@6.2.3: {}
3052
+
3053
+
any-promise@1.3.0: {}
3054
+
3055
+
anymatch@3.1.3:
3056
+
dependencies:
3057
+
normalize-path: 3.0.0
3058
+
picomatch: 2.3.1
3059
+
3060
+
arg@5.0.2: {}
3061
+
3062
+
array-differ@4.0.0: {}
3063
+
3064
+
array-union@3.0.1: {}
3065
+
3066
+
async-mutex@0.5.0:
3067
+
dependencies:
3068
+
tslib: 2.8.1
3069
+
3070
+
async@3.2.6: {}
3071
+
3072
+
atomic-sleep@1.0.0: {}
3073
+
3074
+
atomically@2.0.3:
3075
+
dependencies:
3076
+
stubborn-fs: 1.2.5
3077
+
when-exit: 2.1.4
3078
+
3079
+
autoprefixer@10.4.21(postcss@8.5.6):
3080
+
dependencies:
3081
+
browserslist: 4.25.3
3082
+
caniuse-lite: 1.0.30001735
3083
+
fraction.js: 4.3.7
3084
+
normalize-range: 0.1.2
3085
+
picocolors: 1.1.1
3086
+
postcss: 8.5.6
3087
+
postcss-value-parser: 4.2.0
3088
+
3089
+
balanced-match@1.0.2: {}
3090
+
3091
+
base64-js@1.5.1: {}
3092
+
3093
+
baseline-browser-mapping@2.8.2: {}
3094
+
3095
+
binary-extensions@2.3.0: {}
3096
+
3097
+
bl@5.1.0:
3098
+
dependencies:
3099
+
buffer: 6.0.3
3100
+
inherits: 2.0.4
3101
+
readable-stream: 3.6.2
3102
+
3103
+
bluebird@3.7.2: {}
3104
+
3105
+
boolbase@1.0.0: {}
3106
+
3107
+
boxen@8.0.1:
3108
+
dependencies:
3109
+
ansi-align: 3.0.1
3110
+
camelcase: 8.0.0
3111
+
chalk: 5.6.2
3112
+
cli-boxes: 3.0.0
3113
+
string-width: 7.2.0
3114
+
type-fest: 4.41.0
3115
+
widest-line: 5.0.0
3116
+
wrap-ansi: 9.0.2
3117
+
3118
+
brace-expansion@1.1.12:
3119
+
dependencies:
3120
+
balanced-match: 1.0.2
3121
+
concat-map: 0.0.1
3122
+
3123
+
brace-expansion@2.0.2:
3124
+
dependencies:
3125
+
balanced-match: 1.0.2
3126
+
3127
+
braces@3.0.3:
3128
+
dependencies:
3129
+
fill-range: 7.1.1
3130
+
3131
+
browserslist@4.25.3:
3132
+
dependencies:
3133
+
caniuse-lite: 1.0.30001735
3134
+
electron-to-chromium: 1.5.207
3135
+
node-releases: 2.0.19
3136
+
update-browserslist-db: 1.1.3(browserslist@4.25.3)
3137
+
3138
+
browserslist@4.26.0:
3139
+
dependencies:
3140
+
baseline-browser-mapping: 2.8.2
3141
+
caniuse-lite: 1.0.30001741
3142
+
electron-to-chromium: 1.5.218
3143
+
node-releases: 2.0.21
3144
+
update-browserslist-db: 1.1.3(browserslist@4.26.0)
3145
+
3146
+
buffer-crc32@0.2.13: {}
3147
+
3148
+
buffer-from@1.1.2: {}
3149
+
3150
+
buffer@6.0.3:
3151
+
dependencies:
3152
+
base64-js: 1.5.1
3153
+
ieee754: 1.2.1
3154
+
3155
+
bundle-name@4.1.0:
3156
+
dependencies:
3157
+
run-applescript: 7.1.0
3158
+
3159
+
c12@3.2.0(magicast@0.3.5):
3160
+
dependencies:
3161
+
chokidar: 4.0.3
3162
+
confbox: 0.2.2
3163
+
defu: 6.1.4
3164
+
dotenv: 17.2.2
3165
+
exsolve: 1.0.7
3166
+
giget: 2.0.0
3167
+
jiti: 2.5.1
3168
+
ohash: 2.0.11
3169
+
pathe: 2.0.3
3170
+
perfect-debounce: 1.0.0
3171
+
pkg-types: 2.3.0
3172
+
rc9: 2.1.2
3173
+
optionalDependencies:
3174
+
magicast: 0.3.5
3175
+
3176
+
cac@6.7.14: {}
3177
+
3178
+
camelcase-css@2.0.1: {}
3179
+
3180
+
camelcase@8.0.0: {}
3181
+
3182
+
caniuse-lite@1.0.30001735: {}
3183
+
3184
+
caniuse-lite@1.0.30001741: {}
3185
+
3186
+
chalk@4.1.2:
3187
+
dependencies:
3188
+
ansi-styles: 4.3.0
3189
+
supports-color: 7.2.0
3190
+
3191
+
chalk@5.6.2: {}
3192
+
3193
+
chokidar@3.6.0:
3194
+
dependencies:
3195
+
anymatch: 3.1.3
3196
+
braces: 3.0.3
3197
+
glob-parent: 5.1.2
3198
+
is-binary-path: 2.1.0
3199
+
is-glob: 4.0.3
3200
+
normalize-path: 3.0.0
3201
+
readdirp: 3.6.0
3202
+
optionalDependencies:
3203
+
fsevents: 2.3.3
3204
+
3205
+
chokidar@4.0.3:
3206
+
dependencies:
3207
+
readdirp: 4.1.2
3208
+
3209
+
chrome-launcher@1.2.0:
3210
+
dependencies:
3211
+
'@types/node': 24.3.2
3212
+
escape-string-regexp: 4.0.0
3213
+
is-wsl: 2.2.0
3214
+
lighthouse-logger: 2.0.2
3215
+
transitivePeerDependencies:
3216
+
- supports-color
3217
+
3218
+
ci-info@4.3.0: {}
3219
+
3220
+
citty@0.1.6:
3221
+
dependencies:
3222
+
consola: 3.4.2
3223
+
3224
+
cli-boxes@3.0.0: {}
3225
+
3226
+
cli-cursor@4.0.0:
3227
+
dependencies:
3228
+
restore-cursor: 4.0.0
3229
+
3230
+
cli-cursor@5.0.0:
3231
+
dependencies:
3232
+
restore-cursor: 5.1.0
3233
+
3234
+
cli-highlight@2.1.11:
3235
+
dependencies:
3236
+
chalk: 4.1.2
3237
+
highlight.js: 10.7.3
3238
+
mz: 2.7.0
3239
+
parse5: 5.1.1
3240
+
parse5-htmlparser2-tree-adapter: 6.0.1
3241
+
yargs: 16.2.0
3242
+
3243
+
cli-spinners@2.9.2: {}
3244
+
3245
+
cli-truncate@4.0.0:
3246
+
dependencies:
3247
+
slice-ansi: 5.0.0
3248
+
string-width: 7.2.0
3249
+
3250
+
cliui@7.0.4:
3251
+
dependencies:
3252
+
string-width: 4.2.3
3253
+
strip-ansi: 6.0.1
3254
+
wrap-ansi: 7.0.0
3255
+
3256
+
cliui@8.0.1:
3257
+
dependencies:
3258
+
string-width: 4.2.3
3259
+
strip-ansi: 6.0.1
3260
+
wrap-ansi: 7.0.0
3261
+
3262
+
clone@1.0.4: {}
3263
+
3264
+
color-convert@2.0.1:
3265
+
dependencies:
3266
+
color-name: 1.1.4
3267
+
3268
+
color-name@1.1.4: {}
3269
+
3270
+
color-string@1.9.1:
3271
+
dependencies:
3272
+
color-name: 1.1.4
3273
+
simple-swizzle: 0.2.2
3274
+
3275
+
color@4.2.3:
3276
+
dependencies:
3277
+
color-convert: 2.0.1
3278
+
color-string: 1.9.1
3279
+
3280
+
colorette@2.0.20: {}
3281
+
3282
+
commander@2.9.0:
3283
+
dependencies:
3284
+
graceful-readlink: 1.0.1
3285
+
3286
+
commander@4.1.1: {}
3287
+
3288
+
commander@9.5.0: {}
3289
+
3290
+
concat-map@0.0.1: {}
3291
+
3292
+
concat-stream@1.6.2:
3293
+
dependencies:
3294
+
buffer-from: 1.1.2
3295
+
inherits: 2.0.4
3296
+
readable-stream: 2.3.8
3297
+
typedarray: 0.0.6
3298
+
3299
+
confbox@0.1.8: {}
3300
+
3301
+
confbox@0.2.2: {}
3302
+
3303
+
config-chain@1.1.13:
3304
+
dependencies:
3305
+
ini: 1.3.8
3306
+
proto-list: 1.2.4
3307
+
3308
+
configstore@7.0.0:
3309
+
dependencies:
3310
+
atomically: 2.0.3
3311
+
dot-prop: 9.0.0
3312
+
graceful-fs: 4.2.11
3313
+
xdg-basedir: 5.1.0
3314
+
3315
+
consola@3.4.2: {}
3316
+
3317
+
convert-source-map@2.0.0: {}
3318
+
3319
+
core-util-is@1.0.3: {}
3320
+
3321
+
cross-spawn@7.0.6:
3322
+
dependencies:
3323
+
path-key: 3.1.1
3324
+
shebang-command: 2.0.0
3325
+
which: 2.0.2
3326
+
3327
+
css-select@5.2.2:
3328
+
dependencies:
3329
+
boolbase: 1.0.0
3330
+
css-what: 6.2.2
3331
+
domhandler: 5.0.3
3332
+
domutils: 3.2.2
3333
+
nth-check: 2.1.1
3334
+
3335
+
css-what@6.2.2: {}
3336
+
3337
+
cssesc@3.0.0: {}
3338
+
3339
+
cssom@0.5.0: {}
3340
+
3341
+
csstype@3.1.3: {}
3342
+
3343
+
debounce@1.2.1: {}
3344
+
3345
+
debug@4.3.7:
3346
+
dependencies:
3347
+
ms: 2.1.3
3348
+
3349
+
debug@4.4.1:
3350
+
dependencies:
3351
+
ms: 2.1.3
3352
+
3353
+
deep-extend@0.6.0: {}
3354
+
3355
+
default-browser-id@5.0.0: {}
3356
+
3357
+
default-browser@5.2.1:
3358
+
dependencies:
3359
+
bundle-name: 4.1.0
3360
+
default-browser-id: 5.0.0
3361
+
3362
+
defaults@1.0.4:
3363
+
dependencies:
3364
+
clone: 1.0.4
3365
+
3366
+
define-lazy-prop@2.0.0: {}
3367
+
3368
+
define-lazy-prop@3.0.0: {}
3369
+
3370
+
defu@6.1.4: {}
3371
+
3372
+
dequal@2.0.3: {}
3373
+
3374
+
destr@2.0.5: {}
3375
+
3376
+
detect-libc@2.0.4: {}
3377
+
3378
+
didyoumean@1.2.2: {}
3379
+
3380
+
dlv@1.1.3: {}
3381
+
3382
+
dom-serializer@2.0.0:
3383
+
dependencies:
3384
+
domelementtype: 2.3.0
3385
+
domhandler: 5.0.3
3386
+
entities: 4.5.0
3387
+
3388
+
domelementtype@2.3.0: {}
3389
+
3390
+
domhandler@5.0.3:
3391
+
dependencies:
3392
+
domelementtype: 2.3.0
3393
+
3394
+
domutils@3.2.2:
3395
+
dependencies:
3396
+
dom-serializer: 2.0.0
3397
+
domelementtype: 2.3.0
3398
+
domhandler: 5.0.3
3399
+
3400
+
dot-prop@9.0.0:
3401
+
dependencies:
3402
+
type-fest: 4.41.0
3403
+
3404
+
dotenv-expand@12.0.3:
3405
+
dependencies:
3406
+
dotenv: 16.6.1
3407
+
3408
+
dotenv@16.6.1: {}
3409
+
3410
+
dotenv@17.2.2: {}
3411
+
3412
+
eastasianwidth@0.2.0: {}
3413
+
3414
+
electron-to-chromium@1.5.207: {}
3415
+
3416
+
electron-to-chromium@1.5.218: {}
3417
+
3418
+
emoji-regex@10.5.0: {}
3419
+
3420
+
emoji-regex@8.0.0: {}
3421
+
3422
+
emoji-regex@9.2.2: {}
3423
+
3424
+
end-of-stream@1.4.5:
3425
+
dependencies:
3426
+
once: 1.4.0
3427
+
3428
+
entities@4.5.0: {}
3429
+
3430
+
entities@6.0.1: {}
3431
+
3432
+
environment@1.1.0: {}
3433
+
3434
+
error-ex@1.3.2:
3435
+
dependencies:
3436
+
is-arrayish: 0.2.1
3437
+
3438
+
es-module-lexer@1.7.0: {}
3439
+
3440
+
es6-error@4.1.1: {}
3441
+
3442
+
esbuild@0.25.9:
3443
+
optionalDependencies:
3444
+
'@esbuild/aix-ppc64': 0.25.9
3445
+
'@esbuild/android-arm': 0.25.9
3446
+
'@esbuild/android-arm64': 0.25.9
3447
+
'@esbuild/android-x64': 0.25.9
3448
+
'@esbuild/darwin-arm64': 0.25.9
3449
+
'@esbuild/darwin-x64': 0.25.9
3450
+
'@esbuild/freebsd-arm64': 0.25.9
3451
+
'@esbuild/freebsd-x64': 0.25.9
3452
+
'@esbuild/linux-arm': 0.25.9
3453
+
'@esbuild/linux-arm64': 0.25.9
3454
+
'@esbuild/linux-ia32': 0.25.9
3455
+
'@esbuild/linux-loong64': 0.25.9
3456
+
'@esbuild/linux-mips64el': 0.25.9
3457
+
'@esbuild/linux-ppc64': 0.25.9
3458
+
'@esbuild/linux-riscv64': 0.25.9
3459
+
'@esbuild/linux-s390x': 0.25.9
3460
+
'@esbuild/linux-x64': 0.25.9
3461
+
'@esbuild/netbsd-arm64': 0.25.9
3462
+
'@esbuild/netbsd-x64': 0.25.9
3463
+
'@esbuild/openbsd-arm64': 0.25.9
3464
+
'@esbuild/openbsd-x64': 0.25.9
3465
+
'@esbuild/openharmony-arm64': 0.25.9
3466
+
'@esbuild/sunos-x64': 0.25.9
3467
+
'@esbuild/win32-arm64': 0.25.9
3468
+
'@esbuild/win32-ia32': 0.25.9
3469
+
'@esbuild/win32-x64': 0.25.9
3470
+
3471
+
escalade@3.2.0: {}
3472
+
3473
+
escape-goat@4.0.0: {}
3474
+
3475
+
escape-string-regexp@4.0.0: {}
3476
+
3477
+
escape-string-regexp@5.0.0: {}
3478
+
3479
+
estree-walker@3.0.3:
3480
+
dependencies:
3481
+
'@types/estree': 1.0.8
3482
+
3483
+
eventemitter3@5.0.1: {}
3484
+
3485
+
exsolve@1.0.7: {}
3486
+
3487
+
extract-zip@2.0.1:
3488
+
dependencies:
3489
+
debug: 4.4.1
3490
+
get-stream: 5.2.0
3491
+
yauzl: 2.10.0
3492
+
optionalDependencies:
3493
+
'@types/yauzl': 2.10.3
3494
+
transitivePeerDependencies:
3495
+
- supports-color
3496
+
3497
+
fast-glob@3.3.3:
3498
+
dependencies:
3499
+
'@nodelib/fs.stat': 2.0.5
3500
+
'@nodelib/fs.walk': 1.2.8
3501
+
glob-parent: 5.1.2
3502
+
merge2: 1.4.1
3503
+
micromatch: 4.0.8
3504
+
3505
+
fast-redact@3.5.0: {}
3506
+
3507
+
fastq@1.19.1:
3508
+
dependencies:
3509
+
reusify: 1.1.0
3510
+
3511
+
fd-slicer@1.1.0:
3512
+
dependencies:
3513
+
pend: 1.2.0
3514
+
3515
+
fdir@6.5.0(picomatch@4.0.3):
3516
+
optionalDependencies:
3517
+
picomatch: 4.0.3
3518
+
3519
+
filesize@11.0.2: {}
3520
+
3521
+
fill-range@7.1.1:
3522
+
dependencies:
3523
+
to-regex-range: 5.0.1
3524
+
3525
+
firefox-profile@4.7.0:
3526
+
dependencies:
3527
+
adm-zip: 0.5.16
3528
+
fs-extra: 11.3.1
3529
+
ini: 4.1.3
3530
+
minimist: 1.2.8
3531
+
xml2js: 0.6.2
3532
+
3533
+
foreground-child@3.3.1:
3534
+
dependencies:
3535
+
cross-spawn: 7.0.6
3536
+
signal-exit: 4.1.0
3537
+
3538
+
formdata-node@6.0.3: {}
3539
+
3540
+
fraction.js@4.3.7: {}
3541
+
3542
+
fs-extra@11.3.1:
3543
+
dependencies:
3544
+
graceful-fs: 4.2.11
3545
+
jsonfile: 6.2.0
3546
+
universalify: 2.0.1
3547
+
3548
+
fsevents@2.3.3:
3549
+
optional: true
3550
+
3551
+
function-bind@1.1.2: {}
3552
+
3553
+
fx-runner@1.4.0:
3554
+
dependencies:
3555
+
commander: 2.9.0
3556
+
shell-quote: 1.7.3
3557
+
spawn-sync: 1.0.15
3558
+
when: 3.7.7
3559
+
which: 1.2.4
3560
+
winreg: 0.0.12
3561
+
3562
+
gensync@1.0.0-beta.2: {}
3563
+
3564
+
get-caller-file@2.0.5: {}
3565
+
3566
+
get-east-asian-width@1.4.0: {}
3567
+
3568
+
get-port-please@3.2.0: {}
3569
+
3570
+
get-stream@5.2.0:
3571
+
dependencies:
3572
+
pump: 3.0.3
3573
+
3574
+
giget@2.0.0:
3575
+
dependencies:
3576
+
citty: 0.1.6
3577
+
consola: 3.4.2
3578
+
defu: 6.1.4
3579
+
node-fetch-native: 1.6.7
3580
+
nypm: 0.6.1
3581
+
pathe: 2.0.3
3582
+
3583
+
glob-parent@5.1.2:
3584
+
dependencies:
3585
+
is-glob: 4.0.3
3586
+
3587
+
glob-parent@6.0.2:
3588
+
dependencies:
3589
+
is-glob: 4.0.3
3590
+
3591
+
glob-to-regexp@0.4.1: {}
3592
+
3593
+
glob@10.4.5:
3594
+
dependencies:
3595
+
foreground-child: 3.3.1
3596
+
jackspeak: 3.4.3
3597
+
minimatch: 9.0.5
3598
+
minipass: 7.1.2
3599
+
package-json-from-dist: 1.0.1
3600
+
path-scurry: 1.11.1
3601
+
3602
+
global-directory@4.0.1:
3603
+
dependencies:
3604
+
ini: 4.1.1
3605
+
3606
+
graceful-fs@4.2.10: {}
3607
+
3608
+
graceful-fs@4.2.11: {}
3609
+
3610
+
graceful-readlink@1.0.1: {}
3611
+
3612
+
growly@1.3.0: {}
3613
+
3614
+
has-flag@4.0.0: {}
3615
+
3616
+
hasown@2.0.2:
3617
+
dependencies:
3618
+
function-bind: 1.1.2
3619
+
3620
+
highlight.js@10.7.3: {}
3621
+
3622
+
hookable@5.5.3: {}
3623
+
3624
+
html-escaper@3.0.3: {}
3625
+
3626
+
htmlparser2@10.0.0:
3627
+
dependencies:
3628
+
domelementtype: 2.3.0
3629
+
domhandler: 5.0.3
3630
+
domutils: 3.2.2
3631
+
entities: 6.0.1
3632
+
3633
+
ieee754@1.2.1: {}
3634
+
3635
+
immediate@3.0.6: {}
3636
+
3637
+
import-meta-resolve@4.2.0: {}
3638
+
3639
+
inherits@2.0.4: {}
3640
+
3641
+
ini@1.3.8: {}
3642
+
3643
+
ini@4.1.1: {}
3644
+
3645
+
ini@4.1.3: {}
3646
+
3647
+
is-absolute@0.1.7:
3648
+
dependencies:
3649
+
is-relative: 0.1.3
3650
+
3651
+
is-arrayish@0.2.1: {}
3652
+
3653
+
is-arrayish@0.3.2: {}
3654
+
3655
+
is-binary-path@2.1.0:
3656
+
dependencies:
3657
+
binary-extensions: 2.3.0
3658
+
3659
+
is-core-module@2.16.1:
3660
+
dependencies:
3661
+
hasown: 2.0.2
3662
+
3663
+
is-docker@2.2.1: {}
3664
+
3665
+
is-docker@3.0.0: {}
3666
+
3667
+
is-extglob@2.1.1: {}
3668
+
3669
+
is-fullwidth-code-point@3.0.0: {}
3670
+
3671
+
is-fullwidth-code-point@4.0.0: {}
3672
+
3673
+
is-fullwidth-code-point@5.1.0:
3674
+
dependencies:
3675
+
get-east-asian-width: 1.4.0
3676
+
3677
+
is-glob@4.0.3:
3678
+
dependencies:
3679
+
is-extglob: 2.1.1
3680
+
3681
+
is-in-ci@1.0.0: {}
3682
+
3683
+
is-inside-container@1.0.0:
3684
+
dependencies:
3685
+
is-docker: 3.0.0
3686
+
3687
+
is-installed-globally@1.0.0:
3688
+
dependencies:
3689
+
global-directory: 4.0.1
3690
+
is-path-inside: 4.0.0
3691
+
3692
+
is-interactive@2.0.0: {}
3693
+
3694
+
is-npm@6.1.0: {}
3695
+
3696
+
is-number@7.0.0: {}
3697
+
3698
+
is-path-inside@4.0.0: {}
3699
+
3700
+
is-plain-object@2.0.4:
3701
+
dependencies:
3702
+
isobject: 3.0.1
3703
+
3704
+
is-potential-custom-element-name@1.0.1: {}
3705
+
3706
+
is-primitive@3.0.1: {}
3707
+
3708
+
is-relative@0.1.3: {}
3709
+
3710
+
is-unicode-supported@1.3.0: {}
3711
+
3712
+
is-unicode-supported@2.1.0: {}
3713
+
3714
+
is-wsl@2.2.0:
3715
+
dependencies:
3716
+
is-docker: 2.2.1
3717
+
3718
+
is-wsl@3.1.0:
3719
+
dependencies:
3720
+
is-inside-container: 1.0.0
3721
+
3722
+
isarray@1.0.0: {}
3723
+
3724
+
isexe@1.1.2: {}
3725
+
3726
+
isexe@2.0.0: {}
3727
+
3728
+
isobject@3.0.1: {}
3729
+
3730
+
jackspeak@3.4.3:
3731
+
dependencies:
3732
+
'@isaacs/cliui': 8.0.2
3733
+
optionalDependencies:
3734
+
'@pkgjs/parseargs': 0.11.0
3735
+
3736
+
jiti@1.21.7: {}
3737
+
3738
+
jiti@2.5.1: {}
3739
+
3740
+
js-tokens@4.0.0: {}
3741
+
3742
+
js-tokens@9.0.1: {}
3743
+
3744
+
jsesc@3.1.0: {}
3745
+
3746
+
json-parse-even-better-errors@3.0.2: {}
3747
+
3748
+
json5@2.2.3: {}
3749
+
3750
+
jsonfile@6.2.0:
3751
+
dependencies:
3752
+
universalify: 2.0.1
3753
+
optionalDependencies:
3754
+
graceful-fs: 4.2.11
3755
+
3756
+
jszip@3.10.1:
3757
+
dependencies:
3758
+
lie: 3.3.0
3759
+
pako: 1.0.11
3760
+
readable-stream: 2.3.8
3761
+
setimmediate: 1.0.5
3762
+
3763
+
kleur@3.0.3: {}
3764
+
3765
+
ky@1.10.0: {}
3766
+
3767
+
latest-version@9.0.0:
3768
+
dependencies:
3769
+
package-json: 10.0.1
3770
+
3771
+
lie@3.3.0:
3772
+
dependencies:
3773
+
immediate: 3.0.6
3774
+
3775
+
lighthouse-logger@2.0.2:
3776
+
dependencies:
3777
+
debug: 4.4.1
3778
+
marky: 1.3.0
3779
+
transitivePeerDependencies:
3780
+
- supports-color
3781
+
3782
+
lilconfig@2.1.0: {}
3783
+
3784
+
lilconfig@3.1.3: {}
3785
+
3786
+
lines-and-columns@1.2.4: {}
3787
+
3788
+
lines-and-columns@2.0.4: {}
3789
+
3790
+
linkedom@0.18.12:
3791
+
dependencies:
3792
+
css-select: 5.2.2
3793
+
cssom: 0.5.0
3794
+
html-escaper: 3.0.3
3795
+
htmlparser2: 10.0.0
3796
+
uhyphen: 0.2.0
3797
+
3798
+
listr2@8.3.3:
3799
+
dependencies:
3800
+
cli-truncate: 4.0.0
3801
+
colorette: 2.0.20
3802
+
eventemitter3: 5.0.1
3803
+
log-update: 6.1.0
3804
+
rfdc: 1.4.1
3805
+
wrap-ansi: 9.0.2
3806
+
3807
+
local-pkg@1.1.2:
3808
+
dependencies:
3809
+
mlly: 1.8.0
3810
+
pkg-types: 2.3.0
3811
+
quansync: 0.2.11
3812
+
3813
+
lodash.camelcase@4.3.0: {}
3814
+
3815
+
lodash.kebabcase@4.1.1: {}
3816
+
3817
+
lodash.merge@4.6.2: {}
3818
+
3819
+
lodash.snakecase@4.1.1: {}
3820
+
3821
+
log-symbols@5.1.0:
3822
+
dependencies:
3823
+
chalk: 5.6.2
3824
+
is-unicode-supported: 1.3.0
3825
+
3826
+
log-symbols@6.0.0:
3827
+
dependencies:
3828
+
chalk: 5.6.2
3829
+
is-unicode-supported: 1.3.0
3830
+
3831
+
log-update@6.1.0:
3832
+
dependencies:
3833
+
ansi-escapes: 7.1.0
3834
+
cli-cursor: 5.0.0
3835
+
slice-ansi: 7.1.2
3836
+
strip-ansi: 7.1.2
3837
+
wrap-ansi: 9.0.2
3838
+
3839
+
lru-cache@10.4.3: {}
3840
+
3841
+
lru-cache@5.1.1:
3842
+
dependencies:
3843
+
yallist: 3.1.1
3844
+
3845
+
magic-string@0.30.19:
3846
+
dependencies:
3847
+
'@jridgewell/sourcemap-codec': 1.5.5
3848
+
3849
+
magicast@0.3.5:
3850
+
dependencies:
3851
+
'@babel/parser': 7.28.4
3852
+
'@babel/types': 7.28.4
3853
+
source-map-js: 1.2.1
3854
+
3855
+
make-error@1.3.6: {}
3856
+
3857
+
many-keys-map@2.0.1: {}
3858
+
3859
+
marky@1.3.0: {}
3860
+
3861
+
merge2@1.4.1: {}
3862
+
3863
+
micromatch@4.0.8:
3864
+
dependencies:
3865
+
braces: 3.0.3
3866
+
picomatch: 2.3.1
3867
+
3868
+
mimic-fn@2.1.0: {}
3869
+
3870
+
mimic-function@5.0.1: {}
3871
+
3872
+
minimatch@10.0.3:
3873
+
dependencies:
3874
+
'@isaacs/brace-expansion': 5.0.0
3875
+
3876
+
minimatch@3.1.2:
3877
+
dependencies:
3878
+
brace-expansion: 1.1.12
3879
+
3880
+
minimatch@9.0.5:
3881
+
dependencies:
3882
+
brace-expansion: 2.0.2
3883
+
3884
+
minimist@1.2.8: {}
3885
+
3886
+
minipass@7.1.2: {}
3887
+
3888
+
mlly@1.8.0:
3889
+
dependencies:
3890
+
acorn: 8.15.0
3891
+
pathe: 2.0.3
3892
+
pkg-types: 1.3.1
3893
+
ufo: 1.6.1
3894
+
3895
+
ms@2.1.3: {}
3896
+
3897
+
multimatch@6.0.0:
3898
+
dependencies:
3899
+
'@types/minimatch': 3.0.5
3900
+
array-differ: 4.0.0
3901
+
array-union: 3.0.1
3902
+
minimatch: 3.1.2
3903
+
3904
+
mz@2.7.0:
3905
+
dependencies:
3906
+
any-promise: 1.3.0
3907
+
object-assign: 4.1.1
3908
+
thenify-all: 1.6.0
3909
+
3910
+
nano-spawn@1.0.3: {}
3911
+
3912
+
nanoid@3.3.11: {}
3913
+
3914
+
node-fetch-native@1.6.7: {}
3915
+
3916
+
node-forge@1.3.1: {}
3917
+
3918
+
node-notifier@10.0.1:
3919
+
dependencies:
3920
+
growly: 1.3.0
3921
+
is-wsl: 2.2.0
3922
+
semver: 7.7.2
3923
+
shellwords: 0.1.1
3924
+
uuid: 8.3.2
3925
+
which: 2.0.2
3926
+
3927
+
node-releases@2.0.19: {}
3928
+
3929
+
node-releases@2.0.21: {}
3930
+
3931
+
normalize-path@3.0.0: {}
3932
+
3933
+
normalize-range@0.1.2: {}
3934
+
3935
+
nth-check@2.1.1:
3936
+
dependencies:
3937
+
boolbase: 1.0.0
3938
+
3939
+
nypm@0.6.1:
3940
+
dependencies:
3941
+
citty: 0.1.6
3942
+
consola: 3.4.2
3943
+
pathe: 2.0.3
3944
+
pkg-types: 2.3.0
3945
+
tinyexec: 1.0.1
3946
+
3947
+
object-assign@4.1.1: {}
3948
+
3949
+
object-hash@3.0.0: {}
3950
+
3951
+
ofetch@1.4.1:
3952
+
dependencies:
3953
+
destr: 2.0.5
3954
+
node-fetch-native: 1.6.7
3955
+
ufo: 1.6.1
3956
+
3957
+
ohash@2.0.11: {}
3958
+
3959
+
on-exit-leak-free@2.1.2: {}
3960
+
3961
+
once@1.4.0:
3962
+
dependencies:
3963
+
wrappy: 1.0.2
3964
+
3965
+
onetime@5.1.2:
3966
+
dependencies:
3967
+
mimic-fn: 2.1.0
3968
+
3969
+
onetime@7.0.0:
3970
+
dependencies:
3971
+
mimic-function: 5.0.1
3972
+
3973
+
open@10.2.0:
3974
+
dependencies:
3975
+
default-browser: 5.2.1
3976
+
define-lazy-prop: 3.0.0
3977
+
is-inside-container: 1.0.0
3978
+
wsl-utils: 0.1.0
3979
+
3980
+
open@8.4.2:
3981
+
dependencies:
3982
+
define-lazy-prop: 2.0.0
3983
+
is-docker: 2.2.1
3984
+
is-wsl: 2.2.0
3985
+
3986
+
ora@6.3.1:
3987
+
dependencies:
3988
+
chalk: 5.6.2
3989
+
cli-cursor: 4.0.0
3990
+
cli-spinners: 2.9.2
3991
+
is-interactive: 2.0.0
3992
+
is-unicode-supported: 1.3.0
3993
+
log-symbols: 5.1.0
3994
+
stdin-discarder: 0.1.0
3995
+
strip-ansi: 7.1.2
3996
+
wcwidth: 1.0.1
3997
+
3998
+
ora@8.2.0:
3999
+
dependencies:
4000
+
chalk: 5.6.2
4001
+
cli-cursor: 5.0.0
4002
+
cli-spinners: 2.9.2
4003
+
is-interactive: 2.0.0
4004
+
is-unicode-supported: 2.1.0
4005
+
log-symbols: 6.0.0
4006
+
stdin-discarder: 0.2.2
4007
+
string-width: 7.2.0
4008
+
strip-ansi: 7.1.2
4009
+
4010
+
os-shim@0.1.3: {}
4011
+
4012
+
package-json-from-dist@1.0.1: {}
4013
+
4014
+
package-json@10.0.1:
4015
+
dependencies:
4016
+
ky: 1.10.0
4017
+
registry-auth-token: 5.1.0
4018
+
registry-url: 6.0.1
4019
+
semver: 7.7.2
4020
+
4021
+
pako@1.0.11: {}
4022
+
4023
+
parse-json@7.1.1:
4024
+
dependencies:
4025
+
'@babel/code-frame': 7.27.1
4026
+
error-ex: 1.3.2
4027
+
json-parse-even-better-errors: 3.0.2
4028
+
lines-and-columns: 2.0.4
4029
+
type-fest: 3.13.1
4030
+
4031
+
parse5-htmlparser2-tree-adapter@6.0.1:
4032
+
dependencies:
4033
+
parse5: 6.0.1
4034
+
4035
+
parse5@5.1.1: {}
4036
+
4037
+
parse5@6.0.1: {}
4038
+
4039
+
path-key@3.1.1: {}
4040
+
4041
+
path-parse@1.0.7: {}
4042
+
4043
+
path-scurry@1.11.1:
4044
+
dependencies:
4045
+
lru-cache: 10.4.3
4046
+
minipass: 7.1.2
4047
+
4048
+
pathe@2.0.3: {}
4049
+
4050
+
pend@1.2.0: {}
4051
+
4052
+
perfect-debounce@1.0.0: {}
4053
+
4054
+
perfect-debounce@2.0.0: {}
4055
+
4056
+
picocolors@1.1.1: {}
4057
+
4058
+
picomatch@2.3.1: {}
4059
+
4060
+
picomatch@4.0.3: {}
4061
+
4062
+
pify@2.3.0: {}
4063
+
4064
+
pino-abstract-transport@2.0.0:
4065
+
dependencies:
4066
+
split2: 4.2.0
4067
+
4068
+
pino-std-serializers@7.0.0: {}
4069
+
4070
+
pino@9.7.0:
4071
+
dependencies:
4072
+
atomic-sleep: 1.0.0
4073
+
fast-redact: 3.5.0
4074
+
on-exit-leak-free: 2.1.2
4075
+
pino-abstract-transport: 2.0.0
4076
+
pino-std-serializers: 7.0.0
4077
+
process-warning: 5.0.0
4078
+
quick-format-unescaped: 4.0.4
4079
+
real-require: 0.2.0
4080
+
safe-stable-stringify: 2.5.0
4081
+
sonic-boom: 4.2.0
4082
+
thread-stream: 3.1.0
4083
+
4084
+
pirates@4.0.7: {}
4085
+
4086
+
pkg-types@1.3.1:
4087
+
dependencies:
4088
+
confbox: 0.1.8
4089
+
mlly: 1.8.0
4090
+
pathe: 2.0.3
4091
+
4092
+
pkg-types@2.3.0:
4093
+
dependencies:
4094
+
confbox: 0.2.2
4095
+
exsolve: 1.0.7
4096
+
pathe: 2.0.3
4097
+
4098
+
postcss-import@15.1.0(postcss@8.5.6):
4099
+
dependencies:
4100
+
postcss: 8.5.6
4101
+
postcss-value-parser: 4.2.0
4102
+
read-cache: 1.0.0
4103
+
resolve: 1.22.10
4104
+
4105
+
postcss-js@4.0.1(postcss@8.5.6):
4106
+
dependencies:
4107
+
camelcase-css: 2.0.1
4108
+
postcss: 8.5.6
4109
+
4110
+
postcss-load-config@4.0.2(postcss@8.5.6):
4111
+
dependencies:
4112
+
lilconfig: 3.1.3
4113
+
yaml: 2.8.1
4114
+
optionalDependencies:
4115
+
postcss: 8.5.6
4116
+
4117
+
postcss-nested@6.2.0(postcss@8.5.6):
4118
+
dependencies:
4119
+
postcss: 8.5.6
4120
+
postcss-selector-parser: 6.1.2
4121
+
4122
+
postcss-selector-parser@6.1.2:
4123
+
dependencies:
4124
+
cssesc: 3.0.0
4125
+
util-deprecate: 1.0.2
4126
+
4127
+
postcss-value-parser@4.2.0: {}
4128
+
4129
+
postcss@8.5.6:
4130
+
dependencies:
4131
+
nanoid: 3.3.11
4132
+
picocolors: 1.1.1
4133
+
source-map-js: 1.2.1
4134
+
4135
+
process-nextick-args@2.0.1: {}
4136
+
4137
+
process-warning@5.0.0: {}
4138
+
4139
+
promise-toolbox@0.21.0:
4140
+
dependencies:
4141
+
make-error: 1.3.6
4142
+
4143
+
prompts@2.4.2:
4144
+
dependencies:
4145
+
kleur: 3.0.3
4146
+
sisteransi: 1.0.5
4147
+
4148
+
proto-list@1.2.4: {}
4149
+
4150
+
publish-browser-extension@3.0.2:
4151
+
dependencies:
4152
+
cac: 6.7.14
4153
+
cli-highlight: 2.1.11
4154
+
consola: 3.4.2
4155
+
dotenv: 16.6.1
4156
+
extract-zip: 2.0.1
4157
+
formdata-node: 6.0.3
4158
+
listr2: 8.3.3
4159
+
lodash.camelcase: 4.3.0
4160
+
lodash.kebabcase: 4.1.1
4161
+
lodash.snakecase: 4.1.1
4162
+
ofetch: 1.4.1
4163
+
open: 10.2.0
4164
+
ora: 6.3.1
4165
+
prompts: 2.4.2
4166
+
zod: 3.25.76
4167
+
transitivePeerDependencies:
4168
+
- supports-color
4169
+
4170
+
pump@3.0.3:
4171
+
dependencies:
4172
+
end-of-stream: 1.4.5
4173
+
once: 1.4.0
4174
+
4175
+
pupa@3.1.0:
4176
+
dependencies:
4177
+
escape-goat: 4.0.0
4178
+
4179
+
quansync@0.2.11: {}
4180
+
4181
+
queue-microtask@1.2.3: {}
4182
+
4183
+
quick-format-unescaped@4.0.4: {}
4184
+
4185
+
rc9@2.1.2:
4186
+
dependencies:
4187
+
defu: 6.1.4
4188
+
destr: 2.0.5
4189
+
4190
+
rc@1.2.8:
4191
+
dependencies:
4192
+
deep-extend: 0.6.0
4193
+
ini: 1.3.8
4194
+
minimist: 1.2.8
4195
+
strip-json-comments: 2.0.1
4196
+
4197
+
react-dom@19.1.1(react@19.1.1):
4198
+
dependencies:
4199
+
react: 19.1.1
4200
+
scheduler: 0.26.0
4201
+
4202
+
react-icons@5.5.0(react@19.1.1):
4203
+
dependencies:
4204
+
react: 19.1.1
4205
+
4206
+
react-refresh@0.17.0: {}
4207
+
4208
+
react@19.1.1: {}
4209
+
4210
+
read-cache@1.0.0:
4211
+
dependencies:
4212
+
pify: 2.3.0
4213
+
4214
+
readable-stream@2.3.8:
4215
+
dependencies:
4216
+
core-util-is: 1.0.3
4217
+
inherits: 2.0.4
4218
+
isarray: 1.0.0
4219
+
process-nextick-args: 2.0.1
4220
+
safe-buffer: 5.1.2
4221
+
string_decoder: 1.1.1
4222
+
util-deprecate: 1.0.2
4223
+
4224
+
readable-stream@3.6.2:
4225
+
dependencies:
4226
+
inherits: 2.0.4
4227
+
string_decoder: 1.3.0
4228
+
util-deprecate: 1.0.2
4229
+
4230
+
readdirp@3.6.0:
4231
+
dependencies:
4232
+
picomatch: 2.3.1
4233
+
4234
+
readdirp@4.1.2: {}
4235
+
4236
+
real-require@0.2.0: {}
4237
+
4238
+
registry-auth-token@5.1.0:
4239
+
dependencies:
4240
+
'@pnpm/npm-conf': 2.3.1
4241
+
4242
+
registry-url@6.0.1:
4243
+
dependencies:
4244
+
rc: 1.2.8
4245
+
4246
+
require-directory@2.1.1: {}
4247
+
4248
+
resolve@1.22.10:
4249
+
dependencies:
4250
+
is-core-module: 2.16.1
4251
+
path-parse: 1.0.7
4252
+
supports-preserve-symlinks-flag: 1.0.0
4253
+
4254
+
restore-cursor@4.0.0:
4255
+
dependencies:
4256
+
onetime: 5.1.2
4257
+
signal-exit: 3.0.7
4258
+
4259
+
restore-cursor@5.1.0:
4260
+
dependencies:
4261
+
onetime: 7.0.0
4262
+
signal-exit: 4.1.0
4263
+
4264
+
reusify@1.1.0: {}
4265
+
4266
+
rfdc@1.4.1: {}
4267
+
4268
+
rollup@4.50.1:
4269
+
dependencies:
4270
+
'@types/estree': 1.0.8
4271
+
optionalDependencies:
4272
+
'@rollup/rollup-android-arm-eabi': 4.50.1
4273
+
'@rollup/rollup-android-arm64': 4.50.1
4274
+
'@rollup/rollup-darwin-arm64': 4.50.1
4275
+
'@rollup/rollup-darwin-x64': 4.50.1
4276
+
'@rollup/rollup-freebsd-arm64': 4.50.1
4277
+
'@rollup/rollup-freebsd-x64': 4.50.1
4278
+
'@rollup/rollup-linux-arm-gnueabihf': 4.50.1
4279
+
'@rollup/rollup-linux-arm-musleabihf': 4.50.1
4280
+
'@rollup/rollup-linux-arm64-gnu': 4.50.1
4281
+
'@rollup/rollup-linux-arm64-musl': 4.50.1
4282
+
'@rollup/rollup-linux-loongarch64-gnu': 4.50.1
4283
+
'@rollup/rollup-linux-ppc64-gnu': 4.50.1
4284
+
'@rollup/rollup-linux-riscv64-gnu': 4.50.1
4285
+
'@rollup/rollup-linux-riscv64-musl': 4.50.1
4286
+
'@rollup/rollup-linux-s390x-gnu': 4.50.1
4287
+
'@rollup/rollup-linux-x64-gnu': 4.50.1
4288
+
'@rollup/rollup-linux-x64-musl': 4.50.1
4289
+
'@rollup/rollup-openharmony-arm64': 4.50.1
4290
+
'@rollup/rollup-win32-arm64-msvc': 4.50.1
4291
+
'@rollup/rollup-win32-ia32-msvc': 4.50.1
4292
+
'@rollup/rollup-win32-x64-msvc': 4.50.1
4293
+
fsevents: 2.3.3
4294
+
4295
+
run-applescript@7.1.0: {}
4296
+
4297
+
run-parallel@1.2.0:
4298
+
dependencies:
4299
+
queue-microtask: 1.2.3
4300
+
4301
+
safe-buffer@5.1.2: {}
4302
+
4303
+
safe-buffer@5.2.1: {}
4304
+
4305
+
safe-stable-stringify@2.5.0: {}
4306
+
4307
+
sax@1.4.1: {}
4308
+
4309
+
scheduler@0.26.0: {}
4310
+
4311
+
scule@1.3.0: {}
4312
+
4313
+
semver@6.3.1: {}
4314
+
4315
+
semver@7.7.2: {}
4316
+
4317
+
set-value@4.1.0:
4318
+
dependencies:
4319
+
is-plain-object: 2.0.4
4320
+
is-primitive: 3.0.1
4321
+
4322
+
setimmediate@1.0.5: {}
4323
+
4324
+
sharp@0.34.3:
4325
+
dependencies:
4326
+
color: 4.2.3
4327
+
detect-libc: 2.0.4
4328
+
semver: 7.7.2
4329
+
optionalDependencies:
4330
+
'@img/sharp-darwin-arm64': 0.34.3
4331
+
'@img/sharp-darwin-x64': 0.34.3
4332
+
'@img/sharp-libvips-darwin-arm64': 1.2.0
4333
+
'@img/sharp-libvips-darwin-x64': 1.2.0
4334
+
'@img/sharp-libvips-linux-arm': 1.2.0
4335
+
'@img/sharp-libvips-linux-arm64': 1.2.0
4336
+
'@img/sharp-libvips-linux-ppc64': 1.2.0
4337
+
'@img/sharp-libvips-linux-s390x': 1.2.0
4338
+
'@img/sharp-libvips-linux-x64': 1.2.0
4339
+
'@img/sharp-libvips-linuxmusl-arm64': 1.2.0
4340
+
'@img/sharp-libvips-linuxmusl-x64': 1.2.0
4341
+
'@img/sharp-linux-arm': 0.34.3
4342
+
'@img/sharp-linux-arm64': 0.34.3
4343
+
'@img/sharp-linux-ppc64': 0.34.3
4344
+
'@img/sharp-linux-s390x': 0.34.3
4345
+
'@img/sharp-linux-x64': 0.34.3
4346
+
'@img/sharp-linuxmusl-arm64': 0.34.3
4347
+
'@img/sharp-linuxmusl-x64': 0.34.3
4348
+
'@img/sharp-wasm32': 0.34.3
4349
+
'@img/sharp-win32-arm64': 0.34.3
4350
+
'@img/sharp-win32-ia32': 0.34.3
4351
+
'@img/sharp-win32-x64': 0.34.3
4352
+
4353
+
shebang-command@2.0.0:
4354
+
dependencies:
4355
+
shebang-regex: 3.0.0
4356
+
4357
+
shebang-regex@3.0.0: {}
4358
+
4359
+
shell-quote@1.7.3: {}
4360
+
4361
+
shellwords@0.1.1: {}
4362
+
4363
+
signal-exit@3.0.7: {}
4364
+
4365
+
signal-exit@4.1.0: {}
4366
+
4367
+
simple-swizzle@0.2.2:
4368
+
dependencies:
4369
+
is-arrayish: 0.3.2
4370
+
4371
+
sisteransi@1.0.5: {}
4372
+
4373
+
slice-ansi@5.0.0:
4374
+
dependencies:
4375
+
ansi-styles: 6.2.3
4376
+
is-fullwidth-code-point: 4.0.0
4377
+
4378
+
slice-ansi@7.1.2:
4379
+
dependencies:
4380
+
ansi-styles: 6.2.3
4381
+
is-fullwidth-code-point: 5.1.0
4382
+
4383
+
sonic-boom@4.2.0:
4384
+
dependencies:
4385
+
atomic-sleep: 1.0.0
4386
+
4387
+
source-map-js@1.2.1: {}
4388
+
4389
+
source-map-support@0.5.21:
4390
+
dependencies:
4391
+
buffer-from: 1.1.2
4392
+
source-map: 0.6.1
4393
+
4394
+
source-map@0.6.1: {}
4395
+
4396
+
source-map@0.7.6: {}
4397
+
4398
+
spawn-sync@1.0.15:
4399
+
dependencies:
4400
+
concat-stream: 1.6.2
4401
+
os-shim: 0.1.3
4402
+
4403
+
split2@4.2.0: {}
4404
+
4405
+
split@1.0.1:
4406
+
dependencies:
4407
+
through: 2.3.8
4408
+
4409
+
stdin-discarder@0.1.0:
4410
+
dependencies:
4411
+
bl: 5.1.0
4412
+
4413
+
stdin-discarder@0.2.2: {}
4414
+
4415
+
string-width@4.2.3:
4416
+
dependencies:
4417
+
emoji-regex: 8.0.0
4418
+
is-fullwidth-code-point: 3.0.0
4419
+
strip-ansi: 6.0.1
4420
+
4421
+
string-width@5.1.2:
4422
+
dependencies:
4423
+
eastasianwidth: 0.2.0
4424
+
emoji-regex: 9.2.2
4425
+
strip-ansi: 7.1.0
4426
+
4427
+
string-width@7.2.0:
4428
+
dependencies:
4429
+
emoji-regex: 10.5.0
4430
+
get-east-asian-width: 1.4.0
4431
+
strip-ansi: 7.1.2
4432
+
4433
+
string_decoder@1.1.1:
4434
+
dependencies:
4435
+
safe-buffer: 5.1.2
4436
+
4437
+
string_decoder@1.3.0:
4438
+
dependencies:
4439
+
safe-buffer: 5.2.1
4440
+
4441
+
strip-ansi@6.0.1:
4442
+
dependencies:
4443
+
ansi-regex: 5.0.1
4444
+
4445
+
strip-ansi@7.1.0:
4446
+
dependencies:
4447
+
ansi-regex: 6.2.0
4448
+
4449
+
strip-ansi@7.1.2:
4450
+
dependencies:
4451
+
ansi-regex: 6.2.2
4452
+
4453
+
strip-bom@5.0.0: {}
4454
+
4455
+
strip-json-comments@2.0.1: {}
4456
+
4457
+
strip-json-comments@5.0.2: {}
4458
+
4459
+
strip-literal@3.0.0:
4460
+
dependencies:
4461
+
js-tokens: 9.0.1
4462
+
4463
+
stubborn-fs@1.2.5: {}
4464
+
4465
+
sucrase@3.35.0:
4466
+
dependencies:
4467
+
'@jridgewell/gen-mapping': 0.3.13
4468
+
commander: 4.1.1
4469
+
glob: 10.4.5
4470
+
lines-and-columns: 1.2.4
4471
+
mz: 2.7.0
4472
+
pirates: 4.0.7
4473
+
ts-interface-checker: 0.1.13
4474
+
4475
+
supports-color@7.2.0:
4476
+
dependencies:
4477
+
has-flag: 4.0.0
4478
+
4479
+
supports-preserve-symlinks-flag@1.0.0: {}
4480
+
4481
+
tailwindcss@3.4.1:
4482
+
dependencies:
4483
+
'@alloc/quick-lru': 5.2.0
4484
+
arg: 5.0.2
4485
+
chokidar: 3.6.0
4486
+
didyoumean: 1.2.2
4487
+
dlv: 1.1.3
4488
+
fast-glob: 3.3.3
4489
+
glob-parent: 6.0.2
4490
+
is-glob: 4.0.3
4491
+
jiti: 1.21.7
4492
+
lilconfig: 2.1.0
4493
+
micromatch: 4.0.8
4494
+
normalize-path: 3.0.0
4495
+
object-hash: 3.0.0
4496
+
picocolors: 1.1.1
4497
+
postcss: 8.5.6
4498
+
postcss-import: 15.1.0(postcss@8.5.6)
4499
+
postcss-js: 4.0.1(postcss@8.5.6)
4500
+
postcss-load-config: 4.0.2(postcss@8.5.6)
4501
+
postcss-nested: 6.2.0(postcss@8.5.6)
4502
+
postcss-selector-parser: 6.1.2
4503
+
resolve: 1.22.10
4504
+
sucrase: 3.35.0
4505
+
transitivePeerDependencies:
4506
+
- ts-node
4507
+
4508
+
thenify-all@1.6.0:
4509
+
dependencies:
4510
+
thenify: 3.3.1
4511
+
4512
+
thenify@3.3.1:
4513
+
dependencies:
4514
+
any-promise: 1.3.0
4515
+
4516
+
thread-stream@3.1.0:
4517
+
dependencies:
4518
+
real-require: 0.2.0
4519
+
4520
+
through@2.3.8: {}
4521
+
4522
+
tinyexec@1.0.1: {}
4523
+
4524
+
tinyglobby@0.2.15:
4525
+
dependencies:
4526
+
fdir: 6.5.0(picomatch@4.0.3)
4527
+
picomatch: 4.0.3
4528
+
4529
+
tmp@0.2.5: {}
4530
+
4531
+
to-regex-range@5.0.1:
4532
+
dependencies:
4533
+
is-number: 7.0.0
4534
+
4535
+
ts-interface-checker@0.1.13: {}
4536
+
4537
+
tslib@2.8.1: {}
4538
+
4539
+
type-fest@3.13.1: {}
4540
+
4541
+
type-fest@4.41.0: {}
4542
+
4543
+
typedarray@0.0.6: {}
4544
+
4545
+
typescript@5.9.2: {}
4546
+
4547
+
ufo@1.6.1: {}
4548
+
4549
+
uhyphen@0.2.0: {}
4550
+
4551
+
undici-types@7.10.0: {}
4552
+
4553
+
unimport@5.2.0:
4554
+
dependencies:
4555
+
acorn: 8.15.0
4556
+
escape-string-regexp: 5.0.0
4557
+
estree-walker: 3.0.3
4558
+
local-pkg: 1.1.2
4559
+
magic-string: 0.30.19
4560
+
mlly: 1.8.0
4561
+
pathe: 2.0.3
4562
+
picomatch: 4.0.3
4563
+
pkg-types: 2.3.0
4564
+
scule: 1.3.0
4565
+
strip-literal: 3.0.0
4566
+
tinyglobby: 0.2.15
4567
+
unplugin: 2.3.10
4568
+
unplugin-utils: 0.2.5
4569
+
4570
+
universalify@2.0.1: {}
4571
+
4572
+
unplugin-utils@0.2.5:
4573
+
dependencies:
4574
+
pathe: 2.0.3
4575
+
picomatch: 4.0.3
4576
+
4577
+
unplugin@2.3.10:
4578
+
dependencies:
4579
+
'@jridgewell/remapping': 2.3.5
4580
+
acorn: 8.15.0
4581
+
picomatch: 4.0.3
4582
+
webpack-virtual-modules: 0.6.2
4583
+
4584
+
update-browserslist-db@1.1.3(browserslist@4.25.3):
4585
+
dependencies:
4586
+
browserslist: 4.25.3
4587
+
escalade: 3.2.0
4588
+
picocolors: 1.1.1
4589
+
4590
+
update-browserslist-db@1.1.3(browserslist@4.26.0):
4591
+
dependencies:
4592
+
browserslist: 4.26.0
4593
+
escalade: 3.2.0
4594
+
picocolors: 1.1.1
4595
+
4596
+
update-notifier@7.3.1:
4597
+
dependencies:
4598
+
boxen: 8.0.1
4599
+
chalk: 5.6.2
4600
+
configstore: 7.0.0
4601
+
is-in-ci: 1.0.0
4602
+
is-installed-globally: 1.0.0
4603
+
is-npm: 6.1.0
4604
+
latest-version: 9.0.0
4605
+
pupa: 3.1.0
4606
+
semver: 7.7.2
4607
+
xdg-basedir: 5.1.0
4608
+
4609
+
util-deprecate@1.0.2: {}
4610
+
4611
+
uuid@8.3.2: {}
4612
+
4613
+
vite-node@3.2.4(@types/node@24.3.2)(jiti@2.5.1)(yaml@2.8.1):
4614
+
dependencies:
4615
+
cac: 6.7.14
4616
+
debug: 4.4.1
4617
+
es-module-lexer: 1.7.0
4618
+
pathe: 2.0.3
4619
+
vite: 7.1.5(@types/node@24.3.2)(jiti@2.5.1)(yaml@2.8.1)
4620
+
transitivePeerDependencies:
4621
+
- '@types/node'
4622
+
- jiti
4623
+
- less
4624
+
- lightningcss
4625
+
- sass
4626
+
- sass-embedded
4627
+
- stylus
4628
+
- sugarss
4629
+
- supports-color
4630
+
- terser
4631
+
- tsx
4632
+
- yaml
4633
+
4634
+
vite@7.1.5(@types/node@24.3.2)(jiti@2.5.1)(yaml@2.8.1):
4635
+
dependencies:
4636
+
esbuild: 0.25.9
4637
+
fdir: 6.5.0(picomatch@4.0.3)
4638
+
picomatch: 4.0.3
4639
+
postcss: 8.5.6
4640
+
rollup: 4.50.1
4641
+
tinyglobby: 0.2.15
4642
+
optionalDependencies:
4643
+
'@types/node': 24.3.2
4644
+
fsevents: 2.3.3
4645
+
jiti: 2.5.1
4646
+
yaml: 2.8.1
4647
+
4648
+
watchpack@2.4.4:
4649
+
dependencies:
4650
+
glob-to-regexp: 0.4.1
4651
+
graceful-fs: 4.2.11
4652
+
4653
+
wcwidth@1.0.1:
4654
+
dependencies:
4655
+
defaults: 1.0.4
4656
+
4657
+
web-ext-run@0.2.4:
4658
+
dependencies:
4659
+
'@babel/runtime': 7.28.2
4660
+
'@devicefarmer/adbkit': 3.3.8
4661
+
chrome-launcher: 1.2.0
4662
+
debounce: 1.2.1
4663
+
es6-error: 4.1.1
4664
+
firefox-profile: 4.7.0
4665
+
fx-runner: 1.4.0
4666
+
multimatch: 6.0.0
4667
+
node-notifier: 10.0.1
4668
+
parse-json: 7.1.1
4669
+
pino: 9.7.0
4670
+
promise-toolbox: 0.21.0
4671
+
set-value: 4.1.0
4672
+
source-map-support: 0.5.21
4673
+
strip-bom: 5.0.0
4674
+
strip-json-comments: 5.0.2
4675
+
tmp: 0.2.5
4676
+
update-notifier: 7.3.1
4677
+
watchpack: 2.4.4
4678
+
zip-dir: 2.0.0
4679
+
transitivePeerDependencies:
4680
+
- supports-color
4681
+
4682
+
webpack-virtual-modules@0.6.2: {}
4683
+
4684
+
when-exit@2.1.4: {}
4685
+
4686
+
when@3.7.7: {}
4687
+
4688
+
which@1.2.4:
4689
+
dependencies:
4690
+
is-absolute: 0.1.7
4691
+
isexe: 1.1.2
4692
+
4693
+
which@2.0.2:
4694
+
dependencies:
4695
+
isexe: 2.0.0
4696
+
4697
+
widest-line@5.0.0:
4698
+
dependencies:
4699
+
string-width: 7.2.0
4700
+
4701
+
winreg@0.0.12: {}
4702
+
4703
+
wrap-ansi@7.0.0:
4704
+
dependencies:
4705
+
ansi-styles: 4.3.0
4706
+
string-width: 4.2.3
4707
+
strip-ansi: 6.0.1
4708
+
4709
+
wrap-ansi@8.1.0:
4710
+
dependencies:
4711
+
ansi-styles: 6.2.1
4712
+
string-width: 5.1.2
4713
+
strip-ansi: 7.1.0
4714
+
4715
+
wrap-ansi@9.0.2:
4716
+
dependencies:
4717
+
ansi-styles: 6.2.3
4718
+
string-width: 7.2.0
4719
+
strip-ansi: 7.1.2
4720
+
4721
+
wrappy@1.0.2: {}
4722
+
4723
+
wsl-utils@0.1.0:
4724
+
dependencies:
4725
+
is-wsl: 3.1.0
4726
+
4727
+
wxt@0.20.11(@types/node@24.3.2)(jiti@2.5.1)(rollup@4.50.1)(yaml@2.8.1):
4728
+
dependencies:
4729
+
'@1natsu/wait-element': 4.1.2
4730
+
'@aklinker1/rollup-plugin-visualizer': 5.12.0(rollup@4.50.1)
4731
+
'@webext-core/fake-browser': 1.3.2
4732
+
'@webext-core/isolated-element': 1.1.2
4733
+
'@webext-core/match-patterns': 1.0.3
4734
+
'@wxt-dev/browser': 0.1.4
4735
+
'@wxt-dev/storage': 1.2.0
4736
+
async-mutex: 0.5.0
4737
+
c12: 3.2.0(magicast@0.3.5)
4738
+
cac: 6.7.14
4739
+
chokidar: 4.0.3
4740
+
ci-info: 4.3.0
4741
+
consola: 3.4.2
4742
+
defu: 6.1.4
4743
+
dotenv: 17.2.2
4744
+
dotenv-expand: 12.0.3
4745
+
esbuild: 0.25.9
4746
+
fast-glob: 3.3.3
4747
+
filesize: 11.0.2
4748
+
fs-extra: 11.3.1
4749
+
get-port-please: 3.2.0
4750
+
giget: 2.0.0
4751
+
hookable: 5.5.3
4752
+
import-meta-resolve: 4.2.0
4753
+
is-wsl: 3.1.0
4754
+
json5: 2.2.3
4755
+
jszip: 3.10.1
4756
+
linkedom: 0.18.12
4757
+
magicast: 0.3.5
4758
+
minimatch: 10.0.3
4759
+
nano-spawn: 1.0.3
4760
+
normalize-path: 3.0.0
4761
+
nypm: 0.6.1
4762
+
ohash: 2.0.11
4763
+
open: 10.2.0
4764
+
ora: 8.2.0
4765
+
perfect-debounce: 2.0.0
4766
+
picocolors: 1.1.1
4767
+
prompts: 2.4.2
4768
+
publish-browser-extension: 3.0.2
4769
+
scule: 1.3.0
4770
+
unimport: 5.2.0
4771
+
vite: 7.1.5(@types/node@24.3.2)(jiti@2.5.1)(yaml@2.8.1)
4772
+
vite-node: 3.2.4(@types/node@24.3.2)(jiti@2.5.1)(yaml@2.8.1)
4773
+
web-ext-run: 0.2.4
4774
+
transitivePeerDependencies:
4775
+
- '@types/node'
4776
+
- canvas
4777
+
- jiti
4778
+
- less
4779
+
- lightningcss
4780
+
- rollup
4781
+
- sass
4782
+
- sass-embedded
4783
+
- stylus
4784
+
- sugarss
4785
+
- supports-color
4786
+
- terser
4787
+
- tsx
4788
+
- yaml
4789
+
4790
+
xdg-basedir@5.1.0: {}
4791
+
4792
+
xml2js@0.6.2:
4793
+
dependencies:
4794
+
sax: 1.4.1
4795
+
xmlbuilder: 11.0.1
4796
+
4797
+
xmlbuilder@11.0.1: {}
4798
+
4799
+
y18n@5.0.8: {}
4800
+
4801
+
yallist@3.1.1: {}
4802
+
4803
+
yaml@2.8.1: {}
4804
+
4805
+
yargs-parser@20.2.9: {}
4806
+
4807
+
yargs-parser@21.1.1: {}
4808
+
4809
+
yargs@16.2.0:
4810
+
dependencies:
4811
+
cliui: 7.0.4
4812
+
escalade: 3.2.0
4813
+
get-caller-file: 2.0.5
4814
+
require-directory: 2.1.1
4815
+
string-width: 4.2.3
4816
+
y18n: 5.0.8
4817
+
yargs-parser: 20.2.9
4818
+
4819
+
yargs@17.7.2:
4820
+
dependencies:
4821
+
cliui: 8.0.1
4822
+
escalade: 3.2.0
4823
+
get-caller-file: 2.0.5
4824
+
require-directory: 2.1.1
4825
+
string-width: 4.2.3
4826
+
y18n: 5.0.8
4827
+
yargs-parser: 21.1.1
4828
+
4829
+
yauzl@2.10.0:
4830
+
dependencies:
4831
+
buffer-crc32: 0.2.13
4832
+
fd-slicer: 1.1.0
4833
+
4834
+
zip-dir@2.0.0:
4835
+
dependencies:
4836
+
async: 3.2.6
4837
+
jszip: 3.10.1
4838
+
4839
+
zod@3.25.76: {}
+4
pnpm-workspace.yaml
+4
pnpm-workspace.yaml
+6
postcss.config.js
+6
postcss.config.js
+34
tailwind.config.ts
+34
tailwind.config.ts
···
···
1
+
import type { Config } from 'tailwindcss';
2
+
3
+
export default {
4
+
content: ['./entrypoints/popup/**/*.{js,ts,jsx,tsx}'],
5
+
theme: {
6
+
colors: {
7
+
accent: 'rgb(63, 93, 179)',
8
+
background: 'rgb(31, 30, 30)',
9
+
border: 'rgb(68, 67, 66)',
10
+
header: 'rgba(24, 23, 23)',
11
+
input: 'rgb(31, 30, 30)',
12
+
item: 'rgb(46, 45, 45)',
13
+
subtext: 'rgb(175, 172, 171)',
14
+
text: 'rgb(247, 245, 244)',
15
+
16
+
warning: 'rgb(229, 153, 62)',
17
+
danger: '#ef4444',
18
+
success: 'rgb(30, 166, 114)',
19
+
20
+
pink: '#f5c2e7',
21
+
},
22
+
extend: {
23
+
animation: {
24
+
'spin-fast': 'spin 1s linear infinite',
25
+
},
26
+
keyframes: {
27
+
spin: {
28
+
to: { transform: 'rotate(360deg)' },
29
+
},
30
+
},
31
+
},
32
+
},
33
+
plugins: [],
34
+
} satisfies Config;
+7
tsconfig.json
+7
tsconfig.json
+16
types/eligibility.ts
+16
types/eligibility.ts
···
···
1
+
export interface Eligibility {
2
+
eligible: boolean;
3
+
reason: string;
4
+
id?: string;
5
+
checkedAt?: number;
6
+
error?: string;
7
+
offerType?: string;
8
+
}
9
+
10
+
export interface EligibilityApiResponse {
11
+
error?: string;
12
+
data?: {
13
+
offerType?: string;
14
+
tcds?: Array<{ tcd: string; token: string }>;
15
+
};
16
+
}
+10
types/messages.ts
+10
types/messages.ts
+7
types/storage.ts
+7
types/storage.ts
+15
types/tokens.ts
+15
types/tokens.ts
···
···
1
+
import type { DefaultStorageOptions } from '$types/storage';
2
+
3
+
export interface Token {
4
+
tcd: string;
5
+
token: string;
6
+
timestamp?: number;
7
+
}
8
+
9
+
export interface Offer {
10
+
tcds: Token[];
11
+
}
12
+
13
+
export interface StoreTokensResult extends DefaultStorageOptions {
14
+
tailscaleTokens?: Token[];
15
+
}
+38
wxt.config.ts
+38
wxt.config.ts
···
···
1
+
import { resolve } from 'node:path';
2
+
import { defineConfig } from 'wxt';
3
+
4
+
// See https://wxt.dev/api/config.html
5
+
export default defineConfig({
6
+
modules: ['@wxt-dev/module-react', '@wxt-dev/auto-icons'],
7
+
manifest: {
8
+
name: 'tailname',
9
+
description: 'Search for custom tailnet name offers with keywords.',
10
+
version: '1.0',
11
+
permissions: [
12
+
'cookies',
13
+
'alarms',
14
+
'storage',
15
+
'notifications',
16
+
'scripting',
17
+
'tabs',
18
+
],
19
+
host_permissions: ['https://login.tailscale.com/*'],
20
+
incognito: import.meta.env.CHROME ? 'split' : 'spanning',
21
+
browser_specific_settings: {
22
+
gecko: {
23
+
id: 'tailname@sapphic.moe',
24
+
},
25
+
},
26
+
},
27
+
alias: {
28
+
$background: resolve('entrypoints/background'),
29
+
$components: resolve('entrypoints/popup/components'),
30
+
$config: resolve('config.ts'),
31
+
$handlers: resolve('handlers'),
32
+
$helpers: resolve('helpers'),
33
+
$hooks: resolve('entrypoints/popup/hooks'),
34
+
$popup: resolve('entrypoints/popup'),
35
+
$screens: resolve('entrypoints/popup/screens'),
36
+
$types: resolve('types'),
37
+
},
38
+
});