Files
infiplot-web/lib/options.ts
T
yuanzonghao e095650944 refactor(web): enforce content-free Umami fields at compile time
Address the Copilot review on #26.

#1 The game_start / art_style_select payload fields were typed as bare
   `string`, so free text could still slip through despite the "content-free
   by construction" claim. Add lib/options.ts as the single source of truth
   for the selector option sets (`as const` → literal-union types), have the
   home OPTS render from those arrays, and type the analytics fields from the
   derived unions (gender/art_style/plot_style/pacing/style) plus a template
   type for `card`. Free text now fails to compile; no casts at call sites.

#2 The /play heartbeat scheduled its 30s interval unconditionally. Gate the
   effect on the same NEXT_PUBLIC_UMAMI_* env used for script injection, so
   nothing is scheduled when the tracker is off (visibility check kept — a
   hidden tab still never emits).

#3 choice_select no longer emits a -1 choice_index: skip the event when the
   index can't be resolved instead of polluting the index distribution.

Verified with tsc (exit 0) and a throwaway negative test: free text in any
of the six fields raises TS2322, valid enum/template values compile.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 10:59:31 +08:00

38 lines
1.3 KiB
TypeScript

// Single source of truth for the home-page selector option sets. Kept as
// `as const` so each list also yields a literal-union type: the play-start
// UI (app/page.tsx) renders from the arrays, and the analytics schema
// (lib/analytics.ts) types its payload fields from the unions. That shared
// origin is what keeps the "content-free" events honest — an event field can
// only ever be one of these fixed labels, never free-form player text.
export const GENDERS = ["男性向", "女性向"] as const;
export const ART_STYLES = [
"自动",
"自定义",
"京阿尼细腻日常",
"新海诚唯美光影",
"Galgame CG",
"3D 动漫电影",
"赛博朋克",
"蒸汽波",
"吉卜力治愈手绘",
"哥特庄园",
"废土科幻",
// 以下为小众/区域性画风,留作长尾选项
"古典厚涂油画",
"极简中国水墨",
"浮世绘木刻",
"莫高窟壁画",
"波斯细密画",
] as const;
export const PLOT_STYLES = ["平铺直叙", "多线转折", "悬疑烧脑", "治愈日常"] as const;
export const PACINGS = ["慢热细腻", "紧凑爽快"] as const;
export type Gender = (typeof GENDERS)[number];
export type ArtStyle = (typeof ART_STYLES)[number];
export type PlotStyle = (typeof PLOT_STYLES)[number];
export type Pacing = (typeof PACINGS)[number];