mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-18 07:24:07 +01:00
Run oxfmt across repo, add format script and docs
Add .oxfmtignore to skip generated bindings and wasm-pack output. Add npm format script, update DEVELOPMENT.md for Vite+ toolchain, and format all non-generated files with oxfmt. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,26 +16,26 @@ remembered for next time.
|
||||
|
||||
Each language supports one or more libraries:
|
||||
|
||||
| Language | Libraries |
|
||||
|---|---|
|
||||
| C | libcurl |
|
||||
| Clojure | clj-http |
|
||||
| C# | HttpClient, RestSharp |
|
||||
| Go | Native |
|
||||
| HTTP | HTTP/1.1 |
|
||||
| Java | AsyncHttp, NetHttp, OkHttp, Unirest |
|
||||
| JavaScript | Axios, fetch, jQuery, XHR |
|
||||
| Kotlin | OkHttp |
|
||||
| Node.js | Axios, fetch, HTTP, Request, Unirest |
|
||||
| Objective-C | NSURLSession |
|
||||
| OCaml | CoHTTP |
|
||||
| PHP | cURL, Guzzle, HTTP v1, HTTP v2 |
|
||||
| PowerShell | Invoke-WebRequest, RestMethod |
|
||||
| Python | http.client, Requests |
|
||||
| R | httr |
|
||||
| Ruby | Native |
|
||||
| Shell | cURL, HTTPie, Wget |
|
||||
| Swift | URLSession |
|
||||
| Language | Libraries |
|
||||
| ----------- | ------------------------------------ |
|
||||
| C | libcurl |
|
||||
| Clojure | clj-http |
|
||||
| C# | HttpClient, RestSharp |
|
||||
| Go | Native |
|
||||
| HTTP | HTTP/1.1 |
|
||||
| Java | AsyncHttp, NetHttp, OkHttp, Unirest |
|
||||
| JavaScript | Axios, fetch, jQuery, XHR |
|
||||
| Kotlin | OkHttp |
|
||||
| Node.js | Axios, fetch, HTTP, Request, Unirest |
|
||||
| Objective-C | NSURLSession |
|
||||
| OCaml | CoHTTP |
|
||||
| PHP | cURL, Guzzle, HTTP v1, HTTP v2 |
|
||||
| PowerShell | Invoke-WebRequest, RestMethod |
|
||||
| Python | http.client, Requests |
|
||||
| R | httr |
|
||||
| Ruby | Native |
|
||||
| Shell | cURL, HTTPie, Wget |
|
||||
| Swift | URLSession |
|
||||
|
||||
## Features
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
{
|
||||
"name": "@yaak/httpsnippet",
|
||||
"private": true,
|
||||
"version": "1.0.3",
|
||||
"displayName": "HTTP Snippet",
|
||||
"version": "1.0.3",
|
||||
"private": true,
|
||||
"description": "Generate code snippets for HTTP requests in various languages and frameworks",
|
||||
"minYaakVersion": "2026.2.0-beta.10",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mountain-loop/yaak.git",
|
||||
@@ -20,5 +19,6 @@
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.0.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
},
|
||||
"minYaakVersion": "2026.2.0-beta.10"
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { availableTargets, type HarRequest, HTTPSnippet } from '@readme/httpsnippet';
|
||||
import type { EditorLanguage, HttpRequest, PluginDefinition } from '@yaakapp/api';
|
||||
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']);
|
||||
const excludedTargets = new Set(["json"]);
|
||||
|
||||
// Build language (target) options
|
||||
const languageOptions = targets
|
||||
@@ -17,8 +17,8 @@ const languageOptions = targets
|
||||
|
||||
// Preferred clients per target (shown first in the list)
|
||||
const preferredClients: Record<string, string> = {
|
||||
javascript: 'fetch',
|
||||
node: 'fetch',
|
||||
javascript: "fetch",
|
||||
node: "fetch",
|
||||
};
|
||||
|
||||
// Get client options for a given target key
|
||||
@@ -41,50 +41,50 @@ function getClientOptions(targetKey: string) {
|
||||
// Get default client for a target
|
||||
function getDefaultClient(targetKey: string): string {
|
||||
const options = getClientOptions(targetKey);
|
||||
return options[0]?.value ?? '';
|
||||
return options[0]?.value ?? "";
|
||||
}
|
||||
|
||||
// Defaults
|
||||
const defaultTarget = 'javascript';
|
||||
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',
|
||||
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';
|
||||
return editorLanguageMap[targetKey] ?? "text";
|
||||
}
|
||||
|
||||
// Convert Yaak HttpRequest to HAR format
|
||||
function toHarRequest(request: Partial<HttpRequest>) {
|
||||
// Build URL with query parameters
|
||||
let finalUrl = request.url || '';
|
||||
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 [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}` : '');
|
||||
.join("&");
|
||||
finalUrl = base + separator + queryString + (hash ? `#${hash}` : "");
|
||||
}
|
||||
|
||||
// Build headers array
|
||||
@@ -94,75 +94,75 @@ function toHarRequest(request: Partial<HttpRequest>) {
|
||||
|
||||
// Handle authentication
|
||||
if (request.authentication?.disabled !== true) {
|
||||
if (request.authenticationType === 'basic') {
|
||||
if (request.authenticationType === "basic") {
|
||||
const credentials = btoa(
|
||||
`${request.authentication?.username ?? ''}:${request.authentication?.password ?? ''}`,
|
||||
`${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: "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 ?? '',
|
||||
name: request.authentication?.key ?? "X-Api-Key",
|
||||
value: request.authentication?.value ?? "",
|
||||
});
|
||||
} else if (request.authentication?.location === 'query') {
|
||||
const sep = finalUrl.includes('?') ? '&' : '?';
|
||||
} else if (request.authentication?.location === "query") {
|
||||
const sep = finalUrl.includes("?") ? "&" : "?";
|
||||
finalUrl = [
|
||||
finalUrl,
|
||||
sep,
|
||||
encodeURIComponent(request.authentication?.key ?? 'token'),
|
||||
'=',
|
||||
encodeURIComponent(request.authentication?.value ?? ''),
|
||||
].join('');
|
||||
encodeURIComponent(request.authentication?.key ?? "token"),
|
||||
"=",
|
||||
encodeURIComponent(request.authentication?.value ?? ""),
|
||||
].join("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build HAR request object
|
||||
const har: Record<string, unknown> = {
|
||||
method: request.method || 'GET',
|
||||
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 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',
|
||||
mimeType: "application/x-www-form-urlencoded",
|
||||
params,
|
||||
};
|
||||
} else if (bodyType === 'multipart/form-data' && Array.isArray(request.body.form)) {
|
||||
} 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 || '' };
|
||||
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',
|
||||
mimeType: "multipart/form-data",
|
||||
params,
|
||||
};
|
||||
} else if (bodyType === 'graphql' && typeof request.body.query === 'string') {
|
||||
} else if (bodyType === "graphql" && typeof request.body.query === "string") {
|
||||
const body = {
|
||||
query: request.body.query || '',
|
||||
query: request.body.query || "",
|
||||
variables: maybeParseJSON(request.body.variables, undefined),
|
||||
};
|
||||
har.postData = {
|
||||
mimeType: 'application/json',
|
||||
mimeType: "application/json",
|
||||
text: JSON.stringify(body),
|
||||
};
|
||||
} else if (typeof request.body.text === 'string') {
|
||||
} else if (typeof request.body.text === "string") {
|
||||
har.postData = {
|
||||
mimeType: bodyType,
|
||||
text: request.body.text,
|
||||
@@ -174,7 +174,7 @@ function toHarRequest(request: Partial<HttpRequest>) {
|
||||
}
|
||||
|
||||
function maybeParseJSON<T>(v: unknown, fallback: T): unknown {
|
||||
if (typeof v !== 'string') return fallback;
|
||||
if (typeof v !== "string") return fallback;
|
||||
try {
|
||||
return JSON.parse(v);
|
||||
} catch {
|
||||
@@ -185,20 +185,20 @@ function maybeParseJSON<T>(v: unknown, fallback: T): unknown {
|
||||
export const plugin: PluginDefinition = {
|
||||
httpRequestActions: [
|
||||
{
|
||||
label: 'Generate Code Snippet',
|
||||
icon: 'copy',
|
||||
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',
|
||||
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 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);
|
||||
@@ -210,39 +210,39 @@ export const plugin: PluginDefinition = {
|
||||
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');
|
||||
return (Array.isArray(result) ? result.join("\n") : result || "").replace(/\r\n/g, "\n");
|
||||
};
|
||||
|
||||
// Generate initial code preview
|
||||
let initialCode = '';
|
||||
let initialCode = "";
|
||||
try {
|
||||
initialCode = generateSnippet(initialTarget, initialClient);
|
||||
} catch {
|
||||
initialCode = '// Error generating snippet';
|
||||
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',
|
||||
id: "httpsnippet",
|
||||
title: "Generate Code Snippet",
|
||||
confirmText: "Copy to Clipboard",
|
||||
cancelText: "Cancel",
|
||||
size: "md",
|
||||
inputs: [
|
||||
{
|
||||
type: 'h_stack',
|
||||
type: "h_stack",
|
||||
inputs: [
|
||||
{
|
||||
type: 'select',
|
||||
name: 'target',
|
||||
label: 'Language',
|
||||
type: "select",
|
||||
name: "target",
|
||||
label: "Language",
|
||||
defaultValue: initialTarget,
|
||||
options: languageOptions,
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
type: "select",
|
||||
name: `client-${initialTarget}`,
|
||||
label: 'Library',
|
||||
label: "Library",
|
||||
defaultValue: initialClient,
|
||||
options: getClientOptions(initialTarget),
|
||||
dynamic(_ctx, { values }) {
|
||||
@@ -251,16 +251,16 @@ export const plugin: PluginDefinition = {
|
||||
return {
|
||||
name: `client-${targetKey}`,
|
||||
options,
|
||||
defaultValue: options[0]?.value ?? '',
|
||||
defaultValue: options[0]?.value ?? "",
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'editor',
|
||||
name: 'code',
|
||||
label: 'Preview',
|
||||
type: "editor",
|
||||
name: "code",
|
||||
label: "Preview",
|
||||
language: getEditorLanguage(initialTarget),
|
||||
defaultValue: initialCode,
|
||||
readOnly: true,
|
||||
@@ -274,7 +274,7 @@ export const plugin: PluginDefinition = {
|
||||
try {
|
||||
code = generateSnippet(targetKey, clientKey);
|
||||
} catch {
|
||||
code = '// Error generating snippet';
|
||||
code = "// Error generating snippet";
|
||||
}
|
||||
return {
|
||||
defaultValue: code,
|
||||
@@ -291,7 +291,7 @@ export const plugin: PluginDefinition = {
|
||||
const selectedClient = String(
|
||||
result[`client-${selectedTarget}`] || getDefaultClient(selectedTarget),
|
||||
);
|
||||
await ctx.store.set('selectedTarget', selectedTarget);
|
||||
await ctx.store.set("selectedTarget", selectedTarget);
|
||||
await ctx.store.set(`selectedClient:${selectedTarget}`, selectedClient);
|
||||
|
||||
// Generate snippet for the selected language
|
||||
@@ -299,15 +299,15 @@ export const plugin: PluginDefinition = {
|
||||
const codeText = generateSnippet(selectedTarget, selectedClient);
|
||||
await ctx.clipboard.copyText(codeText);
|
||||
await ctx.toast.show({
|
||||
message: 'Code snippet copied to clipboard',
|
||||
icon: 'copy',
|
||||
color: 'success',
|
||||
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',
|
||||
icon: "alert_triangle",
|
||||
color: "danger",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user