mirror of
https://github.com/Afilmory/afilmory
synced 2026-02-01 22:48:17 +00:00
feat(static-web): implement static web module with controller and service for asset handling
Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
40
be/apps/core/Dockerfile
Normal file
40
be/apps/core/Dockerfile
Normal file
@@ -0,0 +1,40 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
|
||||
ARG NODE_VERSION=22.11.0
|
||||
|
||||
FROM node:${NODE_VERSION}-slim AS builder
|
||||
ENV PNPM_HOME=/pnpm
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
RUN corepack enable && corepack prepare pnpm@10.19.0 --activate
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./
|
||||
COPY be/apps/core/package.json be/apps/core/package.json
|
||||
COPY apps/web/package.json apps/web/package.json
|
||||
|
||||
RUN pnpm fetch --filter core...
|
||||
RUN pnpm fetch --filter '@afilmory/web...'
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN pnpm install --filter core... --filter '@afilmory/web...' --frozen-lockfile
|
||||
RUN pnpm --filter '@afilmory/web' build
|
||||
RUN pnpm --filter core build
|
||||
RUN mkdir -p be/apps/core/dist/static/web && cp -r apps/web/dist/. be/apps/core/dist/static/web/
|
||||
|
||||
FROM node:${NODE_VERSION}-slim AS runner
|
||||
ENV NODE_ENV=production
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=builder /workspace/be/apps/core/dist ./dist
|
||||
COPY --from=builder /workspace/be/apps/core/drizzle ./drizzle
|
||||
|
||||
RUN if [ -f dist/package.json ]; then \
|
||||
cd dist && \
|
||||
npm install --omit=dev --no-audit --no-fund; \
|
||||
fi
|
||||
|
||||
EXPOSE 1841
|
||||
|
||||
CMD ["node", "./dist/main.js"]
|
||||
@@ -28,6 +28,8 @@
|
||||
"better-auth": "1.3.34",
|
||||
"drizzle-orm": "^0.44.7",
|
||||
"hono": "4.10.4",
|
||||
"linkedom": "0.18.12",
|
||||
"mime-types": "2.1.35",
|
||||
"pg": "^8.16.3",
|
||||
"picocolors": "1.1.1",
|
||||
"reflect-metadata": "0.2.2",
|
||||
@@ -35,6 +37,7 @@
|
||||
"zod": "^4.1.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mime-types": "2.1.4",
|
||||
"@types/node": "^24.9.2",
|
||||
"@types/pg": "8.15.6",
|
||||
"nodemon": "3.1.10",
|
||||
|
||||
@@ -14,6 +14,7 @@ import { OnboardingModule } from './onboarding/onboarding.module'
|
||||
import { PhotoModule } from './photo/photo.module'
|
||||
import { ReactionModule } from './reaction/reaction.module'
|
||||
import { SettingModule } from './setting/setting.module'
|
||||
import { StaticWebModule } from './static-web/static-web.module'
|
||||
import { SuperAdminModule } from './super-admin/super-admin.module'
|
||||
import { SystemSettingModule } from './system-setting/system-setting.module'
|
||||
import { TenantModule } from './tenant/tenant.module'
|
||||
@@ -38,6 +39,7 @@ function createEventModuleOptions(redis: RedisAccessor) {
|
||||
DashboardModule,
|
||||
TenantModule,
|
||||
DataSyncModule,
|
||||
StaticWebModule,
|
||||
EventModule.forRootAsync({
|
||||
useFactory: createEventModuleOptions,
|
||||
inject: [RedisAccessor],
|
||||
|
||||
21
be/apps/core/src/modules/static-web/static-web.controller.ts
Normal file
21
be/apps/core/src/modules/static-web/static-web.controller.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { ContextParam, Controller, Get, Head } from '@afilmory/framework'
|
||||
import type { Context } from 'hono'
|
||||
|
||||
import { StaticWebService } from './static-web.service'
|
||||
|
||||
@Controller('static/web')
|
||||
export class StaticWebController {
|
||||
constructor(private readonly staticWebService: StaticWebService) {}
|
||||
|
||||
@Get('/*')
|
||||
async getAsset(@ContextParam() context: Context) {
|
||||
const response = await this.staticWebService.handleRequest(context.req.path, false)
|
||||
return response ?? new Response('Not Found', { status: 404 })
|
||||
}
|
||||
|
||||
@Head('/*')
|
||||
async headAsset(@ContextParam() context: Context) {
|
||||
const response = await this.staticWebService.handleRequest(context.req.path, true)
|
||||
return response ?? new Response(null, { status: 404 })
|
||||
}
|
||||
}
|
||||
10
be/apps/core/src/modules/static-web/static-web.module.ts
Normal file
10
be/apps/core/src/modules/static-web/static-web.module.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@afilmory/framework'
|
||||
|
||||
import { StaticWebController } from './static-web.controller'
|
||||
import { StaticWebService } from './static-web.service'
|
||||
|
||||
@Module({
|
||||
controllers: [StaticWebController],
|
||||
providers: [StaticWebService],
|
||||
})
|
||||
export class StaticWebModule {}
|
||||
301
be/apps/core/src/modules/static-web/static-web.service.ts
Normal file
301
be/apps/core/src/modules/static-web/static-web.service.ts
Normal file
@@ -0,0 +1,301 @@
|
||||
import type { Stats } from 'node:fs'
|
||||
import { createReadStream } from 'node:fs'
|
||||
import { readFile, stat } from 'node:fs/promises'
|
||||
import { extname, isAbsolute, join, normalize, relative, resolve } from 'node:path'
|
||||
import { Readable } from 'node:stream'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
import { createLogger } from '@afilmory/framework'
|
||||
import { DOMParser } from 'linkedom'
|
||||
import { lookup as lookupMimeType } from 'mime-types'
|
||||
import { injectable } from 'tsyringe'
|
||||
|
||||
const STATIC_ROOT_ENV = process.env.STATIC_WEB_ROOT?.trim()
|
||||
|
||||
const MODULE_DIR = fileURLToPath(new URL('.', import.meta.url))
|
||||
|
||||
const STATIC_ROOT_CANDIDATES = Array.from(
|
||||
new Set(
|
||||
[
|
||||
STATIC_ROOT_ENV,
|
||||
resolve(MODULE_DIR, '../../static/web'),
|
||||
resolve(process.cwd(), 'dist/static/web'),
|
||||
resolve(process.cwd(), '../dist/static/web'),
|
||||
resolve(process.cwd(), '../../dist/static/web'),
|
||||
resolve(process.cwd(), '../../../dist/static/web'),
|
||||
resolve(process.cwd(), 'static/web'),
|
||||
resolve(process.cwd(), '../static/web'),
|
||||
resolve(process.cwd(), '../../static/web'),
|
||||
resolve(process.cwd(), '../../../static/web'),
|
||||
resolve(process.cwd(), 'apps/web/dist'),
|
||||
resolve(process.cwd(), '../apps/web/dist'),
|
||||
resolve(process.cwd(), '../../apps/web/dist'),
|
||||
resolve(process.cwd(), '../../../apps/web/dist'),
|
||||
].filter((entry): entry is string => typeof entry === 'string' && entry.length > 0),
|
||||
),
|
||||
)
|
||||
|
||||
const STATIC_ROUTE_SEGMENT = '/static/web'
|
||||
|
||||
interface ResolvedFile {
|
||||
absolutePath: string
|
||||
relativePath: string
|
||||
stats: Stats
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class StaticWebService {
|
||||
private staticRoot: string | null | undefined
|
||||
private warnedMissingRoot = false
|
||||
private readonly logger = createLogger('StaticWebService')
|
||||
|
||||
async handleRequest(fullPath: string, headOnly: boolean): Promise<Response | null> {
|
||||
const staticRoot = await this.resolveStaticRoot()
|
||||
if (!staticRoot) {
|
||||
return null
|
||||
}
|
||||
|
||||
const relativeRequestPath = this.extractRelativePath(fullPath)
|
||||
const target = await this.resolveFile(relativeRequestPath, staticRoot)
|
||||
if (!target) {
|
||||
return null
|
||||
}
|
||||
|
||||
return await this.createResponse(target, headOnly)
|
||||
}
|
||||
|
||||
private async resolveStaticRoot(): Promise<string | null> {
|
||||
if (this.staticRoot !== undefined) {
|
||||
return this.staticRoot
|
||||
}
|
||||
|
||||
for (const candidate of STATIC_ROOT_CANDIDATES) {
|
||||
try {
|
||||
const stats = await stat(candidate)
|
||||
if (stats.isDirectory()) {
|
||||
this.staticRoot = candidate
|
||||
this.logger.info(`Using static assets root: ${candidate}`)
|
||||
return candidate
|
||||
}
|
||||
} catch {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
this.staticRoot = null
|
||||
if (!this.warnedMissingRoot) {
|
||||
this.warnedMissingRoot = true
|
||||
this.logger.warn('No static web root found; static route will return 404')
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private extractRelativePath(fullPath: string): string {
|
||||
const index = fullPath.indexOf(STATIC_ROUTE_SEGMENT)
|
||||
if (index === -1) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const sliceStart = index + STATIC_ROUTE_SEGMENT.length
|
||||
const remainder = sliceStart < fullPath.length ? fullPath.slice(sliceStart) : ''
|
||||
return this.stripLeadingSlashes(remainder)
|
||||
}
|
||||
|
||||
private stripLeadingSlashes(pathname: string): string {
|
||||
let result = pathname
|
||||
while (result.startsWith('/')) {
|
||||
result = result.slice(1)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private async resolveFile(requestPath: string, root: string): Promise<ResolvedFile | null> {
|
||||
const decoded = this.decodePath(requestPath)
|
||||
const normalized = this.normalizePath(decoded)
|
||||
const candidates = this.buildCandidatePaths(normalized)
|
||||
|
||||
for (const candidate of candidates) {
|
||||
const resolved = await this.tryResolveFile(root, candidate)
|
||||
if (resolved) {
|
||||
return resolved
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private decodePath(pathname: string): string {
|
||||
if (pathname.length === 0) {
|
||||
return pathname
|
||||
}
|
||||
|
||||
try {
|
||||
return decodeURIComponent(pathname)
|
||||
} catch {
|
||||
return pathname
|
||||
}
|
||||
}
|
||||
|
||||
private normalizePath(pathname: string): string {
|
||||
if (pathname.length === 0) {
|
||||
return 'index.html'
|
||||
}
|
||||
|
||||
const withoutLeadingSlash = this.stripLeadingSlashes(pathname)
|
||||
if (withoutLeadingSlash.length === 0) {
|
||||
return 'index.html'
|
||||
}
|
||||
|
||||
return withoutLeadingSlash
|
||||
}
|
||||
|
||||
private buildCandidatePaths(normalizedPath: string): string[] {
|
||||
const candidates = new Set<string>()
|
||||
|
||||
const sanitized = this.removeLeadingDotSlash(normalize(normalizedPath))
|
||||
candidates.add(sanitized)
|
||||
|
||||
if (sanitized.endsWith('/')) {
|
||||
candidates.add(join(sanitized, 'index.html'))
|
||||
}
|
||||
|
||||
if (!this.hasFileExtension(sanitized)) {
|
||||
candidates.add('index.html')
|
||||
}
|
||||
|
||||
return Array.from(candidates)
|
||||
}
|
||||
|
||||
private removeLeadingDotSlash(pathname: string): string {
|
||||
let result = pathname
|
||||
while (result.startsWith('./')) {
|
||||
result = result.slice(2)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private hasFileExtension(pathname: string): boolean {
|
||||
return extname(pathname) !== ''
|
||||
}
|
||||
|
||||
private async tryResolveFile(root: string, relativePath: string): Promise<ResolvedFile | null> {
|
||||
const safePath = relativePath.startsWith('/') ? relativePath.slice(1) : relativePath
|
||||
const absolutePath = resolve(root, safePath)
|
||||
|
||||
if (!this.ensureWithinRoot(root, absolutePath)) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
const stats = await stat(absolutePath)
|
||||
if (!stats.isFile()) {
|
||||
return null
|
||||
}
|
||||
|
||||
return { absolutePath, relativePath: safePath, stats }
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private ensureWithinRoot(root: string, filePath: string): boolean {
|
||||
const relativePath = relative(root, filePath)
|
||||
return relativePath !== '' && !relativePath.startsWith('..') && !isAbsolute(relativePath)
|
||||
}
|
||||
|
||||
private async createResponse(file: ResolvedFile, headOnly: boolean): Promise<Response> {
|
||||
if (file.relativePath === 'index.html') {
|
||||
return await this.createIndexResponse(file, headOnly)
|
||||
}
|
||||
|
||||
const mimeType = lookupMimeType(file.absolutePath) || 'application/octet-stream'
|
||||
const headers = new Headers()
|
||||
headers.set('content-type', mimeType)
|
||||
headers.set('content-length', `${file.stats.size}`)
|
||||
headers.set('last-modified', file.stats.mtime.toUTCString())
|
||||
|
||||
this.applyCacheHeaders(headers, file.relativePath)
|
||||
|
||||
if (headOnly) {
|
||||
return new Response(null, { headers, status: 200 })
|
||||
}
|
||||
|
||||
const nodeStream = createReadStream(file.absolutePath)
|
||||
const body = Readable.toWeb(nodeStream) as unknown as ReadableStream
|
||||
return new Response(body, { headers, status: 200 })
|
||||
}
|
||||
|
||||
private async createIndexResponse(file: ResolvedFile, headOnly: boolean): Promise<Response> {
|
||||
const html = await readFile(file.absolutePath, 'utf-8')
|
||||
const transformed = this.transformIndexHtml(html)
|
||||
const headers = new Headers()
|
||||
headers.set('content-type', 'text/html; charset=utf-8')
|
||||
headers.set('content-length', `${Buffer.byteLength(transformed, 'utf-8')}`)
|
||||
headers.set('last-modified', file.stats.mtime.toUTCString())
|
||||
this.applyCacheHeaders(headers, file.relativePath)
|
||||
|
||||
if (headOnly) {
|
||||
return new Response(null, { headers, status: 200 })
|
||||
}
|
||||
|
||||
return new Response(transformed, { headers, status: 200 })
|
||||
}
|
||||
|
||||
private transformIndexHtml(html: string): string {
|
||||
try {
|
||||
const document = new DOMParser().parseFromString(html, 'text/html')
|
||||
const configScript = document.head?.querySelector('#config')
|
||||
if (configScript) {
|
||||
const payload = JSON.stringify({
|
||||
useCloud: true,
|
||||
})
|
||||
configScript.textContent = `window.__CONFIG__ = ${payload}`
|
||||
}
|
||||
return document.documentElement.outerHTML
|
||||
} catch (error) {
|
||||
this.logger.warn('Failed to transform index.html for static web response', error)
|
||||
return html
|
||||
}
|
||||
}
|
||||
|
||||
private shouldTreatAsImmutable(relativePath: string): boolean {
|
||||
if (this.isHtml(relativePath)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return this.hasFileExtension(relativePath)
|
||||
}
|
||||
|
||||
private applyCacheHeaders(headers: Headers, relativePath: string): void {
|
||||
const policy = this.resolveCachePolicy(relativePath)
|
||||
headers.set('cache-control', policy.browser)
|
||||
headers.set('cdn-cache-control', policy.cdn)
|
||||
headers.set('surrogate-control', policy.cdn)
|
||||
}
|
||||
|
||||
private resolveCachePolicy(relativePath: string): { browser: string; cdn: string } {
|
||||
if (this.isHtml(relativePath)) {
|
||||
return {
|
||||
browser: 'no-cache',
|
||||
cdn: 'no-cache',
|
||||
}
|
||||
}
|
||||
|
||||
if (this.shouldTreatAsImmutable(relativePath)) {
|
||||
return {
|
||||
browser: 'public, max-age=31536000, immutable',
|
||||
cdn: 'public, max-age=31536000, immutable',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
browser: 'public, max-age=3600, must-revalidate',
|
||||
cdn: 'public, max-age=86400, stale-while-revalidate=600',
|
||||
}
|
||||
}
|
||||
|
||||
private isHtml(relativePath: string): boolean {
|
||||
return relativePath === 'index.html' || relativePath.endsWith('.html')
|
||||
}
|
||||
}
|
||||
112
pnpm-lock.yaml
generated
112
pnpm-lock.yaml
generated
@@ -603,13 +603,19 @@ importers:
|
||||
version: 1.19.6(hono@4.10.4)
|
||||
better-auth:
|
||||
specifier: 1.3.34
|
||||
version: 1.3.34(next@16.0.1(@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@19.2.0)
|
||||
version: 1.3.34(next@16.0.1(@babel/core@7.28.4)(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)
|
||||
drizzle-orm:
|
||||
specifier: ^0.44.7
|
||||
version: 0.44.7(@types/pg@8.15.6)(@vercel/postgres@0.10.0)(kysely@0.28.8)(pg@8.16.3)(postgres@3.4.7)
|
||||
hono:
|
||||
specifier: 4.10.4
|
||||
version: 4.10.4
|
||||
linkedom:
|
||||
specifier: 0.18.12
|
||||
version: 0.18.12
|
||||
mime-types:
|
||||
specifier: 2.1.35
|
||||
version: 2.1.35
|
||||
pg:
|
||||
specifier: ^8.16.3
|
||||
version: 8.16.3
|
||||
@@ -626,6 +632,9 @@ importers:
|
||||
specifier: ^4.1.11
|
||||
version: 4.1.12
|
||||
devDependencies:
|
||||
'@types/mime-types':
|
||||
specifier: 2.1.4
|
||||
version: 2.1.4
|
||||
'@types/node':
|
||||
specifier: ^24.9.2
|
||||
version: 24.9.2
|
||||
@@ -715,7 +724,7 @@ importers:
|
||||
version: 5.90.5(react@19.2.0)
|
||||
better-auth:
|
||||
specifier: 1.3.34
|
||||
version: 1.3.34(next@16.0.1(@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@19.2.0)
|
||||
version: 1.3.34(next@16.0.1(@babel/core@7.28.4)(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)
|
||||
class-variance-authority:
|
||||
specifier: 0.7.1
|
||||
version: 0.7.1
|
||||
@@ -733,7 +742,7 @@ importers:
|
||||
version: 10.2.0
|
||||
jotai:
|
||||
specifier: 2.15.0
|
||||
version: 2.15.0(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0)
|
||||
version: 2.15.0(@babel/core@7.28.4)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0)
|
||||
lucide-react:
|
||||
specifier: 0.552.0
|
||||
version: 0.552.0(react@19.2.0)
|
||||
@@ -760,7 +769,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.5)(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@4.52.5)
|
||||
version: 0.4.3(@types/react@19.2.2)(next@16.0.1(@babel/core@7.28.4)(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@4.52.5)
|
||||
sonner:
|
||||
specifier: 2.0.7
|
||||
version: 2.0.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
@@ -1332,7 +1341,7 @@ importers:
|
||||
version: 0.15.12(typescript@5.9.3)
|
||||
unplugin-dts:
|
||||
specifier: 1.0.0-beta.6
|
||||
version: 1.0.0-beta.6(@microsoft/api-extractor@7.52.13(@types/node@24.9.2))(esbuild@0.25.11)(rollup@4.52.5)(typescript@5.9.3)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
version: 1.0.0-beta.6(@microsoft/api-extractor@7.52.13(@types/node@24.9.2))(esbuild@0.25.11)(rolldown@1.0.0-beta.45)(rollup@4.52.5)(typescript@5.9.3)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
vite:
|
||||
specifier: 7.1.12
|
||||
version: 7.1.12(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
@@ -5378,15 +5387,15 @@ packages:
|
||||
'@types/mdx@2.0.13':
|
||||
resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==}
|
||||
|
||||
'@types/mime-types@2.1.4':
|
||||
resolution: {integrity: sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==}
|
||||
|
||||
'@types/ms@2.1.0':
|
||||
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
|
||||
|
||||
'@types/node@20.19.24':
|
||||
resolution: {integrity: sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA==}
|
||||
|
||||
'@types/node@24.9.1':
|
||||
resolution: {integrity: sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==}
|
||||
|
||||
'@types/node@24.9.2':
|
||||
resolution: {integrity: sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==}
|
||||
|
||||
@@ -6952,10 +6961,6 @@ packages:
|
||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
entities@6.0.0:
|
||||
resolution: {integrity: sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
entities@6.0.1:
|
||||
resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
|
||||
engines: {node: '>=0.12'}
|
||||
@@ -8716,6 +8721,14 @@ packages:
|
||||
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
||||
engines: {node: '>=8.6'}
|
||||
|
||||
mime-db@1.52.0:
|
||||
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
mime-types@2.1.35:
|
||||
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
mimic-function@5.0.1:
|
||||
resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -16487,17 +16500,14 @@ snapshots:
|
||||
|
||||
'@types/mdx@2.0.13': {}
|
||||
|
||||
'@types/mime-types@2.1.4': {}
|
||||
|
||||
'@types/ms@2.1.0': {}
|
||||
|
||||
'@types/node@20.19.24':
|
||||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
|
||||
'@types/node@24.9.1':
|
||||
dependencies:
|
||||
undici-types: 7.16.0
|
||||
optional: true
|
||||
|
||||
'@types/node@24.9.2':
|
||||
dependencies:
|
||||
undici-types: 7.16.0
|
||||
@@ -16515,7 +16525,7 @@ snapshots:
|
||||
|
||||
'@types/pg@8.11.6':
|
||||
dependencies:
|
||||
'@types/node': 24.9.1
|
||||
'@types/node': 24.9.2
|
||||
pg-protocol: 1.10.3
|
||||
pg-types: 4.1.0
|
||||
optional: true
|
||||
@@ -17315,7 +17325,7 @@ snapshots:
|
||||
|
||||
batch-cluster@15.0.1: {}
|
||||
|
||||
better-auth@1.3.34(next@16.0.1(@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@19.2.0):
|
||||
better-auth@1.3.34(next@16.0.1(@babel/core@7.28.4)(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:
|
||||
'@better-auth/core': 1.3.34(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.0.19)(jose@6.1.0)(kysely@0.28.8)(nanostores@1.0.1)
|
||||
'@better-auth/telemetry': 1.3.34(better-call@1.0.19)(jose@6.1.0)(kysely@0.28.8)(nanostores@1.0.1)
|
||||
@@ -17332,7 +17342,7 @@ snapshots:
|
||||
nanostores: 1.0.1
|
||||
zod: 4.1.12
|
||||
optionalDependencies:
|
||||
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)
|
||||
next: 16.0.1(@babel/core@7.28.4)(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)
|
||||
|
||||
@@ -18164,8 +18174,6 @@ snapshots:
|
||||
|
||||
entities@4.5.0: {}
|
||||
|
||||
entities@6.0.0: {}
|
||||
|
||||
entities@6.0.1: {}
|
||||
|
||||
environment@1.1.0: {}
|
||||
@@ -19768,6 +19776,13 @@ 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
|
||||
@@ -20698,6 +20713,12 @@ snapshots:
|
||||
braces: 3.0.3
|
||||
picomatch: 2.3.1
|
||||
|
||||
mime-db@1.52.0: {}
|
||||
|
||||
mime-types@2.1.35:
|
||||
dependencies:
|
||||
mime-db: 1.52.0
|
||||
|
||||
mimic-function@5.0.1: {}
|
||||
|
||||
minimatch@10.0.3:
|
||||
@@ -20794,6 +20815,30 @@ snapshots:
|
||||
react: 19.2.0
|
||||
react-dom: 19.2.0(react@19.2.0)
|
||||
|
||||
next@16.0.1(@babel/core@7.28.4)(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
|
||||
sharp: 0.34.4
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- babel-plugin-macros
|
||||
optional: true
|
||||
|
||||
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
|
||||
@@ -21005,7 +21050,7 @@ snapshots:
|
||||
|
||||
parse5@7.3.0:
|
||||
dependencies:
|
||||
entities: 6.0.0
|
||||
entities: 6.0.1
|
||||
|
||||
pascal-case@3.1.2:
|
||||
dependencies:
|
||||
@@ -21904,7 +21949,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.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-scan@0.4.3(@types/react@19.2.2)(next@16.0.1(@babel/core@7.28.4)(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@4.52.5):
|
||||
dependencies:
|
||||
'@babel/core': 7.28.4
|
||||
'@babel/generator': 7.28.3
|
||||
@@ -21913,7 +21958,7 @@ snapshots:
|
||||
'@clack/prompts': 0.8.2
|
||||
'@pivanov/utils': 0.0.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@preact/signals': 1.3.2(preact@10.27.2)
|
||||
'@rollup/pluginutils': 5.3.0(rollup@2.79.2)
|
||||
'@rollup/pluginutils': 5.3.0(rollup@4.52.5)
|
||||
'@types/node': 20.19.24
|
||||
bippy: 0.3.27(@types/react@19.2.2)(react@19.2.0)
|
||||
esbuild: 0.25.11
|
||||
@@ -21926,7 +21971,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)(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.4)(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
|
||||
@@ -21935,7 +21980,7 @@ snapshots:
|
||||
- rollup
|
||||
- supports-color
|
||||
|
||||
react-scan@0.4.3(@types/react@19.2.2)(next@16.0.1(@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-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@4.52.5):
|
||||
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
|
||||
@@ -21944,7 +21989,7 @@ snapshots:
|
||||
'@clack/prompts': 0.8.2
|
||||
'@pivanov/utils': 0.0.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@preact/signals': 1.3.2(preact@10.27.2)
|
||||
'@rollup/pluginutils': 5.3.0(rollup@4.52.5)
|
||||
'@rollup/pluginutils': 5.3.0(rollup@2.79.2)
|
||||
'@types/node': 20.19.24
|
||||
bippy: 0.3.27(@types/react@19.2.2)(react@19.2.0)
|
||||
esbuild: 0.25.11
|
||||
@@ -22803,6 +22848,14 @@ 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
|
||||
@@ -23232,7 +23285,7 @@ snapshots:
|
||||
magic-string-ast: 1.0.3
|
||||
unplugin: 2.3.10
|
||||
|
||||
unplugin-dts@1.0.0-beta.6(@microsoft/api-extractor@7.52.13(@types/node@24.9.2))(esbuild@0.25.11)(rollup@4.52.5)(typescript@5.9.3)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)):
|
||||
unplugin-dts@1.0.0-beta.6(@microsoft/api-extractor@7.52.13(@types/node@24.9.2))(esbuild@0.25.11)(rolldown@1.0.0-beta.45)(rollup@4.52.5)(typescript@5.9.3)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)):
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 5.3.0(rollup@4.52.5)
|
||||
'@volar/typescript': 2.4.23
|
||||
@@ -23246,6 +23299,7 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@microsoft/api-extractor': 7.52.13(@types/node@24.9.2)
|
||||
esbuild: 0.25.11
|
||||
rolldown: 1.0.0-beta.45
|
||||
rollup: 4.52.5
|
||||
vite: 7.1.12(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
transitivePeerDependencies:
|
||||
|
||||
Reference in New Issue
Block a user