diff --git a/Dockerfile.oauth b/Dockerfile.oauth new file mode 120000 index 00000000..1caefea4 --- /dev/null +++ b/Dockerfile.oauth @@ -0,0 +1 @@ +be/apps/oauth-gateway/Dockerfile \ No newline at end of file diff --git a/be/apps/core/src/modules/infrastructure/static-web/static-web.controller.ts b/be/apps/core/src/modules/infrastructure/static-web/static-web.controller.ts index e6e9da4d..d136e34a 100644 --- a/be/apps/core/src/modules/infrastructure/static-web/static-web.controller.ts +++ b/be/apps/core/src/modules/infrastructure/static-web/static-web.controller.ts @@ -1,6 +1,7 @@ import { ContextParam, Controller, Get, Param } from '@afilmory/framework' +import { isTenantSlugReserved } from '@afilmory/utils' import { SkipTenantGuard } from 'core/decorators/skip-tenant.decorator' -import { PLACEHOLDER_TENANT_SLUG, ROOT_TENANT_SLUG } from 'core/modules/platform/tenant/tenant.constants' +import { ROOT_TENANT_SLUG } from 'core/modules/platform/tenant/tenant.constants' import { getTenantContext, isPlaceholderTenantContext } from 'core/modules/platform/tenant/tenant.context' import type { Context } from 'hono' @@ -9,7 +10,7 @@ import { STATIC_DASHBOARD_BASENAME, StaticDashboardService } from './static-dash import { StaticWebService } from './static-web.service' const TENANT_MISSING_ENTRY_PATH = `${STATIC_DASHBOARD_BASENAME}/tenant-missing.html` -const RESTRICTED_STATIC_WEB_TENANT_SLUGS = new Set([ROOT_TENANT_SLUG, PLACEHOLDER_TENANT_SLUG]) +const TENANT_RESTRICTED_ENTRY_PATH = `${STATIC_DASHBOARD_BASENAME}/tenant-restricted.html` @Controller({ bypassGlobalPrefix: true }) export class StaticWebController { @@ -29,8 +30,8 @@ export class StaticWebController { @Get(`/explory`) @SkipTenantGuard() async getStaticWebIndex(@ContextParam() context: Context) { - if (this.shouldBlockReservedTenantStaticWebAccess()) { - return await this.renderTenantMissingPage() + if (this.isReservedTenant()) { + return await this.renderTenantRestrictedPage() } if (this.shouldRenderTenantMissingPage()) { return await this.renderTenantMissingPage() @@ -45,8 +46,8 @@ export class StaticWebController { @Get(`/photos/:photoId`) async getStaticPhotoPage(@ContextParam() context: Context, @Param('photoId') photoId: string) { - if (this.shouldBlockReservedTenantStaticWebAccess()) { - return await this.renderTenantMissingPage() + if (this.isReservedTenant()) { + return await this.renderTenantRestrictedPage() } if (this.shouldRenderTenantMissingPage()) { return await this.renderTenantMissingPage() @@ -64,11 +65,19 @@ export class StaticWebController { async getStaticDashboardIndexWithBasename(@ContextParam() context: Context) { const pathname = context.req.path const isHtmlRoute = this.isHtmlRoute(pathname) + const normalizedPath = this.normalizePathname(pathname) const allowTenantlessAccess = isHtmlRoute && this.shouldAllowTenantlessDashboardAccess(pathname) + const isRestrictedEntry = normalizedPath === TENANT_RESTRICTED_ENTRY_PATH + if (isHtmlRoute && this.isReservedTenant() && !isRestrictedEntry) { + return await this.renderTenantRestrictedPage() + } if (isHtmlRoute && !allowTenantlessAccess && this.shouldRenderTenantMissingPage()) { return await this.renderTenantMissingPage() } const response = await this.serve(context, this.staticDashboardService, false) + if (isHtmlRoute && this.isReservedTenant() && response.status === 404) { + return await this.renderTenantRestrictedPage() + } if (isHtmlRoute && !allowTenantlessAccess && response.status === 404) { return await this.renderTenantMissingPage() } @@ -179,13 +188,16 @@ export class StaticWebController { return trimmed } - private shouldBlockReservedTenantStaticWebAccess(): boolean { + private isReservedTenant(): boolean { const tenantContext = getTenantContext() const slug = tenantContext?.tenant.slug?.toLowerCase() if (!slug) { return false } - return RESTRICTED_STATIC_WEB_TENANT_SLUGS.has(slug) + if (slug === ROOT_TENANT_SLUG) { + return false + } + return isTenantSlugReserved(slug) } private shouldRenderTenantMissingPage(): boolean { @@ -202,6 +214,15 @@ export class StaticWebController { return new Response('Workspace unavailable', { status: 404 }) } + private async renderTenantRestrictedPage(): Promise { + const response = await this.staticDashboardService.handleRequest(TENANT_RESTRICTED_ENTRY_PATH, false) + if (response) { + return this.cloneResponseWithStatus(response, 403) + } + + return new Response('Workspace access restricted', { status: 403 }) + } + private cloneResponseWithStatus(response: Response, status: number): Response { const headers = new Headers(response.headers) return new Response(response.body, { diff --git a/be/apps/dashboard/src/constants/routes.ts b/be/apps/dashboard/src/constants/routes.ts index 186bb47d..5be2b79c 100644 --- a/be/apps/dashboard/src/constants/routes.ts +++ b/be/apps/dashboard/src/constants/routes.ts @@ -3,6 +3,7 @@ export const ROUTE_PATHS = { ROOT_LOGIN: '/root-login', WELCOME: '/welcome', TENANT_MISSING: '/tenant-missing', + TENANT_RESTRICTED: '/tenant-restricted', DEFAULT_AUTHENTICATED: '/', SUPERADMIN_ROOT: '/superadmin', SUPERADMIN_DEFAULT: '/superadmin/settings', @@ -14,5 +15,6 @@ export const PUBLIC_ROUTES = new Set([ ROUTE_PATHS.ROOT_LOGIN, ROUTE_PATHS.WELCOME, ROUTE_PATHS.TENANT_MISSING, + ROUTE_PATHS.TENANT_RESTRICTED, ROUTE_PATHS.NO_ACCESS, ]) diff --git a/be/apps/dashboard/src/entries/tenant-restricted.tsx b/be/apps/dashboard/src/entries/tenant-restricted.tsx new file mode 100644 index 00000000..bd4a065e --- /dev/null +++ b/be/apps/dashboard/src/entries/tenant-restricted.tsx @@ -0,0 +1,13 @@ +import '../styles/index.css' + +import { createRoot } from 'react-dom/client' + +import { TenantRestrictedStandalone } from '../modules/welcome/components/TenantRestrictedStandalone' + +const root = document.querySelector('#root') + +if (!root) { + throw new Error('Root element not found for tenant restricted entry.') +} + +createRoot(root).render() diff --git a/be/apps/dashboard/src/modules/welcome/components/TenantMissingStandalone.tsx b/be/apps/dashboard/src/modules/welcome/components/TenantMissingStandalone.tsx index 56e27697..f1074349 100644 --- a/be/apps/dashboard/src/modules/welcome/components/TenantMissingStandalone.tsx +++ b/be/apps/dashboard/src/modules/welcome/components/TenantMissingStandalone.tsx @@ -2,44 +2,7 @@ import { Button } from '@afilmory/ui' import { useMemo } from 'react' import { LinearBorderContainer } from './LinearBorderContainer' - -const getCurrentHostname = () => { - if (typeof window === 'undefined') { - return null - } - try { - return window.location.hostname - } catch { - return null - } -} - -const buildRegistrationUrl = () => { - if (typeof window === 'undefined') { - return '/platform/welcome' - } - - try { - const { protocol, host } = window.location - return `${protocol}//${host}/platform/welcome` - } catch { - return '/platform/welcome' - } -} - -const buildHomeUrl = () => { - if (typeof window === 'undefined') { - return '/' - } - - try { - const { protocol, hostname, port } = window.location - const normalizedPort = port ? `:${port}` : '' - return `${protocol}//${hostname}${normalizedPort}` - } catch { - return '/' - } -} +import { buildHomeUrl, buildRegistrationUrl, getCurrentHostname } from './tenant-utils' export const TenantMissingStandalone = () => { const hostname = useMemo(() => getCurrentHostname(), []) diff --git a/be/apps/dashboard/src/modules/welcome/components/TenantRestrictedStandalone.tsx b/be/apps/dashboard/src/modules/welcome/components/TenantRestrictedStandalone.tsx new file mode 100644 index 00000000..52948017 --- /dev/null +++ b/be/apps/dashboard/src/modules/welcome/components/TenantRestrictedStandalone.tsx @@ -0,0 +1,58 @@ +import { Button } from '@afilmory/ui' +import { useMemo } from 'react' + +import { LinearBorderContainer } from './LinearBorderContainer' +import { buildHomeUrl, buildRegistrationUrl, getCurrentHostname } from './tenant-utils' + +export const TenantRestrictedStandalone = () => { + const hostname = useMemo(() => getCurrentHostname(), []) + const registrationUrl = useMemo(() => buildRegistrationUrl(), []) + const homeUrl = useMemo(() => buildHomeUrl(), []) + + return ( +
+
+ +
+
+
+
+
+ +
+
+

403

+

该空间已被保留

+

+ 当前访问的空间属于系统保留地址,无法直接访问仪表盘或公开站点。若要继续体验 + Afilmory,请使用其他地址,或者注册属于你的专属空间。 +

+ + {hostname && ( +
+

+ 请求的地址:{hostname} +

+
+ )} + +
+ + +
+
+
+
+ +
+
+ ) +} diff --git a/be/apps/dashboard/src/modules/welcome/components/tenant-utils.ts b/be/apps/dashboard/src/modules/welcome/components/tenant-utils.ts new file mode 100644 index 00000000..21506230 --- /dev/null +++ b/be/apps/dashboard/src/modules/welcome/components/tenant-utils.ts @@ -0,0 +1,37 @@ +export const getCurrentHostname = (): string | null => { + if (typeof window === 'undefined') { + return null + } + try { + return window.location.hostname + } catch { + return null + } +} + +export const buildRegistrationUrl = (): string => { + if (typeof window === 'undefined') { + return '/platform/welcome' + } + + try { + const { protocol, host } = window.location + return `${protocol}//${host}/platform/welcome` + } catch { + return '/platform/welcome' + } +} + +export const buildHomeUrl = (): string => { + if (typeof window === 'undefined') { + return '/' + } + + try { + const { protocol, hostname, port } = window.location + const normalizedPort = port ? `:${port}` : '' + return `${protocol}//${hostname}${normalizedPort}` + } catch { + return '/' + } +} diff --git a/be/apps/dashboard/tenant-restricted.html b/be/apps/dashboard/tenant-restricted.html new file mode 100644 index 00000000..97ebb981 --- /dev/null +++ b/be/apps/dashboard/tenant-restricted.html @@ -0,0 +1,26 @@ + + + + + + + Afilmory - 空间访问受限 + + + + + + +
+ + + + diff --git a/be/apps/dashboard/vite.config.ts b/be/apps/dashboard/vite.config.ts index e77fe910..a6019bc0 100644 --- a/be/apps/dashboard/vite.config.ts +++ b/be/apps/dashboard/vite.config.ts @@ -77,6 +77,7 @@ export default defineConfig({ input: { main: resolve(ROOT, 'index.html'), 'tenant-missing': resolve(ROOT, 'tenant-missing.html'), + 'tenant-restricted': resolve(ROOT, 'tenant-restricted.html'), }, }, }, diff --git a/be/apps/oauth-gateway/Dockerfile b/be/apps/oauth-gateway/Dockerfile new file mode 100644 index 00000000..05c17a39 --- /dev/null +++ b/be/apps/oauth-gateway/Dockerfile @@ -0,0 +1,29 @@ +# syntax=docker/dockerfile:1.7 + +FROM node:20-alpine AS base +ENV PNPM_HOME=/pnpm +ENV PATH="$PNPM_HOME:$PATH" +RUN corepack enable && corepack prepare pnpm@10.19.0 --activate + +FROM base AS builder +WORKDIR /workspace + +COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./ +COPY be/apps/oauth-gateway/package.json be/apps/oauth-gateway/package.json + +RUN pnpm fetch --filter '@afilmory/oauth-gateway...' + +COPY . . + +RUN pnpm install --filter '@afilmory/oauth-gateway...' --frozen-lockfile +RUN pnpm --filter @afilmory/oauth-gateway build + +FROM base AS runner +ENV NODE_ENV=production +WORKDIR /app + +COPY --from=builder /workspace/be/apps/oauth-gateway/dist ./dist + +EXPOSE 8790 + +CMD ["node", "./dist/main.js"] diff --git a/be/apps/oauth-gateway/README.md b/be/apps/oauth-gateway/README.md new file mode 100644 index 00000000..4c33f500 --- /dev/null +++ b/be/apps/oauth-gateway/README.md @@ -0,0 +1,57 @@ +# OAuth Gateway + +Multi-tenant OAuth callback router that lets every identity provider point to a single domain +(`auth.afilmory.art`, for example) while keeping the actual Better Auth handlers inside each tenant +subdomain. + +## How It Works + +1. Better Auth (running inside `be/apps/core`) builds provider redirect URLs using the tenant slug. +2. Instead of sending the provider back to the tenant domain, the redirect URL is set to + `https://auth.afilmory.art/api/auth/callback/{provider}?tenantSlug=`. +3. The gateway receives the provider callback, validates the slug/host, and issues a 302 redirect to + `https://.afilmory.art/api/auth/callback/{provider}` (preserving `code`, `state`, etc.). + +Because the gateway only rewrites the callback target, it does **not** interact with provider APIs or +tokens. This keeps configuration simple (single callback URL in GitHub/Google) while ensuring tenant +sessions are still created on the correct host. + +## Development + +```bash +pnpm --filter @afilmory/oauth-gateway dev +``` + +The service starts on `http://0.0.0.0:8790` by default. + +## Environment Variables + +| Variable | Default | Description | +| --------------------------------- | -------------------- | ----------------------------------------------------------------- | +| `AUTH_GATEWAY_HOST` | `0.0.0.0` | Interface to bind. | +| `AUTH_GATEWAY_PORT` | `8790` | Port to listen on. | +| `AUTH_GATEWAY_BASE_DOMAIN` | `afilmory.art` | Root domain used when constructing tenant hosts. | +| `AUTH_GATEWAY_CALLBACK_BASE_PATH` | `/api/auth/callback` | Base path that the providers call. | +| `AUTH_GATEWAY_FORCE_HTTPS` | `true` | Forces redirects to `https` unless the host looks like localhost. | +| `AUTH_GATEWAY_ALLOW_CUSTOM_HOST` | `false` | Allow requests to pass an explicit `targetHost` query parameter. | +| `AUTH_GATEWAY_ROOT_SLUG` | `root` | Slug treated as the apex (no subdomain). | + +## Callback Contract + +`GET /api/auth/callback/:provider` + +Query parameters: + +- `tenantSlug` (preferred) or `tenant` — tenant slug to route to. Required unless `targetHost` is + provided or you want to hit the root domain. +- `targetHost` — explicit host override (opt-in via `ALLOW_CUSTOM_HOST`). +- All other query parameters (`code`, `state`, etc.) are forwarded verbatim. + +Example redirect produced by the gateway: + +``` +https://auth.afilmory.art/api/auth/callback/github?tenantSlug=innei&code=...&state=... + ⮕ 302 → https://innei.afilmory.art/api/auth/callback/github?code=...&state=... +``` + +This service is intentionally stateless so it can be deployed behind a simple load balancer. diff --git a/be/apps/oauth-gateway/nodemon.json b/be/apps/oauth-gateway/nodemon.json new file mode 100644 index 00000000..6cf1332e --- /dev/null +++ b/be/apps/oauth-gateway/nodemon.json @@ -0,0 +1,6 @@ +{ + "ignore": ["dist"], + "watch": ["src"], + "ext": "ts,js,json", + "exec": "vite-node src/index.ts" +} diff --git a/be/apps/oauth-gateway/package.json b/be/apps/oauth-gateway/package.json new file mode 100644 index 00000000..6fdf7c41 --- /dev/null +++ b/be/apps/oauth-gateway/package.json @@ -0,0 +1,24 @@ +{ + "name": "@afilmory/oauth-gateway", + "type": "module", + "version": "0.1.0", + "private": true, + "scripts": { + "build": "vite build", + "dev": "nodemon", + "start": "node dist/main.js" + }, + "dependencies": { + "@afilmory/utils": "workspace:*", + "@hono/node-server": "^1.13.5", + "hono": "^4.6.12", + "zod": "catalog:" + }, + "devDependencies": { + "@types/node": "^22.10.2", + "nodemon": "3.1.10", + "typescript": "catalog:", + "vite": "7.1.12", + "vite-node": "3.2.4" + } +} diff --git a/be/apps/oauth-gateway/src/config.ts b/be/apps/oauth-gateway/src/config.ts new file mode 100644 index 00000000..5b3fe747 --- /dev/null +++ b/be/apps/oauth-gateway/src/config.ts @@ -0,0 +1,62 @@ +import { DEFAULT_BASE_DOMAIN } from '@afilmory/utils' +import { z } from 'zod' + +const booleanSchema = z + .union([z.boolean(), z.string()]) + .optional() + .transform((value) => { + if (typeof value === 'boolean') { + return value + } + if (value === undefined) { + return + } + const normalized = value.trim().toLowerCase() + return !['false', '0', 'no', 'off'].includes(normalized) + }) + +const envSchema = z.object({ + HOST: z.string().trim().min(1).default('0.0.0.0'), + PORT: z.coerce.number().int().min(1).max(65_535).default(8790), + BASE_DOMAIN: z + .string() + .trim() + .min(1) + .regex(/^[a-z0-9.-]+$/i, { message: 'BASE_DOMAIN must be a valid hostname.' }) + .default(DEFAULT_BASE_DOMAIN), + FORCE_HTTPS: booleanSchema.default(true), + CALLBACK_BASE_PATH: z + .string() + .trim() + .default('/api/auth/callback') + .transform((value) => value.replace(/\/+$/, '') || '/api/auth/callback'), + ALLOW_CUSTOM_HOST: booleanSchema.default(false), + ROOT_SLUG: z + .string() + .trim() + .min(1) + .regex(/^[a-z0-9-]+$/i) + .default('root'), +}) + +const parsed = envSchema.parse({ + HOST: process.env.AUTH_GATEWAY_HOST ?? process.env.HOST, + PORT: process.env.AUTH_GATEWAY_PORT ?? process.env.PORT, + BASE_DOMAIN: process.env.AUTH_GATEWAY_BASE_DOMAIN, + FORCE_HTTPS: process.env.AUTH_GATEWAY_FORCE_HTTPS, + CALLBACK_BASE_PATH: process.env.AUTH_GATEWAY_CALLBACK_BASE_PATH, + ALLOW_CUSTOM_HOST: process.env.AUTH_GATEWAY_ALLOW_CUSTOM_HOST, + ROOT_SLUG: process.env.AUTH_GATEWAY_ROOT_SLUG, +}) + +export const gatewayConfig = { + host: parsed.HOST, + port: parsed.PORT, + baseDomain: parsed.BASE_DOMAIN.toLowerCase(), + forceHttps: Boolean(parsed.FORCE_HTTPS), + callbackBasePath: parsed.CALLBACK_BASE_PATH, + allowCustomHost: Boolean(parsed.ALLOW_CUSTOM_HOST), + rootSlug: parsed.ROOT_SLUG.toLowerCase(), +} as const + +export type GatewayConfig = typeof gatewayConfig diff --git a/be/apps/oauth-gateway/src/index.ts b/be/apps/oauth-gateway/src/index.ts new file mode 100644 index 00000000..f1fadb6c --- /dev/null +++ b/be/apps/oauth-gateway/src/index.ts @@ -0,0 +1,86 @@ +import { serve } from '@hono/node-server' +import { Hono } from 'hono' + +import { gatewayConfig } from './config' +import { buildForwardLocation, resolveTargetHost, sanitizeExplicitHost, sanitizeTenantSlug } from './resolver' + +const app = new Hono() + +app.get('/healthz', (c) => + c.json({ + status: 'ok', + service: 'oauth-gateway', + timestamp: new Date().toISOString(), + }), +) + +const callbackRouter = new Hono() + +callbackRouter.all('/:provider', (c) => { + const provider = c.req.param('provider') + if (!provider) { + return c.json({ error: 'missing_provider', message: 'Provider param is required.' }, 400) + } + + const requestUrl = new URL(c.req.url) + const tenantSlugParam = requestUrl.searchParams.get('tenantSlug') ?? requestUrl.searchParams.get('tenant') + const explicitHostParam = requestUrl.searchParams.get('targetHost') + const tenantSlug = sanitizeTenantSlug(tenantSlugParam) + const explicitHost = sanitizeExplicitHost(explicitHostParam) + + requestUrl.searchParams.delete('tenant') + requestUrl.searchParams.delete('tenantSlug') + requestUrl.searchParams.delete('targetHost') + + if (tenantSlugParam && !tenantSlug) { + return c.json({ error: 'invalid_tenant', message: 'Tenant slug is invalid.' }, 400) + } + + if (explicitHostParam && !explicitHost) { + return c.json({ error: 'invalid_host', message: 'Target host is invalid.' }, 400) + } + + const targetHost = resolveTargetHost(gatewayConfig, { tenantSlug, explicitHost }) + if (!targetHost) { + return c.json({ error: 'unresolvable_host', message: 'Unable to resolve target tenant host.' }, 400) + } + + const location = buildForwardLocation({ + config: gatewayConfig, + provider, + host: targetHost, + query: requestUrl.searchParams, + }) + + return c.redirect(location, 302) +}) + +app.route(gatewayConfig.callbackBasePath, callbackRouter) + +app.notFound((c) => + c.json( + { + error: 'not_found', + path: c.req.path, + }, + 404, + ), +) + +app.onError((err, c) => { + console.error('[oauth-gateway] Unhandled error', err) + return c.json({ error: 'internal_error', message: 'OAuth gateway encountered an unexpected error.' }, 500) +}) + +serve( + { + fetch: app.fetch, + hostname: gatewayConfig.host, + port: gatewayConfig.port, + }, + (info) => { + console.info( + `[oauth-gateway] listening on http://${info.address}:${info.port} | forwarding to base domain ${gatewayConfig.baseDomain}`, + ) + }, +) diff --git a/be/apps/oauth-gateway/src/resolver.ts b/be/apps/oauth-gateway/src/resolver.ts new file mode 100644 index 00000000..cb69ad00 --- /dev/null +++ b/be/apps/oauth-gateway/src/resolver.ts @@ -0,0 +1,77 @@ +import type { GatewayConfig } from './config' + +export interface TargetResolutionInput { + tenantSlug?: string | null + explicitHost?: string | null +} + +const SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i +const HOST_PATTERN = /^[a-z0-9.-]+(?::\d{1,5})?$/i + +export function sanitizeTenantSlug(slug: string | null | undefined): string | null { + if (!slug) { + return null + } + const trimmed = slug.trim().toLowerCase() + if (!SLUG_PATTERN.test(trimmed)) { + return null + } + return trimmed +} + +export function sanitizeExplicitHost(host: string | null | undefined): string | null { + if (!host) { + return null + } + const normalized = host.trim().toLowerCase() + let value = normalized + if (normalized.startsWith('http://') || normalized.startsWith('https://')) { + try { + value = new URL(normalized).host + } catch { + return null + } + } + if (!HOST_PATTERN.test(value)) { + return null + } + return value +} + +export function resolveTargetHost(config: GatewayConfig, input: TargetResolutionInput): string | null { + if (input.explicitHost && config.allowCustomHost) { + return input.explicitHost + } + + const slug = input.tenantSlug + if (slug && slug !== config.rootSlug) { + return `${slug}.${config.baseDomain}` + } + + return config.baseDomain +} + +export function resolveProtocol(config: GatewayConfig, host: string): 'http' | 'https' { + if (!config.forceHttps) { + return host.includes('localhost') || host.startsWith('127.') || host.endsWith('.local') ? 'http' : 'https' + } + + if (host.includes('localhost') || host.startsWith('127.') || host.endsWith('.local')) { + return 'http' + } + + return 'https' +} + +export function buildForwardLocation(params: { + config: GatewayConfig + provider: string + host: string + query: URLSearchParams +}): string { + const basePath = `${params.config.callbackBasePath}/${params.provider}` + const queryString = params.query.toString() + const protocol = resolveProtocol(params.config, params.host) + const baseUrl = `${protocol}://${params.host}${basePath}` + return queryString ? `${baseUrl}?${queryString}` : baseUrl +} diff --git a/be/apps/oauth-gateway/tsconfig.json b/be/apps/oauth-gateway/tsconfig.json new file mode 100644 index 00000000..50098b54 --- /dev/null +++ b/be/apps/oauth-gateway/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "verbatimModuleSyntax": true, + "strict": true, + "types": ["node"], + "outDir": "./dist", + "rootDir": "./src", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "resolveJsonModule": true + }, + "include": ["./src/**/*.ts"] +} diff --git a/be/apps/oauth-gateway/vite.config.ts b/be/apps/oauth-gateway/vite.config.ts new file mode 100644 index 00000000..2fa01803 --- /dev/null +++ b/be/apps/oauth-gateway/vite.config.ts @@ -0,0 +1,28 @@ +import { builtinModules } from 'node:module' +import { dirname, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' + +import { defineConfig } from 'vite' + +const NODE_BUILT_INS = builtinModules.filter((m) => !m.startsWith('_')) +NODE_BUILT_INS.push(...NODE_BUILT_INS.map((m) => `node:${m}`)) + +const __dirname = dirname(fileURLToPath(import.meta.url)) + +export default defineConfig({ + ssr: { + noExternal: true, + }, + build: { + ssr: true, + rollupOptions: { + external: NODE_BUILT_INS, + input: { + main: resolve(__dirname, 'src/index.ts'), + }, + output: { + entryFileNames: 'main.js', + }, + }, + }, +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fd932d7b..e5cb4a98 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -200,7 +200,7 @@ importers: version: 12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0) next: specifier: 16.0.1 - version: 16.0.1(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) next-themes: specifier: 0.4.6 version: 0.4.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -430,7 +430,7 @@ importers: version: 0.31.6 next: specifier: 16.0.1 - version: 16.0.1(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) postcss: specifier: 8.5.6 version: 8.5.6 @@ -478,7 +478,7 @@ importers: version: 2.2.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@lobehub/fluent-emoji': specifier: 2.0.0 - version: 2.0.0(@babel/core@7.28.4)(@types/react@19.2.2)(acorn@8.15.0)(antd@5.26.2(luxon@3.7.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(framer-motion@12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 2.0.0(@babel/core@7.28.5)(@types/react@19.2.2)(acorn@8.15.0)(antd@5.26.2(luxon@3.7.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(framer-motion@12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@maplibre/maplibre-gl-geocoder': specifier: ^1.9.1 version: 1.9.1(maplibre-gl@5.10.0) @@ -541,7 +541,7 @@ importers: version: 10.2.0 jotai: specifier: 2.15.0 - version: 2.15.0(@babel/core@7.28.4)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0) + version: 2.15.0(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0) maplibre-gl: specifier: ^5.10.0 version: 5.10.0 @@ -589,7 +589,7 @@ importers: version: 7.9.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react-scan: specifier: 0.4.3 - version: 0.4.3(@types/react@19.2.2)(next@16.0.1(@babel/core@7.28.4)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react-router-dom@6.30.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-router@7.9.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)(rollup@2.79.2) + version: 0.4.3(@types/react@19.2.2)(next@16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react-router-dom@6.30.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-router@7.9.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)(rollup@2.79.2) react-use-measure: specifier: 2.1.7 version: 2.1.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -1066,6 +1066,37 @@ importers: specifier: 5.1.4 version: 5.1.4(typescript@5.9.3)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)) + be/apps/oauth-gateway: + dependencies: + '@afilmory/utils': + specifier: workspace:* + version: link:../../../packages/utils + '@hono/node-server': + specifier: ^1.13.5 + version: 1.19.6(hono@4.10.4) + hono: + specifier: ^4.6.12 + version: 4.10.4 + zod: + specifier: 'catalog:' + version: 4.1.12 + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.1 + nodemon: + specifier: 3.1.10 + version: 3.1.10 + typescript: + specifier: 'catalog:' + version: 5.9.3 + vite: + specifier: 7.1.12 + version: 7.1.12(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + vite-node: + specifier: 3.2.4 + version: 3.2.4(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + be/packages/db: dependencies: drizzle-orm: @@ -5803,6 +5834,9 @@ packages: '@types/node@20.19.24': resolution: {integrity: sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA==} + '@types/node@22.19.1': + resolution: {integrity: sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==} + '@types/node@24.10.0': resolution: {integrity: sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==} @@ -12740,9 +12774,9 @@ snapshots: regexpu-core: 6.4.0 semver: 6.3.1 - '@babel/helper-define-polyfill-provider@0.6.4(@babel/core@7.28.4)': + '@babel/helper-define-polyfill-provider@0.6.4(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 debug: 4.4.3(supports-color@5.5.0) @@ -13211,14 +13245,14 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-runtime@7.27.4(@babel/core@7.28.4)': + '@babel/plugin-transform-runtime@7.27.4(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - babel-plugin-polyfill-corejs2: 0.4.13(@babel/core@7.28.4) - babel-plugin-polyfill-corejs3: 0.11.1(@babel/core@7.28.4) - babel-plugin-polyfill-regenerator: 0.6.4(@babel/core@7.28.4) + babel-plugin-polyfill-corejs2: 0.4.13(@babel/core@7.28.5) + babel-plugin-polyfill-corejs3: 0.11.1(@babel/core@7.28.5) + babel-plugin-polyfill-regenerator: 0.6.4(@babel/core@7.28.5) semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -14485,10 +14519,10 @@ snapshots: '@lobehub/emojilib@1.0.0': {} - '@lobehub/fluent-emoji@2.0.0(@babel/core@7.28.4)(@types/react@19.2.2)(acorn@8.15.0)(antd@5.26.2(luxon@3.7.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(framer-motion@12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@lobehub/fluent-emoji@2.0.0(@babel/core@7.28.5)(@types/react@19.2.2)(acorn@8.15.0)(antd@5.26.2(luxon@3.7.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(framer-motion@12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@lobehub/emojilib': 1.0.0 - '@lobehub/ui': 2.7.3(@babel/core@7.28.4)(@types/react@19.2.2)(acorn@8.15.0)(antd@5.26.2(luxon@3.7.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(framer-motion@12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@lobehub/ui': 2.7.3(@babel/core@7.28.5)(@types/react@19.2.2)(acorn@8.15.0)(antd@5.26.2(luxon@3.7.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(framer-motion@12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0) antd: 5.26.2(luxon@3.7.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) antd-style: 3.7.1(@types/react@19.2.2)(antd@5.26.2(luxon@3.7.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0) emoji-regex: 10.4.0 @@ -14505,9 +14539,9 @@ snapshots: - framer-motion - supports-color - '@lobehub/icons@2.7.0(@babel/core@7.28.4)(@types/react@19.2.2)(acorn@8.15.0)(antd@5.26.2(luxon@3.7.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(framer-motion@12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@lobehub/icons@2.7.0(@babel/core@7.28.5)(@types/react@19.2.2)(acorn@8.15.0)(antd@5.26.2(luxon@3.7.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(framer-motion@12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@lobehub/ui': 2.7.3(@babel/core@7.28.4)(@types/react@19.2.2)(acorn@8.15.0)(antd@5.26.2(luxon@3.7.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(framer-motion@12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@lobehub/ui': 2.7.3(@babel/core@7.28.5)(@types/react@19.2.2)(acorn@8.15.0)(antd@5.26.2(luxon@3.7.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(framer-motion@12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0) antd: 5.26.2(luxon@3.7.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) antd-style: 3.7.1(@types/react@19.2.2)(antd@5.26.2(luxon@3.7.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0) lucide-react: 0.469.0(react@19.2.0) @@ -14522,7 +14556,7 @@ snapshots: - framer-motion - supports-color - '@lobehub/ui@2.7.3(@babel/core@7.28.4)(@types/react@19.2.2)(acorn@8.15.0)(antd@5.26.2(luxon@3.7.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(framer-motion@12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@lobehub/ui@2.7.3(@babel/core@7.28.5)(@types/react@19.2.2)(acorn@8.15.0)(antd@5.26.2(luxon@3.7.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(framer-motion@12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@ant-design/cssinjs': 1.23.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@dnd-kit/core': 6.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -14533,8 +14567,8 @@ snapshots: '@emoji-mart/react': 1.1.1(emoji-mart@5.6.0)(react@19.2.0) '@floating-ui/react': 0.27.12(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@giscus/react': 3.1.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@lobehub/fluent-emoji': 2.0.0(@babel/core@7.28.4)(@types/react@19.2.2)(acorn@8.15.0)(antd@5.26.2(luxon@3.7.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(framer-motion@12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@lobehub/icons': 2.7.0(@babel/core@7.28.4)(@types/react@19.2.2)(acorn@8.15.0)(antd@5.26.2(luxon@3.7.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(framer-motion@12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@lobehub/fluent-emoji': 2.0.0(@babel/core@7.28.5)(@types/react@19.2.2)(acorn@8.15.0)(antd@5.26.2(luxon@3.7.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(framer-motion@12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@lobehub/icons': 2.7.0(@babel/core@7.28.5)(@types/react@19.2.2)(acorn@8.15.0)(antd@5.26.2(luxon@3.7.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(framer-motion@12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@mdx-js/mdx': 3.1.0(acorn@8.15.0) '@mdx-js/react': 3.1.1(@types/react@19.2.2)(react@19.2.0) '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0) @@ -14564,7 +14598,7 @@ snapshots: rc-menu: 9.16.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0) re-resizable: 6.11.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: 19.2.0 - react-avatar-editor: 13.0.2(@babel/core@7.28.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react-avatar-editor: 13.0.2(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react-dom: 19.2.0(react@19.2.0) react-error-boundary: 5.0.0(react@19.2.0) react-hotkeys-hook: 5.1.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -17344,6 +17378,10 @@ snapshots: dependencies: undici-types: 6.21.0 + '@types/node@22.19.1': + dependencies: + undici-types: 6.21.0 + '@types/node@24.10.0': dependencies: undici-types: 7.16.0 @@ -18103,11 +18141,11 @@ snapshots: cosmiconfig: 7.1.0 resolve: 1.22.11 - babel-plugin-polyfill-corejs2@0.4.13(@babel/core@7.28.4): + babel-plugin-polyfill-corejs2@0.4.13(@babel/core@7.28.5): dependencies: '@babel/compat-data': 7.28.0 - '@babel/core': 7.28.4 - '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.28.4) + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.28.5) semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -18121,10 +18159,10 @@ snapshots: transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.11.1(@babel/core@7.28.4): + babel-plugin-polyfill-corejs3@0.11.1(@babel/core@7.28.5): dependencies: - '@babel/core': 7.28.4 - '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.28.4) + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.28.5) core-js-compat: 3.46.0 transitivePeerDependencies: - supports-color @@ -18137,10 +18175,10 @@ snapshots: transitivePeerDependencies: - supports-color - babel-plugin-polyfill-regenerator@0.6.4(@babel/core@7.28.4): + babel-plugin-polyfill-regenerator@0.6.4(@babel/core@7.28.5): dependencies: - '@babel/core': 7.28.4 - '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.28.4) + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.28.5) transitivePeerDependencies: - supports-color @@ -18184,7 +18222,7 @@ snapshots: nanostores: 1.0.1 zod: 4.1.12 optionalDependencies: - next: 16.0.1(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + next: 16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) @@ -21048,13 +21086,6 @@ snapshots: jose@6.1.0: {} - jotai@2.15.0(@babel/core@7.28.4)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0): - optionalDependencies: - '@babel/core': 7.28.4 - '@babel/template': 7.27.2 - '@types/react': 19.2.2 - react: 19.2.0 - jotai@2.15.0(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0): optionalDependencies: '@babel/core': 7.28.5 @@ -22101,32 +22132,7 @@ snapshots: react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - next@16.0.1(@babel/core@7.28.4)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): - dependencies: - '@next/env': 16.0.1 - '@swc/helpers': 0.5.15 - caniuse-lite: 1.0.30001752 - postcss: 8.4.31 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - styled-jsx: 5.1.6(@babel/core@7.28.4)(react@19.2.0) - optionalDependencies: - '@next/swc-darwin-arm64': 16.0.1 - '@next/swc-darwin-x64': 16.0.1 - '@next/swc-linux-arm64-gnu': 16.0.1 - '@next/swc-linux-arm64-musl': 16.0.1 - '@next/swc-linux-x64-gnu': 16.0.1 - '@next/swc-linux-x64-musl': 16.0.1 - '@next/swc-win32-arm64-msvc': 16.0.1 - '@next/swc-win32-x64-msvc': 16.0.1 - babel-plugin-react-compiler: 19.1.0-rc.3 - sharp: 0.34.4 - transitivePeerDependencies: - - '@babel/core' - - babel-plugin-macros - optional: true - - next@16.0.1(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + next@16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: '@next/env': 16.0.1 '@swc/helpers': 0.5.15 @@ -22144,6 +22150,7 @@ snapshots: '@next/swc-linux-x64-musl': 16.0.1 '@next/swc-win32-arm64-msvc': 16.0.1 '@next/swc-win32-x64-msvc': 16.0.1 + babel-plugin-react-compiler: 19.1.0-rc.3 sharp: 0.34.4 transitivePeerDependencies: - '@babel/core' @@ -23063,9 +23070,9 @@ snapshots: react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - react-avatar-editor@13.0.2(@babel/core@7.28.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + react-avatar-editor@13.0.2(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: - '@babel/plugin-transform-runtime': 7.27.4(@babel/core@7.28.4) + '@babel/plugin-transform-runtime': 7.27.4(@babel/core@7.28.5) '@babel/runtime': 7.28.4 prop-types: 15.8.1 react: 19.2.0 @@ -23246,7 +23253,7 @@ snapshots: optionalDependencies: react-dom: 19.2.0(react@19.2.0) - react-scan@0.4.3(@types/react@19.2.2)(next@16.0.1(@babel/core@7.28.4)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react-router-dom@6.30.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-router@7.9.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)(rollup@2.79.2): + react-scan@0.4.3(@types/react@19.2.2)(next@16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react-router-dom@6.30.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-router@7.9.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)(rollup@2.79.2): dependencies: '@babel/core': 7.28.4 '@babel/generator': 7.28.3 @@ -23268,7 +23275,7 @@ snapshots: react-dom: 19.2.0(react@19.2.0) tsx: 4.20.6 optionalDependencies: - next: 16.0.1(@babel/core@7.28.4)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + next: 16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react-router: 7.9.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react-router-dom: 6.30.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0) unplugin: 2.1.0 @@ -23299,7 +23306,7 @@ snapshots: react-dom: 19.2.0(react@19.2.0) tsx: 4.20.6 optionalDependencies: - next: 16.0.1(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + next: 16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react-router: 7.9.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react-router-dom: 6.30.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0) unplugin: 2.1.0 @@ -24164,14 +24171,6 @@ snapshots: dependencies: inline-style-parser: 0.2.4 - styled-jsx@5.1.6(@babel/core@7.28.4)(react@19.2.0): - dependencies: - client-only: 0.0.1 - react: 19.2.0 - optionalDependencies: - '@babel/core': 7.28.4 - optional: true - styled-jsx@5.1.6(@babel/core@7.28.5)(react@19.2.0): dependencies: client-only: 0.0.1 @@ -24813,6 +24812,27 @@ snapshots: vite-bundle-analyzer@1.2.3: {} + vite-node@3.2.4(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1): + dependencies: + cac: 6.7.14 + debug: 4.4.3(supports-color@5.5.0) + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 7.1.12(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + vite-node@3.2.4(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1): dependencies: cac: 6.7.14 @@ -24896,6 +24916,23 @@ snapshots: - supports-color - typescript + vite@7.1.12(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1): + dependencies: + esbuild: 0.25.11 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.52.5 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 22.19.1 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.30.2 + terser: 5.44.1 + tsx: 4.20.6 + yaml: 2.8.1 + vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1): dependencies: esbuild: 0.25.11