import {z} from 'zod' export type Identifier = string; export type ResumeSearchParametersLocation = {name?: string | undefined, coordinates?: ({latitude?: (number | null) | undefined, longitude?: (number | null) | undefined} | null) | undefined, distance?: number | undefined, unit?: ('km' | 'mi') | undefined}; export type ResumeSearchParametersSkill = {name?: string | undefined, required?: boolean | undefined}; export type EducationLevel = 'school' | 'certificate' | 'bachelors' | 'masters' | 'doctoral' | null; export type ManagementLevel = 'None' | 'Low' | 'Mid' | 'Upper' | null; export type ResumeSearchParametersCustomData = {filterType: 'equals' | 'range', dataPoint?: (string | null) | undefined, field?: (string | null) | undefined, query: Record, required?: boolean | undefined, weight?: number | undefined}; export type ResumeSearchParameters = {indices: Array, jobDescription?: Identifier | undefined, resume?: Identifier | undefined, jobTitles?: Array | undefined, jobTitlesCurrentOnly?: boolean | undefined, jobTitlesRequired?: boolean | undefined, jobTitlesWeight?: number | undefined, yearsExperienceMin?: (number | null) | undefined, yearsExperienceMax?: (number | null) | undefined, yearsExperienceRequired?: boolean | undefined, yearsExperienceWeight?: number | undefined, locations?: Array | undefined, locationsWeight?: number | undefined, locationsRequired?: boolean | undefined, skills?: Array | undefined, skillsWeight?: number | undefined, languages?: Array | undefined, languagesWeight?: number | undefined, institutions?: Array | undefined, institutionsRequired?: boolean | undefined, degrees?: Array | undefined, degreesRequired?: boolean | undefined, highestDegreeTypes?: Array | undefined, highestDegreeTypesRequired?: boolean | undefined, isCurrentStudent?: boolean | undefined, isCurrentStudentRequired?: boolean | undefined, isRecentGraduate?: boolean | undefined, isRecentGraduateRequired?: boolean | undefined, educationWeight?: number | undefined, searchExpression?: (string | null) | undefined, searchExpressionRequired?: boolean | undefined, searchExpressionWeight?: number | undefined, socCodes?: Array | undefined, socCodesWeight?: number | undefined, socCodesRequired?: boolean | undefined, managementLevel?: ManagementLevel | undefined, managementLevelRequired?: boolean | undefined, managementLevelWeight?: number | undefined, customData?: Array | undefined}; export type CreateApiV3ResumeSearchArgs = {offset?: number | undefined, limit?: number | undefined, body: ResumeSearchParameters}; export type CreateApiV3ResumeSearchOptions = {baseUrl?: string | undefined, ApiKeyAuth?: string | undefined, fetch?: typeof fetch | undefined}; export const identifier = z.string(); export const resumeSearchParametersLocation = z.object({name: z.string().optional(), coordinates: z.object({latitude: z.number().nullable().optional(), longitude: z.number().nullable().optional()}).nullable().optional(), distance: z.number().int().gte(1).optional(), unit: z.enum(["km", "mi"]).optional()}); export const resumeSearchParametersSkill = z.object({name: z.string().optional(), required: z.boolean().optional()}); export const educationLevel = z.enum(["school", "certificate", "bachelors", "masters", "doctoral"]).nullable(); export const managementLevel = z.enum(["None", "Low", "Mid", "Upper"]).nullable(); export const resumeSearchParametersCustomData = z.object({filterType: z.enum(["equals", "range"]), dataPoint: z.string().nullable().optional(), field: z.string().nullable().optional(), query: z.object({}), required: z.boolean().optional(), weight: z.number().gte(0).lte(1).optional()}); export const resumeSearchParameters = z.object({indices: z.array(z.string()), jobDescription: identifier.optional(), resume: identifier.optional(), jobTitles: z.array(z.string()).optional(), jobTitlesCurrentOnly: z.boolean().optional(), jobTitlesRequired: z.boolean().optional(), jobTitlesWeight: z.number().gte(0).lte(1).optional(), yearsExperienceMin: z.number().int().gte(0).nullable().optional(), yearsExperienceMax: z.number().int().gte(0).nullable().optional(), yearsExperienceRequired: z.boolean().optional(), yearsExperienceWeight: z.number().gte(0).lte(1).optional(), locations: z.array(resumeSearchParametersLocation).optional(), locationsWeight: z.number().gte(0).lte(1).optional(), locationsRequired: z.boolean().optional(), skills: z.array(resumeSearchParametersSkill).optional(), skillsWeight: z.number().gte(0).lte(1).optional(), languages: z.array(resumeSearchParametersSkill).optional(), languagesWeight: z.number().gte(0).lte(1).optional(), institutions: z.array(z.string()).optional(), institutionsRequired: z.boolean().optional(), degrees: z.array(z.string()).optional(), degreesRequired: z.boolean().optional(), highestDegreeTypes: z.array(educationLevel).optional(), highestDegreeTypesRequired: z.boolean().optional(), isCurrentStudent: z.boolean().optional(), isCurrentStudentRequired: z.boolean().optional(), isRecentGraduate: z.boolean().optional(), isRecentGraduateRequired: z.boolean().optional(), educationWeight: z.number().gte(0).lte(1).optional(), searchExpression: z.string().nullable().optional(), searchExpressionRequired: z.boolean().optional(), searchExpressionWeight: z.number().gte(0).lte(1).optional(), socCodes: z.array(z.number().int().gte(1).lte(9999)).optional(), socCodesWeight: z.number().gte(0).lte(1).optional(), socCodesRequired: z.boolean().optional(), managementLevel: managementLevel.optional(), managementLevelRequired: z.boolean().optional(), managementLevelWeight: z.number().gte(0).lte(1).optional(), customData: z.array(resumeSearchParametersCustomData).optional()}); export const jobTitleSearchScoreComponent = z.object({value: z.string().optional(), label: z.string(), score: z.number().nullable().optional()}); export const managementLevelSearchScoreComponent = z.object({value: z.string().optional(), label: z.string(), score: z.number().nullable().optional()}); export const experienceSearchScoreComponent = z.object({value: z.string().optional(), label: z.string(), score: z.number().nullable().optional()}); export const skillsSearchScoreComponent = z.object({value: z.string().optional(), label: z.string(), score: z.number().nullable().optional()}); export const languagesSearchScoreComponent = z.object({value: z.string().optional(), label: z.string(), score: z.number().nullable().optional()}); export const locationSearchScoreComponent = z.object({value: z.string().optional(), label: z.string(), score: z.number().nullable().optional()}); export const educationSearchScoreComponent = z.object({value: z.string().optional(), label: z.string(), score: z.number().nullable().optional()}); export const occupationGroupSearchScoreComponent = z.object({value: z.string().optional(), label: z.string(), score: z.number().nullable().optional()}); export const searchExpressionSearchScoreComponent = z.object({label: z.string(), value: z.string().optional(), score: z.number().nullable().optional()}); export const customDataSearchScoreComponent = z.record(z.string(), z.object({value: z.string().optional(), label: z.string(), score: z.number().nullable().optional()})); export const resumeSearchResult = z.object({identifier: identifier, score: z.number(), pdf: z.string().nullable(), name: z.string().optional(), jobTitle: jobTitleSearchScoreComponent, managementLevel: managementLevelSearchScoreComponent, experience: experienceSearchScoreComponent, skills: skillsSearchScoreComponent, languages: languagesSearchScoreComponent, location: locationSearchScoreComponent, education: educationSearchScoreComponent, occupationGroup: occupationGroupSearchScoreComponent, searchExpression: searchExpressionSearchScoreComponent, customData: customDataSearchScoreComponent}); export const resumeSearch = z.object({count: z.number().int().gte(0).optional(), next: z.string().nullable().optional(), previous: z.string().nullable().optional(), parameters: resumeSearchParameters.optional(), results: z.array(resumeSearchResult).optional()}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * Search through parsed resumes * * POST /v3/resume_search */ export const createApiV3ResumeSearch = async (args: CreateApiV3ResumeSearchArgs, options?: CreateApiV3ResumeSearchOptions) => { const {baseUrl = "https://api.affinda.com", fetch: fetchFn = fetch, ApiKeyAuth} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") if (args.offset != null) query.append("offset", String(args.offset)) if (args.limit != null) query.append("limit", String(args.limit)) if (ApiKeyAuth !== undefined) headers.set('authorization', `Bearer ${ApiKeyAuth}`) const url = `${baseUrl.replace(/\/+$/, '')}/v3/resume_search${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "POST", headers, body: JSON.stringify(args.body)}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(resumeSearch, await res.json()) };