refactor: remove obsolete config and cross-layer exports
Drop the temporary static-generation retry override and stale hydration suppression. Refresh outdated Next.js and migration comments, and stop re-exporting Result through the storage layer.
This commit is contained in:
+2
-43
@@ -2,21 +2,9 @@ import { withSentryConfig } from "@sentry/nextjs";
|
|||||||
import type { NextConfig } from "next";
|
import type { NextConfig } from "next";
|
||||||
import { withSerwist } from "@serwist/turbopack";
|
import { withSerwist } from "@serwist/turbopack";
|
||||||
|
|
||||||
/**
|
|
||||||
* Next.js 16 临时绕过 `/_global-error` prerender 的 workStore bug
|
|
||||||
* 标记根 layout 不参与静态预渲染(global-error 会在请求时渲染)
|
|
||||||
* https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config
|
|
||||||
*
|
|
||||||
* 容器化部署模型:`output: "standalone"` + Docker Compose。
|
|
||||||
* - `pnpm build` 产出 .next/standalone、.next/static、public。
|
|
||||||
* - Dockerfile 运行 `node server.js`,仅复制 standalone traced runtime 依赖。
|
|
||||||
*/
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
|
// Docker runs the traced runtime emitted to `.next/standalone`.
|
||||||
output: "standalone",
|
output: "standalone",
|
||||||
experimental: {
|
|
||||||
// 关闭静态优化以规避 16.2.7 global-error prerender bug
|
|
||||||
staticGenerationRetryCount: 0,
|
|
||||||
},
|
|
||||||
images: {
|
images: {
|
||||||
remotePatterns: [
|
remotePatterns: [
|
||||||
{
|
{
|
||||||
@@ -44,47 +32,18 @@ const shouldUploadSentrySourcemaps =
|
|||||||
process.env.CI !== "true" || process.env.SENTRY_UPLOAD_SOURCEMAPS === "1";
|
process.env.CI !== "true" || process.env.SENTRY_UPLOAD_SOURCEMAPS === "1";
|
||||||
|
|
||||||
export default withSentryConfig(withSerwist(nextConfig), {
|
export default withSentryConfig(withSerwist(nextConfig), {
|
||||||
// For all available options, see:
|
|
||||||
// https://www.npmjs.com/package/@sentry/webpack-plugin#options
|
|
||||||
|
|
||||||
org: "shenzhen-banlv-zhidao-technolo",
|
org: "shenzhen-banlv-zhidao-technolo",
|
||||||
|
|
||||||
project: "javascript-nextjs",
|
project: "javascript-nextjs",
|
||||||
|
|
||||||
// Only print logs for uploading source maps in CI
|
|
||||||
silent: !process.env.CI,
|
silent: !process.env.CI,
|
||||||
|
|
||||||
// Keep build metadata local; application errors/performance are configured separately.
|
|
||||||
telemetry: false,
|
telemetry: false,
|
||||||
|
|
||||||
// For all available options, see:
|
|
||||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
|
|
||||||
|
|
||||||
// Upload a larger set of source maps for prettier stack traces (increases build time)
|
|
||||||
widenClientFileUpload: true,
|
widenClientFileUpload: true,
|
||||||
|
// Validation builds keep source maps local; release jobs opt in to upload.
|
||||||
// PR/branch CI should validate builds without uploading source maps. Release jobs can
|
|
||||||
// set SENTRY_UPLOAD_SOURCEMAPS=1 to enable uploads explicitly.
|
|
||||||
sourcemaps: {
|
sourcemaps: {
|
||||||
disable: !shouldUploadSentrySourcemaps,
|
disable: !shouldUploadSentrySourcemaps,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Uncomment to route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.
|
|
||||||
// This can increase your server load as well as your hosting bill.
|
|
||||||
// Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
|
|
||||||
// side errors will fail.
|
|
||||||
// tunnelRoute: "/monitoring",
|
|
||||||
|
|
||||||
webpack: {
|
webpack: {
|
||||||
// Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.)
|
|
||||||
// See the following for more information:
|
|
||||||
// https://docs.sentry.io/product/crons/
|
|
||||||
// https://vercel.com/docs/cron-jobs
|
|
||||||
automaticVercelMonitors: true,
|
automaticVercelMonitors: true,
|
||||||
|
|
||||||
// Tree-shaking options for reducing bundle size
|
|
||||||
treeshake: {
|
treeshake: {
|
||||||
// Automatically tree-shake Sentry logger statements to reduce bundle size
|
|
||||||
removeDebugLogging: true,
|
removeDebugLogging: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,12 +3,8 @@
|
|||||||
/**
|
/**
|
||||||
* 全局 Error Boundary
|
* 全局 Error Boundary
|
||||||
*
|
*
|
||||||
* Next.js 16 文档要求 global-error 必须是 Client Component。
|
* global-error 必须是 Client Component,并自行提供 `<html>` 和 `<body>`,
|
||||||
* 16.2.7 存在 prerender `/_global-error` 的内部 bug(workStore 未初始化),
|
* 因为它会在根 layout 渲染失败时替换整个文档。
|
||||||
* 本文件通过不挂任何 Client Context 副作用、纯静态 HTML 输出来规避。
|
|
||||||
*
|
|
||||||
* 当根 layout 自身抛出错误时渲染;必须包含 `<html>` + `<body>`,
|
|
||||||
* 因为它会替换根 layout。
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ExceptionHandler } from "@/core/errors";
|
import { ExceptionHandler } from "@/core/errors";
|
||||||
|
|||||||
+2
-12
@@ -9,7 +9,7 @@ import { RootProviders } from "@/providers/root-providers";
|
|||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "cozsweet",
|
title: "cozsweet",
|
||||||
description: "cozsweet frontend (Next.js)",
|
description: "cozsweet frontend (Next.js)",
|
||||||
// PWA:链接到 public/manifest.json(由 @ducanh2912/next-pwa 体系使用)
|
// Static web app manifest served from `public/`.
|
||||||
manifest: "/manifest.json",
|
manifest: "/manifest.json",
|
||||||
// iOS Safari "添加到主屏" 元数据
|
// iOS Safari "添加到主屏" 元数据
|
||||||
appleWebApp: {
|
appleWebApp: {
|
||||||
@@ -24,17 +24,11 @@ export const metadata: Metadata = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Next.js 14+ 推荐:viewport 相关元数据独立导出。
|
|
||||||
* 之前放在 `metadata.themeColor` 会在 Next.js 16 触发 deprecation 警告。
|
|
||||||
* 详见 https://nextjs.org/docs/app/api-reference/functions/generate-viewport
|
|
||||||
*/
|
|
||||||
export const viewport: Viewport = {
|
export const viewport: Viewport = {
|
||||||
// 浏览器顶栏 / Android PWA 顶栏颜色
|
// 浏览器顶栏 / Android PWA 顶栏颜色
|
||||||
themeColor: "#f84d96",
|
themeColor: "#f84d96",
|
||||||
// 让页面延伸到 notch / home indicator 区域,
|
// 让页面延伸到 notch / home indicator 区域,
|
||||||
// env(safe-area-inset-bottom) 才能取到非零值(见 chat-input-bar.module.css)。
|
// env(safe-area-inset-bottom) 才能取到非零值(见 chat-input-bar.module.css)。
|
||||||
// Next.js 16 的 Viewport 类型未列出此键,但 generate-viewport.md 允许附加原始 meta 值。
|
|
||||||
viewportFit: "cover",
|
viewportFit: "cover",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -44,19 +38,15 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
// suppressHydrationWarning:next/font + Tailwind v4 偶发类名差异;详见
|
|
||||||
// `src/app/api/auth/[...nextauth]/route.ts` 的"水合闪屏"说明。
|
|
||||||
<html
|
<html
|
||||||
lang="en"
|
lang="en"
|
||||||
suppressHydrationWarning
|
|
||||||
className={`${geistSans.variable} ${geistMono.variable} ${athelas.variable} h-full antialiased`}
|
className={`${geistSans.variable} ${geistMono.variable} ${athelas.variable} h-full antialiased`}
|
||||||
>
|
>
|
||||||
<body className="min-h-full flex flex-col">
|
<body className="min-h-full flex flex-col">
|
||||||
{/* NextAuth SessionProvider:为所有客户端组件提供 useSession() 上下文 */}
|
{/* NextAuth SessionProvider:为所有客户端组件提供 useSession() 上下文 */}
|
||||||
<SessionProvider>
|
<SessionProvider>
|
||||||
<RootProviders>
|
<RootProviders>
|
||||||
{/* PWA Service Worker 注册器 —— Serwist Turbopack 在 build 时编译 app/sw.ts,
|
{/* SerwistProvider registers the service worker built from app/sw.ts. */}
|
||||||
通过 /serwist/sw.js 路由 handler 服务。SerwistProvider 内部处理注册 + 生命周期。 */}
|
|
||||||
<SerwistProvider swUrl="/serwist/sw.js">{children}</SerwistProvider>
|
<SerwistProvider swUrl="/serwist/sw.js">{children}</SerwistProvider>
|
||||||
</RootProviders>
|
</RootProviders>
|
||||||
</SessionProvider>
|
</SessionProvider>
|
||||||
|
|||||||
+2
-6
@@ -1,12 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* 根路径处理
|
* 根路径处理
|
||||||
*
|
*
|
||||||
* Server Component:把访客直接送入 `/splash`,由 splash 页(Client)根据
|
* 根页面只负责进入 splash。浏览器端认证初始化完成后,再由导航守卫决定
|
||||||
* `authStorage.hasLoginToken()` 决定是否再跳 `/chat`。
|
* 进入聊天页或保留在启动页。
|
||||||
*
|
|
||||||
* 这里不做鉴权重定向:Server 端读不到 localStorage,且当前没有 HttpOnly
|
|
||||||
* cookie(见 `src/data/storage/auth/auth_storage.ts` 的 TODO)。
|
|
||||||
* `src/proxy.ts` 已为未来 cookie 化预留了基于 `login_token` cookie 的 short-circuit。
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|||||||
@@ -229,9 +229,7 @@ export const onResponseErrorAuthRefresh: FetchHook = async (ctx) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/** Hooks consumed by the shared ofetch client. */
|
||||||
* 兼容旧接口的 FetchHooks 形式
|
|
||||||
*/
|
|
||||||
export const authRefreshInterceptor = {
|
export const authRefreshInterceptor = {
|
||||||
onResponseError: onResponseErrorAuthRefresh,
|
onResponseError: onResponseErrorAuthRefresh,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -50,9 +50,7 @@ export const onErrorLogging: FetchHook = async (ctx) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/** Hooks consumed by the shared ofetch client. */
|
||||||
* 兼容旧接口的 FetchHooks 形式(保留以备兼容)
|
|
||||||
*/
|
|
||||||
export const loggingInterceptor = {
|
export const loggingInterceptor = {
|
||||||
onRequest: onRequestLogging,
|
onRequest: onRequestLogging,
|
||||||
onResponse: onResponseLogging,
|
onResponse: onResponseLogging,
|
||||||
|
|||||||
@@ -104,9 +104,7 @@ export const onRequestToken: FetchHook = async (ctx) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/** Hooks consumed by the shared ofetch client. */
|
||||||
* 兼容旧接口的 FetchHooks 形式
|
|
||||||
*/
|
|
||||||
export const tokenInterceptor = {
|
export const tokenInterceptor = {
|
||||||
onRequest: onRequestToken,
|
onRequest: onRequestToken,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,12 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* IChatRepository 接口
|
* IChatRepository 接口
|
||||||
*
|
*
|
||||||
* 对齐 Dart 端 `ChatRepository` 抽象(lib/data/repositories/chat_repository.dart):
|
* 聚合聊天远程操作、身份隔离的本地历史,以及媒体缓存能力。
|
||||||
* - 远程:发送消息、获取历史
|
|
||||||
* - 本地:写入 / 批量覆盖 / 读取 / 清空 / 计数
|
|
||||||
*
|
|
||||||
* 私有助手(`_chatToLocal` / `_localToChat` / `_mapLocalStorageResult`)不暴露。
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Result } from "@/utils";
|
import type { Result } from "@/utils";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LocalChatStorage 完整实现(示范 2:IndexedDB via Dexie)
|
* IndexedDB-backed chat message storage.
|
||||||
*
|
*
|
||||||
* Dexie 实例可注入,便于测试用 fake-indexeddb 跑真 Dexie。
|
* Dexie 实例可注入,便于测试用 fake-indexeddb 跑真 Dexie。
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,17 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* @file Storage 公共导出
|
* @file Storage public exports.
|
||||||
*
|
|
||||||
* unstorage 重构后:
|
|
||||||
* - 删除 `./local_storage`(原自定义 LocalStorage class 已被 unstorage 取代)
|
|
||||||
* - 业务层(AuthStorage / ChatStorage / UserStorage / AppStorage)继续走原接口
|
|
||||||
*/
|
*/
|
||||||
export * from "@/utils/result";
|
|
||||||
export * from "./storage_keys";
|
export * from "./storage_keys";
|
||||||
export * from "./storage_migration";
|
export * from "./storage_migration";
|
||||||
export * from "./app/app_storage";
|
export * from "./app/app_storage";
|
||||||
// NOTE: top-level `auth_storage.ts` is intentionally not re-exported here —
|
|
||||||
// it conflicts with `auth/auth_storage.ts` (both define `AuthStorage`).
|
|
||||||
// Import directly via `@/data/storage/auth/auth_storage` instead.
|
|
||||||
export * from "./auth/auth_storage";
|
export * from "./auth/auth_storage";
|
||||||
export * from "./auth/iauth_storage";
|
export * from "./auth/iauth_storage";
|
||||||
export * from "./chat/chat_storage";
|
export * from "./chat/chat_storage";
|
||||||
|
|||||||
+3
-12
@@ -1,16 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* 仓库层统一 Result<T>
|
* 仓库和持久化操作共享的 Result<T>。
|
||||||
*
|
*
|
||||||
* 与 `src/data/storage/result.ts` 的 `Result<T>`(`{kind, value}` 形态)并存:
|
* `error` 固定为 `Error`(非 `unknown`):`Result.wrap` 捕获后用
|
||||||
* - 本类型供仓库层(`src/data/repositories/*`)使用,`{success, data}` 形态更贴近
|
|
||||||
* 原 Dart `Result.success` / `Result.failure` 命名与 API 端 envelope 风格。
|
|
||||||
* - 存储层继续使用 `{kind, value}`,因为它的消费者(localStorage、Dexie)已经在
|
|
||||||
* 大量代码中 pattern-match `r.kind`。
|
|
||||||
* - 两个类型路径不同(`@/data/result` vs `@/data/storage/result`),不冲突;
|
|
||||||
* 如需在同模块中混用,可用 `import { Result as StorageResult } from
|
|
||||||
* "@/data/storage/result"` 别名。
|
|
||||||
*
|
|
||||||
* `error` 仍固定为 `Error`(非 `unknown`):仓库 `Result.wrap` 捕获后用
|
|
||||||
* `ExceptionHandler` 统一归一化为 `AppException`,调用方可以稳定读取
|
* `ExceptionHandler` 统一归一化为 `AppException`,调用方可以稳定读取
|
||||||
* `error.message`;需要结构化错误时可继续通过 `ExceptionHandler.handle(error)`
|
* `error.message`;需要结构化错误时可继续通过 `ExceptionHandler.handle(error)`
|
||||||
* 得到简化后的 `{ code, message }`。
|
* 得到简化后的 `{ code, message }`。
|
||||||
@@ -23,7 +14,7 @@ export type Result<T> =
|
|||||||
| { readonly success: true; readonly data: T }
|
| { readonly success: true; readonly data: T }
|
||||||
| { readonly success: false; readonly error: Error };
|
| { readonly success: false; readonly error: Error };
|
||||||
|
|
||||||
/** 仓库层 Result.wrap 专用 logger —— 自动带 component: "Result" 标签 */
|
/** Result.wrap 专用 logger —— 自动带 component: "Result" 标签 */
|
||||||
const log = new Logger("Result");
|
const log = new Logger("Result");
|
||||||
|
|
||||||
export const Result = {
|
export const Result = {
|
||||||
|
|||||||
Reference in New Issue
Block a user