import {z} from 'zod' export type GetApiV1JobsJobIdResultArgs = {jobId: string, response_format?: ('customer_v1' | 'customer_v1_lite' | 'legacy') | undefined}; export type GetApiV1JobsJobIdResultOptions = {baseUrl?: string | undefined, apiKey?: string | undefined, fetch?: typeof fetch | undefined}; export const jobResultResponse = z.union([z.object({jobId: z.string(), poweredBy: z.literal("On-Page.ai"), meta: z.object({report_date: z.string().optional(), target_keyword: z.string().optional(), location: z.string().optional(), url: z.string().optional()}), categories: z.array(z.object({category: z.string(), score: z.number()}))}), z.object({jobId: z.string(), poweredBy: z.literal("On-Page.ai"), meta: z.object({report_date: z.string().nullable(), target_keyword: z.string().nullable(), location: z.string().nullable(), url: z.string().nullable()}), on_page_optimization: z.object({score: z.number().int().gte(0).lte(100).nullable(), grade: z.enum(["Excellent", "Strong", "Good", "Needs Work", "Under-optimized", "Unavailable"]), confidence: z.number().gte(0).lte(1), summary: z.string(), focus_areas: z.array(z.string()), algorithm_version: z.literal("onpage-optimization-score-v1.1"), chart: z.string().nullable()}), benchmarks: z.record(z.string(), z.unknown()), entity_coverage: z.record(z.string(), z.unknown()), topic_and_classification: z.object({your_page: z.object({category: z.string(), confidence: z.number().int().nullable()}).nullable()}).and(z.record(z.string(), z.unknown())), internal_linking: z.record(z.string(), z.unknown()), competitor_term_coverage: z.record(z.string(), z.unknown()), structured_data_benchmarks: z.object({page1_average: z.object({schema_type_count: z.number().nullable(), schema_node_count: z.number().nullable(), schema_property_count: z.number().nullable(), json_ld_block_count: z.number().nullable(), microdata_item_count: z.number().nullable()}), your_url: z.object({schema_type_count: z.number().nullable(), schema_node_count: z.number().nullable(), schema_property_count: z.number().nullable(), json_ld_block_count: z.number().nullable(), microdata_item_count: z.number().nullable()})}).optional(), structured_data_coverage: z.object({competitor_count: z.number(), top_competitor_count: z.number(), schema_types: z.array(z.object({schema_type: z.string(), present_in_competitors: z.number(), present_in_top_3: z.number(), your_url_count: z.number()}))}).optional(), serp_speed_benchmark: z.object({status: z.enum(["ok", "disabled", "skipped_not_deep", "skipped_no_target_url", "skipped_budget_exhausted", "timeout", "internal_error"]), what_this_measures: z.string(), measurement_type: z.literal("single_run_lab"), device_profile: z.literal("mobile_viewport_unthrottled_lab"), network_profile: z.literal("unthrottled"), cpu_profile: z.literal("unthrottled"), benchmark_version: z.string(), web_vitals_version: z.string().optional(), target: z.record(z.string(), z.unknown()).optional(), competitors: z.record(z.string(), z.unknown()).optional(), chart_core_web_vitals: z.string().nullable(), chart_resource_weight: z.string().nullable()}).and(z.record(z.string(), z.unknown())).optional(), originality: z.object({score: z.number().int().gte(0).lte(100), grade: z.enum(["Highly original", "Moderately original", "Mostly shared"]), sentence_analysis: z.object({original: z.number().int(), shared: z.number().int(), duplicative: z.number().int(), total_scored: z.number().int()}), duplicative_passages: z.array(z.object({snippet: z.string(), matched_domain: z.string()})), information_gain: z.object({content_most_competitors_have: z.array(z.object({snippet: z.string(), competitor_count: z.number().int()})), potential_uncovered_topics_for_information_gain: z.array(z.string()), unique_data_points: z.object({your_count: z.number().int(), page1_average: z.number().nullable(), examples: z.array(z.string())})}), chart: z.string().nullable()}).optional(), link_opportunities: z.object({algorithm_version: z.string(), keyword: z.string().nullable(), items: z.array(z.object({url: z.string(), position: z.number().int(), domain: z.string().nullable(), title: z.string().nullable(), page_format: z.string(), platform: z.string().nullable(), subreddit: z.string().nullable(), content_date: z.string().nullable(), date_source: z.string().nullable(), domain_rank_bucket: z.string(), topic_relation: z.string(), action: z.string(), scores: z.object({attainability: z.number().int(), willingness: z.number().int(), seo_link_value: z.number().int(), ai_citation_value: z.number().int(), overall: z.number().int()}), reasons: z.array(z.string())}))}).optional(), schema_version: z.literal("onpage-report-customer-v1")}).and(z.record(z.string(), z.unknown())), z.object({jobId: z.string(), poweredBy: z.literal("On-Page.ai"), meta: z.object({report_date: z.string().nullable(), target_keyword: z.string().nullable(), location: z.string().nullable(), url: z.string().nullable()}), on_page_optimization: z.object({score: z.number().int().gte(0).lte(100).nullable(), grade: z.enum(["Excellent", "Strong", "Good", "Needs Work", "Under-optimized", "Unavailable"]), confidence: z.number().gte(0).lte(1), summary: z.string(), focus_areas: z.array(z.string()), algorithm_version: z.literal("onpage-optimization-score-v1.1"), chart: z.string().nullable()}), benchmarks: z.record(z.string(), z.unknown()), entity_coverage: z.object({your_url_related_entity_density_score: z.number().nullable(), competitor_related_entity_density_score: z.number().nullable(), natural_language_entities: z.array(z.record(z.string(), z.unknown())), highly_related_terms: z.array(z.record(z.string(), z.unknown())), keyword_variations: z.array(z.record(z.string(), z.unknown())), chart_density_gap: z.string().nullable(), chart_coverage_status: z.string().nullable(), chart_top_missing: z.string().nullable()}), competitor_term_coverage: z.record(z.string(), z.unknown()), schema_version: z.literal("onpage-report-customer-v1-lite")})]); 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 }; /** * Fetch the terminal result payload for a completed job. * * GET /v1/jobs/{jobId}/result */ export const getApiV1JobsJobIdResult = async (args: GetApiV1JobsJobIdResultArgs, options?: GetApiV1JobsJobIdResultOptions) => { const {baseUrl = "https://api.on-page.ai", fetch: fetchFn = fetch, apiKey} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") if (args.response_format != null) query.append("response_format", String(args.response_format)) if (apiKey !== undefined) headers.set('authorization', `Bearer ${apiKey}`) const url = `${baseUrl.replace(/\/+$/, '')}/v1/jobs/${encodeURIComponent(String(args.jobId))}/result${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "GET", headers}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(jobResultResponse, await res.json()) };