first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
# `strip-undefined`
## Purpose
`stripUndefined` is a route-layer adapter that bridges Zod's
`optional()` output (`T | undefined`) with the
`exactOptionalPropertyTypes: true` repository input types declared in
`@suaveplan/campaigns` and the local `CampaignSuppressionStore`. When a
route handler validates a request body through a Zod schema, optional
fields surface as either the declared type or `undefined`; passing the
parsed object straight to the repository fails the `exactOptional`
check at compile time. This helper produces a shallow clone with every
`undefined`-valued key removed so the downstream type narrows.
The helper is intentionally minimal — no recursion, no `null` handling,
no key-name filtering — because every concrete usage in the routes
layer passes a Zod-parsed object whose nesting has already been
normalised. Recursion would obscure the cost model and tempt callers
to use it as a deep-clean utility, which is out of scope.
## Features
- Shallow-only filter; nested objects are forwarded by reference.
- `null` values are preserved (only `=== undefined` triggers removal).
- Returns a fresh object reference every call (never mutates input).
- Generic-typed return narrows `T[K]` to `Exclude<T[K], undefined>`.
## Usage: Basic
```ts
import { stripUndefined } from "@suaveplan/api-plugin-campaigns";
const body = { name: "x", templateId: undefined } as const;
const clean = stripUndefined(body);
// clean: { name: "x" } — `templateId` removed.
```
## Usage: Advanced
```ts
import { stripUndefined } from "@suaveplan/api-plugin-campaigns";
const parsed = CreateCampaignSchema.parse(input);
await repo.create({
id: idGen(),
tenantId,
...stripUndefined(parsed),
status: "draft",
stats: EMPTY_STATS,
createdAt: now,
updatedAt: now,
});
```
## API Reference
- `stripUndefined<T extends Record<string, unknown>>(input: T):
{ [K in keyof T]: Exclude<T[K], undefined> }` — shallow copy with
`undefined`-valued keys removed.
## Implementation Notes
The implementation iterates `Object.entries(input)` once and writes
into a fresh `Record<string, unknown>` accumulator. The output is then
cast to the narrowed mapped type — a single assertion confined to the
return statement. The accumulator is not pre-sized because the helper
is hot-path-cheap (single object scan) and JavaScript engines do not
benefit from `new Object()` capacity hints. The forbidden `Object.assign`
path (which would forward `undefined` properties when iterating
`Object.keys`) is intentionally avoided.