UNPKG

screenshotone-validations

Version:

Validation schemes for the ScreenshotOne API requests.

636 lines (576 loc) 19.9 kB
import Joi from "joi"; import devices from "screenshotone-devices"; const validationOptions = { abortEarly: false }; const accessKeyScheme = { access_key: Joi.string().optional(), }; const signatureScheme = { signature: Joi.string().optional(), }; const createUriValidator = (fieldName: string) => { return (value: string, helper: any) => { try { if (!value) { return helper.message(`"${fieldName}" must be specified`); } if (value.trim().length === 0 || value.trim() !== value) { return helper.message( `"${fieldName}" must be specified without leading and trailing white spaces` ); } const u = new URL(value); if (u.protocol !== "http:" && u.protocol !== "https:") { return helper.message( `"${fieldName}" must be a valid URI with a scheme matching the http|https pattern` ); } const withoutProtocol = value.substring((u.protocol + "//").length); if ( withoutProtocol.startsWith("http://") || withoutProtocol.startsWith("https://") ) { return helper.message( `"${fieldName}" must be a valid URI with a scheme matching the http|https pattern` ); } return value; } catch (e) { return helper.message(`"${fieldName}" must be a valid URI`); } }; }; const validUri = createUriValidator("url"); const screenshotScheme = { // image options image_quality: Joi.when("format", { is: Joi.valid( "jpeg", "jpg", "webp", "png", "tiff", "jp2", "avif", "heif" ), then: Joi.number().integer().min(0).max(100).default(100), otherwise: Joi.forbidden(), }), image_width: Joi.number().integer().optional(), image_height: Joi.number().integer().optional(), omit_background: Joi.when("format", { is: Joi.valid("png"), then: Joi.boolean().default(false), otherwise: Joi.forbidden(), }).messages({ "any.unknown": 'The "omit_background" option is only allowed to use with image formats that support transparent backgrounds, like PNG.', }), // full page options full_page: Joi.boolean().default(false), full_page_scroll: Joi.when("full_page", { is: true, then: Joi.boolean().default(true), otherwise: Joi.forbidden(), }), full_page_scroll_delay: Joi.when("full_page_scroll", { is: true, then: Joi.number().integer().positive().min(100).default(400), otherwise: Joi.forbidden(), }), full_page_scroll_by: Joi.when("full_page_scroll", { is: true, then: Joi.number().integer().positive(), otherwise: Joi.forbidden(), }), full_page_max_height: Joi.when("full_page", { is: true, then: Joi.number().integer().positive().optional(), otherwise: Joi.forbidden(), }), full_page_algorithm: Joi.when("full_page", { is: true, then: Joi.string().valid("by_sections", "default").default("default"), otherwise: Joi.forbidden(), }), capture_beyond_viewport: Joi.when("selector", { is: Joi.string().required(), then: Joi.boolean().default(true), otherwise: Joi.boolean().default(Joi.ref("full_page")), }), selector: Joi.when("format", { is: Joi.valid("pdf"), then: Joi.forbidden(), otherwise: Joi.string().optional(), }).messages({ "any.unknown": 'Rendering PDFs by "selector" is not allowed.', }), selector_algorithm: Joi.when("selector", { is: Joi.string().required(), then: Joi.string().valid("clip", "default").default("default"), otherwise: Joi.forbidden(), }), selector_scroll_into_view: Joi.when("selector", { is: Joi.string().required(), then: Joi.boolean().default(true), otherwise: Joi.forbidden(), }), error_on_selector_not_found: Joi.boolean().default(false), scroll_into_view: Joi.string().optional(), scroll_into_view_adjust_top: Joi.number().integer().default(0), format: Joi.string() .trim() .lowercase() .valid( "png", "jpeg", "jpg", "webp", "gif", "jp2", "tiff", "avif", "heif", "html", "pdf", "markdown" ) .default("jpg"), metadata_image_size: Joi.boolean().default(false), clip_x: Joi.number().integer().optional(), clip_y: Joi.number().integer().optional(), clip_width: Joi.number().integer().optional(), clip_height: Joi.number().integer().optional(), vision_prompt: Joi.string().optional(), vision_max_tokens: Joi.number().integer().optional(), openai_api_key: Joi.string().optional(), pdf_margin: Joi.string().optional(), pdf_margin_top: Joi.string().optional(), pdf_margin_right: Joi.string().optional(), pdf_margin_bottom: Joi.string().optional(), pdf_margin_left: Joi.string().optional(), pdf_print_background: Joi.boolean().optional(), pdf_fit_one_page: Joi.boolean().optional(), pdf_landscape: Joi.boolean().optional(), pdf_paper_format: Joi.string() .valid( "a0", "a1", "a2", "a3", "a4", "a5", "a6", "legal", "letter", "tabloid" ) .optional(), }; const commonOptionsScheme = Joi.object({ // URL or HTML is required url: Joi.string().custom(validUri).optional(), html: Joi.string().optional(), markdown: Joi.string().optional(), response_type: Joi.string() .trim() .lowercase() .valid("by_format", "empty", "json") .default("by_format"), request_gpu_rendering: Joi.boolean().default(false), fail_if_gpu_rendering_fails: Joi.boolean().default(false), include_shadow_dom: Joi.boolean().default(false), // emulation dark_mode: Joi.boolean().optional(), reduced_motion: Joi.boolean().optional(), media_type: Joi.string() .trim() .lowercase() .valid("screen", "print") .optional(), // customization options scripts: Joi.string().optional(), scripts_wait_until: Joi.array() .items( Joi.string().valid( "load", "domcontentloaded", "networkidle0", "networkidle2" ) ) .default([]), styles: Joi.string().optional(), hide_selectors: Joi.array().items(), click: Joi.string().optional(), error_on_click_selector_not_found: Joi.boolean().default(true), // viewport options viewport_device: Joi.string() .valid(...devices.names) .optional(), viewport_width: Joi.number().integer().optional(), viewport_height: Joi.number().integer().optional(), device_scale_factor: Joi.number().min(1).max(5).optional(), viewport_mobile: Joi.boolean().optional(), viewport_has_touch: Joi.boolean().optional(), viewport_landscape: Joi.boolean().optional(), // geolocation options geolocation_latitude: Joi.number().min(-90).max(90).optional(), geolocation_longitude: Joi.number().min(-180).max(180).optional(), geolocation_accuracy: Joi.number().integer().positive().optional(), // location options ip_country_code: Joi.string() .valid( "us", "gb", "de", "it", "fr", "cn", "ca", "es", "jp", "kr", "in", "au", "br", "mx", "nz", "pe", "is", "ie" ) .optional(), servers_region: Joi.string().valid("us-east").default("us-east"), // blocking options block_annoyances: Joi.boolean().default(false), block_cookie_banners: Joi.boolean().default(false), block_banners_by_heuristics: Joi.boolean().default(false), block_chats: Joi.boolean().default(false), block_ads: Joi.boolean().default(false), block_socials: Joi.boolean().default(false), block_trackers: Joi.boolean().default(false), block_requests: Joi.array().items(Joi.string()).default([]), block_resources: Joi.array() .items( Joi.string().valid( "document", "stylesheet", "image", "media", "font", "script", "texttrack", "xhr", "fetch", "eventsource", "websocket", "manifest", "other" ) ) .default([]), // cache options cache: Joi.boolean().default(false).optional(), cache_ttl: Joi.when("cache", { is: true, then: Joi.number() .integer() .min(14400) // 4 hours .max(2592000) // 1 month .default(14400) .optional(), otherwise: Joi.forbidden().messages({ "any.unknown": "The `cache_ttl` option cannot be used when the `cache` option is false or not set.", }), }), cache_key: Joi.when("cache", { is: true, then: Joi.string().alphanum().min(1).max(250).optional(), otherwise: Joi.forbidden().messages({ "any.unknown": "The `cache_key` option cannot be used when the `cache` option is false or not set.", }), }), // request options user_agent: Joi.string().optional(), authorization: Joi.string().optional(), headers: Joi.array().items(), cookies: Joi.array().items(), proxy: Joi.string() // `encodeUri` allows to specify Unicode characters in the proxy URI // it is useful when targeting is used in proxies and cities or countries are specified // with special characters .uri({ scheme: ["http"], encodeUri: true }) // makes sense to double-check it, since it will be anyway validated and fail if not correct .custom(createUriValidator("proxy")) .optional(), attachment_name: Joi.string().optional(), bypass_csp: Joi.boolean().default(false).optional(), time_zone: Joi.string().valid( "America/Belize", "America/Cayman", "America/Chicago", "America/Costa_Rica", "America/Denver", "America/Edmonton", "America/El_Salvador", "America/Guatemala", "America/Guayaquil", "America/Hermosillo", "America/Jamaica", "America/Los_Angeles", "America/Mexico_City", "America/Nassau", "America/New_York", "America/Panama", "America/Port-au-Prince", "America/Santiago", "America/Tegucigalpa", "America/Tijuana", "America/Toronto", "America/Vancouver", "America/Winnipeg", "Asia/Kuala_Lumpur", "Asia/Shanghai", "Asia/Tashkent", "Europe/Berlin", "Europe/Kiev", "Europe/Lisbon", "Europe/London", "Europe/Madrid", "Pacific/Auckland", "Pacific/Majuro" ), // wait, timeout delay: Joi.number() .integer() .min(0) .when("timeout", { is: Joi.number().greater(300), then: Joi.number().max(300), otherwise: Joi.number().max(30), }) .optional(), timeout: Joi.number().when("async", { is: true, then: Joi.number().integer().min(0).max(600).default(600), otherwise: Joi.number().integer().min(0).max(90).default(60), }), navigation_timeout: Joi.number().integer().min(0).max(30).default(30), wait_until: Joi.array() .items( Joi.string().valid( "load", "domcontentloaded", "networkidle0", "networkidle2" ) ) .default([]), wait_for_selector: Joi.string().optional(), wait_for_selector_algorithm: Joi.string() .valid("at_least_one", "at_least_by_count") .default("at_least_one") .optional(), fail_if_content_contains: Joi.array() .items(Joi.string().optional()) .default([]), fail_if_content_missing: Joi.array() .items(Joi.string().optional()) .default([]), fail_if_request_failed: Joi.array() .items(Joi.string().optional()) .default([]), async: Joi.boolean().default(false), webhook_url: Joi.string() .trim() .custom(validUri) .when("response_type", { is: Joi.string().valid("json", "by_format"), then: Joi.optional(), otherwise: Joi.forbidden(), }), webhook_sign: Joi.boolean().default(true), webhook_errors: Joi.boolean().default(false), external_identifier: Joi.string().alphanum().min(1).max(64).optional(), // store store: Joi.boolean().optional(), storage_bucket: Joi.string().optional(), storage_path: Joi.string().when("store", { is: true, then: Joi.required(), otherwise: Joi.forbidden(), }), storage_endpoint: Joi.string().uri().optional(), storage_access_key_id: Joi.string().optional(), storage_secret_access_key: Joi.string().optional(), storage_acl: Joi.string().valid("public-read", "").default(""), storage_class: Joi.string() .valid( "standard", "reduced_redundancy", "standard_ia", "onezone_ia", "intelligent_tiering", "glacier", "deep_archive", "outposts", "glacier_ir" ) .default("standard"), storage_return_location: Joi.boolean().default(false), ignore_host_errors: Joi.boolean().default(false), metadata_fonts: Joi.boolean().default(false), metadata_content: Joi.boolean().default(false), metadata_page_title: Joi.boolean().default(false), metadata_open_graph: Joi.boolean().default(false), metadata_http_response_status_code: Joi.boolean() .default(false) .when("url", { is: Joi.exist(), then: Joi.boolean(), otherwise: Joi.forbidden(), }), metadata_http_response_headers: Joi.boolean().default(false).when("url", { is: Joi.exist(), then: Joi.boolean(), otherwise: Joi.forbidden(), }), metadata_icon: Joi.boolean().default(false), }).oxor("ip_country_code", "proxy"); const optionsScheme = commonOptionsScheme.append(screenshotScheme); const withEssentialsOptionsScheme = optionsScheme.or("html", "url", "markdown"); const bulkScheme = Joi.object({ optimize: Joi.boolean().default(false), execute: Joi.boolean().default(false), options: Joi.object(), requests: Joi.array().items(withEssentialsOptionsScheme).min(1).max(20), }); const withHtmlOrUrlOrMarkdownRequired = commonOptionsScheme.or( "html", "url", "markdown" ); const animateScheme = withHtmlOrUrlOrMarkdownRequired .append({ format: Joi.string() .trim() .lowercase() .valid("mp4", "avi", "mov", "webm", "gif") .default("mp4"), duration: Joi.number().min(1).max(30).default(5), omit_background: Joi.when("format", { is: Joi.valid("mov"), then: Joi.boolean().default(false), otherwise: Joi.forbidden(), }), width: Joi.number().integer().optional(), height: Joi.number().integer().optional(), aspect_ratio: Joi.string() .trim() .lowercase() .valid("4:3", "16:9") .default("4:3"), scenario: Joi.string() .trim() .lowercase() .valid("", "default", "scroll") .default("default"), // scroll scenario parameters scroll_duration: Joi.number().min(100).default(1500), scroll_delay: Joi.number().min(0).default(500), scroll_by: Joi.number().min(1).default(1000), scroll_start_immediately: Joi.boolean().default(true), scroll_start_delay: Joi.number().min(0).default(0), scroll_complete: Joi.boolean().default(true), scroll_back_after_duration: Joi.number().integer().optional(), scroll_back: Joi.boolean().default(true), scroll_back_algorithm: Joi.string() .trim() .lowercase() .valid("once", "repeat") .default("once"), scroll_stop_after_duration: Joi.when("scroll_back", { is: false, then: Joi.number().integer().optional(), otherwise: Joi.forbidden(), }), scroll_till_selector: Joi.string().optional(), scroll_till_selector_adjust_top: Joi.number().integer().optional(), scroll_try_navigate: Joi.boolean().default(false).optional(), scroll_navigate_after: Joi.number().integer().optional(), scroll_navigate_to_url: Joi.string().custom(validUri).optional(), scroll_navigate_link_hints: Joi.array() .items(Joi.string().trim()) .default(["pricing", "about", "customers"]) .optional(), scroll_to_end_after: Joi.when("scenario", { is: Joi.valid("scroll"), then: Joi.number().integer().optional(), otherwise: Joi.forbidden(), }), clip_x: Joi.when("format", { is: Joi.valid("gif"), then: Joi.number().integer().optional(), otherwise: Joi.forbidden(), }), clip_y: Joi.when("format", { is: Joi.valid("gif"), then: Joi.number().integer().optional(), otherwise: Joi.forbidden(), }), clip_height: Joi.when("format", { is: Joi.valid("gif"), then: Joi.number().integer().optional(), otherwise: Joi.forbidden(), }), clip_width: Joi.when("format", { is: Joi.valid("gif"), then: Joi.number().integer().optional(), otherwise: Joi.forbidden(), }), scroll_easing: Joi.string() .trim() .lowercase() .valid( "linear", "ease_in_quad", "ease_out_quad", "ease_in_out_quad", "ease_in_cubic", "ease_out_cubic", "ease_in_out_cubic", "ease_in_quart", "ease_out_quart", "ease_in_out_quart", "ease_in_quint", "ease_out_quint", "ease_in_out_quint" ) .default("ease_in_out_quint"), }) .oxor("scroll_stop_after_duration", "scroll_back_after_duration"); export default { animate: { getScheme: animateScheme.append({ ...accessKeyScheme, ...signatureScheme, }), postScheme: animateScheme.append({ ...accessKeyScheme }), validationOptions, }, bulk: { postScheme: bulkScheme.append(accessKeyScheme), validationOptions, }, take: { getScheme: withEssentialsOptionsScheme.append({ ...accessKeyScheme, ...signatureScheme, }), postScheme: withEssentialsOptionsScheme.append({ ...accessKeyScheme }), validationOptions, }, };