diff --git a/src/app/chat/components/index.ts b/src/app/chat/components/index.ts index 5a830fd3..e11b2589 100644 --- a/src/app/chat/components/index.ts +++ b/src/app/chat/components/index.ts @@ -10,7 +10,6 @@ export * from "./chat-input-bar"; export * from "./chat-input-text-field"; export * from "./chat-quota-exhausted-banner"; export * from "./chat-screen"; - export * from "./chat-send-button"; export * from "./date-header"; export * from "./fullscreen-image-viewer"; diff --git a/src/app/subscription/components/index.ts b/src/app/subscription/components/index.ts index ac8be8c5..cc0d758f 100644 --- a/src/app/subscription/components/index.ts +++ b/src/app/subscription/components/index.ts @@ -5,7 +5,9 @@ export * from "./subscription-back-link"; export * from "./subscription-banner"; export * from "./subscription-benefits-card"; +export * from "./subscription-checkout-button"; export * from "./subscription-cta-button"; +export * from "./subscription-manage-button"; export * from "./subscription-plan-card"; export * from "./subscription-screen"; export * from "./subscription-user-row"; diff --git a/src/data/schemas/nullable-defaults.ts b/src/data/schemas/nullable-defaults.ts new file mode 100644 index 00000000..4d9df94b --- /dev/null +++ b/src/data/schemas/nullable-defaults.ts @@ -0,0 +1,33 @@ +/** + * Zod 默认值辅助器(处理 `null | undefined` → 标量默认) + * + * 防御性解析:后端对 Facebook / Apple 等 OAuth 用户可能返回 `null`(如 `email: null`), + * Zod 默认的 `.default("")` 只接受 `undefined`,遇到 `null` 会抛 ZodError → 登录静默失败。 + * 用 `z.string().nullable().transform(v => v ?? "").default("")` 模式: + * - input 接受 `string | null | undefined`(保持 optional) + * - output 永远是 `string`(User 类的 `email: string` 声明不会破) + * + * 这是前端防御性解析(不要求后端修改)—— 和后端对齐更稳。 + */ +import { z } from "zod"; + +/** `string | null | undefined` → `string`(默认空串) */ +export const stringOrEmpty = z + .string() + .nullable() + .transform((v) => v ?? "") + .default(""); + +/** `number | null | undefined` → `number`(默认 0) */ +export const numberOrZero = z + .number() + .nullable() + .transform((v) => v ?? 0) + .default(0); + +/** `boolean | null | undefined` → `boolean`(默认 false) */ +export const booleanOrFalse = z + .boolean() + .nullable() + .transform((v) => v ?? false) + .default(false); \ No newline at end of file diff --git a/src/data/schemas/user/user.ts b/src/data/schemas/user/user.ts index b2c9f9d8..d1bae781 100644 --- a/src/data/schemas/user/user.ts +++ b/src/data/schemas/user/user.ts @@ -4,19 +4,11 @@ */ import { z } from "zod"; import { PersonalityTraitsSchema, PERSONALITY_TRAITS_DEFAULTS } from "./personality_traits"; - -/** - * 防御性解析:后端对 Facebook / Apple 等 OAuth 用户可能返回 `null`(如 `email: null`), - * Zod 默认的 `.default("")` 只接受 `undefined`,遇到 `null` 会抛 ZodError → 登录静默失败。 - * 用 `z.string().nullable().transform(v => v ?? "").default("")` 模式: - * - input 接受 `string | null | undefined`(保持 optional) - * - output 永远是 `string`(User 类的 `email: string` 声明不会破) - * - * 这是前端防御性解析(不要求后端修改)—— 和后端对齐更稳。 - */ -const stringOrEmpty = z.string().nullable().transform((v) => v ?? "").default(""); -const numberOrZero = z.number().nullable().transform((v) => v ?? 0).default(0); -const booleanOrFalse = z.boolean().nullable().transform((v) => v ?? false).default(false); +import { + booleanOrFalse, + numberOrZero, + stringOrEmpty, +} from "../nullable-defaults"; export const UserSchema = z.object({ id: stringOrEmpty, // 兜底:理论 id 不会 null,但保持防御 diff --git a/src/stores/user/index.ts b/src/stores/user/index.ts index be8f44b5..b6c636b5 100644 --- a/src/stores/user/index.ts +++ b/src/stores/user/index.ts @@ -4,5 +4,7 @@ export * from "./user-context"; export * from "./user-events"; +export * from "./user-machine.actors"; +export * from "./user-machine.helpers"; export * from "./user-machine"; export * from "./user-state";