Files
Jyotisha/frontend/src/lib/onboarding-payload.ts
T
Jesse_Chen 12b6398c7b
Independent Staging Quality Gate / validate (push) Failing after 12m39s
Independent Staging Quality Gate / publish (push) Has been skipped
fix: make starter questions user-centered
2026-08-15 21:52:02 +08:00

49 lines
2.1 KiB
TypeScript

import { z } from "zod";
const detachedStarterQuestionPattern = /印度占星|一般如何|通常(?:会)?(?:看|观察|分析|理解|包含)|哪些(?:因素|证据层)|如何划分/;
const userCenteredStarterQuestionSchema = z.string()
.trim()
.min(4)
.max(80)
.refine((text) => text.includes("我"), "starter_question_must_be_first_person")
.refine((text) => !detachedStarterQuestionPattern.test(text), "starter_question_must_not_be_encyclopedic");
const onboardingSchema = z.object({
greeting: z.string().trim().min(8).max(180),
suggestions: z.tuple([
z.object({ theme: z.literal("career"), text: userCenteredStarterQuestionSchema }),
z.object({ theme: z.literal("marriage"), text: userCenteredStarterQuestionSchema }),
z.object({ theme: z.literal("timing"), text: userCenteredStarterQuestionSchema }),
]),
});
export type OnboardingPayload = z.infer<typeof onboardingSchema>;
export const fallbackOnboardingPayload: OnboardingPayload = {
greeting: "我们从你此刻最关心的事情开始。可以选择下面的方向,也可以直接说出你的问题。",
suggestions: [
{ theme: "career", text: "请帮我看看事业优势更适合怎样发挥。" },
{ theme: "marriage", text: "请帮我看看关系里容易重复什么模式。" },
{ theme: "timing", text: "请帮我看看未来一年哪些阶段值得提前准备。" },
],
};
class OnboardingJsonError extends Error {
readonly name = "OnboardingJsonError";
}
export function parseOnboardingPayload(value: unknown): OnboardingPayload | null {
const parsed = onboardingSchema.safeParse(value);
return parsed.success ? parsed.data : null;
}
export function parseOnboardingText(text: string): OnboardingPayload | null {
const normalized = text.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "");
const start = normalized.indexOf("{");
const end = normalized.lastIndexOf("}");
if (start < 0 || end <= start) throw new OnboardingJsonError("onboarding_json_missing");
const parsed: unknown = JSON.parse(normalized.slice(start, end + 1));
return parseOnboardingPayload(parsed);
}