@decodo/cli
Advanced tools
| export declare class ScrapeFailedError extends Error { | ||
| readonly statusCode: number | undefined; | ||
| constructor(message: string, statusCode?: number); | ||
| } |
| export class ScrapeFailedError extends Error { | ||
| statusCode; | ||
| constructor(message, statusCode) { | ||
| super(message); | ||
| this.name = "ScrapeFailedError"; | ||
| this.statusCode = statusCode; | ||
| } | ||
| } |
| import type { SyncResponse } from "@decodo/sdk-ts"; | ||
| import { ScrapeFailedError } from "../errors/scrape-failed-error.js"; | ||
| export declare function detectScrapeFailure(response: SyncResponse): ScrapeFailedError | undefined; | ||
| export declare function detectDegradedStatus(response: SyncResponse): number | undefined; |
| import { ScrapeFailedError } from "../errors/scrape-failed-error.js"; | ||
| const HTTP_ERROR_THRESHOLD = 400; | ||
| function readString(source, key) { | ||
| const value = source[key]; | ||
| return typeof value === "string" && value.length > 0 ? value : undefined; | ||
| } | ||
| function readNumber(source, key) { | ||
| const value = source[key]; | ||
| return typeof value === "number" ? value : undefined; | ||
| } | ||
| function failureFromEnvelope(value) { | ||
| if (typeof value !== "object" || value === null) { | ||
| return; | ||
| } | ||
| const envelope = value; | ||
| if (envelope.status !== "failed") { | ||
| return; | ||
| } | ||
| const statusCode = readNumber(envelope, "status_code"); | ||
| const message = readString(envelope, "message") ?? | ||
| (statusCode === undefined | ||
| ? "Scrape failed." | ||
| : `Scrape failed with status ${statusCode}.`); | ||
| return new ScrapeFailedError(message, statusCode); | ||
| } | ||
| function hasUsableContent(content) { | ||
| if (content === undefined || content === null) { | ||
| return false; | ||
| } | ||
| if (typeof content === "string") { | ||
| return content.trim().length > 0; | ||
| } | ||
| if (Array.isArray(content)) { | ||
| return content.length > 0; | ||
| } | ||
| if (typeof content === "object") { | ||
| const envelope = content; | ||
| if (Array.isArray(envelope.results)) { | ||
| return envelope.results.length > 0; | ||
| } | ||
| return Object.keys(envelope).length > 0; | ||
| } | ||
| return true; | ||
| } | ||
| function failureFromStatus(entry) { | ||
| const { status_code: statusCode } = entry; | ||
| if (typeof statusCode !== "number" || statusCode < HTTP_ERROR_THRESHOLD) { | ||
| return; | ||
| } | ||
| if (hasUsableContent(entry.content)) { | ||
| return; | ||
| } | ||
| const message = (typeof entry.content === "object" && entry.content !== null | ||
| ? readString(entry.content, "message") | ||
| : undefined) ?? `Scrape request returned status ${statusCode}.`; | ||
| return new ScrapeFailedError(message, statusCode); | ||
| } | ||
| function readResults(response) { | ||
| return Array.isArray(response.results) ? response.results : []; | ||
| } | ||
| export function detectScrapeFailure(response) { | ||
| const topLevelFailure = failureFromEnvelope(response); | ||
| if (topLevelFailure) { | ||
| return topLevelFailure; | ||
| } | ||
| for (const entry of readResults(response)) { | ||
| const failure = failureFromEnvelope(entry.content) ?? failureFromStatus(entry); | ||
| if (failure) { | ||
| return failure; | ||
| } | ||
| } | ||
| return; | ||
| } | ||
| export function detectDegradedStatus(response) { | ||
| for (const entry of readResults(response)) { | ||
| const { status_code: statusCode } = entry; | ||
| if (typeof statusCode === "number" && | ||
| statusCode >= HTTP_ERROR_THRESHOLD && | ||
| hasUsableContent(entry.content)) { | ||
| return statusCode; | ||
| } | ||
| } | ||
| return; | ||
| } |
| import { writeBinaryOutput } from "../../platform/services/write-binary.js"; | ||
| import { extractPngFromResponse } from "../../scrape/services/extract-png.js"; | ||
| import { defaultScreenshotFilename } from "../../scrape/services/screenshot-output-filename.js"; | ||
| import { detectDegradedStatus, detectScrapeFailure, } from "./detect-scrape-failure.js"; | ||
| import { extractPayload } from "./extract-payload.js"; | ||
@@ -11,2 +12,10 @@ import { renderPayload } from "./render-output.js"; | ||
| const { options } = context; | ||
| const failure = detectScrapeFailure(response); | ||
| if (failure) { | ||
| throw failure; | ||
| } | ||
| const degradedStatus = detectDegradedStatus(response); | ||
| if (degradedStatus !== undefined) { | ||
| console.error(`Warning: target returned HTTP ${degradedStatus}; emitting the content it returned`); | ||
| } | ||
| if (context.binary?.kind === "png") { | ||
@@ -13,0 +22,0 @@ writeBinaryOutput(extractPngFromResponse(response), { |
| import { AuthenticationError, DecodoError, RateLimitError, TimeoutError, ValidationError, } from "@decodo/sdk-ts"; | ||
| import { PLAYGROUND_URL } from "../../auth/constants.js"; | ||
| import { AuthRequiredError } from "../../auth/errors/auth-required-error.js"; | ||
| import { ScrapeFailedError } from "../../output/errors/scrape-failed-error.js"; | ||
| import { EXIT } from "../constants.js"; | ||
@@ -47,3 +48,9 @@ import { CliUsageError } from "../errors/cli-usage-error.js"; | ||
| } | ||
| if (err instanceof ScrapeFailedError) { | ||
| return EXIT.NETWORK; | ||
| } | ||
| if (err instanceof DecodoError) { | ||
| if (err.statusCode === 400 || err.statusCode === 422) { | ||
| return EXIT.VALIDATION; | ||
| } | ||
| return EXIT.NETWORK; | ||
@@ -50,0 +57,0 @@ } |
+1
-1
| { | ||
| "name": "@decodo/cli", | ||
| "version": "1.0.0", | ||
| "version": "1.0.1", | ||
| "description": "Official CLI for the Decodo APIs", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
70502
6.2%121
3.42%1512
8.31%