@scalar/snippetz 0.8.0 → 0.9.1
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 +7 -5
- package/dist/libs/http.d.ts +29 -0
- package/dist/libs/http.d.ts.map +1 -1
- package/dist/libs/http.js +53 -0
- package/dist/libs/javascript.d.ts.map +1 -1
- package/dist/libs/javascript.js +6 -3
- package/dist/plugins/c/libcurl/libcurl.d.ts.map +1 -1
- package/dist/plugins/c/libcurl/libcurl.js +145 -5
- package/dist/plugins/go/native/native.d.ts.map +1 -1
- package/dist/plugins/go/native/native.js +150 -5
- 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/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/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/python/aiohttp/aiohttp.d.ts +6 -0
- package/dist/plugins/python/aiohttp/aiohttp.d.ts.map +1 -0
- package/dist/plugins/python/aiohttp/aiohttp.js +107 -0
- package/dist/plugins/python/aiohttp/index.d.ts +2 -0
- package/dist/plugins/python/aiohttp/index.d.ts.map +1 -0
- package/dist/plugins/python/aiohttp/index.js +1 -0
- package/dist/plugins/python/python3/python3.d.ts.map +1 -1
- package/dist/plugins/python/python3/python3.js +192 -5
- package/dist/plugins/python/requestsLike.d.ts +2 -0
- package/dist/plugins/python/requestsLike.d.ts.map +1 -1
- package/dist/plugins/python/requestsLike.js +9 -9
- 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/swift/nsurlsession/nsurlsession.d.ts.map +1 -1
- package/dist/plugins/swift/nsurlsession/nsurlsession.js +118 -5
- package/dist/snippetz.d.ts +2 -2
- package/package.json +17 -7
- package/dist/httpsnippet-lite/targets/c/libcurl/client.d.ts +0 -3
- package/dist/httpsnippet-lite/targets/c/libcurl/client.d.ts.map +0 -1
- package/dist/httpsnippet-lite/targets/c/libcurl/client.js +0 -74
- 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/python/python3/client.d.ts +0 -12
- package/dist/httpsnippet-lite/targets/python/python3/client.d.ts.map +0 -1
- package/dist/httpsnippet-lite/targets/python/python3/client.js +0 -88
- 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/httpsnippet-lite/targets/swift/helpers.d.ts +0 -15
- package/dist/httpsnippet-lite/targets/swift/helpers.d.ts.map +0 -1
- package/dist/httpsnippet-lite/targets/swift/helpers.js +0 -70
- package/dist/httpsnippet-lite/targets/swift/nsurlsession/client.d.ts +0 -12
- package/dist/httpsnippet-lite/targets/swift/nsurlsession/client.d.ts.map +0 -1
- package/dist/httpsnippet-lite/targets/swift/nsurlsession/client.js +0 -128
- 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,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":"nsurlsession.d.ts","sourceRoot":"","sources":["../../../../src/plugins/swift/nsurlsession/nsurlsession.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"nsurlsession.d.ts","sourceRoot":"","sources":["../../../../src/plugins/swift/nsurlsession/nsurlsession.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AA+EpD;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,MA2E/B,CAAA"}
|
|
@@ -1,5 +1,59 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { collectHeaders, joinUrlAndQuery, normalizeMethod, normalizeUrl } from '../../../libs/http.js';
|
|
2
|
+
const swiftStringLiteral = (value) => JSON.stringify(value);
|
|
3
|
+
const rawMultilineStringHashes = (value) => {
|
|
4
|
+
let hashCount = 1;
|
|
5
|
+
while (value.includes(`"""${'#'.repeat(hashCount)}`)) {
|
|
6
|
+
hashCount += 1;
|
|
7
|
+
}
|
|
8
|
+
return '#'.repeat(hashCount);
|
|
9
|
+
};
|
|
10
|
+
const toPrettyJson = (value) => {
|
|
11
|
+
try {
|
|
12
|
+
return JSON.stringify(JSON.parse(value), null, 2);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const encodeFormComponent = (value) => encodeURIComponent(value);
|
|
19
|
+
const escapeForMultipartHeader = (value) => value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
20
|
+
const buildMultipartBody = (params) => {
|
|
21
|
+
const lines = [
|
|
22
|
+
'let boundary = UUID().uuidString',
|
|
23
|
+
'var body = Data()',
|
|
24
|
+
'',
|
|
25
|
+
'func appendToBody(_ value: String) {',
|
|
26
|
+
' body.append(value.data(using: .utf8)!)',
|
|
27
|
+
'}',
|
|
28
|
+
'',
|
|
29
|
+
];
|
|
30
|
+
params.forEach((param) => {
|
|
31
|
+
const escapedName = escapeForMultipartHeader(param.name);
|
|
32
|
+
const escapedFileName = escapeForMultipartHeader(param.fileName ?? '');
|
|
33
|
+
const escapedContentType = param.contentType ? escapeForMultipartHeader(param.contentType) : undefined;
|
|
34
|
+
lines.push(`appendToBody("--\\(boundary)\\r\\n")`);
|
|
35
|
+
if (param.fileName !== undefined) {
|
|
36
|
+
lines.push(`appendToBody(${swiftStringLiteral(`Content-Disposition: form-data; name="${escapedName}"; filename="${escapedFileName}"\r\n`)})`);
|
|
37
|
+
if (escapedContentType) {
|
|
38
|
+
lines.push(`appendToBody(${swiftStringLiteral(`Content-Type: ${escapedContentType}\r\n`)})`);
|
|
39
|
+
}
|
|
40
|
+
lines.push('appendToBody("\\r\\n")');
|
|
41
|
+
lines.push(`appendToBody(${swiftStringLiteral(`<# File data for ${param.fileName || 'file'} #>\r\n`)})`);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
lines.push(`appendToBody(${swiftStringLiteral(`Content-Disposition: form-data; name="${escapedName}"\r\n`)})`);
|
|
45
|
+
if (escapedContentType) {
|
|
46
|
+
lines.push(`appendToBody(${swiftStringLiteral(`Content-Type: ${escapedContentType}\r\n`)})`);
|
|
47
|
+
}
|
|
48
|
+
lines.push('appendToBody("\\r\\n")');
|
|
49
|
+
lines.push(`appendToBody(${swiftStringLiteral(param.value ?? '')})`);
|
|
50
|
+
lines.push('appendToBody("\\r\\n")');
|
|
51
|
+
}
|
|
52
|
+
lines.push('');
|
|
53
|
+
});
|
|
54
|
+
lines.push('appendToBody("--\\(boundary)--\\r\\n")');
|
|
55
|
+
return lines;
|
|
56
|
+
};
|
|
3
57
|
/**
|
|
4
58
|
* swift/nsurlsession
|
|
5
59
|
*/
|
|
@@ -7,8 +61,67 @@ export const swiftNsurlsession = {
|
|
|
7
61
|
target: 'swift',
|
|
8
62
|
client: 'nsurlsession',
|
|
9
63
|
title: 'NSURLSession',
|
|
10
|
-
generate(request) {
|
|
11
|
-
|
|
12
|
-
|
|
64
|
+
generate(request, configuration) {
|
|
65
|
+
if (!request) {
|
|
66
|
+
return '';
|
|
67
|
+
}
|
|
68
|
+
const method = normalizeMethod(request.method);
|
|
69
|
+
const url = normalizeUrl(joinUrlAndQuery(request.url ?? '', request.queryString));
|
|
70
|
+
const headers = collectHeaders(request.headers, request.cookies);
|
|
71
|
+
const lines = [
|
|
72
|
+
'import Foundation',
|
|
73
|
+
'',
|
|
74
|
+
`var request = URLRequest(url: URL(string: ${swiftStringLiteral(url)})!)`,
|
|
75
|
+
];
|
|
76
|
+
lines.push(`request.httpMethod = ${swiftStringLiteral(method)}`);
|
|
77
|
+
headers.forEach((header) => {
|
|
78
|
+
lines.push(`request.setValue(${swiftStringLiteral(header.value)}, forHTTPHeaderField: ${swiftStringLiteral(header.name)})`);
|
|
79
|
+
});
|
|
80
|
+
if (configuration?.auth?.username && configuration?.auth?.password) {
|
|
81
|
+
lines.push(`let credentials = ${swiftStringLiteral(`${configuration.auth.username}:${configuration.auth.password}`)}`);
|
|
82
|
+
lines.push('let encodedCredentials = Data(credentials.utf8).base64EncodedString()');
|
|
83
|
+
lines.push('request.setValue("Basic \\(encodedCredentials)", forHTTPHeaderField: "Authorization")');
|
|
84
|
+
}
|
|
85
|
+
if (request.postData) {
|
|
86
|
+
const { mimeType, text, params } = request.postData;
|
|
87
|
+
if (mimeType === 'application/json' && text !== undefined) {
|
|
88
|
+
const prettyJson = toPrettyJson(text);
|
|
89
|
+
const hashes = rawMultilineStringHashes(prettyJson);
|
|
90
|
+
lines.push(`let jsonBody = ${hashes}"""`);
|
|
91
|
+
lines.push(prettyJson);
|
|
92
|
+
lines.push(`"""${hashes}`);
|
|
93
|
+
lines.push('request.httpBody = jsonBody.data(using: .utf8)');
|
|
94
|
+
}
|
|
95
|
+
else if (mimeType === 'application/x-www-form-urlencoded' && params?.length) {
|
|
96
|
+
const formBody = params
|
|
97
|
+
.map((param) => `${encodeFormComponent(param.name)}=${encodeFormComponent(param.value ?? '')}`)
|
|
98
|
+
.join('&');
|
|
99
|
+
lines.push(`let formBody = ${swiftStringLiteral(formBody)}`);
|
|
100
|
+
lines.push('request.httpBody = formBody.data(using: .utf8)');
|
|
101
|
+
}
|
|
102
|
+
else if (mimeType === 'multipart/form-data' && params?.length) {
|
|
103
|
+
lines.push(...buildMultipartBody(params));
|
|
104
|
+
lines.push('request.setValue("multipart/form-data; boundary=\\(boundary)", forHTTPHeaderField: "Content-Type")');
|
|
105
|
+
lines.push('request.httpBody = body');
|
|
106
|
+
}
|
|
107
|
+
else if (mimeType === 'application/octet-stream') {
|
|
108
|
+
lines.push(`let binaryBody = Data(${swiftStringLiteral(text ?? '')}.utf8)`);
|
|
109
|
+
lines.push('request.httpBody = binaryBody');
|
|
110
|
+
}
|
|
111
|
+
else if (text !== undefined) {
|
|
112
|
+
lines.push(`let rawBody = ${swiftStringLiteral(text)}`);
|
|
113
|
+
lines.push('request.httpBody = rawBody.data(using: .utf8)');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
lines.push('');
|
|
117
|
+
lines.push('let (data, response) = try await URLSession.shared.data(for: request)');
|
|
118
|
+
lines.push('');
|
|
119
|
+
lines.push('guard let httpResponse = response as? HTTPURLResponse,');
|
|
120
|
+
lines.push(' 200..<300 ~= httpResponse.statusCode else {');
|
|
121
|
+
lines.push(' throw URLError(.badServerResponse)');
|
|
122
|
+
lines.push('}');
|
|
123
|
+
lines.push('');
|
|
124
|
+
lines.push('print(String(data: data, encoding: .utf8) ?? "")');
|
|
125
|
+
return lines.join('\n');
|
|
13
126
|
},
|
|
14
127
|
};
|
package/dist/snippetz.d.ts
CHANGED
|
@@ -6,8 +6,8 @@ export declare function snippetz(): {
|
|
|
6
6
|
print<T extends TargetId>(target: T, client: ClientId<T>, request: Partial<HarRequest>): string | undefined;
|
|
7
7
|
clients(): import("@scalar/types/snippetz").Target[];
|
|
8
8
|
plugins(): {
|
|
9
|
-
target: "c" | "clojure" | "csharp" | "
|
|
10
|
-
client: "http" | "libcurl" | "clj_http" | "httpclient" | "restsharp" | "native" | "http1.1" | "asynchttp" | "nethttp" | "okhttp" | "unirest" | "axios" | "fetch" | "ofetch" | "undici" | "nsurlsession" | "cohttp" | "curl" | "guzzle" | "
|
|
9
|
+
target: "c" | "clojure" | "csharp" | "dart" | "fsharp" | "go" | "http" | "java" | "js" | "kotlin" | "node" | "objc" | "ocaml" | "php" | "powershell" | "python" | "r" | "ruby" | "rust" | "shell" | "swift";
|
|
10
|
+
client: "http" | "libcurl" | "clj_http" | "httpclient" | "restsharp" | "native" | "http1.1" | "asynchttp" | "nethttp" | "okhttp" | "unirest" | "axios" | "fetch" | "jquery" | "ofetch" | "xhr" | "undici" | "nsurlsession" | "cohttp" | "curl" | "guzzle" | "laravel" | "restmethod" | "webrequest" | "python3" | "requests" | "aiohttp" | "httpx_sync" | "httpx_async" | "httr2" | "reqwest" | "httpie" | "wget";
|
|
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;
|