# routes/shared/strip-undefined — drop undefined-valued keys ## Purpose Bridges Zod's optional-field semantics (`.optional() → string | undefined`) with the suaveplan `@suaveplan/contacts` input shapes, which use `exactOptionalPropertyTypes: true` and therefore forbid the `| undefined` half of optional members. Returning a shallow copy with `undefined` keys removed lets the route handlers feed Zod output directly into `repo.create({ ...stripUndefined(parsed.data), tenantId })` without losing type safety. Without this helper every route handler would need to manually re-build the input object key-by-key, or every Zod schema would have to redefine its optionality via `.transform()`. The helper is the focused fix. ## Features - Shallow — preserves nested undefineds because the consumer Zod schemas only produce top-level optionals. - Preserves `null`, `0`, `""`, and `false` (only `undefined` is dropped). - Pure / type-safe — returns `Partial`-compatible shape. ## Usage: Basic ```ts import { stripUndefined } from "@suaveplan/api-plugin-contacts"; const parsed = CreateContactSchema.parse(input); const repoInput = stripUndefined(parsed); await repo.create({ ...repoInput, tenantId }); ``` ## Usage: Advanced ```ts import { stripUndefined } from "@suaveplan/api-plugin-contacts"; function safeMerge>(base: T, patch: Partial): T { return { ...base, ...stripUndefined(patch) } as T; } ``` ## API Reference - `stripUndefined(input: T): { [K in keyof T]?: T[K] }` — shallow copy with `undefined` keys omitted. ## Implementation Notes - Uses `Object.entries` so symbol-keyed properties are ignored; route inputs are always plain objects so this is fine. - Does not deep-clone — the returned object shares nested references with the input. - The return type widens to `Partial` for ergonomics; the runtime contract is stricter (any non-`undefined` original value flows through unchanged).