@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.
Files changed (82) hide show
  1. package/dist/clients/index.d.ts.map +1 -1
  2. package/dist/clients/index.js +7 -5
  3. package/dist/libs/http.d.ts +29 -0
  4. package/dist/libs/http.d.ts.map +1 -1
  5. package/dist/libs/http.js +53 -0
  6. package/dist/libs/javascript.d.ts.map +1 -1
  7. package/dist/libs/javascript.js +6 -3
  8. package/dist/plugins/c/libcurl/libcurl.d.ts.map +1 -1
  9. package/dist/plugins/c/libcurl/libcurl.js +145 -5
  10. package/dist/plugins/go/native/native.d.ts.map +1 -1
  11. package/dist/plugins/go/native/native.js +150 -5
  12. package/dist/plugins/js/axios/axios.d.ts +1 -2
  13. package/dist/plugins/js/axios/axios.d.ts.map +1 -1
  14. package/dist/plugins/js/axios/axios.js +2 -11
  15. package/dist/plugins/node/axios/axios.d.ts +1 -2
  16. package/dist/plugins/node/axios/axios.d.ts.map +1 -1
  17. package/dist/plugins/node/axios/axios.js +2 -11
  18. package/dist/plugins/php/laravel/index.d.ts +2 -0
  19. package/dist/plugins/php/laravel/index.d.ts.map +1 -0
  20. package/dist/plugins/php/laravel/index.js +1 -0
  21. package/dist/plugins/php/laravel/laravel.d.ts +6 -0
  22. package/dist/plugins/php/laravel/laravel.d.ts.map +1 -0
  23. package/dist/plugins/php/laravel/laravel.js +134 -0
  24. package/dist/plugins/python/aiohttp/aiohttp.d.ts +6 -0
  25. package/dist/plugins/python/aiohttp/aiohttp.d.ts.map +1 -0
  26. package/dist/plugins/python/aiohttp/aiohttp.js +107 -0
  27. package/dist/plugins/python/aiohttp/index.d.ts +2 -0
  28. package/dist/plugins/python/aiohttp/index.d.ts.map +1 -0
  29. package/dist/plugins/python/aiohttp/index.js +1 -0
  30. package/dist/plugins/python/python3/python3.d.ts.map +1 -1
  31. package/dist/plugins/python/python3/python3.js +192 -5
  32. package/dist/plugins/python/requestsLike.d.ts +2 -0
  33. package/dist/plugins/python/requestsLike.d.ts.map +1 -1
  34. package/dist/plugins/python/requestsLike.js +9 -9
  35. package/dist/plugins/r/httr2/httr2.d.ts +6 -0
  36. package/dist/plugins/r/httr2/httr2.d.ts.map +1 -0
  37. package/dist/plugins/r/httr2/httr2.js +159 -0
  38. package/dist/plugins/r/httr2/index.d.ts +2 -0
  39. package/dist/plugins/r/httr2/index.d.ts.map +1 -0
  40. package/dist/plugins/r/httr2/index.js +1 -0
  41. package/dist/plugins/ruby/native/native.d.ts.map +1 -1
  42. package/dist/plugins/ruby/native/native.js +138 -5
  43. package/dist/plugins/shared/axios.d.ts +3 -0
  44. package/dist/plugins/shared/axios.d.ts.map +1 -0
  45. package/dist/plugins/shared/axios.js +148 -0
  46. package/dist/plugins/swift/nsurlsession/nsurlsession.d.ts.map +1 -1
  47. package/dist/plugins/swift/nsurlsession/nsurlsession.js +118 -5
  48. package/dist/snippetz.d.ts +2 -2
  49. package/package.json +17 -7
  50. package/dist/httpsnippet-lite/targets/c/libcurl/client.d.ts +0 -3
  51. package/dist/httpsnippet-lite/targets/c/libcurl/client.d.ts.map +0 -1
  52. package/dist/httpsnippet-lite/targets/c/libcurl/client.js +0 -74
  53. package/dist/httpsnippet-lite/targets/go/native/client.d.ts +0 -12
  54. package/dist/httpsnippet-lite/targets/go/native/client.d.ts.map +0 -1
  55. package/dist/httpsnippet-lite/targets/go/native/client.js +0 -157
  56. package/dist/httpsnippet-lite/targets/javascript/axios/client.d.ts +0 -12
  57. package/dist/httpsnippet-lite/targets/javascript/axios/client.d.ts.map +0 -1
  58. package/dist/httpsnippet-lite/targets/javascript/axios/client.js +0 -86
  59. package/dist/httpsnippet-lite/targets/node/axios/client.d.ts +0 -3
  60. package/dist/httpsnippet-lite/targets/node/axios/client.d.ts.map +0 -1
  61. package/dist/httpsnippet-lite/targets/node/axios/client.js +0 -78
  62. package/dist/httpsnippet-lite/targets/python/python3/client.d.ts +0 -12
  63. package/dist/httpsnippet-lite/targets/python/python3/client.d.ts.map +0 -1
  64. package/dist/httpsnippet-lite/targets/python/python3/client.js +0 -88
  65. package/dist/httpsnippet-lite/targets/r/httr/client.d.ts +0 -12
  66. package/dist/httpsnippet-lite/targets/r/httr/client.d.ts.map +0 -1
  67. package/dist/httpsnippet-lite/targets/r/httr/client.js +0 -115
  68. package/dist/httpsnippet-lite/targets/ruby/native/client.d.ts +0 -3
  69. package/dist/httpsnippet-lite/targets/ruby/native/client.d.ts.map +0 -1
  70. package/dist/httpsnippet-lite/targets/ruby/native/client.js +0 -67
  71. package/dist/httpsnippet-lite/targets/swift/helpers.d.ts +0 -15
  72. package/dist/httpsnippet-lite/targets/swift/helpers.d.ts.map +0 -1
  73. package/dist/httpsnippet-lite/targets/swift/helpers.js +0 -70
  74. package/dist/httpsnippet-lite/targets/swift/nsurlsession/client.d.ts +0 -12
  75. package/dist/httpsnippet-lite/targets/swift/nsurlsession/client.d.ts.map +0 -1
  76. package/dist/httpsnippet-lite/targets/swift/nsurlsession/client.js +0 -128
  77. package/dist/plugins/r/httr/httr.d.ts +0 -6
  78. package/dist/plugins/r/httr/httr.d.ts.map +0 -1
  79. package/dist/plugins/r/httr/httr.js +0 -14
  80. package/dist/plugins/r/httr/index.d.ts +0 -2
  81. package/dist/plugins/r/httr/index.d.ts.map +0 -1
  82. 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,2 @@
1
+ export { rHttr2 } from './httr2.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -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;AAKpD;;GAEG;AACH,eAAO,MAAM,UAAU,EAAE,MAQxB,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 { native } from '../../../httpsnippet-lite/targets/ruby/native/client.js';
2
- import { convertWithHttpSnippetLite } from '../../../utils/convertWithHttpSnippetLite.js';
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
- // TODO: Write an own converter
12
- return convertWithHttpSnippetLite(native, request);
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,3 @@
1
+ import type { Plugin, TargetId } from '@scalar/types/snippetz';
2
+ export declare const createAxiosPlugin: (target: Extract<TargetId, "js" | "node">) => Plugin;
3
+ //# sourceMappingURL=axios.d.ts.map
@@ -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;AAKpD;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAQ/B,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 { nsurlsession } from '../../../httpsnippet-lite/targets/swift/nsurlsession/client.js';
2
- import { convertWithHttpSnippetLite } from '../../../utils/convertWithHttpSnippetLite.js';
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
- // TODO: Write an own converter
12
- return convertWithHttpSnippetLite(nsurlsession, request);
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
  };
@@ -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" | "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" | "httr" | "httpie" | "wget" | "jquery" | "xhr" | "reqwest";
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;