3.7 KiB
event-schemas
Purpose
The package-local Zod schemas for the event entity surface. Owns
every input / output shape touching the events routes and
repositories: the persisted EventSchema, the create / update
request bodies, the list-query parameters, the path param shape,
the EventLocationSchema value object, and the EventStatusSchema
0..2 integer encoding. The @suaveplan/api-schemas package
ships only event-bus envelope schemas (DomainEvent,
EventEnvelope, EventMetadata); the event-management entity
schemas are owned here because no other package needs them.
The schemas double as the type sources — every exported *Schema
has a matching type Foo = z.infer<typeof FooSchema> so consumers
get a single source of truth for both runtime validation and
compile-time narrowing. The CreateEventSchema and
UpdateEventSchema carry a .refine() constraint that asserts
end_time > start_time at the parser boundary so the repository
never sees a malformed time pair.
Features
- 11 schemas spanning persistence, request, response, and value objects.
CreateEventSchema/UpdateEventSchemaenforceend_time > start_timevia.refine().EventStatusSchemaconstrains status to0..2(draft / active / cancelled) matching the upstream encoding.EventQuerySchemacoerces query-stringlimit/offset/upcoming/pastto typed numbers / booleans.EventLocationSchemavalidates(lat, lng)coordinates against geographic bounds.
Usage: Basic Example
import { CreateEventSchema, EventSchema } from "@suaveplan/api-plugin-events";
const parsed = CreateEventSchema.parse({
title: "Meetup",
start_time: "2026-06-01T18:00:00Z",
end_time: "2026-06-01T22:00:00Z",
});
console.log(parsed.timezone); // "UTC"
Usage: Advanced Example
import {
EventQuerySchema,
EventListResponseSchema,
UpdateEventSchema,
} from "@suaveplan/api-plugin-events";
// Coerce from URL search params
const url = new URL("https://api.example.com/events?limit=10&past=true");
const query = EventQuerySchema.parse(Object.fromEntries(url.searchParams));
// Build a typed response payload
const body = EventListResponseSchema.parse({
data: [
/* events from repository */
],
total: 42,
});
// Forbid time inversion at the boundary
const result = UpdateEventSchema.safeParse({
start_time: "2026-06-01T22:00:00Z",
end_time: "2026-06-01T18:00:00Z",
});
if (!result.success) {
// result.error.issues[0].path === ["end_time"]
}
API Reference
EventStatusSchema: z.ZodNumber— integer 0..2.EventLocationSchema: z.ZodObject—{ name, address?, coordinates? }.AttendeeCountSchema: z.ZodObject—{ going, maybe, invited }.EventSchema: z.ZodObject— the persisted event row.CreateEventSchema: z.ZodEffects<...>— POST/body.UpdateEventSchema: z.ZodEffects<...>— PUT/{id}body.EventQuerySchema: z.ZodObject— GET/query params.EventIdParamSchema: z.ZodObject—{ id: string }.EventListResponseSchema: z.ZodObject— GET/response.
Every schema exports a sibling type Foo = z.infer<typeof FooSchema>.
Implementation Notes
status is encoded as an integer (0=draft, 1=active,
2=cancelled) rather than a string enum to match the upstream
SurrealDB schema and to keep label cardinality low when emitted as
OTel attributes. The two cross-field .refine() rules
(end_time > start_time) are duplicated on CreateEventSchema and
UpdateEventSchema rather than factored to a shared helper because
the UpdateEventSchema variant must tolerate a single-field update
({ title }) without raising the refinement, which means the rule
fires only when both start_time and end_time appear in the same
payload.