curltrim 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +96 -0
- package/dist/src/assertions.d.ts +6 -0
- package/dist/src/assertions.js +176 -0
- package/dist/src/assertions.js.map +1 -0
- package/dist/src/candidates.d.ts +3 -0
- package/dist/src/candidates.js +74 -0
- package/dist/src/candidates.js.map +1 -0
- package/dist/src/cli.d.ts +4 -0
- package/dist/src/cli.js +168 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/config.d.ts +5 -0
- package/dist/src/config.js +87 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/curl-parser.d.ts +2 -0
- package/dist/src/curl-parser.js +110 -0
- package/dist/src/curl-parser.js.map +1 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.js +11 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/inspect.d.ts +2 -0
- package/dist/src/inspect.js +29 -0
- package/dist/src/inspect.js.map +1 -0
- package/dist/src/interactive.d.ts +56 -0
- package/dist/src/interactive.js +181 -0
- package/dist/src/interactive.js.map +1 -0
- package/dist/src/json-output.d.ts +3 -0
- package/dist/src/json-output.js +27 -0
- package/dist/src/json-output.js.map +1 -0
- package/dist/src/minimizer.d.ts +10 -0
- package/dist/src/minimizer.js +61 -0
- package/dist/src/minimizer.js.map +1 -0
- package/dist/src/render-curl.d.ts +2 -0
- package/dist/src/render-curl.js +53 -0
- package/dist/src/render-curl.js.map +1 -0
- package/dist/src/replayer.d.ts +2 -0
- package/dist/src/replayer.js +82 -0
- package/dist/src/replayer.js.map +1 -0
- package/dist/src/request-rendering.d.ts +6 -0
- package/dist/src/request-rendering.js +31 -0
- package/dist/src/request-rendering.js.map +1 -0
- package/dist/src/terminal.d.ts +19 -0
- package/dist/src/terminal.js +187 -0
- package/dist/src/terminal.js.map +1 -0
- package/dist/src/types.d.ts +121 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { toJsonObject } from "curlconverter";
|
|
2
|
+
export function parseCurlCommand(curlCommand) {
|
|
3
|
+
const parsed = toJsonObject(curlCommand);
|
|
4
|
+
const headers = normalizeHeaders(parsed.headers ?? {});
|
|
5
|
+
const cookies = normalizeCookies(parsed.cookies ?? {}, headers);
|
|
6
|
+
const url = new URL(parsed.raw_url);
|
|
7
|
+
const query = normalizeQuery(url.searchParams);
|
|
8
|
+
url.search = "";
|
|
9
|
+
const requestBody = normalizeBody(parsed.data, headers);
|
|
10
|
+
return {
|
|
11
|
+
method: (parsed.method || inferMethod(requestBody.bodyType)).toUpperCase(),
|
|
12
|
+
url: normalizeUrl(url),
|
|
13
|
+
followRedirects: parsed.follow_redirects === true,
|
|
14
|
+
headers,
|
|
15
|
+
cookies,
|
|
16
|
+
query,
|
|
17
|
+
...requestBody
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function normalizeHeaders(headers) {
|
|
21
|
+
return Object.fromEntries(Object.entries(headers)
|
|
22
|
+
.filter((entry) => entry[1] !== null)
|
|
23
|
+
.map(([key, value]) => [key.toLowerCase(), value]));
|
|
24
|
+
}
|
|
25
|
+
function normalizeCookies(parsedCookies, headers) {
|
|
26
|
+
const headerCookies = parseCookieHeader(headers.cookie);
|
|
27
|
+
delete headers.cookie;
|
|
28
|
+
return {
|
|
29
|
+
...headerCookies,
|
|
30
|
+
...parsedCookies
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function parseCookieHeader(cookieHeader) {
|
|
34
|
+
if (!cookieHeader) {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
return Object.fromEntries(cookieHeader
|
|
38
|
+
.split(";")
|
|
39
|
+
.map((part) => part.trim())
|
|
40
|
+
.filter(Boolean)
|
|
41
|
+
.map((part) => {
|
|
42
|
+
const separatorIndex = part.indexOf("=");
|
|
43
|
+
if (separatorIndex === -1) {
|
|
44
|
+
return [part, ""];
|
|
45
|
+
}
|
|
46
|
+
return [part.slice(0, separatorIndex), part.slice(separatorIndex + 1)];
|
|
47
|
+
}));
|
|
48
|
+
}
|
|
49
|
+
function normalizeQuery(searchParams) {
|
|
50
|
+
const query = {};
|
|
51
|
+
for (const [key, value] of searchParams) {
|
|
52
|
+
const existingValue = query[key];
|
|
53
|
+
if (existingValue === undefined) {
|
|
54
|
+
query[key] = value;
|
|
55
|
+
}
|
|
56
|
+
else if (Array.isArray(existingValue)) {
|
|
57
|
+
existingValue.push(value);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
query[key] = [existingValue, value];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return query;
|
|
64
|
+
}
|
|
65
|
+
function normalizeUrl(url) {
|
|
66
|
+
const serializedUrl = url.toString();
|
|
67
|
+
return url.pathname === "/" ? serializedUrl.replace(/\/$/, "") : serializedUrl;
|
|
68
|
+
}
|
|
69
|
+
function normalizeBody(data, headers) {
|
|
70
|
+
if (data === undefined) {
|
|
71
|
+
return { body: undefined, bodyType: "none" };
|
|
72
|
+
}
|
|
73
|
+
const contentType = headers["content-type"]?.toLowerCase() ?? "";
|
|
74
|
+
if (typeof data === "string") {
|
|
75
|
+
if (contentType.includes("application/x-www-form-urlencoded")) {
|
|
76
|
+
const urlencodedBody = parseUrlEncodedBody(data);
|
|
77
|
+
if (urlencodedBody !== undefined) {
|
|
78
|
+
return { body: urlencodedBody, bodyType: "urlencoded" };
|
|
79
|
+
}
|
|
80
|
+
return { body: data, bodyType: "raw" };
|
|
81
|
+
}
|
|
82
|
+
if (contentType.includes("application/json")) {
|
|
83
|
+
try {
|
|
84
|
+
return { body: JSON.parse(data), bodyType: "json" };
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return { body: data, bodyType: "raw" };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return { body: data, bodyType: "raw" };
|
|
91
|
+
}
|
|
92
|
+
if (contentType.includes("application/json")) {
|
|
93
|
+
return { body: data, bodyType: "json" };
|
|
94
|
+
}
|
|
95
|
+
if (contentType.includes("application/x-www-form-urlencoded")) {
|
|
96
|
+
return { body: data, bodyType: "urlencoded" };
|
|
97
|
+
}
|
|
98
|
+
return { body: data, bodyType: "form" };
|
|
99
|
+
}
|
|
100
|
+
function parseUrlEncodedBody(data) {
|
|
101
|
+
const parts = data.split("&");
|
|
102
|
+
if (!parts.every((part) => part.indexOf("=") > 0)) {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
return Object.fromEntries(new URLSearchParams(data));
|
|
106
|
+
}
|
|
107
|
+
function inferMethod(bodyType) {
|
|
108
|
+
return bodyType === "none" ? "GET" : "POST";
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=curl-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"curl-parser.js","sourceRoot":"","sources":["../../src/curl-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from "./types.js";
|
|
2
|
+
export * from "./config.js";
|
|
3
|
+
export * from "./assertions.js";
|
|
4
|
+
export * from "./curl-parser.js";
|
|
5
|
+
export * from "./candidates.js";
|
|
6
|
+
export * from "./render-curl.js";
|
|
7
|
+
export * from "./replayer.js";
|
|
8
|
+
export * from "./minimizer.js";
|
|
9
|
+
export * from "./inspect.js";
|
|
10
|
+
export * from "./json-output.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from "./types.js";
|
|
2
|
+
export * from "./config.js";
|
|
3
|
+
export * from "./assertions.js";
|
|
4
|
+
export * from "./curl-parser.js";
|
|
5
|
+
export * from "./candidates.js";
|
|
6
|
+
export * from "./render-curl.js";
|
|
7
|
+
export * from "./replayer.js";
|
|
8
|
+
export * from "./minimizer.js";
|
|
9
|
+
export * from "./inspect.js";
|
|
10
|
+
export * from "./json-output.js";
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createCandidates } from "./candidates.js";
|
|
2
|
+
export function buildInspectSummary(request, targets, assertions) {
|
|
3
|
+
const candidates = createCandidates(request, targets);
|
|
4
|
+
return {
|
|
5
|
+
method: request.method,
|
|
6
|
+
url: request.url,
|
|
7
|
+
followRedirects: request.followRedirects,
|
|
8
|
+
headers: summarizeRecord(request.headers),
|
|
9
|
+
cookies: summarizeRecord(request.cookies),
|
|
10
|
+
query: summarizeRecord(request.query),
|
|
11
|
+
body: summarizeBody(request),
|
|
12
|
+
targets,
|
|
13
|
+
assertions,
|
|
14
|
+
candidateCount: candidates.length,
|
|
15
|
+
estimatedReplayCount: candidates.length + 1
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function summarizeRecord(record) {
|
|
19
|
+
const keys = Object.keys(record);
|
|
20
|
+
return { count: keys.length, keys };
|
|
21
|
+
}
|
|
22
|
+
function summarizeBody(request) {
|
|
23
|
+
if (request.body !== null && typeof request.body === "object" && !Array.isArray(request.body)) {
|
|
24
|
+
const keys = Object.keys(request.body);
|
|
25
|
+
return { type: request.bodyType, candidateCount: keys.length, keys };
|
|
26
|
+
}
|
|
27
|
+
return { type: request.bodyType, candidateCount: 0, keys: [] };
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=inspect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inspect.js","sourceRoot":"","sources":["../../src/inspect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAUnD,MAAM,UAAU,mBAAmB,CACjC,OAAqB,EACrB,OAAiB,EACjB,UAA2B;IAE3B,MAAM,UAAU,GAAG,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAEtD,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,eAAe,EAAE,OAAO,CAAC,eAAe;QACxC,OAAO,EAAE,eAAe,CAAC,OAAO,CAAC,OAAO,CAAC;QACzC,OAAO,EAAE,eAAe,CAAC,OAAO,CAAC,OAAO,CAAC;QACzC,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC;QACrC,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC;QAC5B,OAAO;QACP,UAAU;QACV,cAAc,EAAE,UAAU,CAAC,MAAM;QACjC,oBAAoB,EAAE,UAAU,CAAC,MAAM,GAAG,CAAC;KAC5C,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,MAA+B;IACtD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;AACtC,CAAC;AAED,SAAS,aAAa,CAAC,OAAqB;IAC1C,IAAI,OAAO,CAAC,IAAI,KAAK,IAAI,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9F,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,EAAE,cAAc,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;IACvE,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,EAAE,cAAc,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;AACjE,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { CliConfig, Target } from "./types.js";
|
|
2
|
+
type InputMode = "paste" | "file";
|
|
3
|
+
type RunMode = "trim" | "inspect";
|
|
4
|
+
type OutputFormat = "terminal" | "json";
|
|
5
|
+
type SelectValue = InputMode | RunMode | OutputFormat;
|
|
6
|
+
type SelectChoice<T extends string> = T | {
|
|
7
|
+
name: string;
|
|
8
|
+
value: T;
|
|
9
|
+
};
|
|
10
|
+
type CheckboxChoice<T extends string> = T | {
|
|
11
|
+
name: string;
|
|
12
|
+
value: T;
|
|
13
|
+
checked?: boolean;
|
|
14
|
+
};
|
|
15
|
+
interface SelectPromptConfig<T extends string = SelectValue> {
|
|
16
|
+
message: string;
|
|
17
|
+
choices: readonly SelectChoice<T>[];
|
|
18
|
+
default?: T;
|
|
19
|
+
}
|
|
20
|
+
interface InputPromptConfig {
|
|
21
|
+
message: string;
|
|
22
|
+
default?: string;
|
|
23
|
+
required?: boolean;
|
|
24
|
+
}
|
|
25
|
+
interface EditorPromptConfig {
|
|
26
|
+
message: string;
|
|
27
|
+
default?: string;
|
|
28
|
+
required?: boolean;
|
|
29
|
+
}
|
|
30
|
+
interface CheckboxPromptConfig<T extends string> {
|
|
31
|
+
message: string;
|
|
32
|
+
choices: readonly CheckboxChoice<T>[];
|
|
33
|
+
}
|
|
34
|
+
interface ConfirmPromptConfig {
|
|
35
|
+
message: string;
|
|
36
|
+
default?: boolean;
|
|
37
|
+
}
|
|
38
|
+
interface ErrorPromptConfig {
|
|
39
|
+
message: string;
|
|
40
|
+
}
|
|
41
|
+
export interface PromptApi {
|
|
42
|
+
select<T extends SelectValue>(config: SelectPromptConfig<T>): Promise<T>;
|
|
43
|
+
input(config: InputPromptConfig): Promise<string>;
|
|
44
|
+
editor(config: EditorPromptConfig): Promise<string>;
|
|
45
|
+
checkbox(config: CheckboxPromptConfig<Target>): Promise<Target[]>;
|
|
46
|
+
confirm(config: ConfirmPromptConfig): Promise<boolean>;
|
|
47
|
+
error(config: ErrorPromptConfig): void;
|
|
48
|
+
}
|
|
49
|
+
export interface InteractiveOptions {
|
|
50
|
+
plain?: boolean;
|
|
51
|
+
inspect?: boolean;
|
|
52
|
+
json?: boolean;
|
|
53
|
+
}
|
|
54
|
+
export declare function runInteractive(options?: InteractiveOptions): Promise<CliConfig>;
|
|
55
|
+
export declare function collectInteractiveConfig(prompts: PromptApi, options?: InteractiveOptions): Promise<CliConfig>;
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { checkbox, confirm, editor, input, select } from "@inquirer/prompts";
|
|
3
|
+
import { DEFAULT_TARGETS, parseTargets } from "./config.js";
|
|
4
|
+
import { parseCurlCommand } from "./curl-parser.js";
|
|
5
|
+
const defaultPrompts = {
|
|
6
|
+
select: (config) => select(config),
|
|
7
|
+
input: (config) => input(config),
|
|
8
|
+
editor: (config) => editor(config),
|
|
9
|
+
checkbox: (config) => checkbox(config),
|
|
10
|
+
confirm: (config) => confirm(config),
|
|
11
|
+
error: (config) => console.error(config.message)
|
|
12
|
+
};
|
|
13
|
+
export async function runInteractive(options = { plain: false }) {
|
|
14
|
+
if (!process.stdin.isTTY) {
|
|
15
|
+
throw new Error("No curl input provided. Pass a curl command, use --file, or run curltrim in an interactive TTY.");
|
|
16
|
+
}
|
|
17
|
+
return collectInteractiveConfig(defaultPrompts, options);
|
|
18
|
+
}
|
|
19
|
+
export async function collectInteractiveConfig(prompts, options = {}) {
|
|
20
|
+
const curlCommand = await readValidatedCurlCommand(prompts);
|
|
21
|
+
const output = await collectOutputOptions(prompts, options);
|
|
22
|
+
if (output.inspect) {
|
|
23
|
+
return {
|
|
24
|
+
curlCommand: curlCommand.trim(),
|
|
25
|
+
targets: [...DEFAULT_TARGETS],
|
|
26
|
+
assertions: {
|
|
27
|
+
text: [],
|
|
28
|
+
regex: [],
|
|
29
|
+
json: []
|
|
30
|
+
},
|
|
31
|
+
delayMs: 0,
|
|
32
|
+
plain: Boolean(options.plain),
|
|
33
|
+
inspect: true,
|
|
34
|
+
json: output.json
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const selectedTargets = await prompts.checkbox({
|
|
38
|
+
message: "Targets to trim",
|
|
39
|
+
choices: [
|
|
40
|
+
{ name: "cookies", value: "cookies", checked: true },
|
|
41
|
+
{ name: "headers", value: "headers", checked: true },
|
|
42
|
+
{ name: "query", value: "query" },
|
|
43
|
+
{ name: "body", value: "body" }
|
|
44
|
+
]
|
|
45
|
+
});
|
|
46
|
+
const status = await prompts.input({
|
|
47
|
+
message: "Expected HTTP status",
|
|
48
|
+
default: "200-299"
|
|
49
|
+
});
|
|
50
|
+
const text = splitPromptList(await prompts.input({ message: "Expected text contains, one per line or empty" }));
|
|
51
|
+
const regex = splitPromptList(await prompts.input({ message: "Expected regex, one per line or empty" }));
|
|
52
|
+
const json = splitPromptList(await prompts.input({
|
|
53
|
+
message: "Expected JSONPath assertions, one per line or empty"
|
|
54
|
+
}));
|
|
55
|
+
const delay = await prompts.input({
|
|
56
|
+
message: "Request delay in milliseconds",
|
|
57
|
+
default: "0"
|
|
58
|
+
});
|
|
59
|
+
const delayMs = parseDelay(delay);
|
|
60
|
+
const accepted = await prompts.confirm({
|
|
61
|
+
message: "Start replay and trimming now?",
|
|
62
|
+
default: true
|
|
63
|
+
});
|
|
64
|
+
if (!accepted) {
|
|
65
|
+
throw new Error("Interactive configuration cancelled");
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
curlCommand: curlCommand.trim(),
|
|
69
|
+
targets: parseTargets(selectedTargets.length > 0 ? selectedTargets.join(",") : undefined),
|
|
70
|
+
assertions: {
|
|
71
|
+
status: status.trim() || "200-299",
|
|
72
|
+
text,
|
|
73
|
+
regex,
|
|
74
|
+
json
|
|
75
|
+
},
|
|
76
|
+
delayMs,
|
|
77
|
+
plain: Boolean(options.plain),
|
|
78
|
+
inspect: false,
|
|
79
|
+
json: output.json
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
async function collectOutputOptions(prompts, options) {
|
|
83
|
+
if (options.inspect !== undefined || options.json !== undefined) {
|
|
84
|
+
return {
|
|
85
|
+
inspect: Boolean(options.inspect),
|
|
86
|
+
json: Boolean(options.json)
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const runMode = await prompts.select({
|
|
90
|
+
message: "Run mode",
|
|
91
|
+
choices: [
|
|
92
|
+
{ name: "Trim request", value: "trim" },
|
|
93
|
+
{ name: "Inspect only", value: "inspect" }
|
|
94
|
+
],
|
|
95
|
+
default: "trim"
|
|
96
|
+
});
|
|
97
|
+
const outputFormat = await prompts.select({
|
|
98
|
+
message: "Output format",
|
|
99
|
+
choices: [
|
|
100
|
+
{ name: "Terminal", value: "terminal" },
|
|
101
|
+
{ name: "JSON", value: "json" }
|
|
102
|
+
],
|
|
103
|
+
default: "terminal"
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
inspect: runMode === "inspect",
|
|
107
|
+
json: outputFormat === "json"
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
async function readValidatedCurlCommand(prompts) {
|
|
111
|
+
while (true) {
|
|
112
|
+
const inputMode = await prompts.select({
|
|
113
|
+
message: "How do you want to provide the curl request?",
|
|
114
|
+
choices: [
|
|
115
|
+
{ name: "Paste curl text", value: "paste" },
|
|
116
|
+
{ name: "Read from file", value: "file" }
|
|
117
|
+
],
|
|
118
|
+
default: "paste"
|
|
119
|
+
});
|
|
120
|
+
const result = inputMode === "file" ? await readCurlFile(prompts) : await readCurlText(prompts);
|
|
121
|
+
if (!result.ok) {
|
|
122
|
+
switch (result.kind) {
|
|
123
|
+
case "read":
|
|
124
|
+
prompts.error({ message: `Unable to read curl file: ${result.error}` });
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const error = validateCurlCommand(result.value);
|
|
129
|
+
if (error === undefined) {
|
|
130
|
+
return result.value.trim();
|
|
131
|
+
}
|
|
132
|
+
prompts.error({ message: `Invalid curl command: ${error}` });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async function readCurlFile(prompts) {
|
|
136
|
+
const filePath = (await prompts.input({ message: "curl file path", required: true })).trim();
|
|
137
|
+
try {
|
|
138
|
+
return { ok: true, value: await readFile(filePath, "utf8") };
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
return { ok: false, kind: "read", error: formatError(error) };
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async function readCurlText(prompts) {
|
|
145
|
+
return {
|
|
146
|
+
ok: true,
|
|
147
|
+
value: await prompts.editor({
|
|
148
|
+
message: "curl command, paste text then save and exit",
|
|
149
|
+
required: true
|
|
150
|
+
})
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
function validateCurlCommand(curlCommand) {
|
|
154
|
+
if (!curlCommand.trim()) {
|
|
155
|
+
return "curl command is empty";
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
parseCurlCommand(curlCommand);
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
return formatError(error);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function splitPromptList(value) {
|
|
166
|
+
return value
|
|
167
|
+
.split(/\r?\n/)
|
|
168
|
+
.map((item) => item.trim())
|
|
169
|
+
.filter(Boolean);
|
|
170
|
+
}
|
|
171
|
+
function parseDelay(value) {
|
|
172
|
+
const delayMs = Number(value.trim() || "0");
|
|
173
|
+
if (!Number.isInteger(delayMs) || delayMs < 0) {
|
|
174
|
+
throw new Error("Delay must be a non-negative integer");
|
|
175
|
+
}
|
|
176
|
+
return delayMs;
|
|
177
|
+
}
|
|
178
|
+
function formatError(error) {
|
|
179
|
+
return error instanceof Error ? error.message : String(error);
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=interactive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interactive.js","sourceRoot":"","sources":["../../src/interactive.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function renderInspectJson(summary) {
|
|
2
|
+
return renderJson({
|
|
3
|
+
mode: "inspect",
|
|
4
|
+
requestSummary: summary,
|
|
5
|
+
targets: summary.targets,
|
|
6
|
+
assertions: summary.assertions,
|
|
7
|
+
candidateCount: summary.candidateCount,
|
|
8
|
+
estimatedReplayCount: summary.estimatedReplayCount
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
export function renderTrimJson(result) {
|
|
12
|
+
return renderJson({
|
|
13
|
+
mode: "trim",
|
|
14
|
+
originalOk: result.originalOk,
|
|
15
|
+
removed: result.removed,
|
|
16
|
+
kept: result.kept,
|
|
17
|
+
finalCurl: result.finalCurl,
|
|
18
|
+
finalRequest: {
|
|
19
|
+
...result.finalRequest,
|
|
20
|
+
body: result.finalRequest.body === undefined ? null : result.finalRequest.body
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function renderJson(value) {
|
|
25
|
+
return `${JSON.stringify(value, null, 2)}\n`;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=json-output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-output.js","sourceRoot":"","sources":["../../src/json-output.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,iBAAiB,CAAC,OAAuB;IACvD,OAAO,UAAU,CAAC;QAChB,IAAI,EAAE,SAAS;QACf,cAAc,EAAE,OAAO;QACvB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,oBAAoB,EAAE,OAAO,CAAC,oBAAoB;KACnD,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAkB;IAC/C,OAAO,UAAU,CAAC;QAChB,IAAI,EAAE,MAAM;QACZ,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,YAAY,EAAE;YACZ,GAAG,MAAM,CAAC,YAAY;YACtB,IAAI,EAAE,MAAM,CAAC,YAAY,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI;SAC/E;KACF,CAAC,CAAC;AACL,CAAC;AAED,SAAS,UAAU,CAAC,KAAc;IAChC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AssertionConfig, MinimizeProgressEvent, ReplayResponse, RequestModel, Target, TrimResult } from "./types.js";
|
|
2
|
+
export interface MinimizeOptions {
|
|
3
|
+
request: RequestModel;
|
|
4
|
+
targets: Target[];
|
|
5
|
+
assertions: AssertionConfig;
|
|
6
|
+
delayMs: number;
|
|
7
|
+
replay(request: RequestModel, delayMs: number): Promise<ReplayResponse>;
|
|
8
|
+
onProgress?(event: MinimizeProgressEvent): void;
|
|
9
|
+
}
|
|
10
|
+
export declare function minimizeRequest(options: MinimizeOptions): Promise<TrimResult>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { assertReplaySuccess } from "./assertions.js";
|
|
2
|
+
import { createCandidates, removeCandidate } from "./candidates.js";
|
|
3
|
+
import { renderCurl } from "./render-curl.js";
|
|
4
|
+
export async function minimizeRequest(options) {
|
|
5
|
+
options.onProgress?.({ type: "original:start", label: "original request" });
|
|
6
|
+
const original = await replaySafely(options, options.request);
|
|
7
|
+
if (!original.ok) {
|
|
8
|
+
options.onProgress?.({
|
|
9
|
+
type: "original:failure",
|
|
10
|
+
label: "original request",
|
|
11
|
+
error: formatFailure(original.error)
|
|
12
|
+
});
|
|
13
|
+
throw new Error(`Original request did not satisfy success assertions: ${formatFailure(original.error)}`);
|
|
14
|
+
}
|
|
15
|
+
options.onProgress?.({ type: "original:success", label: "original request" });
|
|
16
|
+
let current = options.request;
|
|
17
|
+
const removed = [];
|
|
18
|
+
const kept = [];
|
|
19
|
+
const candidates = createCandidates(current, options.targets);
|
|
20
|
+
for (const [offset, candidate] of candidates.entries()) {
|
|
21
|
+
const index = offset + 1;
|
|
22
|
+
const total = candidates.length;
|
|
23
|
+
options.onProgress?.({ type: "candidate:start", index, total, label: candidate.label });
|
|
24
|
+
const next = removeCandidate(current, candidate);
|
|
25
|
+
const result = await replaySafely(options, next);
|
|
26
|
+
if (result.ok) {
|
|
27
|
+
current = next;
|
|
28
|
+
removed.push({ ...candidate, ok: true });
|
|
29
|
+
options.onProgress?.({ type: "candidate:removed", index, total, label: candidate.label });
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
const error = formatFailure(result.error);
|
|
33
|
+
kept.push({ ...candidate, ok: false, error });
|
|
34
|
+
options.onProgress?.({ type: "candidate:kept", index, total, label: candidate.label, error });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
originalOk: true,
|
|
39
|
+
removed,
|
|
40
|
+
kept,
|
|
41
|
+
finalRequest: current,
|
|
42
|
+
finalCurl: renderCurl(current)
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
async function replaySafely(options, request) {
|
|
46
|
+
try {
|
|
47
|
+
const response = await options.replay(request, options.delayMs);
|
|
48
|
+
const result = assertReplaySuccess(response, options.assertions);
|
|
49
|
+
return result.ok ? { ok: true } : { ok: false, error: result.reasons.join("; ") };
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
return { ok: false, error: formatError(error) };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function formatFailure(reason) {
|
|
56
|
+
return reason || "success assertions failed";
|
|
57
|
+
}
|
|
58
|
+
function formatError(error) {
|
|
59
|
+
return error instanceof Error ? error.message : String(error);
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=minimizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"minimizer.js","sourceRoot":"","sources":["../../src/minimizer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { renderCookieHeader, renderRequestUrl, renderUrlEncodedBody } from "./request-rendering.js";
|
|
2
|
+
export function renderCurl(request) {
|
|
3
|
+
const parts = ["curl"];
|
|
4
|
+
const method = request.method.toUpperCase();
|
|
5
|
+
if (request.followRedirects) {
|
|
6
|
+
parts.push("-L");
|
|
7
|
+
}
|
|
8
|
+
parts.push(shellQuote(renderRequestUrl(request)));
|
|
9
|
+
if (method !== "GET") {
|
|
10
|
+
parts.push("-X", method);
|
|
11
|
+
}
|
|
12
|
+
for (const [key, value] of Object.entries(request.headers)) {
|
|
13
|
+
if (key.toLowerCase() !== "cookie") {
|
|
14
|
+
parts.push("-H", shellQuote(`${key}: ${value}`));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const cookieHeader = renderCookieHeader(request.cookies);
|
|
18
|
+
if (cookieHeader) {
|
|
19
|
+
parts.push("-H", shellQuote(`Cookie: ${cookieHeader}`));
|
|
20
|
+
}
|
|
21
|
+
parts.push(...renderBodyParts(request));
|
|
22
|
+
return parts.join(" ");
|
|
23
|
+
}
|
|
24
|
+
function renderBodyParts(request) {
|
|
25
|
+
if (request.bodyType === "none") {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
if (request.bodyType === "form") {
|
|
29
|
+
return renderFormFields(request.body).flatMap((field) => ["-F", shellQuote(field)]);
|
|
30
|
+
}
|
|
31
|
+
if (request.bodyType === "json") {
|
|
32
|
+
return ["--data", shellQuote(JSON.stringify(request.body) ?? String(request.body))];
|
|
33
|
+
}
|
|
34
|
+
if (request.bodyType === "urlencoded") {
|
|
35
|
+
return ["--data", shellQuote(renderUrlEncodedBody(request.body))];
|
|
36
|
+
}
|
|
37
|
+
return ["--data", shellQuote(String(request.body))];
|
|
38
|
+
}
|
|
39
|
+
function renderFormFields(body) {
|
|
40
|
+
if (body !== null && typeof body === "object" && !Array.isArray(body)) {
|
|
41
|
+
return Object.entries(body).flatMap(([key, value]) => {
|
|
42
|
+
if (Array.isArray(value)) {
|
|
43
|
+
return value.map((item) => `${key}=${String(item)}`);
|
|
44
|
+
}
|
|
45
|
+
return `${key}=${String(value)}`;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return [String(body)];
|
|
49
|
+
}
|
|
50
|
+
function shellQuote(value) {
|
|
51
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=render-curl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-curl.js","sourceRoot":"","sources":["../../src/render-curl.ts"],"names":[],"mappings":"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"}
|