start project
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
* Runtime mirrors of the TypeScript interfaces in `src/types/index.ts`.
|
||||
*
|
||||
* A TS interface is a compile-time-only promise: nothing stops the Django
|
||||
* side from renaming a field, changing its nullability, or dropping a facet,
|
||||
* and the frontend would only find out at runtime, in production, as a blank
|
||||
* screen or a silently-wrong render. These schemas are validated against the
|
||||
* real API response in `search-response.contract.test.ts`, so that class of
|
||||
* drift fails a test instead.
|
||||
*/
|
||||
|
||||
export const FacetBucketSchema = z.object({
|
||||
key: z.string(),
|
||||
count: z.number().int().nonnegative(),
|
||||
});
|
||||
|
||||
export const FACET_NAMES = [
|
||||
'skills',
|
||||
'job_title_role',
|
||||
'seniority',
|
||||
'country',
|
||||
'industry',
|
||||
'company',
|
||||
] as const;
|
||||
|
||||
export const SearchHitSchema = z.object({
|
||||
id: z.number(),
|
||||
score: z.number(),
|
||||
full_name: z.string(),
|
||||
headline: z.string(),
|
||||
job_title: z.string(),
|
||||
job_title_role: z.string(),
|
||||
seniority_levels: z.array(z.string()),
|
||||
company: z.string(),
|
||||
industry: z.string(),
|
||||
location_name: z.string(),
|
||||
location_country: z.string(),
|
||||
years_experience: z.number().nullable(),
|
||||
linkedin_url: z.string(),
|
||||
skills: z.array(z.string()),
|
||||
highlight: z.record(z.string(), z.array(z.string())),
|
||||
});
|
||||
|
||||
export const SearchResponseSchema = z.object({
|
||||
count: z.number().int().nonnegative(),
|
||||
page: z.number().int().positive(),
|
||||
page_size: z.number().int().positive(),
|
||||
total_pages: z.number().int().nonnegative(),
|
||||
took_ms: z.number().int().nonnegative(),
|
||||
backend: z.string(),
|
||||
results: z.array(SearchHitSchema),
|
||||
facets: z.record(z.enum(FACET_NAMES), z.array(FacetBucketSchema)),
|
||||
});
|
||||
|
||||
export const UserSchema = z.object({
|
||||
id: z.number(),
|
||||
email: z.string(),
|
||||
full_name: z.string(),
|
||||
role: z.enum(['admin', 'analyst', 'viewer']),
|
||||
can_view_pii: z.boolean(),
|
||||
date_joined: z.string(),
|
||||
});
|
||||
|
||||
export const TokenPairSchema = z.object({
|
||||
access: z.string(),
|
||||
refresh: z.string(),
|
||||
user: UserSchema,
|
||||
});
|
||||
|
||||
export const ApiErrorSchema = z.object({
|
||||
error: z.object({
|
||||
code: z.string(),
|
||||
message: z.string(),
|
||||
details: z.unknown().optional(),
|
||||
request_id: z.string(),
|
||||
}),
|
||||
});
|
||||
Reference in New Issue
Block a user