snapshot of https://github.com/mountain-loop/yaak.git (main, 2026-09-23; blobs > 131072B omitted)
Some checks failed
Lint and Test / Lint/Test (push) Has been cancelled
Generate Sponsors README / deploy (push) Has been skipped

This commit is contained in:
upstream-mirror
2026-09-23 17:39:41 +05:00
commit 906fb231cc
1544 changed files with 164131 additions and 0 deletions

View File

@@ -0,0 +1,52 @@
# Yaak HTTP Snippet Plugin
Generate code snippets for HTTP requests in various languages and frameworks,
powered by [@readme/httpsnippet](https://github.com/readmeio/httpsnippet).
![Httpsnippet plugin](https://assets.yaak.app/uploads/httpsnippet-guiaX_1786x1420.png)
## How It Works
Right-click any HTTP request (or use the `...` menu) and select **Generate Code Snippet**.
A dialog lets you pick a language and library, with a live preview of the generated code.
Click **Copy to Clipboard** to copy the snippet. Your language and library selections are
remembered for next time.
## Supported Languages
Each language supports one or more libraries, listed in the order they appear in the
dialog:
| Language | Libraries |
| ----------- | ----------------------------------------- |
| Agent | Agent Prompt |
| C | Libcurl |
| Clojure | clj-http |
| Crystal | http::client |
| C# | HttpClient, RestSharp |
| Go | NewRequest |
| HTTP | HTTP/1.1 |
| Java | AsyncHttp, java.net.http, OkHttp, Unirest |
| JavaScript | XMLHttpRequest, Axios, fetch, jQuery |
| Kotlin | OkHttp |
| Node.js | HTTP, Axios, fetch |
| Objective-C | NSURLSession |
| OCaml | CoHTTP |
| PHP | cURL, Guzzle, HTTP v1, HTTP v2 |
| Powershell | Invoke-WebRequest, Invoke-RestMethod |
| Python | Requests |
| R | httr |
| Ruby | net::http, faraday |
| Rust | reqwest |
| Shell | cURL, HTTPie, Wget |
| Swift | URLSession |
The **Agent** target produces a prompt describing the request rather than code, for handing
to an AI coding agent.
## Features
- Renders template variables before generating snippets, so the output reflects real values
- Supports all body types: JSON, form-urlencoded, multipart, GraphQL, and raw text
- Includes authentication headers (Basic, Bearer, and API Key)
- Includes query parameters and custom headers

View File

@@ -0,0 +1,24 @@
{
"name": "@yaak/httpsnippet",
"displayName": "HTTP Snippet",
"version": "1.0.3",
"private": true,
"description": "Generate code snippets for HTTP requests in various languages and frameworks",
"repository": {
"type": "git",
"url": "https://github.com/mountain-loop/yaak.git",
"directory": "plugins-external/httpsnippet"
},
"scripts": {
"build": "yaakcli build",
"dev": "yaakcli dev"
},
"dependencies": {
"@readme/httpsnippet": "^11.0.0"
},
"devDependencies": {
"@types/node": "^22.0.0",
"typescript": "^5.9.3"
},
"minYaakVersion": "2026.2.0-beta.10"
}

View File

@@ -0,0 +1,317 @@
import { availableTargets, type HarRequest, HTTPSnippet } from "@readme/httpsnippet";
import type { EditorLanguage, HttpRequest, PluginDefinition } from "@yaakapp/api";
// Get all available targets and build select options
const targets = availableTargets();
// Targets to exclude from the language list
const excludedTargets = new Set(["json"]);
// Build language (target) options
const languageOptions = targets
.filter((target) => !excludedTargets.has(target.key))
.map((target) => ({
label: target.title,
value: target.key,
}));
// Preferred clients per target (shown first in the list)
const preferredClients: Record<string, string> = {
javascript: "fetch",
node: "fetch",
};
// Get client options for a given target key
function getClientOptions(targetKey: string) {
const target = targets.find((t) => t.key === targetKey);
if (!target) return [];
const preferred = preferredClients[targetKey];
return target.clients
.map((client) => ({
label: client.title,
value: client.key,
}))
.sort((a, b) => {
if (a.value === preferred) return -1;
if (b.value === preferred) return 1;
return 0;
});
}
// Get default client for a target
function getDefaultClient(targetKey: string): string {
const options = getClientOptions(targetKey);
return options[0]?.value ?? "";
}
// Defaults
const defaultTarget = "javascript";
// Map httpsnippet target key to editor language for syntax highlighting
const editorLanguageMap: Record<string, EditorLanguage> = {
c: "c",
clojure: "clojure",
csharp: "csharp",
go: "go",
http: "http",
java: "java",
javascript: "javascript",
kotlin: "kotlin",
node: "javascript",
objc: "objective_c",
ocaml: "ocaml",
php: "php",
powershell: "powershell",
python: "python",
r: "r",
ruby: "ruby",
shell: "shell",
swift: "swift",
};
function getEditorLanguage(targetKey: string): EditorLanguage {
return editorLanguageMap[targetKey] ?? "text";
}
// Convert Yaak HttpRequest to HAR format
function toHarRequest(request: Partial<HttpRequest>) {
// Build URL with query parameters
let finalUrl = request.url || "";
const urlParams = (request.urlParameters ?? []).filter((p) => p.enabled !== false && !!p.name);
if (urlParams.length > 0) {
const [base, hash] = finalUrl.split("#");
const separator = base?.includes("?") ? "&" : "?";
const queryString = urlParams
.map((p) => `${encodeURIComponent(p.name)}=${encodeURIComponent(p.value)}`)
.join("&");
finalUrl = base + separator + queryString + (hash ? `#${hash}` : "");
}
// Build headers array
const headers: Array<{ name: string; value: string }> = (request.headers ?? [])
.filter((h) => h.enabled !== false && !!h.name)
.map((h) => ({ name: h.name, value: h.value }));
// Handle authentication
if (request.authentication?.disabled !== true) {
if (request.authenticationType === "basic") {
const credentials = btoa(
`${request.authentication?.username ?? ""}:${request.authentication?.password ?? ""}`,
);
headers.push({ name: "Authorization", value: `Basic ${credentials}` });
} else if (request.authenticationType === "bearer") {
const prefix = request.authentication?.prefix ?? "Bearer";
const token = request.authentication?.token ?? "";
headers.push({ name: "Authorization", value: `${prefix} ${token}`.trim() });
} else if (request.authenticationType === "apikey") {
if (request.authentication?.location === "header") {
headers.push({
name: request.authentication?.key ?? "X-Api-Key",
value: request.authentication?.value ?? "",
});
} else if (request.authentication?.location === "query") {
const sep = finalUrl.includes("?") ? "&" : "?";
finalUrl = [
finalUrl,
sep,
encodeURIComponent(request.authentication?.key ?? "token"),
"=",
encodeURIComponent(request.authentication?.value ?? ""),
].join("");
}
}
}
// Build HAR request object
const har: Record<string, unknown> = {
method: request.method || "GET",
url: finalUrl,
headers,
};
// Handle request body
const bodyType = request.bodyType ?? "none";
if (bodyType !== "none" && request.body) {
if (bodyType === "application/x-www-form-urlencoded" && Array.isArray(request.body.form)) {
const params = request.body.form
.filter((p: { enabled?: boolean; name?: string }) => p.enabled !== false && !!p.name)
.map((p: { name: string; value: string }) => ({ name: p.name, value: p.value }));
har.postData = {
mimeType: "application/x-www-form-urlencoded",
params,
};
} else if (bodyType === "multipart/form-data" && Array.isArray(request.body.form)) {
const params = request.body.form
.filter((p: { enabled?: boolean; name?: string }) => p.enabled !== false && !!p.name)
.map((p: { name: string; value: string; file?: string; contentType?: string }) => {
const param: Record<string, string> = { name: p.name, value: p.value || "" };
if (p.file) param.fileName = p.file;
if (p.contentType) param.contentType = p.contentType;
return param;
});
har.postData = {
mimeType: "multipart/form-data",
params,
};
} else if (bodyType === "graphql" && typeof request.body.query === "string") {
const body = {
query: request.body.query || "",
variables: maybeParseJSON(request.body.variables, undefined),
};
har.postData = {
mimeType: "application/json",
text: JSON.stringify(body),
};
} else if (typeof request.body.text === "string") {
har.postData = {
mimeType: bodyType,
text: request.body.text,
};
}
}
return har;
}
function maybeParseJSON<T>(v: unknown, fallback: T): unknown {
if (typeof v !== "string") return fallback;
try {
return JSON.parse(v);
} catch {
return fallback;
}
}
export const plugin: PluginDefinition = {
httpRequestActions: [
{
label: "Generate Code Snippet",
icon: "copy",
async onSelect(ctx, args) {
// Render the request with variables resolved
const renderedRequest = await ctx.httpRequest.render({
httpRequest: args.httpRequest,
purpose: "send",
});
// Convert to HAR format
const harRequest = toHarRequest(renderedRequest) as HarRequest;
// Get previously selected language or use defaults
const storedTarget = await ctx.store.get<string>("selectedTarget");
const initialTarget = storedTarget || defaultTarget;
const storedClient = await ctx.store.get<string>(`selectedClient:${initialTarget}`);
const initialClient = storedClient || getDefaultClient(initialTarget);
// Create snippet generator
const snippet = new HTTPSnippet(harRequest);
const generateSnippet = (target: string, client: string): string => {
const result = snippet.convert(
target as Parameters<typeof snippet.convert>[0],
client as Parameters<typeof snippet.convert>[1],
);
return (Array.isArray(result) ? result.join("\n") : result || "").replace(/\r\n/g, "\n");
};
// Generate initial code preview
let initialCode = "";
try {
initialCode = generateSnippet(initialTarget, initialClient);
} catch {
initialCode = "// Error generating snippet";
}
// Show dialog with language/library selectors and code preview
const result = await ctx.prompt.form({
id: "httpsnippet",
title: "Generate Code Snippet",
confirmText: "Copy to Clipboard",
cancelText: "Cancel",
size: "md",
inputs: [
{
type: "h_stack",
inputs: [
{
type: "select",
name: "target",
label: "Language",
defaultValue: initialTarget,
options: languageOptions,
},
{
type: "select",
name: `client-${initialTarget}`,
label: "Library",
defaultValue: initialClient,
options: getClientOptions(initialTarget),
dynamic(_ctx, { values }) {
const targetKey = String(values.target || defaultTarget);
const options = getClientOptions(targetKey);
return {
name: `client-${targetKey}`,
options,
defaultValue: options[0]?.value ?? "",
};
},
},
],
},
{
type: "editor",
name: "code",
label: "Preview",
language: getEditorLanguage(initialTarget),
defaultValue: initialCode,
readOnly: true,
rows: 15,
dynamic(_ctx, { values }) {
const targetKey = String(values.target || defaultTarget);
const clientKey = String(
values[`client-${targetKey}`] || getDefaultClient(targetKey),
);
let code: string;
try {
code = generateSnippet(targetKey, clientKey);
} catch {
code = "// Error generating snippet";
}
return {
defaultValue: code,
language: getEditorLanguage(targetKey),
};
},
},
],
});
if (result) {
// Store the selected language and library for next time
const selectedTarget = String(result.target || initialTarget);
const selectedClient = String(
result[`client-${selectedTarget}`] || getDefaultClient(selectedTarget),
);
await ctx.store.set("selectedTarget", selectedTarget);
await ctx.store.set(`selectedClient:${selectedTarget}`, selectedClient);
// Generate snippet for the selected language
try {
const codeText = generateSnippet(selectedTarget, selectedClient);
await ctx.clipboard.copyText(codeText);
await ctx.toast.show({
message: "Code snippet copied to clipboard",
icon: "copy",
color: "success",
});
} catch (err) {
await ctx.toast.show({
message: `Failed to generate snippet: ${err instanceof Error ? err.message : String(err)}`,
icon: "alert_triangle",
color: "danger",
});
}
}
},
},
],
};