Roberto Vidal
commited on
feat: oauth-based login (#7)
Browse files- README.md +6 -0
- package.json +1 -2
- packages/bolt/README.md +3 -4
- packages/bolt/app/components/header/Header.tsx +5 -1
- packages/bolt/app/lib/.server/login.ts +27 -5
- packages/bolt/app/lib/.server/sessions.ts +138 -27
- packages/bolt/app/lib/auth.ts +4 -0
- packages/bolt/app/lib/constants.ts +2 -0
- packages/bolt/app/lib/fetch.ts +14 -0
- packages/bolt/app/lib/webcontainer/index.ts +5 -1
- packages/bolt/app/routes/api.chat.ts +6 -1
- packages/bolt/app/routes/api.enhancer.ts +6 -1
- packages/bolt/app/routes/login.tsx +156 -54
- packages/bolt/app/routes/logout.tsx +10 -0
- packages/bolt/app/utils/logger.ts +1 -1
- packages/bolt/package.json +3 -0
- pnpm-lock.yaml +136 -5
README.md
CHANGED
|
@@ -32,6 +32,12 @@ cd bolt
|
|
| 32 |
pnpm i
|
| 33 |
```
|
| 34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
### Development
|
| 36 |
|
| 37 |
To start developing the Bolt UI:
|
|
|
|
| 32 |
pnpm i
|
| 33 |
```
|
| 34 |
|
| 35 |
+
3. Optionally, init git hooks:
|
| 36 |
+
|
| 37 |
+
```bash
|
| 38 |
+
pnpmx husky
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
### Development
|
| 42 |
|
| 43 |
To start developing the Bolt UI:
|
package.json
CHANGED
|
@@ -6,8 +6,7 @@
|
|
| 6 |
"playground:dev": "pnpm run --filter=playground dev",
|
| 7 |
"lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .",
|
| 8 |
"test": "pnpm run -r test",
|
| 9 |
-
"typecheck": "pnpm run -r typecheck"
|
| 10 |
-
"prepare": "husky"
|
| 11 |
},
|
| 12 |
"commitlint": {
|
| 13 |
"extends": [
|
|
|
|
| 6 |
"playground:dev": "pnpm run --filter=playground dev",
|
| 7 |
"lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .",
|
| 8 |
"test": "pnpm run -r test",
|
| 9 |
+
"typecheck": "pnpm run -r typecheck"
|
|
|
|
| 10 |
},
|
| 11 |
"commitlint": {
|
| 12 |
"extends": [
|
packages/bolt/README.md
CHANGED
|
@@ -36,12 +36,11 @@ Optionally, you an set the debug level:
|
|
| 36 |
VITE_LOG_LEVEL=debug
|
| 37 |
```
|
| 38 |
|
| 39 |
-
If you want to
|
| 40 |
-
|
| 41 |
```
|
| 42 |
-
|
| 43 |
-
LOGIN_PASSWORD=XXX
|
| 44 |
```
|
|
|
|
| 45 |
|
| 46 |
**Important**: Never commit your `.env.local` file to version control. It's already included in .gitignore.
|
| 47 |
|
|
|
|
| 36 |
VITE_LOG_LEVEL=debug
|
| 37 |
```
|
| 38 |
|
| 39 |
+
If you want to run authentication against a local StackBlitz instance, add:
|
|
|
|
| 40 |
```
|
| 41 |
+
VITE_CLIENT_ORIGIN=https://local.stackblitz.com:3000
|
|
|
|
| 42 |
```
|
| 43 |
+
`
|
| 44 |
|
| 45 |
**Important**: Never commit your `.env.local` file to version control. It's already included in .gitignore.
|
| 46 |
|
packages/bolt/app/components/header/Header.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
import { ClientOnly } from 'remix-utils/client-only';
|
| 2 |
import { OpenStackBlitz } from './OpenStackBlitz.client';
|
|
|
|
| 3 |
|
| 4 |
export function Header() {
|
| 5 |
return (
|
|
@@ -7,8 +8,11 @@ export function Header() {
|
|
| 7 |
<div className="flex items-center gap-2">
|
| 8 |
<div className="text-2xl font-semibold text-accent">Bolt</div>
|
| 9 |
</div>
|
| 10 |
-
<div className="ml-auto">
|
| 11 |
<ClientOnly>{() => <OpenStackBlitz />}</ClientOnly>
|
|
|
|
|
|
|
|
|
|
| 12 |
</div>
|
| 13 |
</header>
|
| 14 |
);
|
|
|
|
| 1 |
import { ClientOnly } from 'remix-utils/client-only';
|
| 2 |
import { OpenStackBlitz } from './OpenStackBlitz.client';
|
| 3 |
+
import { IconButton } from '~/components/ui/IconButton';
|
| 4 |
|
| 5 |
export function Header() {
|
| 6 |
return (
|
|
|
|
| 8 |
<div className="flex items-center gap-2">
|
| 9 |
<div className="text-2xl font-semibold text-accent">Bolt</div>
|
| 10 |
</div>
|
| 11 |
+
<div className="ml-auto flex gap-2">
|
| 12 |
<ClientOnly>{() => <OpenStackBlitz />}</ClientOnly>
|
| 13 |
+
<a href="/logout">
|
| 14 |
+
<IconButton icon="i-ph:sign-out" />
|
| 15 |
+
</a>
|
| 16 |
</div>
|
| 17 |
</header>
|
| 18 |
);
|
packages/bolt/app/lib/.server/login.ts
CHANGED
|
@@ -8,12 +8,34 @@ export function verifyPassword(password: string, cloudflareEnv: Env) {
|
|
| 8 |
return password === loginPassword;
|
| 9 |
}
|
| 10 |
|
| 11 |
-
|
| 12 |
-
const authenticated = await isAuthenticated(request, context.cloudflare.env);
|
| 13 |
|
| 14 |
-
|
| 15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
}
|
| 17 |
|
| 18 |
-
return
|
| 19 |
}
|
|
|
|
| 8 |
return password === loginPassword;
|
| 9 |
}
|
| 10 |
|
| 11 |
+
type RequestArgs = Pick<LoaderFunctionArgs, 'request' | 'context'>;
|
|
|
|
| 12 |
|
| 13 |
+
export async function handleAuthRequest<T extends RequestArgs>(args: T, body: object = {}) {
|
| 14 |
+
const { request, context } = args;
|
| 15 |
+
const { authenticated, response } = await isAuthenticated(request, context.cloudflare.env);
|
| 16 |
+
|
| 17 |
+
if (authenticated) {
|
| 18 |
+
return json(body, response);
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
return redirect('/login', response);
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
export async function handleWithAuth<T extends RequestArgs>(args: T, handler: (args: T) => Promise<Response>) {
|
| 25 |
+
const { request, context } = args;
|
| 26 |
+
const { authenticated, response } = await isAuthenticated(request, context.cloudflare.env);
|
| 27 |
+
|
| 28 |
+
if (authenticated) {
|
| 29 |
+
const handlerResponse = await handler(args);
|
| 30 |
+
|
| 31 |
+
if (response) {
|
| 32 |
+
for (const [key, value] of Object.entries(response.headers)) {
|
| 33 |
+
handlerResponse.headers.append(key, value);
|
| 34 |
+
}
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
return handlerResponse;
|
| 38 |
}
|
| 39 |
|
| 40 |
+
return json({}, { status: 401 });
|
| 41 |
}
|
packages/bolt/app/lib/.server/sessions.ts
CHANGED
|
@@ -1,31 +1,89 @@
|
|
| 1 |
import { createCookieSessionStorage, redirect } from '@remix-run/cloudflare';
|
| 2 |
-
import {
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
-
const
|
| 5 |
|
| 6 |
-
|
| 7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
cookie: {
|
| 9 |
name: '__session',
|
| 10 |
httpOnly: true,
|
| 11 |
path: '/',
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
secure: false,
|
| 15 |
},
|
| 16 |
});
|
| 17 |
}
|
| 18 |
|
| 19 |
-
export async function getSession(request: Request, env: Env) {
|
| 20 |
-
const sessionStorage = createSessionStorage(env);
|
| 21 |
-
const cookie = request.headers.get('Cookie');
|
| 22 |
-
|
| 23 |
-
return { session: await sessionStorage.getSession(cookie), sessionStorage };
|
| 24 |
-
}
|
| 25 |
-
|
| 26 |
export async function logout(request: Request, env: Env) {
|
| 27 |
const { session, sessionStorage } = await getSession(request, env);
|
| 28 |
|
|
|
|
|
|
|
| 29 |
return redirect('/login', {
|
| 30 |
headers: {
|
| 31 |
'Set-Cookie': await sessionStorage.destroySession(session),
|
|
@@ -33,23 +91,76 @@ export async function logout(request: Request, env: Env) {
|
|
| 33 |
});
|
| 34 |
}
|
| 35 |
|
| 36 |
-
export
|
| 37 |
-
const
|
| 38 |
-
|
|
|
|
| 39 |
|
| 40 |
-
return
|
| 41 |
}
|
| 42 |
|
| 43 |
-
|
| 44 |
-
const
|
|
|
|
| 45 |
|
| 46 |
-
session.
|
|
|
|
| 47 |
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
}),
|
| 53 |
-
}
|
| 54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
}
|
|
|
|
| 1 |
import { createCookieSessionStorage, redirect } from '@remix-run/cloudflare';
|
| 2 |
+
import { request as doRequest } from '~/lib/fetch';
|
| 3 |
+
import { CLIENT_ID, CLIENT_ORIGIN } from '~/lib/constants';
|
| 4 |
+
import { logger } from '~/utils/logger';
|
| 5 |
+
import { decode } from 'jsonwebtoken';
|
| 6 |
|
| 7 |
+
const DEV_SESSION_SECRET = import.meta.env.DEV ? 'LZQMrERo3Ewn/AbpSYJ9aw==' : undefined;
|
| 8 |
|
| 9 |
+
interface SessionData {
|
| 10 |
+
refresh: string;
|
| 11 |
+
expiresAt: number;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
export async function isAuthenticated(request: Request, env: Env) {
|
| 15 |
+
const { session, sessionStorage } = await getSession(request, env);
|
| 16 |
+
const token = session.get('refresh');
|
| 17 |
+
|
| 18 |
+
const header = async (cookie: Promise<string>) => ({ headers: { 'Set-Cookie': await cookie } });
|
| 19 |
+
const destroy = () => header(sessionStorage.destroySession(session));
|
| 20 |
+
|
| 21 |
+
if (token == null) {
|
| 22 |
+
return { authenticated: false as const, response: await destroy() };
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
const expiresAt = session.get('expiresAt') ?? 0;
|
| 26 |
+
|
| 27 |
+
if (Date.now() < expiresAt) {
|
| 28 |
+
return { authenticated: true as const };
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
let data: Awaited<ReturnType<typeof refreshToken>> | null = null;
|
| 32 |
+
|
| 33 |
+
try {
|
| 34 |
+
data = await refreshToken(token);
|
| 35 |
+
} catch {
|
| 36 |
+
// ignore
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
if (data != null) {
|
| 40 |
+
const expiresAt = cookieExpiration(data.expires_in, data.created_at);
|
| 41 |
+
session.set('expiresAt', expiresAt);
|
| 42 |
+
|
| 43 |
+
return { authenticated: true as const, response: await header(sessionStorage.commitSession(session)) };
|
| 44 |
+
} else {
|
| 45 |
+
return { authenticated: false as const, response: await destroy() };
|
| 46 |
+
}
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
export async function createUserSession(
|
| 50 |
+
request: Request,
|
| 51 |
+
env: Env,
|
| 52 |
+
tokens: { refresh: string; expires_in: number; created_at: number },
|
| 53 |
+
): Promise<ResponseInit> {
|
| 54 |
+
const { session, sessionStorage } = await getSession(request, env);
|
| 55 |
+
|
| 56 |
+
const expiresAt = cookieExpiration(tokens.expires_in, tokens.created_at);
|
| 57 |
+
|
| 58 |
+
session.set('refresh', tokens.refresh);
|
| 59 |
+
session.set('expiresAt', expiresAt);
|
| 60 |
+
|
| 61 |
+
return {
|
| 62 |
+
headers: {
|
| 63 |
+
'Set-Cookie': await sessionStorage.commitSession(session, {
|
| 64 |
+
maxAge: 3600 * 24 * 30, // 1 month
|
| 65 |
+
}),
|
| 66 |
+
},
|
| 67 |
+
};
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
function getSessionStorage(cloudflareEnv: Env) {
|
| 71 |
+
return createCookieSessionStorage<SessionData>({
|
| 72 |
cookie: {
|
| 73 |
name: '__session',
|
| 74 |
httpOnly: true,
|
| 75 |
path: '/',
|
| 76 |
+
secrets: [DEV_SESSION_SECRET || cloudflareEnv.SESSION_SECRET],
|
| 77 |
+
secure: import.meta.env.PROD,
|
|
|
|
| 78 |
},
|
| 79 |
});
|
| 80 |
}
|
| 81 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
export async function logout(request: Request, env: Env) {
|
| 83 |
const { session, sessionStorage } = await getSession(request, env);
|
| 84 |
|
| 85 |
+
revokeToken(session.get('refresh'));
|
| 86 |
+
|
| 87 |
return redirect('/login', {
|
| 88 |
headers: {
|
| 89 |
'Set-Cookie': await sessionStorage.destroySession(session),
|
|
|
|
| 91 |
});
|
| 92 |
}
|
| 93 |
|
| 94 |
+
export function validateAccessToken(access: string) {
|
| 95 |
+
const jwtPayload = decode(access);
|
| 96 |
+
|
| 97 |
+
const boltEnabled = typeof jwtPayload === 'object' && jwtPayload != null && jwtPayload.bolt === true;
|
| 98 |
|
| 99 |
+
return boltEnabled;
|
| 100 |
}
|
| 101 |
|
| 102 |
+
async function getSession(request: Request, env: Env) {
|
| 103 |
+
const sessionStorage = getSessionStorage(env);
|
| 104 |
+
const cookie = request.headers.get('Cookie');
|
| 105 |
|
| 106 |
+
return { session: await sessionStorage.getSession(cookie), sessionStorage };
|
| 107 |
+
}
|
| 108 |
|
| 109 |
+
async function refreshToken(refresh: string): Promise<{ expires_in: number; created_at: number }> {
|
| 110 |
+
const response = await doRequest(`${CLIENT_ORIGIN}/oauth/token`, {
|
| 111 |
+
method: 'POST',
|
| 112 |
+
body: urlParams({ grant_type: 'refresh_token', client_id: CLIENT_ID, refresh_token: refresh }),
|
| 113 |
+
});
|
| 114 |
+
|
| 115 |
+
const body = await response.json();
|
| 116 |
+
|
| 117 |
+
if (!response.ok) {
|
| 118 |
+
throw new Error(`Unable to refresh token\n${JSON.stringify(body)}`);
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
const { access_token: access } = body;
|
| 122 |
+
|
| 123 |
+
if (!validateAccessToken(access)) {
|
| 124 |
+
throw new Error('User is no longer authorized for Bolt');
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
return body;
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
function cookieExpiration(expireIn: number, createdAt: number) {
|
| 131 |
+
return (expireIn + createdAt - 10 * 60) * 1000;
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
async function revokeToken(refresh?: string) {
|
| 135 |
+
if (refresh == null) {
|
| 136 |
+
return;
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
try {
|
| 140 |
+
const response = await doRequest(`${CLIENT_ORIGIN}/oauth/revoke`, {
|
| 141 |
+
method: 'POST',
|
| 142 |
+
body: urlParams({
|
| 143 |
+
token: refresh,
|
| 144 |
+
token_type_hint: 'refresh_token',
|
| 145 |
+
client_id: CLIENT_ID,
|
| 146 |
}),
|
| 147 |
+
});
|
| 148 |
+
|
| 149 |
+
if (!response.ok) {
|
| 150 |
+
throw new Error(`Unable to revoke token: ${response.status}`);
|
| 151 |
+
}
|
| 152 |
+
} catch (error) {
|
| 153 |
+
logger.debug(error);
|
| 154 |
+
return;
|
| 155 |
+
}
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
function urlParams(data: Record<string, string>) {
|
| 159 |
+
const encoded = new URLSearchParams();
|
| 160 |
+
|
| 161 |
+
for (const [key, value] of Object.entries(data)) {
|
| 162 |
+
encoded.append(key, value);
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
return encoded;
|
| 166 |
}
|
packages/bolt/app/lib/auth.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export function forgetAuth() {
|
| 2 |
+
// FIXME: use dedicated method
|
| 3 |
+
localStorage.removeItem('__wc_api_tokens__');
|
| 4 |
+
}
|
packages/bolt/app/lib/constants.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export const CLIENT_ID = 'bolt';
|
| 2 |
+
export const CLIENT_ORIGIN = import.meta.env.VITE_CLIENT_ORIGIN ?? 'https://stackblitz.com';
|
packages/bolt/app/lib/fetch.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
type CommonRequest = Omit<RequestInit, 'body'> & { body?: URLSearchParams };
|
| 2 |
+
|
| 3 |
+
export async function request(url: string, init?: CommonRequest) {
|
| 4 |
+
if (import.meta.env.DEV) {
|
| 5 |
+
const nodeFetch = await import('node-fetch');
|
| 6 |
+
const https = await import('node:https');
|
| 7 |
+
|
| 8 |
+
const agent = url.startsWith('https') ? new https.Agent({ rejectUnauthorized: false }) : undefined;
|
| 9 |
+
|
| 10 |
+
return nodeFetch.default(url, { ...init, agent });
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
return fetch(url, init);
|
| 14 |
+
}
|
packages/bolt/app/lib/webcontainer/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
import { WebContainer } from '@webcontainer/api';
|
| 2 |
import { WORK_DIR_NAME } from '~/utils/constants';
|
|
|
|
| 3 |
|
| 4 |
interface WebContainerContext {
|
| 5 |
loaded: boolean;
|
|
@@ -21,7 +22,10 @@ if (!import.meta.env.SSR) {
|
|
| 21 |
webcontainer =
|
| 22 |
import.meta.hot?.data.webcontainer ??
|
| 23 |
Promise.resolve()
|
| 24 |
-
.then(() =>
|
|
|
|
|
|
|
|
|
|
| 25 |
.then((webcontainer) => {
|
| 26 |
webcontainerContext.loaded = true;
|
| 27 |
return webcontainer;
|
|
|
|
| 1 |
import { WebContainer } from '@webcontainer/api';
|
| 2 |
import { WORK_DIR_NAME } from '~/utils/constants';
|
| 3 |
+
import { forgetAuth } from '~/lib/auth';
|
| 4 |
|
| 5 |
interface WebContainerContext {
|
| 6 |
loaded: boolean;
|
|
|
|
| 22 |
webcontainer =
|
| 23 |
import.meta.hot?.data.webcontainer ??
|
| 24 |
Promise.resolve()
|
| 25 |
+
.then(() => {
|
| 26 |
+
forgetAuth();
|
| 27 |
+
return WebContainer.boot({ workdirName: WORK_DIR_NAME });
|
| 28 |
+
})
|
| 29 |
.then((webcontainer) => {
|
| 30 |
webcontainerContext.loaded = true;
|
| 31 |
return webcontainer;
|
packages/bolt/app/routes/api.chat.ts
CHANGED
|
@@ -4,8 +4,13 @@ import { MAX_RESPONSE_SEGMENTS, MAX_TOKENS } from '~/lib/.server/llm/constants';
|
|
| 4 |
import { CONTINUE_PROMPT } from '~/lib/.server/llm/prompts';
|
| 5 |
import { streamText, type Messages, type StreamingOptions } from '~/lib/.server/llm/stream-text';
|
| 6 |
import SwitchableStream from '~/lib/.server/llm/switchable-stream';
|
|
|
|
| 7 |
|
| 8 |
-
export async function action(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
const { messages } = await request.json<{ messages: Messages }>();
|
| 10 |
|
| 11 |
const stream = new SwitchableStream();
|
|
|
|
| 4 |
import { CONTINUE_PROMPT } from '~/lib/.server/llm/prompts';
|
| 5 |
import { streamText, type Messages, type StreamingOptions } from '~/lib/.server/llm/stream-text';
|
| 6 |
import SwitchableStream from '~/lib/.server/llm/switchable-stream';
|
| 7 |
+
import { handleWithAuth } from '~/lib/.server/login';
|
| 8 |
|
| 9 |
+
export async function action(args: ActionFunctionArgs) {
|
| 10 |
+
return handleWithAuth(args, chatAction);
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
async function chatAction({ context, request }: ActionFunctionArgs) {
|
| 14 |
const { messages } = await request.json<{ messages: Messages }>();
|
| 15 |
|
| 16 |
const stream = new SwitchableStream();
|
packages/bolt/app/routes/api.enhancer.ts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
| 1 |
import { type ActionFunctionArgs } from '@remix-run/cloudflare';
|
| 2 |
import { StreamingTextResponse, parseStreamPart } from 'ai';
|
| 3 |
import { streamText } from '~/lib/.server/llm/stream-text';
|
|
|
|
| 4 |
import { stripIndents } from '~/utils/stripIndent';
|
| 5 |
|
| 6 |
const encoder = new TextEncoder();
|
| 7 |
const decoder = new TextDecoder();
|
| 8 |
|
| 9 |
-
export async function action(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
const { message } = await request.json<{ message: string }>();
|
| 11 |
|
| 12 |
try {
|
|
|
|
| 1 |
import { type ActionFunctionArgs } from '@remix-run/cloudflare';
|
| 2 |
import { StreamingTextResponse, parseStreamPart } from 'ai';
|
| 3 |
import { streamText } from '~/lib/.server/llm/stream-text';
|
| 4 |
+
import { handleWithAuth } from '~/lib/.server/login';
|
| 5 |
import { stripIndents } from '~/utils/stripIndent';
|
| 6 |
|
| 7 |
const encoder = new TextEncoder();
|
| 8 |
const decoder = new TextDecoder();
|
| 9 |
|
| 10 |
+
export async function action(args: ActionFunctionArgs) {
|
| 11 |
+
return handleWithAuth(args, enhancerAction);
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
async function enhancerAction({ context, request }: ActionFunctionArgs) {
|
| 15 |
const { message } = await request.json<{ message: string }>();
|
| 16 |
|
| 17 |
try {
|
packages/bolt/app/routes/login.tsx
CHANGED
|
@@ -3,49 +3,96 @@ import {
|
|
| 3 |
redirect,
|
| 4 |
type ActionFunctionArgs,
|
| 5 |
type LoaderFunctionArgs,
|
| 6 |
-
|
| 7 |
} from '@remix-run/cloudflare';
|
| 8 |
-
import {
|
| 9 |
-
import {
|
| 10 |
-
import {
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
}
|
| 15 |
|
| 16 |
export async function loader({ request, context }: LoaderFunctionArgs) {
|
| 17 |
-
const authenticated = await isAuthenticated(request, context.cloudflare.env);
|
| 18 |
|
| 19 |
if (authenticated) {
|
| 20 |
-
return redirect('/');
|
| 21 |
}
|
| 22 |
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
}
|
| 25 |
|
| 26 |
-
export async function action({ request, context }: ActionFunctionArgs)
|
| 27 |
const formData = await request.formData();
|
| 28 |
-
const password = String(formData.get('password'));
|
| 29 |
|
| 30 |
-
const
|
|
|
|
|
|
|
|
|
|
| 31 |
|
| 32 |
-
|
| 33 |
-
errors.password = 'Please provide a password';
|
| 34 |
-
}
|
| 35 |
|
| 36 |
-
|
| 37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
}
|
| 39 |
|
| 40 |
-
|
| 41 |
-
|
|
|
|
|
|
|
| 42 |
}
|
| 43 |
|
| 44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
}
|
| 46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
export default function Login() {
|
| 48 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
return (
|
| 51 |
<div className="min-h-screen flex items-center justify-center">
|
|
@@ -53,38 +100,93 @@ export default function Login() {
|
|
| 53 |
<div>
|
| 54 |
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">Login</h2>
|
| 55 |
</div>
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
<label htmlFor="password" className="sr-only">
|
| 59 |
-
Password
|
| 60 |
-
</label>
|
| 61 |
-
<input
|
| 62 |
-
id="password"
|
| 63 |
-
name="password"
|
| 64 |
-
type="password"
|
| 65 |
-
autoComplete="off"
|
| 66 |
-
data-1p-ignore
|
| 67 |
-
required
|
| 68 |
-
className="appearance-none rounded-md relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none"
|
| 69 |
-
placeholder="Password"
|
| 70 |
-
/>
|
| 71 |
-
{actionData?.errors?.password ? (
|
| 72 |
-
<em className="flex items-center space-x-1.5 p-2 mt-2 bg-negative-200 text-negative-600 rounded-lg">
|
| 73 |
-
<div className="i-ph:x-circle text-xl"></div>
|
| 74 |
-
<span>{actionData?.errors.password}</span>
|
| 75 |
-
</em>
|
| 76 |
-
) : null}
|
| 77 |
-
</div>
|
| 78 |
-
<div>
|
| 79 |
-
<button
|
| 80 |
-
type="submit"
|
| 81 |
-
className="w-full text-white bg-accent-600 hover:bg-accent-700 focus:ring-4 focus:outline-none font-medium rounded-lg text-sm px-5 py-2.5 text-center"
|
| 82 |
-
>
|
| 83 |
-
Login
|
| 84 |
-
</button>
|
| 85 |
-
</div>
|
| 86 |
-
</Form>
|
| 87 |
</div>
|
| 88 |
</div>
|
| 89 |
);
|
| 90 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
redirect,
|
| 4 |
type ActionFunctionArgs,
|
| 5 |
type LoaderFunctionArgs,
|
| 6 |
+
redirectDocument,
|
| 7 |
} from '@remix-run/cloudflare';
|
| 8 |
+
import { useFetcher, useLoaderData } from '@remix-run/react';
|
| 9 |
+
import { auth, type AuthAPI } from '@webcontainer/api';
|
| 10 |
+
import { useEffect, useState } from 'react';
|
| 11 |
+
import { createUserSession, isAuthenticated, validateAccessToken } from '~/lib/.server/sessions';
|
| 12 |
+
import { request as doRequest } from '~/lib/fetch';
|
| 13 |
+
import { CLIENT_ID, CLIENT_ORIGIN } from '~/lib/constants';
|
| 14 |
+
import { logger } from '~/utils/logger';
|
| 15 |
|
| 16 |
export async function loader({ request, context }: LoaderFunctionArgs) {
|
| 17 |
+
const { authenticated, response } = await isAuthenticated(request, context.cloudflare.env);
|
| 18 |
|
| 19 |
if (authenticated) {
|
| 20 |
+
return redirect('/', response);
|
| 21 |
}
|
| 22 |
|
| 23 |
+
const url = new URL(request.url);
|
| 24 |
+
|
| 25 |
+
return json(
|
| 26 |
+
{
|
| 27 |
+
redirected: url.searchParams.has('code') || url.searchParams.has('error'),
|
| 28 |
+
},
|
| 29 |
+
response,
|
| 30 |
+
);
|
| 31 |
}
|
| 32 |
|
| 33 |
+
export async function action({ request, context }: ActionFunctionArgs) {
|
| 34 |
const formData = await request.formData();
|
|
|
|
| 35 |
|
| 36 |
+
const payload = {
|
| 37 |
+
access: String(formData.get('access')),
|
| 38 |
+
refresh: String(formData.get('refresh')),
|
| 39 |
+
};
|
| 40 |
|
| 41 |
+
let response: Awaited<ReturnType<typeof doRequest>> | undefined;
|
|
|
|
|
|
|
| 42 |
|
| 43 |
+
try {
|
| 44 |
+
response = await doRequest(`${CLIENT_ORIGIN}/oauth/token/info`, {
|
| 45 |
+
headers: { authorization: `Bearer ${payload.access}` },
|
| 46 |
+
});
|
| 47 |
+
|
| 48 |
+
if (!response.ok) {
|
| 49 |
+
throw await response.json();
|
| 50 |
+
}
|
| 51 |
+
} catch (error) {
|
| 52 |
+
logger.warn('Authentication failure');
|
| 53 |
+
logger.warn(error);
|
| 54 |
+
|
| 55 |
+
return json({ error: 'invalid-token' as const }, { status: 401 });
|
| 56 |
}
|
| 57 |
|
| 58 |
+
const boltEnabled = validateAccessToken(payload.access);
|
| 59 |
+
|
| 60 |
+
if (!boltEnabled) {
|
| 61 |
+
return json({ error: 'bolt-access' as const }, { status: 401 });
|
| 62 |
}
|
| 63 |
|
| 64 |
+
const tokenInfo: { expires_in: number; created_at: number } = await response.json();
|
| 65 |
+
|
| 66 |
+
const init = await createUserSession(request, context.cloudflare.env, { ...payload, ...tokenInfo });
|
| 67 |
+
|
| 68 |
+
return redirectDocument('/', init);
|
| 69 |
}
|
| 70 |
|
| 71 |
+
type LoginState =
|
| 72 |
+
| {
|
| 73 |
+
kind: 'error';
|
| 74 |
+
error: string;
|
| 75 |
+
description: string;
|
| 76 |
+
}
|
| 77 |
+
| { kind: 'pending' };
|
| 78 |
+
|
| 79 |
+
const ERRORS = {
|
| 80 |
+
'bolt-access': 'You do not have access to Bolt.',
|
| 81 |
+
'invalid-token': 'Authentication failed.',
|
| 82 |
+
};
|
| 83 |
+
|
| 84 |
export default function Login() {
|
| 85 |
+
const { redirected } = useLoaderData<typeof loader>();
|
| 86 |
+
|
| 87 |
+
useEffect(() => {
|
| 88 |
+
if (!import.meta.hot?.data.wcAuth) {
|
| 89 |
+
auth.init({ clientId: CLIENT_ID, scope: 'public', editorOrigin: CLIENT_ORIGIN });
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
if (import.meta.hot) {
|
| 93 |
+
import.meta.hot.data.wcAuth = true;
|
| 94 |
+
}
|
| 95 |
+
}, []);
|
| 96 |
|
| 97 |
return (
|
| 98 |
<div className="min-h-screen flex items-center justify-center">
|
|
|
|
| 100 |
<div>
|
| 101 |
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">Login</h2>
|
| 102 |
</div>
|
| 103 |
+
|
| 104 |
+
{redirected ? 'Processing auth...' : <LoginForm />}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
</div>
|
| 106 |
</div>
|
| 107 |
);
|
| 108 |
}
|
| 109 |
+
|
| 110 |
+
function LoginForm() {
|
| 111 |
+
const [login, setLogin] = useState<LoginState | null>(null);
|
| 112 |
+
|
| 113 |
+
const fetcher = useFetcher<typeof action>();
|
| 114 |
+
|
| 115 |
+
useEffect(() => {
|
| 116 |
+
auth.logout({ ignoreRevokeError: true });
|
| 117 |
+
}, []);
|
| 118 |
+
|
| 119 |
+
useEffect(() => {
|
| 120 |
+
if (fetcher.data?.error) {
|
| 121 |
+
auth.logout({ ignoreRevokeError: true });
|
| 122 |
+
|
| 123 |
+
setLogin({
|
| 124 |
+
kind: 'error' as const,
|
| 125 |
+
...{ error: fetcher.data.error, description: ERRORS[fetcher.data.error] },
|
| 126 |
+
});
|
| 127 |
+
}
|
| 128 |
+
}, [fetcher.data]);
|
| 129 |
+
|
| 130 |
+
async function attemptLogin() {
|
| 131 |
+
startAuthFlow();
|
| 132 |
+
|
| 133 |
+
function startAuthFlow() {
|
| 134 |
+
auth.startAuthFlow({ popup: true });
|
| 135 |
+
|
| 136 |
+
Promise.race([authEvent(auth, 'auth-failed'), auth.loggedIn()]).then((error) => {
|
| 137 |
+
if (error) {
|
| 138 |
+
setLogin({ kind: 'error', ...error });
|
| 139 |
+
} else {
|
| 140 |
+
onTokens();
|
| 141 |
+
}
|
| 142 |
+
});
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
function onTokens() {
|
| 146 |
+
const tokens = auth.tokens()!;
|
| 147 |
+
|
| 148 |
+
fetcher.submit(tokens, {
|
| 149 |
+
method: 'POST',
|
| 150 |
+
});
|
| 151 |
+
|
| 152 |
+
setLogin({ kind: 'pending' });
|
| 153 |
+
}
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
return (
|
| 157 |
+
<>
|
| 158 |
+
<button
|
| 159 |
+
className="w-full text-white bg-accent-600 hover:bg-accent-700 focus:ring-4 focus:outline-none font-medium rounded-lg text-sm px-5 py-2.5 text-center"
|
| 160 |
+
onClick={attemptLogin}
|
| 161 |
+
disabled={login?.kind === 'pending'}
|
| 162 |
+
>
|
| 163 |
+
{login?.kind === 'pending' ? 'Authenticating...' : 'Continue with StackBlitz'}
|
| 164 |
+
</button>
|
| 165 |
+
|
| 166 |
+
{login?.kind === 'error' && (
|
| 167 |
+
<div>
|
| 168 |
+
<h2>
|
| 169 |
+
<code>{login.error}</code>
|
| 170 |
+
</h2>
|
| 171 |
+
<p>{login.description}</p>
|
| 172 |
+
</div>
|
| 173 |
+
)}
|
| 174 |
+
</>
|
| 175 |
+
);
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
interface AuthError {
|
| 179 |
+
error: string;
|
| 180 |
+
description: string;
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
function authEvent(auth: AuthAPI, event: 'logged-out'): Promise<void>;
|
| 184 |
+
function authEvent(auth: AuthAPI, event: 'auth-failed'): Promise<AuthError>;
|
| 185 |
+
function authEvent(auth: AuthAPI, event: 'logged-out' | 'auth-failed') {
|
| 186 |
+
return new Promise((resolve) => {
|
| 187 |
+
const unsubscribe = auth.on(event as any, (arg: any) => {
|
| 188 |
+
unsubscribe();
|
| 189 |
+
resolve(arg);
|
| 190 |
+
});
|
| 191 |
+
});
|
| 192 |
+
}
|
packages/bolt/app/routes/logout.tsx
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { LoaderFunctionArgs } from '@remix-run/cloudflare';
|
| 2 |
+
import { logout } from '~/lib/.server/sessions';
|
| 3 |
+
|
| 4 |
+
export async function loader({ request, context }: LoaderFunctionArgs) {
|
| 5 |
+
return logout(request, context.cloudflare.env);
|
| 6 |
+
}
|
| 7 |
+
|
| 8 |
+
export default function Logout() {
|
| 9 |
+
return '';
|
| 10 |
+
}
|
packages/bolt/app/utils/logger.ts
CHANGED
|
@@ -11,7 +11,7 @@ interface Logger {
|
|
| 11 |
setLevel: (level: DebugLevel) => void;
|
| 12 |
}
|
| 13 |
|
| 14 |
-
let currentLevel: DebugLevel = import.meta.env.VITE_LOG_LEVEL ?? '
|
| 15 |
|
| 16 |
export const logger: Logger = {
|
| 17 |
trace: (...messages: any[]) => log('trace', undefined, messages),
|
|
|
|
| 11 |
setLevel: (level: DebugLevel) => void;
|
| 12 |
}
|
| 13 |
|
| 14 |
+
let currentLevel: DebugLevel = import.meta.env.VITE_LOG_LEVEL ?? import.meta.env.DEV ? 'debug' : 'info';
|
| 15 |
|
| 16 |
export const logger: Logger = {
|
| 17 |
trace: (...messages: any[]) => log('trace', undefined, messages),
|
packages/bolt/package.json
CHANGED
|
@@ -48,6 +48,7 @@
|
|
| 48 |
"framer-motion": "^11.2.12",
|
| 49 |
"isbot": "^4.1.0",
|
| 50 |
"istextorbinary": "^9.5.0",
|
|
|
|
| 51 |
"nanostores": "^0.10.3",
|
| 52 |
"react": "^18.2.0",
|
| 53 |
"react-dom": "^18.2.0",
|
|
@@ -64,9 +65,11 @@
|
|
| 64 |
"@cloudflare/workers-types": "^4.20240620.0",
|
| 65 |
"@remix-run/dev": "^2.10.0",
|
| 66 |
"@types/diff": "^5.2.1",
|
|
|
|
| 67 |
"@types/react": "^18.2.20",
|
| 68 |
"@types/react-dom": "^18.2.7",
|
| 69 |
"fast-glob": "^3.3.2",
|
|
|
|
| 70 |
"typescript": "^5.5.2",
|
| 71 |
"unified": "^11.0.5",
|
| 72 |
"unocss": "^0.61.3",
|
|
|
|
| 48 |
"framer-motion": "^11.2.12",
|
| 49 |
"isbot": "^4.1.0",
|
| 50 |
"istextorbinary": "^9.5.0",
|
| 51 |
+
"jsonwebtoken": "^9.0.2",
|
| 52 |
"nanostores": "^0.10.3",
|
| 53 |
"react": "^18.2.0",
|
| 54 |
"react-dom": "^18.2.0",
|
|
|
|
| 65 |
"@cloudflare/workers-types": "^4.20240620.0",
|
| 66 |
"@remix-run/dev": "^2.10.0",
|
| 67 |
"@types/diff": "^5.2.1",
|
| 68 |
+
"@types/jsonwebtoken": "^9.0.6",
|
| 69 |
"@types/react": "^18.2.20",
|
| 70 |
"@types/react-dom": "^18.2.7",
|
| 71 |
"fast-glob": "^3.3.2",
|
| 72 |
+
"node-fetch": "^3.3.2",
|
| 73 |
"typescript": "^5.5.2",
|
| 74 |
"unified": "^11.0.5",
|
| 75 |
"unocss": "^0.61.3",
|
pnpm-lock.yaml
CHANGED
|
@@ -131,6 +131,9 @@ importers:
|
|
| 131 |
istextorbinary:
|
| 132 |
specifier: ^9.5.0
|
| 133 |
version: 9.5.0
|
|
|
|
|
|
|
|
|
|
| 134 |
nanostores:
|
| 135 |
specifier: ^0.10.3
|
| 136 |
version: 0.10.3
|
|
@@ -174,6 +177,9 @@ importers:
|
|
| 174 |
'@types/diff':
|
| 175 |
specifier: ^5.2.1
|
| 176 |
version: 5.2.1
|
|
|
|
|
|
|
|
|
|
| 177 |
'@types/react':
|
| 178 |
specifier: ^18.2.20
|
| 179 |
version: 18.3.3
|
|
@@ -183,6 +189,9 @@ importers:
|
|
| 183 |
fast-glob:
|
| 184 |
specifier: ^3.3.2
|
| 185 |
version: 3.3.2
|
|
|
|
|
|
|
|
|
|
| 186 |
typescript:
|
| 187 |
specifier: ^5.5.2
|
| 188 |
version: 5.5.2
|
|
@@ -1484,6 +1493,9 @@ packages:
|
|
| 1484 |
'@types/[email protected]':
|
| 1485 |
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
| 1486 |
|
|
|
|
|
|
|
|
|
|
| 1487 |
'@types/[email protected]':
|
| 1488 |
resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==}
|
| 1489 |
|
|
@@ -1949,6 +1961,9 @@ packages:
|
|
| 1949 |
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
| 1950 |
hasBin: true
|
| 1951 |
|
|
|
|
|
|
|
|
|
|
| 1952 | |
| 1953 |
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
| 1954 |
|
|
@@ -2234,6 +2249,10 @@ packages:
|
|
| 2234 |
resolution: {integrity: sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==}
|
| 2235 |
engines: {node: '>= 6'}
|
| 2236 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2237 | |
| 2238 |
resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==}
|
| 2239 |
|
|
@@ -2353,6 +2372,9 @@ packages:
|
|
| 2353 | |
| 2354 |
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
| 2355 |
|
|
|
|
|
|
|
|
|
|
| 2356 | |
| 2357 |
resolution: {integrity: sha512-ofkXJtn7z0urokN62DI3SBo/5xAtF0rR7tn+S/bSYV79Ka8pTajIIl+fFQ1q88DQEImymmo97M4azY3WX/nUdg==}
|
| 2358 |
engines: {node: '>=4'}
|
|
@@ -2615,6 +2637,10 @@ packages:
|
|
| 2615 | |
| 2616 |
resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==}
|
| 2617 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2618 | |
| 2619 |
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
|
| 2620 |
engines: {node: '>=16.0.0'}
|
|
@@ -2653,6 +2679,10 @@ packages:
|
|
| 2653 |
resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==}
|
| 2654 |
engines: {node: '>=0.4.x'}
|
| 2655 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2656 | |
| 2657 |
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
| 2658 |
engines: {node: '>= 0.6'}
|
|
@@ -3144,6 +3174,16 @@ packages:
|
|
| 3144 |
resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==}
|
| 3145 |
engines: {'0': node >= 0.2.0}
|
| 3146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3147 | |
| 3148 |
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
| 3149 |
|
|
@@ -3190,9 +3230,24 @@ packages:
|
|
| 3190 | |
| 3191 |
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
|
| 3192 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3193 | |
| 3194 |
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
|
| 3195 |
|
|
|
|
|
|
|
|
|
|
| 3196 | |
| 3197 |
resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==}
|
| 3198 |
|
|
@@ -3202,6 +3257,9 @@ packages:
|
|
| 3202 | |
| 3203 |
resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==}
|
| 3204 |
|
|
|
|
|
|
|
|
|
|
| 3205 | |
| 3206 |
resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==}
|
| 3207 |
|
|
@@ -3687,9 +3745,17 @@ packages:
|
|
| 3687 |
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
|
| 3688 |
engines: {node: '>= 0.6'}
|
| 3689 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3690 | |
| 3691 |
resolution: {integrity: sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==}
|
| 3692 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3693 | |
| 3694 |
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
|
| 3695 |
engines: {node: '>= 6.13.0'}
|
|
@@ -4679,8 +4745,8 @@ packages:
|
|
| 4679 |
resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==}
|
| 4680 |
engines: {node: '>=14.0'}
|
| 4681 |
|
| 4682 |
-
[email protected].
|
| 4683 |
-
resolution: {integrity: sha512-
|
| 4684 |
engines: {node: '>=18.17'}
|
| 4685 |
|
| 4686 | |
|
@@ -6189,7 +6255,7 @@ snapshots:
|
|
| 6189 |
cookie-signature: 1.2.1
|
| 6190 |
source-map-support: 0.5.21
|
| 6191 |
stream-slice: 0.1.2
|
| 6192 |
-
undici: 6.19.
|
| 6193 |
optionalDependencies:
|
| 6194 |
typescript: 5.5.2
|
| 6195 |
|
|
@@ -6201,7 +6267,7 @@ snapshots:
|
|
| 6201 |
cookie-signature: 1.2.1
|
| 6202 |
source-map-support: 0.5.21
|
| 6203 |
stream-slice: 0.1.2
|
| 6204 |
-
undici: 6.19.
|
| 6205 |
optionalDependencies:
|
| 6206 |
typescript: 5.5.2
|
| 6207 |
optional: true
|
|
@@ -6416,6 +6482,10 @@ snapshots:
|
|
| 6416 |
|
| 6417 |
'@types/[email protected]': {}
|
| 6418 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6419 |
'@types/[email protected]':
|
| 6420 |
dependencies:
|
| 6421 |
'@types/unist': 2.0.10
|
|
@@ -7091,6 +7161,8 @@ snapshots:
|
|
| 7091 |
node-releases: 2.0.14
|
| 7092 |
update-browserslist-db: 1.0.16([email protected])
|
| 7093 |
|
|
|
|
|
|
|
| 7094 | |
| 7095 |
|
| 7096 | |
|
@@ -7396,6 +7468,8 @@ snapshots:
|
|
| 7396 |
|
| 7397 | |
| 7398 |
|
|
|
|
|
|
|
| 7399 | |
| 7400 |
|
| 7401 | |
|
@@ -7490,6 +7564,10 @@ snapshots:
|
|
| 7490 |
|
| 7491 | |
| 7492 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7493 | |
| 7494 |
dependencies:
|
| 7495 |
version-range: 4.14.0
|
|
@@ -7876,6 +7954,11 @@ snapshots:
|
|
| 7876 |
dependencies:
|
| 7877 |
format: 0.2.2
|
| 7878 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7879 | |
| 7880 |
dependencies:
|
| 7881 |
flat-cache: 4.0.1
|
|
@@ -7925,6 +8008,10 @@ snapshots:
|
|
| 7925 |
|
| 7926 | |
| 7927 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7928 | |
| 7929 |
|
| 7930 | |
|
@@ -8406,6 +8493,30 @@ snapshots:
|
|
| 8406 |
|
| 8407 | |
| 8408 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8409 | |
| 8410 |
dependencies:
|
| 8411 |
json-buffer: 3.0.1
|
|
@@ -8444,14 +8555,26 @@ snapshots:
|
|
| 8444 |
|
| 8445 | |
| 8446 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8447 | |
| 8448 |
|
|
|
|
|
|
|
| 8449 | |
| 8450 |
|
| 8451 | |
| 8452 |
|
| 8453 | |
| 8454 |
|
|
|
|
|
|
|
| 8455 | |
| 8456 |
|
| 8457 | |
|
@@ -9317,8 +9440,16 @@ snapshots:
|
|
| 9317 |
|
| 9318 | |
| 9319 |
|
|
|
|
|
|
|
| 9320 | |
| 9321 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9322 | |
| 9323 |
|
| 9324 | |
|
@@ -10393,7 +10524,7 @@ snapshots:
|
|
| 10393 |
dependencies:
|
| 10394 |
'@fastify/busboy': 2.1.1
|
| 10395 |
|
| 10396 |
-
[email protected].
|
| 10397 |
|
| 10398 | |
| 10399 |
dependencies:
|
|
|
|
| 131 |
istextorbinary:
|
| 132 |
specifier: ^9.5.0
|
| 133 |
version: 9.5.0
|
| 134 |
+
jsonwebtoken:
|
| 135 |
+
specifier: ^9.0.2
|
| 136 |
+
version: 9.0.2
|
| 137 |
nanostores:
|
| 138 |
specifier: ^0.10.3
|
| 139 |
version: 0.10.3
|
|
|
|
| 177 |
'@types/diff':
|
| 178 |
specifier: ^5.2.1
|
| 179 |
version: 5.2.1
|
| 180 |
+
'@types/jsonwebtoken':
|
| 181 |
+
specifier: ^9.0.6
|
| 182 |
+
version: 9.0.6
|
| 183 |
'@types/react':
|
| 184 |
specifier: ^18.2.20
|
| 185 |
version: 18.3.3
|
|
|
|
| 189 |
fast-glob:
|
| 190 |
specifier: ^3.3.2
|
| 191 |
version: 3.3.2
|
| 192 |
+
node-fetch:
|
| 193 |
+
specifier: ^3.3.2
|
| 194 |
+
version: 3.3.2
|
| 195 |
typescript:
|
| 196 |
specifier: ^5.5.2
|
| 197 |
version: 5.5.2
|
|
|
|
| 1493 |
'@types/[email protected]':
|
| 1494 |
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
| 1495 |
|
| 1496 |
+
'@types/[email protected]':
|
| 1497 |
+
resolution: {integrity: sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==}
|
| 1498 |
+
|
| 1499 |
'@types/[email protected]':
|
| 1500 |
resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==}
|
| 1501 |
|
|
|
|
| 1961 |
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
| 1962 |
hasBin: true
|
| 1963 |
|
| 1964 | |
| 1965 |
+
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
|
| 1966 |
+
|
| 1967 | |
| 1968 |
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
| 1969 |
|
|
|
|
| 2249 |
resolution: {integrity: sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==}
|
| 2250 |
engines: {node: '>= 6'}
|
| 2251 |
|
| 2252 | |
| 2253 |
+
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
|
| 2254 |
+
engines: {node: '>= 12'}
|
| 2255 |
+
|
| 2256 | |
| 2257 |
resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==}
|
| 2258 |
|
|
|
|
| 2372 | |
| 2373 |
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
| 2374 |
|
| 2375 | |
| 2376 |
+
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
|
| 2377 |
+
|
| 2378 | |
| 2379 |
resolution: {integrity: sha512-ofkXJtn7z0urokN62DI3SBo/5xAtF0rR7tn+S/bSYV79Ka8pTajIIl+fFQ1q88DQEImymmo97M4azY3WX/nUdg==}
|
| 2380 |
engines: {node: '>=4'}
|
|
|
|
| 2637 | |
| 2638 |
resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==}
|
| 2639 |
|
| 2640 | |
| 2641 |
+
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
|
| 2642 |
+
engines: {node: ^12.20 || >= 14.13}
|
| 2643 |
+
|
| 2644 | |
| 2645 |
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
|
| 2646 |
engines: {node: '>=16.0.0'}
|
|
|
|
| 2679 |
resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==}
|
| 2680 |
engines: {node: '>=0.4.x'}
|
| 2681 |
|
| 2682 | |
| 2683 |
+
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
|
| 2684 |
+
engines: {node: '>=12.20.0'}
|
| 2685 |
+
|
| 2686 | |
| 2687 |
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
| 2688 |
engines: {node: '>= 0.6'}
|
|
|
|
| 3174 |
resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==}
|
| 3175 |
engines: {'0': node >= 0.2.0}
|
| 3176 |
|
| 3177 | |
| 3178 |
+
resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==}
|
| 3179 |
+
engines: {node: '>=12', npm: '>=6'}
|
| 3180 |
+
|
| 3181 | |
| 3182 |
+
resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==}
|
| 3183 |
+
|
| 3184 | |
| 3185 |
+
resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==}
|
| 3186 |
+
|
| 3187 | |
| 3188 |
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
| 3189 |
|
|
|
|
| 3230 | |
| 3231 |
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
|
| 3232 |
|
| 3233 | |
| 3234 |
+
resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==}
|
| 3235 |
+
|
| 3236 | |
| 3237 |
+
resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==}
|
| 3238 |
+
|
| 3239 | |
| 3240 |
+
resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==}
|
| 3241 |
+
|
| 3242 | |
| 3243 |
+
resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==}
|
| 3244 |
+
|
| 3245 | |
| 3246 |
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
|
| 3247 |
|
| 3248 | |
| 3249 |
+
resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==}
|
| 3250 |
+
|
| 3251 | |
| 3252 |
resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==}
|
| 3253 |
|
|
|
|
| 3257 | |
| 3258 |
resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==}
|
| 3259 |
|
| 3260 | |
| 3261 |
+
resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
|
| 3262 |
+
|
| 3263 | |
| 3264 |
resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==}
|
| 3265 |
|
|
|
|
| 3745 |
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
|
| 3746 |
engines: {node: '>= 0.6'}
|
| 3747 |
|
| 3748 | |
| 3749 |
+
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
|
| 3750 |
+
engines: {node: '>=10.5.0'}
|
| 3751 |
+
|
| 3752 | |
| 3753 |
resolution: {integrity: sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==}
|
| 3754 |
|
| 3755 | |
| 3756 |
+
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
|
| 3757 |
+
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
| 3758 |
+
|
| 3759 | |
| 3760 |
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
|
| 3761 |
engines: {node: '>= 6.13.0'}
|
|
|
|
| 4745 |
resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==}
|
| 4746 |
engines: {node: '>=14.0'}
|
| 4747 |
|
| 4748 |
+
[email protected].4:
|
| 4749 |
+
resolution: {integrity: sha512-i3uaEUwNdkRq2qtTRRJb13moW5HWqviu7Vl7oYRYz++uPtGHJj+x7TGjcEuwS5Mt2P4nA0U9dhIX3DdB6JGY0g==}
|
| 4750 |
engines: {node: '>=18.17'}
|
| 4751 |
|
| 4752 | |
|
|
|
| 6255 |
cookie-signature: 1.2.1
|
| 6256 |
source-map-support: 0.5.21
|
| 6257 |
stream-slice: 0.1.2
|
| 6258 |
+
undici: 6.19.4
|
| 6259 |
optionalDependencies:
|
| 6260 |
typescript: 5.5.2
|
| 6261 |
|
|
|
|
| 6267 |
cookie-signature: 1.2.1
|
| 6268 |
source-map-support: 0.5.21
|
| 6269 |
stream-slice: 0.1.2
|
| 6270 |
+
undici: 6.19.4
|
| 6271 |
optionalDependencies:
|
| 6272 |
typescript: 5.5.2
|
| 6273 |
optional: true
|
|
|
|
| 6482 |
|
| 6483 |
'@types/[email protected]': {}
|
| 6484 |
|
| 6485 |
+
'@types/[email protected]':
|
| 6486 |
+
dependencies:
|
| 6487 |
+
'@types/node': 20.14.9
|
| 6488 |
+
|
| 6489 |
'@types/[email protected]':
|
| 6490 |
dependencies:
|
| 6491 |
'@types/unist': 2.0.10
|
|
|
|
| 7161 |
node-releases: 2.0.14
|
| 7162 |
update-browserslist-db: 1.0.16([email protected])
|
| 7163 |
|
| 7164 |
+
[email protected]: {}
|
| 7165 |
+
|
| 7166 | |
| 7167 |
|
| 7168 | |
|
|
|
| 7468 |
|
| 7469 | |
| 7470 |
|
| 7471 |
+
[email protected]: {}
|
| 7472 |
+
|
| 7473 | |
| 7474 |
|
| 7475 | |
|
|
|
| 7564 |
|
| 7565 | |
| 7566 |
|
| 7567 | |
| 7568 |
+
dependencies:
|
| 7569 |
+
safe-buffer: 5.2.1
|
| 7570 |
+
|
| 7571 | |
| 7572 |
dependencies:
|
| 7573 |
version-range: 4.14.0
|
|
|
|
| 7954 |
dependencies:
|
| 7955 |
format: 0.2.2
|
| 7956 |
|
| 7957 | |
| 7958 |
+
dependencies:
|
| 7959 |
+
node-domexception: 1.0.0
|
| 7960 |
+
web-streams-polyfill: 3.3.3
|
| 7961 |
+
|
| 7962 | |
| 7963 |
dependencies:
|
| 7964 |
flat-cache: 4.0.1
|
|
|
|
| 8008 |
|
| 8009 | |
| 8010 |
|
| 8011 | |
| 8012 |
+
dependencies:
|
| 8013 |
+
fetch-blob: 3.2.0
|
| 8014 |
+
|
| 8015 | |
| 8016 |
|
| 8017 | |
|
|
|
| 8493 |
|
| 8494 | |
| 8495 |
|
| 8496 | |
| 8497 |
+
dependencies:
|
| 8498 |
+
jws: 3.2.2
|
| 8499 |
+
lodash.includes: 4.3.0
|
| 8500 |
+
lodash.isboolean: 3.0.3
|
| 8501 |
+
lodash.isinteger: 4.0.4
|
| 8502 |
+
lodash.isnumber: 3.0.3
|
| 8503 |
+
lodash.isplainobject: 4.0.6
|
| 8504 |
+
lodash.isstring: 4.0.1
|
| 8505 |
+
lodash.once: 4.1.1
|
| 8506 |
+
ms: 2.1.3
|
| 8507 |
+
semver: 7.6.2
|
| 8508 |
+
|
| 8509 | |
| 8510 |
+
dependencies:
|
| 8511 |
+
buffer-equal-constant-time: 1.0.1
|
| 8512 |
+
ecdsa-sig-formatter: 1.0.11
|
| 8513 |
+
safe-buffer: 5.2.1
|
| 8514 |
+
|
| 8515 | |
| 8516 |
+
dependencies:
|
| 8517 |
+
jwa: 1.4.1
|
| 8518 |
+
safe-buffer: 5.2.1
|
| 8519 |
+
|
| 8520 | |
| 8521 |
dependencies:
|
| 8522 |
json-buffer: 3.0.1
|
|
|
|
| 8555 |
|
| 8556 | |
| 8557 |
|
| 8558 |
+
[email protected]: {}
|
| 8559 |
+
|
| 8560 |
+
[email protected]: {}
|
| 8561 |
+
|
| 8562 |
+
[email protected]: {}
|
| 8563 |
+
|
| 8564 |
+
[email protected]: {}
|
| 8565 |
+
|
| 8566 | |
| 8567 |
|
| 8568 |
+
[email protected]: {}
|
| 8569 |
+
|
| 8570 | |
| 8571 |
|
| 8572 | |
| 8573 |
|
| 8574 | |
| 8575 |
|
| 8576 |
+
[email protected]: {}
|
| 8577 |
+
|
| 8578 | |
| 8579 |
|
| 8580 | |
|
|
|
| 9440 |
|
| 9441 | |
| 9442 |
|
| 9443 |
+
[email protected]: {}
|
| 9444 |
+
|
| 9445 | |
| 9446 |
|
| 9447 | |
| 9448 |
+
dependencies:
|
| 9449 |
+
data-uri-to-buffer: 4.0.1
|
| 9450 |
+
fetch-blob: 3.2.0
|
| 9451 |
+
formdata-polyfill: 4.0.10
|
| 9452 |
+
|
| 9453 | |
| 9454 |
|
| 9455 | |
|
|
|
| 10524 |
dependencies:
|
| 10525 |
'@fastify/busboy': 2.1.1
|
| 10526 |
|
| 10527 |
+
[email protected].4: {}
|
| 10528 |
|
| 10529 | |
| 10530 |
dependencies:
|