import { z } from 'zod'; import { NextRequest } from 'next/server'; /** * 공통 Zod 필드 스키마 * * 폼 스키마를 조합할 때 필드 단위로 픽해서 사용한다. * * @example * import { z } from "zod"; * import { phoneField, bizNumField, passwordField } from "@linkup/validators"; * * const memberSchema = z.object({ * name: z.string().min(1, "이름을 입력해주세요."), * phone: phoneField, * bizNo: bizNumField.optional(), * password: passwordField, * }); */ /** 필수 문자열 */ declare const requiredStringField: z.ZodString; /** 선택 문자열 (빈 문자열 허용) */ declare const optionalStringField: z.ZodUnion<[z.ZodOptional, z.ZodLiteral<"">]>; /** * 전화번호 — 숫자 10~11자리 (하이픈 제외) * AdminFormField type="tel" 과 함께 사용하면 자동 포맷팅됨 */ declare const phoneField: z.ZodString; /** * 사업자번호 — 숫자 10자리 (하이픈 제외) * AdminFormField type="bizNum" 과 함께 사용하면 자동 포맷팅됨 */ declare const bizNumField: z.ZodString; declare const emailField: z.ZodString; /** * 비밀번호 — 8자 이상, 영문 + 숫자 포함 */ declare const passwordField: z.ZodString; /** * 날짜 — YYYY-MM-DD 형식 * AdminDatePicker / AdminDateInput 의 value 타입과 일치 */ declare const dateField: z.ZodString; /** 선택 날짜 (빈 문자열 허용) */ declare const optionalDateField: z.ZodString; /** * 콤마 포맷 숫자 문자열 (예: "1,234") * AdminFormField type="comma" 와 함께 사용 */ declare const commaNumberField: z.ZodString; /** 양의 정수 */ declare const positiveIntField: z.ZodNumber; declare const urlField: z.ZodUnion<[z.ZodOptional, z.ZodLiteral<"">]>; /** * baseFilterSchema — 관리자 목록 API 공통 필터 스키마 * * 모든 목록 페이지에 공통으로 들어가는 필드를 정의한다. * .extend()로 프로젝트별 필드를 추가해서 사용한다. * * @example * // 기본 그대로 사용 * type MemberFilter = BaseFilter; * const init = baseFilterDefaults; * * @example * // 프로젝트별 필드 추가 * const memberFilterSchema = baseFilterSchema.extend({ * role: z.string().default(""), * grade: z.string().default(""), * }); * type MemberFilter = z.infer; * const initFilters = memberFilterSchema.parse({}); // 모든 default 적용 * * @example * // API Route에서 query params 파싱 * export async function GET(req: NextRequest) { * const filter = parseSearchParams(req.nextUrl.searchParams); * // filter.keyword, filter.page, filter.pageSize ... * } */ declare const baseFilterSchema: z.ZodObject<{ keywordType: z.ZodDefault; keyword: z.ZodDefault; dateType: z.ZodDefault; startDate: z.ZodDefault; endDate: z.ZodDefault; options: z.ZodDefault>; page: z.ZodOptional>; pageSize: z.ZodOptional>; }, z.core.$strip>; type BaseFilter = z.infer; /** 초기값 — useState 초기화에 사용 */ declare const baseFilterDefaults: BaseFilter; /** * URLSearchParams → BaseFilter 파싱 * API Route에서 query params를 안전하게 파싱할 때 사용한다. */ declare function parseSearchParams(searchParams: URLSearchParams): BaseFilter; /** * 날짜 범위 유효성 검사 refine * baseFilterSchema.extend({...}).superRefine(dateRangeRefine) 형태로 사용한다. * * @example * const memberFilterSchema = baseFilterSchema * .extend({ role: z.string().default("") }) * .superRefine(dateRangeRefine); */ declare function dateRangeRefine(data: { startDate?: string; endDate?: string; }, ctx: z.RefinementCtx): void; /** * baseQuerySchema — 목록 API 공통 쿼리 스키마 (필터 + 정렬 + 페이지네이션) * * baseFilterSchema에 sortKey / sortType을 추가한 버전. * antdQuerySchema() 대체용으로 설계되었다. * * @example * const schema = baseQuerySchema.extend({ * deliveryKey: z.coerce.number().int().positive().optional(), * }); * const data = parseRequestParams(req, schema); * // data.keyword, data.page, data.sortKey, data.sortType ... */ declare const baseQuerySchema: z.ZodObject<{ keywordType: z.ZodDefault; keyword: z.ZodDefault; dateType: z.ZodDefault; startDate: z.ZodDefault; endDate: z.ZodDefault; options: z.ZodDefault>; page: z.ZodOptional>; pageSize: z.ZodOptional>; sortKey: z.ZodDefault; sortType: z.ZodOptional>; }, z.core.$strip>; type BaseQuery = z.infer; /** * parseRequestParams — NextRequest의 searchParams를 Zod 스키마로 파싱 * * - URLSearchParams → plain object 변환 * - bracket 표기법("options[progress]")을 그대로 Zod 키로 사용 가능 * - 유효성 실패 시 status 400 에러를 throw (apiWrapper의 errorHandler가 처리) * * @example * const schema = baseQuerySchema.extend({ * "options[progress]": z.string().trim().optional(), * }).transform((d) => ({ * ...d, * progress: d["options[progress]"], * })); * * const data = parseRequestParams(req, schema); * // data.keyword, data.progress, data.page ... */ declare function parseRequestParams(req: NextRequest, schema: T): z.infer; /** * extractSearchParams — URLSearchParams를 baseFilterSchema.parse()에 바로 넣을 수 있는 * 순수 객체로 변환한다. * * "options[key]" 형태의 값들은 스키마의 options 필드에 정의된 키만 화이트리스트로 인정해서 * 모아준다. 화이트리스트에 없는 옵션 키는 무시된다. 나머지 필드는 Object.fromEntries와 * 동일하게 그대로 펼친다. * * 각 키를 스키마에 적은 타입 그대로 뽑아준다 — 스키마를 두 번 쓸 필요가 없다. * - z.array(...) 로 선언 → string[] / number[] * - z.string() 등 배열이 아닌 스키마로 선언 → 단일 값 * - 원소(또는 값) 타입이 z.number() 계열이면 문자열을 숫자로 캐스팅한다. * * 이 스키마를 buildFilterSearchParams(schema, data)로 다시 URLSearchParams로 만들면 * 여기서 파싱한 것과 항상 같은 포맷으로 왕복된다 (브라우저 주소창 ↔ SSR ↔ API 요청 통일). * * @example * const orderFilterSchema = baseFilterSchema.extend({ * options: z.object({ * type: z.array(z.string()).default([]), // 다중 선택 → string[] * categoryId: z.number().array().default([]), // 다중 선택(숫자) → number[] * status: z.string().optional(), // 단일 값 → string * page: z.number().optional(), // 단일 값(숫자) → number * }).default({}), * }); * * const { searchParams } = new URL(req.url); * const data = orderFilterSchema.parse( * extractSearchParams(orderFilterSchema, searchParams) * ); * // data.options.type → string[] * // data.options.categoryId → number[] * // data.options.status → string | undefined * // data.options.page → number | undefined */ declare function extractSearchParams(schema: z.ZodObject, searchParams: URLSearchParams): Record; /** * buildFilterSearchParams — extractSearchParams()의 반대 방향. * 파싱된(또는 그와 같은 모양의) 데이터 객체를 URLSearchParams로 직렬화한다. * * options 필드는 항상 "options[key]=value" 브래킷 표기로 쓴다 (extractSearchParams가 * 읽는 포맷과 동일). 배열은 값마다 반복 append, 스칼라는 set. 빈 값(undefined/null/""/[])은 * URL을 깨끗하게 유지하기 위해 생략한다. * * 브라우저 주소창에 필터 상태를 동기화할 때(usePageSearchState 등) 이 함수로 만든 * URLSearchParams를 그대로 쓰면, 나중에 그 URL을 extractSearchParams로 다시 파싱했을 때 * 항상 원래 값으로 왕복된다. (SSR에서 같은 URL을 그대로 파싱해도 동일한 결과) * * @example * const data = orderFilterSchema.parse({ keyword: "hi", options: { type: ["A", "B"] } }); * const sp = buildFilterSearchParams(orderFilterSchema, data); * // sp.toString() === "keyword=hi&options%5Btype%5D=A&options%5Btype%5D=B" * * const restored = orderFilterSchema.parse(extractSearchParams(orderFilterSchema, sp)); * // restored.options.type → ["A", "B"] (왕복 성공) */ declare function buildFilterSearchParams(schema: z.ZodObject, data: Record): URLSearchParams; /** * 문자열 / 문자열배열 / undefined 를 trim된 비어있지 않은 문자열 배열로 정규화한다. * "a,b,c" 처럼 콤마로 구분된 값이나, ["a,b", "c"] 처럼 배열 안에 콤마가 섞인 값도 모두 분리한다. */ declare function toStringArray(value: unknown): string[] | undefined; export { type BaseFilter, type BaseQuery, baseFilterDefaults, baseFilterSchema, baseQuerySchema, bizNumField, buildFilterSearchParams, commaNumberField, dateField, dateRangeRefine, emailField, extractSearchParams, optionalDateField, optionalStringField, parseRequestParams, parseSearchParams, passwordField, phoneField, positiveIntField, requiredStringField, toStringArray, urlField };