swaggler
Version:
Swaggler helps you smuggle your existing API requests into structured, well-documented specs with ease
142 lines (141 loc) • 5.53 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CurlParser = void 0;
class CurlParser {
static parse(curlCommand) {
const result = {
method: "GET",
url: "",
headers: {},
queryParams: {},
data: undefined,
contentType: undefined,
};
// Extract URL + query params
const urlMatch = curlCommand.match(/curl\s+'([^']+)'|curl\s+"([^"]+)"|curl\s+([^\s]+)/);
if (urlMatch) {
const url = urlMatch[1] || urlMatch[2] || urlMatch[3];
try {
const parsedUrl = new URL(url);
result.url = parsedUrl.origin + parsedUrl.pathname;
parsedUrl.searchParams.forEach((value, key) => {
result.queryParams[key] = value;
});
}
catch {
result.url = url;
}
}
// Extract explicit -X METHOD
const methodMatch = curlCommand.match(/-X\s+([A-Z]+)/i);
if (methodMatch) {
result.method = methodMatch[1].toUpperCase();
}
// Extract headers
for (const match of curlCommand.matchAll(/-H\s+'([^']+)'|-H\s+"([^"]+)"/g)) {
const header = match[1] || match[2];
const [key, ...valueParts] = header.split(": ");
result.headers[key] = valueParts.join(": ");
// Check for content-type header
if (key.toLowerCase() === "content-type") {
result.contentType = valueParts.join(": ");
}
}
// Gather all body/form flags
const dataRawMatches = Array.from(curlCommand.matchAll(/--data-raw\s+['"]?([^'"\s]+)['"]?/gi)).map((m) => m[1]);
const dataMatches = Array.from(curlCommand.matchAll(/(?:-d|--data)\s+['"]([^'"]+)['"]/gi)).map((m) => m[1]);
const urlEncMatches = Array.from(curlCommand.matchAll(/--data-urlencode\s+['"]?([^'"\s]+)['"]?/gi)).map((m) => m[1]);
const formMatches = Array.from(curlCommand.matchAll(/(?:-F|--form)\s+['"]([^'"]+)['"]/gi)).map((m) => m[1]);
// If there's any body/form but no explicit -X, assume POST
if (!methodMatch &&
(dataRawMatches.length ||
dataMatches.length ||
urlEncMatches.length ||
formMatches.length)) {
result.method = "POST";
}
// Process multipart form fields
if (formMatches.length) {
result.contentType = "multipart/form-data";
result.data = {};
formMatches.forEach((pair) => {
const [key, val] = pair.split("=");
if (val !== undefined) {
result.data[key] = val;
}
});
}
// Process URL-encoded bodies
else if (dataMatches.length || urlEncMatches.length) {
result.contentType =
result.contentType || "application/x-www-form-urlencoded";
result.data = {};
[...dataMatches, ...urlEncMatches].forEach((chunk) => {
chunk.split("&").forEach((kv) => {
const [key, val] = kv.split("=");
if (val !== undefined) {
result.data[key] = decodeURIComponent(val);
}
});
});
}
// Process raw JSON or other payloads
else if (dataRawMatches.length) {
const raw = dataRawMatches.join("&");
// If content-type header is set to application/x-www-form-urlencoded, parse as form data
if (result.contentType === "application/x-www-form-urlencoded") {
result.data = {};
raw.split("&").forEach((kv) => {
const [key, val] = kv.split("=");
if (val !== undefined) {
result.data[key] = decodeURIComponent(val);
}
});
}
else {
try {
result.data = JSON.parse(raw);
result.contentType = "application/json";
}
catch {
result.data = raw;
result.contentType = "text/plain";
}
}
}
return result;
}
static sanitize(curlCommand) {
// Remove authorization header
curlCommand = curlCommand.replace(/-H\s+'Authorization:[^']+'/gi, "");
curlCommand = curlCommand.replace(/-H\s+"Authorization:[^"]+"/gi, "");
// Remove common browser headers
const headersToRemove = [
"accept-encoding",
"accept-language",
"sec-fetch-dest",
"sec-fetch-mode",
"sec-fetch-site",
"user-agent",
"priority",
"referer",
"origin",
"cookie",
"connection",
"cache-control",
"pragma",
"accept",
];
headersToRemove.forEach((header) => {
const regex = new RegExp(`-H\\s+'${header}:[^']+'|\\s*-H\\s+"${header}:[^"]+"`, "gi");
curlCommand = curlCommand.replace(regex, "");
});
// Clean up the command
return curlCommand
.replace(/\\\s*\n/g, " ")
.replace(/\s+/g, " ")
.replace(/\s*-H\s+/g, " -H ")
.trim();
}
}
exports.CurlParser = CurlParser;