feat: add form and page platform

This commit is contained in:
donghyeon-ka
2026-07-26 15:22:52 +09:00
parent fdcf0de5bf
commit b327d7370b
54 changed files with 2036 additions and 122 deletions
+69
View File
@@ -0,0 +1,69 @@
import type { ApiFailure } from "../../contracts/errors.js";
export type FormValues = Readonly<Record<string, unknown>>;
export type FieldName<Values extends FormValues> = Extract<keyof Values, string>;
export type FieldErrors<Values extends FormValues> = Readonly<
Partial<Record<FieldName<Values>, string>>
>;
export type FormResult<Value> =
| Readonly<{ ok: true; value: Value }>
| Readonly<{ ok: false; error: ApiFailure }>;
export type FormResultState =
| "idle"
| "success"
| "validation-error"
| "conflict"
| "unavailable";
export type MappedValidationFailure<Values extends FormValues> = Readonly<{
fieldErrors: FieldErrors<Values>;
formErrors: readonly string[];
}>;
const VALIDATION_COPY = Object.freeze({
REQUIRED: "필수 입력값입니다.",
too_small: "입력값이 너무 짧습니다.",
too_big: "입력값이 너무 깁니다.",
invalid_type: "입력 형식을 확인해 주세요.",
invalid_format: "입력 형식을 확인해 주세요.",
invalid_value: "허용된 값을 선택해 주세요.",
});
export function validationMessage(code: string): string {
return (
VALIDATION_COPY[code as keyof typeof VALIDATION_COPY] ??
"입력값을 확인해 주세요."
);
}
export function mapValidationFailureToFields<Values extends FormValues>(
failure: ApiFailure,
allowedFields: readonly FieldName<Values>[],
): MappedValidationFailure<Values> {
if (failure.kind !== "VALIDATION_REJECTED") {
return Object.freeze({ fieldErrors: Object.freeze({}), formErrors: [] });
}
const allowed = new Set<string>(allowedFields);
const fieldErrors: Partial<Record<FieldName<Values>, string>> = {};
const formErrors: string[] = [];
const issues = failure.validationIssues ?? [];
if (issues.length === 0) {
formErrors.push("입력값을 다시 확인해 주세요.");
}
for (const issue of issues) {
const field = issue.path.split(".").at(0) ?? "";
if (allowed.has(field)) {
const name = field as FieldName<Values>;
fieldErrors[name] ??= validationMessage(issue.code);
} else {
formErrors.push("서버가 확인하지 못한 입력 항목이 있습니다.");
}
}
return Object.freeze({
fieldErrors: Object.freeze(fieldErrors),
formErrors: Object.freeze([...new Set(formErrors)]),
});
}