@clickup/rest-client
Version:
A syntax sugar tool around Node fetch() API, tailored to work with TypeScript and response validators
68 lines (57 loc) • 1.86 kB
text/typescript
import { inspect } from "util";
import sortBy from "lodash/sortBy";
import ellipsis from "./ellipsis";
export default function inspectPossibleJSON(
headers: { get(name: string): string | null },
text: string | Buffer | NodeJS.ReadableStream,
maxOutputLen: number,
): string {
const MAX_LEN_TO_TRY_PARSE = 1024 * 1024;
if (typeof text === "string" && text.length > MAX_LEN_TO_TRY_PARSE) {
// Don't even try to JSON-parse if the text is too long.
return ellipsis(text, maxOutputLen);
}
if (text instanceof Buffer) {
return `<Buffer: ${text.length} bytes>`;
}
if (!text || typeof text === "string") {
if (!(headers.get("content-type") || "").match(/json/)) {
return ellipsis(text, maxOutputLen);
}
try {
const json = JSON.parse(text);
if (json && typeof json === "object" && !(json instanceof Array)) {
// Move error/errors fields on top for better logging. This is a poor
// man's approach: of course not all APIs return error/errors fields at
// all, but it's hard to reorder at any other layer of abstraction.
reorderObjectProps(json, (k) =>
k === "error" || k === "errors" ? "" : k,
);
}
return ellipsis(
inspect(json, { depth: 20, compact: true }),
maxOutputLen,
);
} catch (e: any) {
return ellipsis(text, maxOutputLen);
}
}
return "<Stream>";
}
/**
* In-place-reorders keys in a given object. The important part is to do it
* in-place to e.g. be able to alter some @Memoized values.
*/
function reorderObjectProps(
obj: Record<string, any>,
ranker: (k: string, v: any) => string | number,
) {
const entries = Object.entries(obj);
for (const k in obj) {
delete obj[k];
}
Object.assign(
obj,
Object.fromEntries(sortBy(entries, ([k, v]) => ranker(k, v))),
);
}