Sign In

@decodo/cli

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@decodo/cli - npm Package Compare versions

Comparing version
0.1.6
to
0.1.7
+3
build/esm/platform/errors/cli-usage-error.d.ts
export declare class CliUsageError extends Error {
constructor(message: string);
}
export class CliUsageError extends Error {
constructor(message) {
super(message);
this.name = "CliUsageError";
}
}
+2
-1
import { Command } from "commander";
import { getRootOpts } from "../../cli/services/global-opts.js";
import { CliUsageError, handleCliError, } from "../../platform/services/handle-cli-error.js";
import { CliUsageError } from "../../platform/errors/cli-usage-error.js";
import { handleCliError } from "../../platform/services/handle-cli-error.js";
import { promptHidden } from "../../platform/services/prompt-hidden.js";

@@ -5,0 +6,0 @@ import { validateAuthToken } from "../../scrape/services/auth-validation.js";

export function applyRequestDefaults(body, target, schema) {
if (body.headless === "png") {
return;
}
const properties = schema.getTargetParameterSchema(target)?.properties ?? {};

@@ -3,0 +6,0 @@ if (properties.parse !== undefined && body.parse === undefined) {

@@ -1,4 +0,1 @@

export declare class CliUsageError extends Error {
constructor(message: string);
}
export declare function resolveCliExitCode(err: unknown): number;

@@ -5,0 +2,0 @@ export declare function handleCliError(err: unknown, options?: {

@@ -5,8 +5,27 @@ import { AuthenticationError, DecodoError, RateLimitError, TimeoutError, ValidationError, } from "@decodo/sdk-ts";

import { EXIT } from "../constants.js";
import { CliUsageError } from "../errors/cli-usage-error.js";
const EXIT_SIGNAL_PREFIX = "process.exit:";
export class CliUsageError extends Error {
constructor(message) {
super(message);
this.name = "CliUsageError";
const NETWORK_ERROR_CODES = new Set([
"ENOTFOUND",
"ECONNREFUSED",
"ECONNRESET",
"ETIMEDOUT",
"EAI_AGAIN",
"EHOSTUNREACH",
"ENETUNREACH",
"EPIPE",
]);
function findNetworkCause(err) {
const seen = new Set();
let current = err;
while (current && typeof current === "object" && !seen.has(current)) {
seen.add(current);
const code = current.code;
if (typeof code === "string" && NETWORK_ERROR_CODES.has(code)) {
const message = current.message;
return { code, message: typeof message === "string" ? message : code };
}
current = current.cause;
}
return;
}

@@ -32,2 +51,5 @@ export function resolveCliExitCode(err) {

}
if (findNetworkCause(err)) {
return EXIT.NETWORK;
}
return EXIT.ERROR;

@@ -76,2 +98,6 @@ }

console.error(`Error: ${message}`);
const networkCause = findNetworkCause(err);
if (networkCause) {
console.error(`Cause: ${networkCause.code} (${networkCause.message})`);
}
if (err instanceof ValidationError) {

@@ -78,0 +104,0 @@ const details = extractValidationDetails(err);

import { stdin, stdout } from "node:process";
import { createInterface } from "node:readline/promises";
import { CliUsageError } from "../errors/cli-usage-error.js";
const CHAR_ETX = 3;
const CHAR_EOT = 4;
const CHAR_DEL = 127;
const CHAR_BACKSPACE = 8;
function handleHiddenPromptChar(char, state) {
if (char === "\u0003") {
const code = char.charCodeAt(0);
if (code === CHAR_ETX) {
state.cleanup();

@@ -10,2 +16,8 @@ stdout.write("\n");

}
if (code === CHAR_EOT) {
state.cleanup();
stdout.write("\n");
state.reject(new CliUsageError("No auth token provided on stdin."));
return;
}
if (char === "\r" || char === "\n") {

@@ -17,3 +29,3 @@ state.cleanup();

}
if (char === "\u007f" || char === "\b") {
if (code === CHAR_DEL || code === CHAR_BACKSPACE) {
if (state.input.length > 0) {

@@ -27,11 +39,19 @@ state.input = state.input.slice(0, -1);

}
async function promptViaReadline(message) {
const rl = createInterface({ input: stdin, output: stdout });
try {
return await new Promise((resolve, reject) => {
rl.question(message).then((answer) => resolve(answer.trim()), reject);
rl.once("close", () => {
reject(new CliUsageError("No auth token provided on stdin."));
});
});
}
finally {
rl.close();
}
}
export async function promptHidden(message) {
if (!stdin.isTTY) {
const rl = createInterface({ input: stdin, output: stdout });
try {
return (await rl.question(message)).trim();
}
finally {
rl.close();
}
return await promptViaReadline(message);
}

@@ -54,2 +74,7 @@ stdout.write(message);

};
const onEnd = () => {
state.cleanup();
stdout.write("\n");
reject(new CliUsageError("No auth token provided on stdin."));
};
state.cleanup = () => {

@@ -59,5 +84,7 @@ stdin.setRawMode(false);

stdin.removeListener("data", onData);
stdin.removeListener("end", onEnd);
};
stdin.on("data", onData);
stdin.on("end", onEnd);
});
}
import { writeFileSync } from "node:fs";
import { resolve } from "node:path";
import { CliUsageError, handleCliError } from "./handle-cli-error.js";
import { CliUsageError } from "../errors/cli-usage-error.js";
import { handleCliError } from "./handle-cli-error.js";
import { resolveOutputFilePath } from "./resolve-output-file.js";

@@ -5,0 +6,0 @@ export const BINARY_TTY_ERROR = "Refusing to write binary data to a TTY. Use -o <file> or pipe to a file.";

@@ -1,2 +0,2 @@

import { Option } from "commander";
import { InvalidArgumentError, Option } from "commander";
import { attachScrapeOutputOptions } from "../../output/commands/attach-output-options.js";

@@ -20,4 +20,20 @@ import { applyRequestDefaults } from "../../output/services/apply-request-defaults.js";

}
if (propertySchema.type === "array") {
return "JSON array";
}
if (propertySchema.type === "object") {
return "JSON object";
}
return String(propertySchema.type ?? "value");
}
function parseJsonArg(field) {
return (value) => {
try {
return JSON.parse(value);
}
catch {
throw new InvalidArgumentError(`--${snakeToKebab(field)} expects valid JSON.`);
}
};
}
function addPropertyOption(command, field, propertySchema) {

@@ -42,2 +58,6 @@ const kebabFlag = snakeToKebab(field);

}
if (propertySchema.type === "array" || propertySchema.type === "object") {
command.option(`--${kebabFlag} <json>`, help, parseJsonArg(field));
return;
}
command.option(`--${kebabFlag} <value>`, help);

@@ -44,0 +64,0 @@ }

@@ -21,2 +21,11 @@ import { AuthRequiredError } from "../../auth/errors/auth-required-error.js";

}
function resolveOutputContext(explicit, body, input) {
if (explicit?.binary) {
return explicit;
}
if (body.headless === "png") {
return { ...explicit, binary: { kind: "png" }, input };
}
return explicit;
}
export function createTargetAction(target, schema, buildBody, getOutputContext) {

@@ -47,3 +56,3 @@ const config = getTargetCommandConfig(target, schema);

verboseLog(verbose, formatScrapeRequestLog(body));
const outputContext = getOutputContext?.(input, options);
const outputContext = resolveOutputContext(getOutputContext?.(input, options), body, input);
await executeScrape({

@@ -50,0 +59,0 @@ token: auth.token,

{
"name": "@decodo/cli",
"version": "0.1.6",
"version": "0.1.7",
"description": "Official CLI for the Decodo APIs",

@@ -5,0 +5,0 @@ "license": "MIT",

@@ -283,3 +283,3 @@ # Decodo CLI

Ensure npm's global bin directory is on your `PATH` after `npm install -g`. Re-run the [install script](https://decodo.github.io/cli/install.sh) or use `npx @decodo/cli`.
The [install script](https://decodo.github.io/cli/install.sh) auto-configures PATH and prints a `source` step — re-run it or open a new terminal. You can also use `npx @decodo/cli`.

@@ -286,0 +286,0 @@ **Validation / API errors**