From ef6bde89eb30ea4fb8f4f086e69e45c536ea0a77 Mon Sep 17 00:00:00 2001 From: AranaDeDoros <37278601+AranaDeDoros@users.noreply.github.com> Date: Sat, 29 Aug 2026 00:28:55 -0600 Subject: [PATCH 1/3] add scala as a target for code generation --- src/targets/targets.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/targets/targets.ts b/src/targets/targets.ts index 7f9362d47..6a674d0c9 100644 --- a/src/targets/targets.ts +++ b/src/targets/targets.ts @@ -22,6 +22,7 @@ import { ruby } from './ruby/target'; import { rust } from './rust/target'; import { shell } from './shell/target'; import { swift } from './swift/target'; +import { scala } from './scala/target'; export type TargetId = keyof typeof targets; @@ -79,6 +80,7 @@ export const targets = { rust, shell, swift, + scala }; export const isTarget = (target: Target): target is Target => { @@ -131,8 +133,7 @@ export const isTarget = (target: Target): target is Target => { if (!Object.prototype.hasOwnProperty.call(target.clientsById, target.info.default)) { throw new Error( - `target ${target.info.key} is configured with a default client ${ - target.info.default + `target ${target.info.key} is configured with a default client ${target.info.default }, but no such client was found in the property \`clientsById\` (found ${JSON.stringify( Object.keys(target.clientsById), )})`, From acef7a4ae6cb161ba517952ef75e7bfe1f9504a0 Mon Sep 17 00:00:00 2001 From: AranaDeDoros <37278601+AranaDeDoros@users.noreply.github.com> Date: Sat, 29 Aug 2026 00:29:48 -0600 Subject: [PATCH 2/3] add scala client and fixtures --- src/targets/scala/sttp/client.ts | 50 +++++++++++++++++++ .../fixtures/application-form-encoded.scala | 7 +++ .../sttp/fixtures/application-json.scala | 7 +++ src/targets/scala/sttp/fixtures/cookies.scala | 7 +++ .../scala/sttp/fixtures/custom-method.scala | 1 + src/targets/scala/sttp/fixtures/full.scala | 10 ++++ src/targets/scala/sttp/fixtures/headers.scala | 8 +++ src/targets/scala/sttp/fixtures/https.scala | 5 ++ .../sttp/fixtures/jsonObj-multiline.scala | 7 +++ .../sttp/fixtures/jsonObj-null-value.scala | 7 +++ .../scala/sttp/fixtures/multipart-data.scala | 7 +++ .../scala/sttp/fixtures/multipart-file.scala | 7 +++ .../multipart-form-data-no-params.scala | 6 +++ .../sttp/fixtures/multipart-form-data.scala | 7 +++ src/targets/scala/sttp/fixtures/nested.scala | 5 ++ src/targets/scala/sttp/fixtures/query.scala | 5 ++ src/targets/scala/sttp/fixtures/short.scala | 5 ++ .../scala/sttp/fixtures/text-plain.scala | 7 +++ src/targets/scala/target.ts | 14 ++++++ 19 files changed, 172 insertions(+) create mode 100644 src/targets/scala/sttp/client.ts create mode 100644 src/targets/scala/sttp/fixtures/application-form-encoded.scala create mode 100644 src/targets/scala/sttp/fixtures/application-json.scala create mode 100644 src/targets/scala/sttp/fixtures/cookies.scala create mode 100644 src/targets/scala/sttp/fixtures/custom-method.scala create mode 100644 src/targets/scala/sttp/fixtures/full.scala create mode 100644 src/targets/scala/sttp/fixtures/headers.scala create mode 100644 src/targets/scala/sttp/fixtures/https.scala create mode 100644 src/targets/scala/sttp/fixtures/jsonObj-multiline.scala create mode 100644 src/targets/scala/sttp/fixtures/jsonObj-null-value.scala create mode 100644 src/targets/scala/sttp/fixtures/multipart-data.scala create mode 100644 src/targets/scala/sttp/fixtures/multipart-file.scala create mode 100644 src/targets/scala/sttp/fixtures/multipart-form-data-no-params.scala create mode 100644 src/targets/scala/sttp/fixtures/multipart-form-data.scala create mode 100644 src/targets/scala/sttp/fixtures/nested.scala create mode 100644 src/targets/scala/sttp/fixtures/query.scala create mode 100644 src/targets/scala/sttp/fixtures/short.scala create mode 100644 src/targets/scala/sttp/fixtures/text-plain.scala create mode 100644 src/targets/scala/target.ts diff --git a/src/targets/scala/sttp/client.ts b/src/targets/scala/sttp/client.ts new file mode 100644 index 000000000..dd3c22ad7 --- /dev/null +++ b/src/targets/scala/sttp/client.ts @@ -0,0 +1,50 @@ +import { CodeBuilder } from '../../../helpers/code-builder'; +import { escapeForDoubleQuotes } from '../../../helpers/escape'; +import { Client } from '../../targets'; + +export const sttp: Client = { + info: { + key: 'sttp', + title: 'sttp', + link: 'https://sttp.softwaremill.com/', + description: 'The Scala HTTP Client API you always wanted', + }, + convert: ({ allHeaders, method, fullUrl, headersObj, cookies, postData }) => { + const { push, join } = new CodeBuilder(); + const formattedMethod = method.toLowerCase(); + const supportedMethods = ['get', 'post', 'put', 'delete', 'patch', 'head', 'options']; + + if (!supportedMethods.includes(formattedMethod)) { + return 'Method not supported'; + } + + push('import sttp.client4.*'); + push(''); + push('val backend = DefaultSyncBackend()'); + + let requestChain = `basicRequest.${formattedMethod}(uri"${fullUrl}")`; + + // Add headers + Object.keys(headersObj).forEach(key => { + const escapedVal = escapeForDoubleQuotes(headersObj[key]); + requestChain += `\n .header("${escapeForDoubleQuotes(key)}", "${escapedVal}")`; + }); + + // Add cookies (sttp has native .cookie support) + cookies.forEach(({ name, value }) => { + requestChain += `\n .cookie("${escapeForDoubleQuotes(name)}", "${escapeForDoubleQuotes(value)}")`; + }); + + // Add request body + if (postData && postData.text) { + const escapedBody = escapeForDoubleQuotes(postData.text); + requestChain += `\n .body("${escapedBody}")`; + } + + push(`val response = ${requestChain}`); + push(' .send(backend)'); + push(''); + + return join(); + }, +}; \ No newline at end of file diff --git a/src/targets/scala/sttp/fixtures/application-form-encoded.scala b/src/targets/scala/sttp/fixtures/application-form-encoded.scala new file mode 100644 index 000000000..bfe7fa051 --- /dev/null +++ b/src/targets/scala/sttp/fixtures/application-form-encoded.scala @@ -0,0 +1,7 @@ +import sttp.client4.* + +val backend = DefaultSyncBackend() +val response = basicRequest.post(uri"http://mockbin.com/har") + .header("content-type", "application/x-www-form-urlencoded") + .body("foo=bar&hello=world") + .send(backend) diff --git a/src/targets/scala/sttp/fixtures/application-json.scala b/src/targets/scala/sttp/fixtures/application-json.scala new file mode 100644 index 000000000..ec97b9952 --- /dev/null +++ b/src/targets/scala/sttp/fixtures/application-json.scala @@ -0,0 +1,7 @@ +import sttp.client4.* + +val backend = DefaultSyncBackend() +val response = basicRequest.post(uri"http://mockbin.com/har") + .header("content-type", "application/json") + .body("{\"number\":1,\"string\":\"f\\\"oo\",\"arr\":[1,2,3],\"nested\":{\"a\":\"b\"},\"arr_mix\":[1,\"a\",{\"arr_mix_nested\":{}}],\"boolean\":false}") + .send(backend) diff --git a/src/targets/scala/sttp/fixtures/cookies.scala b/src/targets/scala/sttp/fixtures/cookies.scala new file mode 100644 index 000000000..84a53ed19 --- /dev/null +++ b/src/targets/scala/sttp/fixtures/cookies.scala @@ -0,0 +1,7 @@ +import sttp.client4.* + +val backend = DefaultSyncBackend() +val response = basicRequest.post(uri"http://mockbin.com/har") + .cookie("foo", "bar") + .cookie("bar", "baz") + .send(backend) diff --git a/src/targets/scala/sttp/fixtures/custom-method.scala b/src/targets/scala/sttp/fixtures/custom-method.scala new file mode 100644 index 000000000..8eb41a680 --- /dev/null +++ b/src/targets/scala/sttp/fixtures/custom-method.scala @@ -0,0 +1 @@ +Method not supported \ No newline at end of file diff --git a/src/targets/scala/sttp/fixtures/full.scala b/src/targets/scala/sttp/fixtures/full.scala new file mode 100644 index 000000000..73fd52234 --- /dev/null +++ b/src/targets/scala/sttp/fixtures/full.scala @@ -0,0 +1,10 @@ +import sttp.client4.* + +val backend = DefaultSyncBackend() +val response = basicRequest.post(uri"http://mockbin.com/har?foo=bar&foo=baz&baz=abc&key=value") + .header("accept", "application/json") + .header("content-type", "application/x-www-form-urlencoded") + .cookie("foo", "bar") + .cookie("bar", "baz") + .body("foo=bar") + .send(backend) diff --git a/src/targets/scala/sttp/fixtures/headers.scala b/src/targets/scala/sttp/fixtures/headers.scala new file mode 100644 index 000000000..5824cf6e3 --- /dev/null +++ b/src/targets/scala/sttp/fixtures/headers.scala @@ -0,0 +1,8 @@ +import sttp.client4.* + +val backend = DefaultSyncBackend() +val response = basicRequest.get(uri"http://mockbin.com/har") + .header("accept", "application/json") + .header("x-foo", "Bar") + .header("quoted-value", "\"quoted\" 'string'") + .send(backend) diff --git a/src/targets/scala/sttp/fixtures/https.scala b/src/targets/scala/sttp/fixtures/https.scala new file mode 100644 index 000000000..0e36dd7a8 --- /dev/null +++ b/src/targets/scala/sttp/fixtures/https.scala @@ -0,0 +1,5 @@ +import sttp.client4.* + +val backend = DefaultSyncBackend() +val response = basicRequest.get(uri"https://mockbin.com/har") + .send(backend) diff --git a/src/targets/scala/sttp/fixtures/jsonObj-multiline.scala b/src/targets/scala/sttp/fixtures/jsonObj-multiline.scala new file mode 100644 index 000000000..5ba74389d --- /dev/null +++ b/src/targets/scala/sttp/fixtures/jsonObj-multiline.scala @@ -0,0 +1,7 @@ +import sttp.client4.* + +val backend = DefaultSyncBackend() +val response = basicRequest.post(uri"http://mockbin.com/har") + .header("content-type", "application/json") + .body("{\n \"foo\": \"bar\"\n}") + .send(backend) diff --git a/src/targets/scala/sttp/fixtures/jsonObj-null-value.scala b/src/targets/scala/sttp/fixtures/jsonObj-null-value.scala new file mode 100644 index 000000000..364826e2a --- /dev/null +++ b/src/targets/scala/sttp/fixtures/jsonObj-null-value.scala @@ -0,0 +1,7 @@ +import sttp.client4.* + +val backend = DefaultSyncBackend() +val response = basicRequest.post(uri"http://mockbin.com/har") + .header("content-type", "application/json") + .body("{\"foo\":null}") + .send(backend) diff --git a/src/targets/scala/sttp/fixtures/multipart-data.scala b/src/targets/scala/sttp/fixtures/multipart-data.scala new file mode 100644 index 000000000..cbc5b4b57 --- /dev/null +++ b/src/targets/scala/sttp/fixtures/multipart-data.scala @@ -0,0 +1,7 @@ +import sttp.client4.* + +val backend = DefaultSyncBackend() +val response = basicRequest.post(uri"http://mockbin.com/har") + .header("content-type", "multipart/form-data; boundary=---011000010111000001101001") + .body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"; filename=\"hello.txt\"\r\nContent-Type: text/plain\r\n\r\nHello World\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"bar\"\r\n\r\nBonjour le monde\r\n-----011000010111000001101001--\r\n") + .send(backend) diff --git a/src/targets/scala/sttp/fixtures/multipart-file.scala b/src/targets/scala/sttp/fixtures/multipart-file.scala new file mode 100644 index 000000000..e3171a149 --- /dev/null +++ b/src/targets/scala/sttp/fixtures/multipart-file.scala @@ -0,0 +1,7 @@ +import sttp.client4.* + +val backend = DefaultSyncBackend() +val response = basicRequest.post(uri"http://mockbin.com/har") + .header("content-type", "multipart/form-data; boundary=---011000010111000001101001") + .body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"; filename=\"hello.txt\"\r\nContent-Type: text/plain\r\n\r\n\r\n-----011000010111000001101001--\r\n") + .send(backend) diff --git a/src/targets/scala/sttp/fixtures/multipart-form-data-no-params.scala b/src/targets/scala/sttp/fixtures/multipart-form-data-no-params.scala new file mode 100644 index 000000000..bca3c0f6c --- /dev/null +++ b/src/targets/scala/sttp/fixtures/multipart-form-data-no-params.scala @@ -0,0 +1,6 @@ +import sttp.client4.* + +val backend = DefaultSyncBackend() +val response = basicRequest.post(uri"http://mockbin.com/har") + .header("Content-Type", "multipart/form-data") + .send(backend) diff --git a/src/targets/scala/sttp/fixtures/multipart-form-data.scala b/src/targets/scala/sttp/fixtures/multipart-form-data.scala new file mode 100644 index 000000000..f0c5caf1d --- /dev/null +++ b/src/targets/scala/sttp/fixtures/multipart-form-data.scala @@ -0,0 +1,7 @@ +import sttp.client4.* + +val backend = DefaultSyncBackend() +val response = basicRequest.post(uri"http://mockbin.com/har") + .header("Content-Type", "multipart/form-data; boundary=---011000010111000001101001") + .body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n-----011000010111000001101001--\r\n") + .send(backend) diff --git a/src/targets/scala/sttp/fixtures/nested.scala b/src/targets/scala/sttp/fixtures/nested.scala new file mode 100644 index 000000000..00560e818 --- /dev/null +++ b/src/targets/scala/sttp/fixtures/nested.scala @@ -0,0 +1,5 @@ +import sttp.client4.* + +val backend = DefaultSyncBackend() +val response = basicRequest.get(uri"http://mockbin.com/har?foo%5Bbar%5D=baz%2Czap&fiz=buz&key=value") + .send(backend) diff --git a/src/targets/scala/sttp/fixtures/query.scala b/src/targets/scala/sttp/fixtures/query.scala new file mode 100644 index 000000000..de513f678 --- /dev/null +++ b/src/targets/scala/sttp/fixtures/query.scala @@ -0,0 +1,5 @@ +import sttp.client4.* + +val backend = DefaultSyncBackend() +val response = basicRequest.get(uri"http://mockbin.com/har?foo=bar&foo=baz&baz=abc&key=value") + .send(backend) diff --git a/src/targets/scala/sttp/fixtures/short.scala b/src/targets/scala/sttp/fixtures/short.scala new file mode 100644 index 000000000..112b53eb5 --- /dev/null +++ b/src/targets/scala/sttp/fixtures/short.scala @@ -0,0 +1,5 @@ +import sttp.client4.* + +val backend = DefaultSyncBackend() +val response = basicRequest.get(uri"http://mockbin.com/har") + .send(backend) diff --git a/src/targets/scala/sttp/fixtures/text-plain.scala b/src/targets/scala/sttp/fixtures/text-plain.scala new file mode 100644 index 000000000..f47747e2d --- /dev/null +++ b/src/targets/scala/sttp/fixtures/text-plain.scala @@ -0,0 +1,7 @@ +import sttp.client4.* + +val backend = DefaultSyncBackend() +val response = basicRequest.post(uri"http://mockbin.com/har") + .header("content-type", "text/plain") + .body("Hello World") + .send(backend) diff --git a/src/targets/scala/target.ts b/src/targets/scala/target.ts new file mode 100644 index 000000000..2bb1081c3 --- /dev/null +++ b/src/targets/scala/target.ts @@ -0,0 +1,14 @@ +import { Target } from '../targets'; +import { sttp } from './sttp/client'; + +export const scala: Target = { + info: { + key: 'scala', + title: 'Scala', + extname: '.scala', + default: 'sttp', + }, + clientsById: { + sttp, + }, +}; \ No newline at end of file From b845349c5a330ce61586c2515d4a57f173ac17a3 Mon Sep 17 00:00:00 2001 From: AranaDeDoros <37278601+AranaDeDoros@users.noreply.github.com> Date: Sat, 29 Aug 2026 00:30:14 -0600 Subject: [PATCH 3/3] add scala as keyword and remove duplicate swift --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 1316314a6..1c1991e86 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "shell", "snippet", "swift", - "swift", + "scala", "unirest", "xhr", "xmlhttprequest" @@ -93,4 +93,4 @@ "stringify-object": "3.3.0", "yargs": "^17.4.0" } -} +} \ No newline at end of file