@scalar/snippetz 0.7.8 → 0.9.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/dist/clients/index.d.ts.map +1 -1
- package/dist/clients/index.js +5 -4
- package/dist/httpsnippet-lite/targets/c/libcurl/client.d.ts.map +1 -1
- package/dist/httpsnippet-lite/targets/c/libcurl/client.js +4 -2
- package/dist/libs/http.d.ts +0 -4
- package/dist/libs/http.d.ts.map +1 -1
- package/dist/libs/http.js +1 -11
- package/dist/libs/javascript.d.ts.map +1 -1
- package/dist/libs/javascript.js +6 -3
- package/dist/plugins/csharp/httpclient/httpclient.d.ts.map +1 -1
- package/dist/plugins/csharp/httpclient/httpclient.js +2 -3
- package/dist/plugins/dart/http/http.d.ts.map +1 -1
- package/dist/plugins/dart/http/http.js +1 -4
- package/dist/plugins/go/native/native.d.ts.map +1 -1
- package/dist/plugins/go/native/native.js +191 -5
- package/dist/plugins/http/http11/http11.d.ts.map +1 -1
- package/dist/plugins/http/http11/http11.js +2 -6
- package/dist/plugins/js/axios/axios.d.ts +1 -2
- package/dist/plugins/js/axios/axios.d.ts.map +1 -1
- package/dist/plugins/js/axios/axios.js +2 -11
- package/dist/plugins/js/fetch/fetch.d.ts.map +1 -1
- package/dist/plugins/js/fetch/fetch.js +2 -3
- package/dist/plugins/node/axios/axios.d.ts +1 -2
- package/dist/plugins/node/axios/axios.d.ts.map +1 -1
- package/dist/plugins/node/axios/axios.js +2 -11
- package/dist/plugins/node/fetch/fetch.d.ts.map +1 -1
- package/dist/plugins/node/fetch/fetch.js +2 -3
- package/dist/plugins/node/undici/undici.d.ts.map +1 -1
- package/dist/plugins/node/undici/undici.js +2 -3
- package/dist/plugins/php/curl/curl.d.ts.map +1 -1
- package/dist/plugins/php/curl/curl.js +1 -3
- package/dist/plugins/php/laravel/index.d.ts +2 -0
- package/dist/plugins/php/laravel/index.d.ts.map +1 -0
- package/dist/plugins/php/laravel/index.js +1 -0
- package/dist/plugins/php/laravel/laravel.d.ts +6 -0
- package/dist/plugins/php/laravel/laravel.d.ts.map +1 -0
- package/dist/plugins/php/laravel/laravel.js +134 -0
- package/dist/plugins/r/httr2/httr2.d.ts +6 -0
- package/dist/plugins/r/httr2/httr2.d.ts.map +1 -0
- package/dist/plugins/r/httr2/httr2.js +159 -0
- package/dist/plugins/r/httr2/index.d.ts +2 -0
- package/dist/plugins/r/httr2/index.d.ts.map +1 -0
- package/dist/plugins/r/httr2/index.js +1 -0
- package/dist/plugins/ruby/native/native.d.ts.map +1 -1
- package/dist/plugins/ruby/native/native.js +138 -5
- package/dist/plugins/shared/axios.d.ts +3 -0
- package/dist/plugins/shared/axios.d.ts.map +1 -0
- package/dist/plugins/shared/axios.js +148 -0
- package/dist/plugins/shell/curl/curl.d.ts.map +1 -1
- package/dist/plugins/shell/curl/curl.js +1 -3
- package/dist/snippetz.d.ts +1 -1
- package/package.json +12 -7
- package/dist/httpsnippet-lite/targets/go/native/client.d.ts +0 -12
- package/dist/httpsnippet-lite/targets/go/native/client.d.ts.map +0 -1
- package/dist/httpsnippet-lite/targets/go/native/client.js +0 -157
- package/dist/httpsnippet-lite/targets/javascript/axios/client.d.ts +0 -12
- package/dist/httpsnippet-lite/targets/javascript/axios/client.d.ts.map +0 -1
- package/dist/httpsnippet-lite/targets/javascript/axios/client.js +0 -86
- package/dist/httpsnippet-lite/targets/node/axios/client.d.ts +0 -3
- package/dist/httpsnippet-lite/targets/node/axios/client.d.ts.map +0 -1
- package/dist/httpsnippet-lite/targets/node/axios/client.js +0 -78
- package/dist/httpsnippet-lite/targets/r/httr/client.d.ts +0 -12
- package/dist/httpsnippet-lite/targets/r/httr/client.d.ts.map +0 -1
- package/dist/httpsnippet-lite/targets/r/httr/client.js +0 -115
- package/dist/httpsnippet-lite/targets/ruby/native/client.d.ts +0 -3
- package/dist/httpsnippet-lite/targets/ruby/native/client.d.ts.map +0 -1
- package/dist/httpsnippet-lite/targets/ruby/native/client.js +0 -67
- package/dist/plugins/r/httr/httr.d.ts +0 -6
- package/dist/plugins/r/httr/httr.d.ts.map +0 -1
- package/dist/plugins/r/httr/httr.js +0 -14
- package/dist/plugins/r/httr/index.d.ts +0 -2
- package/dist/plugins/r/httr/index.d.ts.map +0 -1
- package/dist/plugins/r/httr/index.js +0 -1
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { accumulateRepeatedValue, buildQueryString } from '../../../libs/http.js';
|
|
2
|
+
import { objectToString } from '../../../libs/php.js';
|
|
3
|
+
const escapePhpString = (value) => value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
4
|
+
const quotePhpString = (value) => `'${escapePhpString(value)}'`;
|
|
5
|
+
const escapePhpObjectKey = escapePhpString;
|
|
6
|
+
const escapeObjectKeys = (value) => {
|
|
7
|
+
if (Array.isArray(value)) {
|
|
8
|
+
return value.map((item) => escapeObjectKeys(item));
|
|
9
|
+
}
|
|
10
|
+
if (value && typeof value === 'object') {
|
|
11
|
+
return Object.entries(value).reduce((acc, [key, nestedValue]) => {
|
|
12
|
+
acc[escapePhpObjectKey(key)] = escapeObjectKeys(nestedValue);
|
|
13
|
+
return acc;
|
|
14
|
+
}, {});
|
|
15
|
+
}
|
|
16
|
+
return value;
|
|
17
|
+
};
|
|
18
|
+
const getCookieDomain = (url) => {
|
|
19
|
+
if (!url) {
|
|
20
|
+
return 'localhost';
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
return new URL(url).hostname || 'localhost';
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return 'localhost';
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* php/laravel
|
|
31
|
+
*/
|
|
32
|
+
export const phpLaravel = {
|
|
33
|
+
target: 'php',
|
|
34
|
+
client: 'laravel',
|
|
35
|
+
title: 'Laravel HTTP Client',
|
|
36
|
+
generate(request, configuration) {
|
|
37
|
+
const normalizedRequest = {
|
|
38
|
+
method: 'GET',
|
|
39
|
+
...request,
|
|
40
|
+
};
|
|
41
|
+
normalizedRequest.method = normalizedRequest.method.toUpperCase();
|
|
42
|
+
const queryString = buildQueryString(normalizedRequest.queryString);
|
|
43
|
+
const url = `${normalizedRequest.url ?? ''}${queryString}`;
|
|
44
|
+
const chain = [];
|
|
45
|
+
if (configuration?.auth?.username && configuration.auth.password) {
|
|
46
|
+
chain.push(`withBasicAuth(${quotePhpString(configuration.auth.username)}, ${quotePhpString(configuration.auth.password)})`);
|
|
47
|
+
}
|
|
48
|
+
if (normalizedRequest.headers?.length) {
|
|
49
|
+
const headers = {};
|
|
50
|
+
normalizedRequest.headers.forEach((header) => {
|
|
51
|
+
accumulateRepeatedValue(headers, header.name, header.value);
|
|
52
|
+
});
|
|
53
|
+
chain.push(`withHeaders(${objectToString(escapeObjectKeys(headers))})`);
|
|
54
|
+
}
|
|
55
|
+
if (normalizedRequest.cookies?.length) {
|
|
56
|
+
const cookies = {};
|
|
57
|
+
normalizedRequest.cookies.forEach((cookie) => {
|
|
58
|
+
accumulateRepeatedValue(cookies, cookie.name, cookie.value);
|
|
59
|
+
});
|
|
60
|
+
chain.push(`withCookies(${objectToString(escapeObjectKeys(cookies))}, ${quotePhpString(getCookieDomain(normalizedRequest.url ?? ''))})`);
|
|
61
|
+
}
|
|
62
|
+
let payload;
|
|
63
|
+
if (normalizedRequest.postData) {
|
|
64
|
+
if (normalizedRequest.postData.mimeType === 'application/json') {
|
|
65
|
+
if (normalizedRequest.postData.text) {
|
|
66
|
+
try {
|
|
67
|
+
payload = JSON.parse(normalizedRequest.postData.text);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
chain.push(`withBody(${quotePhpString(normalizedRequest.postData.text)}, 'application/json')`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else if (normalizedRequest.postData.mimeType === 'application/x-www-form-urlencoded' &&
|
|
75
|
+
normalizedRequest.postData.params) {
|
|
76
|
+
const formData = {};
|
|
77
|
+
normalizedRequest.postData.params.forEach((param) => {
|
|
78
|
+
accumulateRepeatedValue(formData, param.name, param.value ?? '');
|
|
79
|
+
});
|
|
80
|
+
chain.push('asForm()');
|
|
81
|
+
payload = formData;
|
|
82
|
+
}
|
|
83
|
+
else if (normalizedRequest.postData.mimeType === 'multipart/form-data' && normalizedRequest.postData.params) {
|
|
84
|
+
const multipartData = {};
|
|
85
|
+
normalizedRequest.postData.params.forEach((param) => {
|
|
86
|
+
if (param.fileName !== undefined) {
|
|
87
|
+
const args = [
|
|
88
|
+
quotePhpString(param.name),
|
|
89
|
+
`file_get_contents(${quotePhpString(param.fileName)})`,
|
|
90
|
+
quotePhpString(param.fileName),
|
|
91
|
+
];
|
|
92
|
+
if (param.contentType) {
|
|
93
|
+
args.push(objectToString(escapeObjectKeys({ 'Content-Type': param.contentType })));
|
|
94
|
+
}
|
|
95
|
+
chain.push(`attach(${args.join(', ')})`);
|
|
96
|
+
}
|
|
97
|
+
else if (param.contentType) {
|
|
98
|
+
chain.push(`attach(${quotePhpString(param.name)}, ${quotePhpString(param.value ?? '')}, null, ${objectToString(escapeObjectKeys({ 'Content-Type': param.contentType }))})`);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
accumulateRepeatedValue(multipartData, param.name, param.value ?? '');
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
if (Object.keys(multipartData).length > 0) {
|
|
105
|
+
payload = multipartData;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else if (normalizedRequest.postData.mimeType === 'application/octet-stream') {
|
|
109
|
+
chain.push(`withBody(${quotePhpString(normalizedRequest.postData.text ?? '')}, 'application/octet-stream')`);
|
|
110
|
+
}
|
|
111
|
+
else if (normalizedRequest.postData.text) {
|
|
112
|
+
try {
|
|
113
|
+
payload = JSON.parse(normalizedRequest.postData.text);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
chain.push(`withBody(${quotePhpString(normalizedRequest.postData.text)}, ${quotePhpString(normalizedRequest.postData.mimeType || 'text/plain')})`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const requestMethod = normalizedRequest.method.toLowerCase();
|
|
121
|
+
const supportsDirectMethod = ['delete', 'get', 'head', 'patch', 'post', 'put'].includes(requestMethod);
|
|
122
|
+
const requestCall = supportsDirectMethod
|
|
123
|
+
? payload !== undefined && requestMethod !== 'head'
|
|
124
|
+
? `${requestMethod}(${quotePhpString(url)}, ${objectToString(escapeObjectKeys(payload), 1)})`
|
|
125
|
+
: `${requestMethod}(${quotePhpString(url)})`
|
|
126
|
+
: payload !== undefined
|
|
127
|
+
? `send(${quotePhpString(normalizedRequest.method)}, ${quotePhpString(url)}, ${objectToString(escapeObjectKeys(payload), 1)})`
|
|
128
|
+
: `send(${quotePhpString(normalizedRequest.method)}, ${quotePhpString(url)})`;
|
|
129
|
+
const expression = chain.length > 0
|
|
130
|
+
? [`Http::${chain[0]}`, ...chain.slice(1).map((method) => ` ->${method}`), ` ->${requestCall}`].join('\n')
|
|
131
|
+
: `Http::${requestCall}`;
|
|
132
|
+
return `use Illuminate\\Support\\Facades\\Http;\n\n$response = ${expression};`;
|
|
133
|
+
},
|
|
134
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"httr2.d.ts","sourceRoot":"","sources":["../../../../src/plugins/r/httr2/httr2.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,MAAM,EAAuB,MAAM,wBAAwB,CAAA;AAkDrF;;GAEG;AACH,eAAO,MAAM,MAAM,EAAE,MAuHpB,CAAA"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { reduceQueryParams } from '../../../libs/http.js';
|
|
2
|
+
/**
|
|
3
|
+
* Formats JSON text as an R list structure
|
|
4
|
+
*/
|
|
5
|
+
const jsonToRList = (text, indent) => {
|
|
6
|
+
try {
|
|
7
|
+
const obj = JSON.parse(text);
|
|
8
|
+
return formatRValue(obj, indent);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return `"${text}"`;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Recursively formats a JS value as R syntax
|
|
16
|
+
*/
|
|
17
|
+
const formatRValue = (value, indent) => {
|
|
18
|
+
if (value === null || value === undefined) {
|
|
19
|
+
return 'NULL';
|
|
20
|
+
}
|
|
21
|
+
if (typeof value === 'boolean') {
|
|
22
|
+
return value ? 'TRUE' : 'FALSE';
|
|
23
|
+
}
|
|
24
|
+
if (typeof value === 'number') {
|
|
25
|
+
return String(value);
|
|
26
|
+
}
|
|
27
|
+
if (typeof value === 'string') {
|
|
28
|
+
return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
29
|
+
}
|
|
30
|
+
if (Array.isArray(value)) {
|
|
31
|
+
if (value.length === 0) {
|
|
32
|
+
return 'list()';
|
|
33
|
+
}
|
|
34
|
+
const items = value.map((v) => `${indent} ${formatRValue(v, indent + ' ')}`).join(',\n');
|
|
35
|
+
return `list(\n${items}\n${indent})`;
|
|
36
|
+
}
|
|
37
|
+
if (typeof value === 'object') {
|
|
38
|
+
const entries = Object.entries(value);
|
|
39
|
+
if (entries.length === 0) {
|
|
40
|
+
return 'list()';
|
|
41
|
+
}
|
|
42
|
+
const items = entries.map(([k, v]) => `${indent} ${k} = ${formatRValue(v, indent + ' ')}`).join(',\n');
|
|
43
|
+
return `list(\n${items}\n${indent})`;
|
|
44
|
+
}
|
|
45
|
+
return String(value);
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* r/httr2
|
|
49
|
+
*/
|
|
50
|
+
export const rHttr2 = {
|
|
51
|
+
target: 'r',
|
|
52
|
+
client: 'httr2',
|
|
53
|
+
title: 'httr2',
|
|
54
|
+
generate(request, configuration) {
|
|
55
|
+
const normalizedRequest = {
|
|
56
|
+
url: 'https://example.com',
|
|
57
|
+
method: 'GET',
|
|
58
|
+
...request,
|
|
59
|
+
};
|
|
60
|
+
const method = normalizedRequest.method.toUpperCase();
|
|
61
|
+
const lines = ['library(httr2)', ''];
|
|
62
|
+
// Start the pipe chain
|
|
63
|
+
lines.push(`response <- request("${normalizedRequest.url}") |>`);
|
|
64
|
+
// Collect pipe steps
|
|
65
|
+
const steps = [];
|
|
66
|
+
// Method (GET is default, so only add for non-GET)
|
|
67
|
+
if (method !== 'GET') {
|
|
68
|
+
steps.push(` req_method("${method}")`);
|
|
69
|
+
}
|
|
70
|
+
// Headers
|
|
71
|
+
const headers = {};
|
|
72
|
+
if (normalizedRequest.headers?.length) {
|
|
73
|
+
for (const header of normalizedRequest.headers) {
|
|
74
|
+
headers[header.name] = header.value;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Cookies as Cookie header
|
|
78
|
+
if (normalizedRequest.cookies?.length) {
|
|
79
|
+
const cookieString = normalizedRequest.cookies.map((c) => `${c.name}=${c.value}`).join('; ');
|
|
80
|
+
headers['Cookie'] = cookieString;
|
|
81
|
+
}
|
|
82
|
+
// Auth
|
|
83
|
+
if (configuration?.auth?.username && configuration?.auth?.password) {
|
|
84
|
+
steps.push(` req_auth_basic("${configuration.auth.username}", "${configuration.auth.password}")`);
|
|
85
|
+
}
|
|
86
|
+
if (Object.keys(headers).length) {
|
|
87
|
+
const headerEntries = Object.entries(headers);
|
|
88
|
+
if (headerEntries.length === 1) {
|
|
89
|
+
const [name, value] = headerEntries[0];
|
|
90
|
+
steps.push(` req_headers("${name}" = "${value}")`);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
const headerLines = headerEntries.map(([name, value]) => ` "${name}" = "${value}"`).join(',\n');
|
|
94
|
+
steps.push(` req_headers(\n${headerLines}\n )`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Query parameters
|
|
98
|
+
if (normalizedRequest.queryString?.length) {
|
|
99
|
+
const params = reduceQueryParams(normalizedRequest.queryString);
|
|
100
|
+
const entries = Object.entries(params);
|
|
101
|
+
if (entries.length === 1) {
|
|
102
|
+
const [name, value] = entries[0];
|
|
103
|
+
if (Array.isArray(value)) {
|
|
104
|
+
const items = value.map((v) => `"${v}"`).join(', ');
|
|
105
|
+
steps.push(` req_url_query("${name}" = c(${items}))`);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
steps.push(` req_url_query("${name}" = "${value}")`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
const paramLines = entries
|
|
113
|
+
.map(([name, value]) => {
|
|
114
|
+
if (Array.isArray(value)) {
|
|
115
|
+
const items = value.map((v) => `"${v}"`).join(', ');
|
|
116
|
+
return ` "${name}" = c(${items})`;
|
|
117
|
+
}
|
|
118
|
+
return ` "${name}" = "${value}"`;
|
|
119
|
+
})
|
|
120
|
+
.join(',\n');
|
|
121
|
+
steps.push(` req_url_query(\n${paramLines}\n )`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// Body
|
|
125
|
+
if (normalizedRequest.postData) {
|
|
126
|
+
const { mimeType, text, params } = normalizedRequest.postData;
|
|
127
|
+
if (mimeType === 'application/json' && text) {
|
|
128
|
+
const rList = jsonToRList(text, ' ');
|
|
129
|
+
steps.push(` req_body_json(${rList})`);
|
|
130
|
+
}
|
|
131
|
+
else if (mimeType === 'multipart/form-data' && params) {
|
|
132
|
+
const paramLines = params
|
|
133
|
+
.map((p) => {
|
|
134
|
+
if (p.fileName !== undefined) {
|
|
135
|
+
return ` ${p.name} = curl::form_file("${p.fileName}")`;
|
|
136
|
+
}
|
|
137
|
+
return ` ${p.name} = "${p.value ?? ''}"`;
|
|
138
|
+
})
|
|
139
|
+
.join(',\n');
|
|
140
|
+
steps.push(` req_body_multipart(\n${paramLines}\n )`);
|
|
141
|
+
}
|
|
142
|
+
else if (mimeType === 'application/x-www-form-urlencoded' && params) {
|
|
143
|
+
const paramLines = params.map((p) => ` "${p.name}" = "${p.value ?? ''}"`).join(',\n');
|
|
144
|
+
steps.push(` req_body_form(\n${paramLines}\n )`);
|
|
145
|
+
}
|
|
146
|
+
else if (text) {
|
|
147
|
+
steps.push(` req_body_raw("${text}", type = "${mimeType ?? 'application/octet-stream'}")`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Always end with req_perform
|
|
151
|
+
steps.push(' req_perform()');
|
|
152
|
+
// Join steps with pipe operator
|
|
153
|
+
lines[lines.length - 1] = `response <- request("${normalizedRequest.url}") |>`;
|
|
154
|
+
lines.push(steps.join(' |>\n'));
|
|
155
|
+
lines.push('');
|
|
156
|
+
lines.push('resp_body_string(response)');
|
|
157
|
+
return lines.join('\n');
|
|
158
|
+
},
|
|
159
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/plugins/r/httr2/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { rHttr2 } from './httr2.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../../../../src/plugins/ruby/native/native.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../../../../src/plugins/ruby/native/native.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AA8DpD;;GAEG;AACH,eAAO,MAAM,UAAU,EAAE,MAuGxB,CAAA"}
|
|
@@ -1,5 +1,51 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { buildQueryString } from '../../../libs/http.js';
|
|
2
|
+
const escapeRubyDoubleQuoted = (value) => value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
3
|
+
const escapeRubySingleQuoted = (value) => value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
4
|
+
const encodeFormComponent = (value) => encodeURIComponent(value).replace(/'/g, '%27');
|
|
5
|
+
const standardMethods = new Set([
|
|
6
|
+
'GET',
|
|
7
|
+
'POST',
|
|
8
|
+
'HEAD',
|
|
9
|
+
'DELETE',
|
|
10
|
+
'PATCH',
|
|
11
|
+
'PUT',
|
|
12
|
+
'OPTIONS',
|
|
13
|
+
'COPY',
|
|
14
|
+
'LOCK',
|
|
15
|
+
'UNLOCK',
|
|
16
|
+
'MOVE',
|
|
17
|
+
'TRACE',
|
|
18
|
+
]);
|
|
19
|
+
const toRubyMethodClass = (method) => method.charAt(0) + method.slice(1).toLowerCase();
|
|
20
|
+
const maybeAddCustomMethodClass = (lines, method, hasBody) => {
|
|
21
|
+
if (standardMethods.has(method)) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const methodClass = toRubyMethodClass(method);
|
|
25
|
+
lines.push(`class Net::HTTP::${methodClass} < Net::HTTPRequest`);
|
|
26
|
+
lines.push(` METHOD = '${method}'`);
|
|
27
|
+
lines.push(` REQUEST_HAS_BODY = '${hasBody ? 'true' : 'false'}'`);
|
|
28
|
+
lines.push(' RESPONSE_HAS_BODY = true');
|
|
29
|
+
lines.push('end');
|
|
30
|
+
lines.push('');
|
|
31
|
+
};
|
|
32
|
+
const encodeUrlWithPathPreservedBrackets = (url) => {
|
|
33
|
+
try {
|
|
34
|
+
const parsedUrl = new URL(url);
|
|
35
|
+
const encodedPath = parsedUrl.pathname
|
|
36
|
+
.split('/')
|
|
37
|
+
.map((segment) => encodeURIComponent(decodeURIComponent(segment)).replace(/%5B/g, '[').replace(/%5D/g, ']').replace(/%24/g, '$'))
|
|
38
|
+
.join('/');
|
|
39
|
+
// Keep legacy behavior from the previous converter: omit trailing slash for origin-only URLs.
|
|
40
|
+
if (parsedUrl.pathname === '/') {
|
|
41
|
+
return `${parsedUrl.origin}${parsedUrl.search}${parsedUrl.hash}`;
|
|
42
|
+
}
|
|
43
|
+
return `${parsedUrl.origin}${encodedPath}${parsedUrl.search}${parsedUrl.hash}`;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return url;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
3
49
|
/**
|
|
4
50
|
* ruby/native
|
|
5
51
|
*/
|
|
@@ -7,8 +53,95 @@ export const rubyNative = {
|
|
|
7
53
|
target: 'ruby',
|
|
8
54
|
client: 'native',
|
|
9
55
|
title: 'net::http',
|
|
10
|
-
generate(request) {
|
|
11
|
-
|
|
12
|
-
|
|
56
|
+
generate(request, configuration) {
|
|
57
|
+
const normalizedRequest = {
|
|
58
|
+
method: 'GET',
|
|
59
|
+
...request,
|
|
60
|
+
};
|
|
61
|
+
normalizedRequest.method = normalizedRequest.method.toUpperCase();
|
|
62
|
+
const queryString = buildQueryString(normalizedRequest.queryString);
|
|
63
|
+
const rawUrl = `${normalizedRequest.url ?? ''}${queryString}`;
|
|
64
|
+
const encodedUrl = encodeUrlWithPathPreservedBrackets(rawUrl);
|
|
65
|
+
const lines = ["require 'uri'", "require 'net/http'", ''];
|
|
66
|
+
maybeAddCustomMethodClass(lines, normalizedRequest.method, Boolean(normalizedRequest.postData?.text));
|
|
67
|
+
lines.push(`url = URI("${escapeRubyDoubleQuoted(encodedUrl)}")`, '');
|
|
68
|
+
lines.push('http = Net::HTTP.new(url.host, url.port)');
|
|
69
|
+
if (encodedUrl.startsWith('https://')) {
|
|
70
|
+
lines.push('http.use_ssl = true');
|
|
71
|
+
}
|
|
72
|
+
lines.push('');
|
|
73
|
+
const methodClass = toRubyMethodClass(normalizedRequest.method);
|
|
74
|
+
lines.push(`request = Net::HTTP::${methodClass}.new(url)`);
|
|
75
|
+
if (configuration?.auth?.username && configuration?.auth?.password) {
|
|
76
|
+
const username = escapeRubyDoubleQuoted(configuration.auth.username);
|
|
77
|
+
const password = escapeRubyDoubleQuoted(configuration.auth.password);
|
|
78
|
+
lines.push(`request.basic_auth("${username}", "${password}")`);
|
|
79
|
+
}
|
|
80
|
+
if (normalizedRequest.headers?.length) {
|
|
81
|
+
normalizedRequest.headers.forEach((header) => {
|
|
82
|
+
lines.push(`request["${escapeRubyDoubleQuoted(header.name)}"] = '${escapeRubySingleQuoted(header.value)}'`);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (normalizedRequest.cookies?.length) {
|
|
86
|
+
const cookieString = normalizedRequest.cookies
|
|
87
|
+
.map((cookie) => `${encodeFormComponent(cookie.name)}=${encodeFormComponent(cookie.value)}`)
|
|
88
|
+
.join('; ');
|
|
89
|
+
lines.push(`request["Cookie"] = '${escapeRubySingleQuoted(cookieString)}'`);
|
|
90
|
+
}
|
|
91
|
+
if (normalizedRequest.postData) {
|
|
92
|
+
const { mimeType, text, params } = normalizedRequest.postData;
|
|
93
|
+
if (mimeType === 'application/json' && text !== undefined) {
|
|
94
|
+
try {
|
|
95
|
+
const parsed = JSON.parse(text);
|
|
96
|
+
const prettyJson = JSON.stringify(parsed, null, 2);
|
|
97
|
+
lines.push('request.body = <<~JSON');
|
|
98
|
+
lines.push(prettyJson);
|
|
99
|
+
lines.push('JSON');
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
lines.push(`request.body = ${JSON.stringify(text)}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else if (mimeType === 'application/x-www-form-urlencoded' && params) {
|
|
106
|
+
const encodedForm = params
|
|
107
|
+
.map((param) => `${encodeFormComponent(param.name)}=${encodeFormComponent(param.value ?? '')}`)
|
|
108
|
+
.join('&');
|
|
109
|
+
lines.push(`request.body = '${escapeRubySingleQuoted(encodedForm)}'`);
|
|
110
|
+
}
|
|
111
|
+
else if (mimeType === 'multipart/form-data' && params) {
|
|
112
|
+
lines.push('form_data = []');
|
|
113
|
+
params.forEach((param) => {
|
|
114
|
+
const name = escapeRubySingleQuoted(param.name);
|
|
115
|
+
if (param.fileName !== undefined) {
|
|
116
|
+
const fileName = escapeRubySingleQuoted(param.fileName);
|
|
117
|
+
if (param.contentType) {
|
|
118
|
+
const contentType = escapeRubySingleQuoted(param.contentType);
|
|
119
|
+
lines.push(`form_data << ['${name}', File.open('${fileName}'), { filename: '${fileName}', content_type: '${contentType}' }]`);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
lines.push(`form_data << ['${name}', File.open('${fileName}')]`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
else if (param.contentType) {
|
|
126
|
+
const value = escapeRubySingleQuoted(param.value ?? '');
|
|
127
|
+
const contentType = escapeRubySingleQuoted(param.contentType);
|
|
128
|
+
lines.push(`form_data << ['${name}', '${value}', { content_type: '${contentType}' }]`);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
const value = escapeRubySingleQuoted(param.value ?? '');
|
|
132
|
+
lines.push(`form_data << ['${name}', '${value}']`);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
lines.push("request.set_form(form_data, 'multipart/form-data')");
|
|
136
|
+
}
|
|
137
|
+
else if (mimeType === 'application/octet-stream') {
|
|
138
|
+
lines.push(`request.body = ${JSON.stringify(text ?? '')}`);
|
|
139
|
+
}
|
|
140
|
+
else if (text !== undefined) {
|
|
141
|
+
lines.push(`request.body = ${JSON.stringify(text)}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
lines.push('', 'response = http.request(request)', 'puts response.read_body');
|
|
145
|
+
return lines.join('\n');
|
|
13
146
|
},
|
|
14
147
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"axios.d.ts","sourceRoot":"","sources":["../../../src/plugins/shared/axios.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AA4I9D,eAAO,MAAM,iBAAiB,GAAI,QAAQ,OAAO,CAAC,QAAQ,EAAE,IAAI,GAAG,MAAM,CAAC,KAAG,MAmD3E,CAAA"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { accumulateRepeatedValue, reduceQueryParams } from '../../libs/http.js';
|
|
2
|
+
import { Raw, objectToString } from '../../libs/javascript.js';
|
|
3
|
+
const escapeJsString = (value) => value.replaceAll('\\', '\\\\').replaceAll('\n', '\\n').replaceAll('\r', '\\r').replaceAll("'", "\\'");
|
|
4
|
+
const sanitizeForGeneratedCode = (value) => {
|
|
5
|
+
if (typeof value === 'string') {
|
|
6
|
+
return escapeJsString(value);
|
|
7
|
+
}
|
|
8
|
+
if (Array.isArray(value)) {
|
|
9
|
+
return value.map((item) => sanitizeForGeneratedCode(item));
|
|
10
|
+
}
|
|
11
|
+
if (value && typeof value === 'object' && !(value instanceof Raw)) {
|
|
12
|
+
return Object.entries(value).reduce((acc, [key, objectValue]) => {
|
|
13
|
+
acc[key] = sanitizeForGeneratedCode(objectValue);
|
|
14
|
+
return acc;
|
|
15
|
+
}, {});
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
18
|
+
};
|
|
19
|
+
const addHeaderValue = (headers, name, value) => {
|
|
20
|
+
if (value === '') {
|
|
21
|
+
headers[name] = value;
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
accumulateRepeatedValue(headers, name, value);
|
|
25
|
+
};
|
|
26
|
+
const buildHeaders = (request) => {
|
|
27
|
+
const headers = {};
|
|
28
|
+
request?.headers?.forEach((header) => {
|
|
29
|
+
addHeaderValue(headers, header.name, header.value);
|
|
30
|
+
});
|
|
31
|
+
if (request?.cookies?.length) {
|
|
32
|
+
const cookieValue = request.cookies.map((cookie) => `${cookie.name}=${cookie.value}`).join('; ');
|
|
33
|
+
addHeaderValue(headers, 'Cookie', cookieValue);
|
|
34
|
+
}
|
|
35
|
+
return Object.keys(headers).length ? headers : undefined;
|
|
36
|
+
};
|
|
37
|
+
const buildData = (request) => {
|
|
38
|
+
const setup = [];
|
|
39
|
+
const postData = request?.postData;
|
|
40
|
+
if (!postData) {
|
|
41
|
+
return { setup };
|
|
42
|
+
}
|
|
43
|
+
if (postData.mimeType === 'application/json') {
|
|
44
|
+
if (!postData.text) {
|
|
45
|
+
return { setup };
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
return {
|
|
49
|
+
setup,
|
|
50
|
+
data: JSON.parse(postData.text),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return {
|
|
55
|
+
setup,
|
|
56
|
+
data: postData.text,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (postData.mimeType === 'application/x-www-form-urlencoded' && postData.params?.length) {
|
|
61
|
+
setup.push('const encodedParams = new URLSearchParams()');
|
|
62
|
+
postData.params.forEach((param) => {
|
|
63
|
+
const encodedName = escapeJsString(param.name);
|
|
64
|
+
const encodedValue = escapeJsString(param.value ?? '');
|
|
65
|
+
setup.push(`encodedParams.append('${encodedName}', '${encodedValue}')`);
|
|
66
|
+
});
|
|
67
|
+
return {
|
|
68
|
+
setup,
|
|
69
|
+
data: new Raw('encodedParams'),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (postData.mimeType === 'multipart/form-data' && postData.params?.length) {
|
|
73
|
+
setup.push('const formData = new FormData()');
|
|
74
|
+
postData.params.forEach((param) => {
|
|
75
|
+
const encodedName = escapeJsString(param.name);
|
|
76
|
+
if (param.fileName !== undefined) {
|
|
77
|
+
const encodedFileName = escapeJsString(param.fileName);
|
|
78
|
+
const blobWithType = param.contentType ? `, { type: '${escapeJsString(param.contentType)}' }` : '';
|
|
79
|
+
setup.push(`formData.append('${encodedName}', new Blob([]${blobWithType}), '${encodedFileName}')`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (param.contentType) {
|
|
83
|
+
const encodedContentType = escapeJsString(param.contentType);
|
|
84
|
+
const encodedValue = escapeJsString(param.value ?? '');
|
|
85
|
+
setup.push(`formData.append('${encodedName}', new Blob(['${encodedValue}'], { type: '${encodedContentType}' }))`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const encodedValue = escapeJsString(param.value ?? '');
|
|
89
|
+
setup.push(`formData.append('${encodedName}', '${encodedValue}')`);
|
|
90
|
+
});
|
|
91
|
+
return {
|
|
92
|
+
setup,
|
|
93
|
+
data: new Raw('formData'),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (!postData.text) {
|
|
97
|
+
return { setup };
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
setup,
|
|
101
|
+
data: postData.text,
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
export const createAxiosPlugin = (target) => ({
|
|
105
|
+
target,
|
|
106
|
+
client: 'axios',
|
|
107
|
+
title: 'Axios',
|
|
108
|
+
generate(request, configuration) {
|
|
109
|
+
const normalizedRequest = {
|
|
110
|
+
method: 'GET',
|
|
111
|
+
...request,
|
|
112
|
+
};
|
|
113
|
+
normalizedRequest.method = normalizedRequest.method.toUpperCase();
|
|
114
|
+
const options = {
|
|
115
|
+
method: normalizedRequest.method,
|
|
116
|
+
url: escapeJsString(normalizedRequest.url ?? ''),
|
|
117
|
+
};
|
|
118
|
+
const params = reduceQueryParams(normalizedRequest.queryString);
|
|
119
|
+
if (Object.keys(params).length) {
|
|
120
|
+
options.params = sanitizeForGeneratedCode(params);
|
|
121
|
+
}
|
|
122
|
+
const headers = buildHeaders(normalizedRequest);
|
|
123
|
+
if (headers) {
|
|
124
|
+
options.headers = sanitizeForGeneratedCode(headers);
|
|
125
|
+
}
|
|
126
|
+
if (configuration?.auth?.username && configuration?.auth?.password) {
|
|
127
|
+
options.auth = {
|
|
128
|
+
username: escapeJsString(configuration.auth.username),
|
|
129
|
+
password: escapeJsString(configuration.auth.password),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
const { setup, data } = buildData(normalizedRequest);
|
|
133
|
+
if (data !== undefined) {
|
|
134
|
+
options.data = data instanceof Raw ? data : sanitizeForGeneratedCode(data);
|
|
135
|
+
}
|
|
136
|
+
const setupBlock = setup.length ? `${setup.join('\n')}\n\n` : '';
|
|
137
|
+
return `import axios from 'axios'
|
|
138
|
+
|
|
139
|
+
${setupBlock}const options = ${objectToString(options)}
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const { data } = await axios.request(options)
|
|
143
|
+
console.log(data)
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.error(error)
|
|
146
|
+
}`;
|
|
147
|
+
},
|
|
148
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"curl.d.ts","sourceRoot":"","sources":["../../../../src/plugins/shell/curl/curl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAIpD;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"curl.d.ts","sourceRoot":"","sources":["../../../../src/plugins/shell/curl/curl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAIpD;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,MAkIvB,CAAA"}
|
|
@@ -22,9 +22,7 @@ export const shellCurl = {
|
|
|
22
22
|
normalizedRequest.queryString
|
|
23
23
|
.map((param) => {
|
|
24
24
|
// Ensure both name and value are fully URI encoded
|
|
25
|
-
|
|
26
|
-
const encodedValue = encodeURIComponent(param.value);
|
|
27
|
-
return `${encodedName}=${encodedValue}`;
|
|
25
|
+
return `${param.name}=${param.value}`;
|
|
28
26
|
})
|
|
29
27
|
.join('&')
|
|
30
28
|
: '';
|
package/dist/snippetz.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export declare function snippetz(): {
|
|
|
7
7
|
clients(): import("@scalar/types/snippetz").Target[];
|
|
8
8
|
plugins(): {
|
|
9
9
|
target: "c" | "clojure" | "csharp" | "go" | "http" | "java" | "kotlin" | "node" | "objc" | "ocaml" | "php" | "powershell" | "python" | "r" | "ruby" | "shell" | "swift" | "dart" | "fsharp" | "js" | "rust";
|
|
10
|
-
client: "http" | "libcurl" | "clj_http" | "httpclient" | "restsharp" | "native" | "http1.1" | "asynchttp" | "nethttp" | "okhttp" | "unirest" | "axios" | "fetch" | "ofetch" | "undici" | "nsurlsession" | "cohttp" | "curl" | "guzzle" | "restmethod" | "webrequest" | "python3" | "requests" | "httpx_sync" | "httpx_async" | "
|
|
10
|
+
client: "http" | "libcurl" | "clj_http" | "httpclient" | "restsharp" | "native" | "http1.1" | "asynchttp" | "nethttp" | "okhttp" | "unirest" | "axios" | "fetch" | "ofetch" | "undici" | "nsurlsession" | "cohttp" | "curl" | "guzzle" | "laravel" | "restmethod" | "webrequest" | "python3" | "requests" | "httpx_sync" | "httpx_async" | "httr2" | "httpie" | "wget" | "jquery" | "xhr" | "reqwest";
|
|
11
11
|
}[];
|
|
12
12
|
findPlugin: <T extends TargetId>(target: T | string, client: ClientId<T> | string) => import("@scalar/types/snippetz").Plugin | undefined;
|
|
13
13
|
hasPlugin<T extends TargetId>(target: T | string, client: ClientId<T> | string): boolean;
|