{
  "version": 1,
  "workflows": [
    {
      "id": "26B48E0A-A8B1-4E3D-89B9-1DD19F2B517F",
      "defaultIntroducedVersion": 11,
      "name": "Convert cURL Request",
      "nameLocalizationKey": "convert_curl_request",
      "summary": "URLSession · fetch · Axios",
      "symbolName": "curlybraces.square",
      "isEnabled": true,
      "requiresTrustConfirmation": false,
      "sortOrder": 13,
      "mode": "scriptFilter",
      "supportedTypes": [
        "text"
      ],
      "script": {
        "source": "inline",
        "code": "ObjC.import(\"Foundation\");\n\nfunction readJSON() {\n    var data = $.NSFileHandle.fileHandleWithStandardInput.readDataToEndOfFile;\n    var text = $.NSString.alloc.initWithDataEncoding(data, $.NSUTF8StringEncoding).js;\n    return JSON.parse(text);\n}\n\nfunction writeJSON(value) {\n    var text = JSON.stringify(value);\n    var data = $(text).dataUsingEncoding($.NSUTF8StringEncoding);\n    $.NSFileHandle.fileHandleWithStandardOutput.writeData(data);\n}\n\nfunction tokenize(command) {\n    var normalized = command.replace(/\\\\\\s*\\r?\\n/g, \" \");\n    var tokens = [];\n    var token = \"\";\n    var quote = null;\n    var escaped = false;\n    var hasToken = false;\n\n    for (var index = 0; index < normalized.length; index++) {\n        var character = normalized.charAt(index);\n        if (escaped) {\n            token += character;\n            hasToken = true;\n            escaped = false;\n            continue;\n        }\n        if (quote) {\n            if (character === quote) {\n                quote = null;\n            } else if (quote === \"\\\"\" && character === \"\\\\\") {\n                escaped = true;\n            } else {\n                token += character;\n                hasToken = true;\n            }\n            continue;\n        }\n        if (character === \"\\\\\") {\n            escaped = true;\n            hasToken = true;\n        } else if (character === \"\\\"\" || character === \"'\") {\n            quote = character;\n            hasToken = true;\n        } else if (/\\s/.test(character)) {\n            if (hasToken) {\n                tokens.push(token);\n                token = \"\";\n                hasToken = false;\n            }\n        } else {\n            token += character;\n            hasToken = true;\n        }\n    }\n    if (escaped || quote) {\n        throw new Error(\"Invalid cURL quoting\");\n    }\n    if (hasToken) tokens.push(token);\n    return tokens;\n}\n\nfunction base64(value) {\n    var data = $(value).dataUsingEncoding($.NSUTF8StringEncoding);\n    return data.base64EncodedStringWithOptions(0).js;\n}\n\nfunction parseCurl(command) {\n    var input = command.trim();\n    if (!/^curl(?:\\s|$)/i.test(input) || input.indexOf(\"$(\") >= 0 || input.indexOf(\"`\") >= 0) {\n        throw new Error(\"Unsupported cURL command\");\n    }\n\n    var tokens = tokenize(input);\n    if (!tokens.length || tokens[0].toLowerCase() !== \"curl\") {\n        throw new Error(\"Invalid cURL command\");\n    }\n\n    var method = \"\";\n    var headers = [];\n    var dataParts = [];\n    var url = \"\";\n    var headOnly = false;\n\n    function nextValue(index, flag) {\n        if (index + 1 >= tokens.length) throw new Error(\"Missing value for \" + flag);\n        return tokens[index + 1];\n    }\n\n    for (var index = 1; index < tokens.length; index++) {\n        var token = tokens[index];\n        if (token === \"-X\" || token === \"--request\") {\n            method = nextValue(index, token).toUpperCase();\n            index += 1;\n        } else if (token.indexOf(\"--request=\") === 0) {\n            method = token.slice(\"--request=\".length).toUpperCase();\n        } else if (/^-X.+/.test(token)) {\n            method = token.slice(2).toUpperCase();\n        } else if (token === \"-H\" || token === \"--header\") {\n            var header = nextValue(index, token);\n            var separator = header.indexOf(\":\");\n            if (separator > 0) {\n                headers.push({\n                    name: header.slice(0, separator).trim(),\n                    value: header.slice(separator + 1).trim()\n                });\n            }\n            index += 1;\n        } else if (token.indexOf(\"--header=\") === 0 || (/^-H.+/.test(token))) {\n            var inlineHeader = token.indexOf(\"--header=\") === 0\n                ? token.slice(\"--header=\".length)\n                : token.slice(2);\n            var inlineSeparator = inlineHeader.indexOf(\":\");\n            if (inlineSeparator > 0) {\n                headers.push({\n                    name: inlineHeader.slice(0, inlineSeparator).trim(),\n                    value: inlineHeader.slice(inlineSeparator + 1).trim()\n                });\n            }\n        } else if (token === \"-d\" || token === \"--data\" || token === \"--data-raw\" ||\n                   token === \"--data-binary\" || token === \"--data-ascii\" ||\n                   token === \"--data-urlencode\") {\n            dataParts.push(nextValue(index, token));\n            index += 1;\n        } else if (/^-d.+/.test(token)) {\n            dataParts.push(token.slice(2));\n        } else if (/^--data(?:-raw|-binary|-ascii|-urlencode)?=/.test(token)) {\n            dataParts.push(token.slice(token.indexOf(\"=\") + 1));\n        } else if (token === \"-u\" || token === \"--user\") {\n            headers.push({\n                name: \"Authorization\",\n                value: \"Basic \" + base64(nextValue(index, token))\n            });\n            index += 1;\n        } else if (token === \"-I\" || token === \"--head\") {\n            headOnly = true;\n        } else if (token === \"--url\") {\n            url = nextValue(index, token);\n            index += 1;\n        } else if (token.indexOf(\"--url=\") === 0) {\n            url = token.slice(\"--url=\".length);\n        } else if (/^https?:\\/\\//i.test(token)) {\n            url = token;\n        } else if (token.charAt(0) === \"-\") {\n            var flagsWithValues = [\n                \"-A\", \"--user-agent\", \"-b\", \"--cookie\", \"-e\", \"--referer\",\n                \"-o\", \"--output\", \"-x\", \"--proxy\", \"--connect-timeout\",\n                \"--max-time\", \"--retry\", \"--cacert\", \"--cert\", \"--key\"\n            ];\n            if (flagsWithValues.indexOf(token) >= 0) {\n                nextValue(index, token);\n                index += 1;\n            }\n        } else if (!url) {\n            url = token;\n        }\n    }\n\n    if (!url || !/^https?:\\/\\//i.test(url)) {\n        throw new Error(\"cURL URL is missing or unsupported\");\n    }\n    var body = dataParts.length ? dataParts.join(\"&\") : null;\n    if (headOnly) method = \"HEAD\";\n    if (!method) method = body === null ? \"GET\" : \"POST\";\n    return { url: url, method: method, headers: headers, body: body };\n}\n\nfunction quoted(value) {\n    return JSON.stringify(String(value));\n}\n\nfunction swiftCode(request) {\n    var lines = [\n        \"import Foundation\",\n        \"\",\n        \"var request = URLRequest(url: URL(string: \" + quoted(request.url) + \")!)\",\n        \"request.httpMethod = \" + quoted(request.method)\n    ];\n    request.headers.forEach(function (header) {\n        lines.push(\"request.setValue(\" + quoted(header.value) +\n            \", forHTTPHeaderField: \" + quoted(header.name) + \")\");\n    });\n    if (request.body !== null) {\n        lines.push(\"request.httpBody = \" + quoted(request.body) + \".data(using: .utf8)\");\n    }\n    lines.push(\n        \"\",\n        \"URLSession.shared.dataTask(with: request) { data, response, error in\",\n        \"    if let error {\",\n        \"        print(error)\",\n        \"        return\",\n        \"    }\",\n        \"\",\n        \"    if let data, let body = String(data: data, encoding: .utf8) {\",\n        \"        print(body)\",\n        \"    }\",\n        \"}.resume()\"\n    );\n    return lines.join(\"\\n\");\n}\n\nfunction javascriptObject(entries, indent, closingIndent) {\n    if (!entries.length) return \"{}\";\n    return \"{\\n\" + entries.map(function (entry) {\n        return indent + quoted(entry.name) + \": \" + quoted(entry.value);\n    }).join(\",\\n\") + \"\\n\" + closingIndent + \"}\";\n}\n\nfunction fetchCode(request) {\n    var options = [\"  method: \" + quoted(request.method)];\n    if (request.headers.length) {\n        options.push(\"  headers: \" + javascriptObject(request.headers, \"    \", \"  \"));\n    }\n    if (request.body !== null) {\n        options.push(\"  body: \" + quoted(request.body));\n    }\n    return [\n        \"fetch(\" + quoted(request.url) + \", {\",\n        options.join(\",\\n\"),\n        \"})\",\n        \"  .then((response) => response.text())\",\n        \"  .then((body) => console.log(body))\",\n        \"  .catch((error) => console.error(error));\"\n    ].join(\"\\n\");\n}\n\nfunction axiosCode(request) {\n    var options = [\n        \"  method: \" + quoted(request.method.toLowerCase()),\n        \"  url: \" + quoted(request.url)\n    ];\n    if (request.headers.length) {\n        options.push(\"  headers: \" + javascriptObject(request.headers, \"    \", \"  \"));\n    }\n    if (request.body !== null) {\n        options.push(\"  data: \" + quoted(request.body));\n    }\n    return [\n        \"axios({\",\n        options.join(\",\\n\"),\n        \"})\",\n        \"  .then((response) => console.log(response.data))\",\n        \"  .catch((error) => console.error(error));\"\n    ].join(\"\\n\");\n}\n\nfunction orderedActions(payload) {\n    var actions = [\n        { id: \"urlSession\", title: \"Convert to URLSession\", subtitle: \"Swift\", icon: \"swift\" },\n        { id: \"fetch\", title: \"Convert to fetch\", subtitle: \"JavaScript\", icon: \"curlybraces\" },\n        { id: \"axios\", title: \"Convert to Axios\", subtitle: \"JavaScript\", icon: \"a.square.fill\" }\n    ];\n    var target = payload.targetApp || {};\n    var description = ((target.bundleIdentifier || \"\") + \" \" + (target.name || \"\")).toLowerCase();\n    var preferred = null;\n    if (/xcode|com\\.apple\\.dt\\.xcode|appcode/.test(description)) preferred = \"urlSession\";\n    else if (/visual studio code|vscode|cursor|windsurf|webstorm/.test(description)) preferred = \"fetch\";\n    if (!preferred) return actions;\n    return actions.sort(function (left, right) {\n        return (left.id === preferred ? -1 : 0) - (right.id === preferred ? -1 : 0);\n    });\n}\n\nfunction main() {\n    var payload = readJSON();\n    var input = payload.paste && payload.paste.text ? payload.paste.text : \"\";\n    var request = parseCurl(input);\n    var actions = orderedActions(payload);\n\n    if (payload.phase === \"filter\") {\n        writeJSON({\n            version: 1,\n            actions: actions.map(function (action) {\n                return {\n                    id: action.id,\n                    title: action.title,\n                    subtitle: action.subtitle,\n                    icon: action.icon,\n                    argument: null\n                };\n            })\n        });\n        return;\n    }\n\n    if (payload.phase !== \"execute\") {\n        throw new Error(\"Unsupported phase\");\n    }\n\n    var result;\n    if (payload.actionID === \"urlSession\") result = swiftCode(request);\n    else if (payload.actionID === \"fetch\") result = fetchCode(request);\n    else if (payload.actionID === \"axios\") result = axiosCode(request);\n    else throw new Error(\"Unknown cURL conversion action\");\n\n    writeJSON({\n        version: 1,\n        result: {\n            type: \"text\",\n            value: result\n        }\n    });\n}\n\nmain();\n",
        "scriptPath": "",
        "interpreterPath": "/usr/bin/osascript",
        "arguments": [
          "-l",
          "JavaScript"
        ],
        "workingDirectory": "",
        "filterTimeout": 5,
        "executeTimeout": 10
      },
      "workflowVersion": 1
    }
  ]
}
