{
  "version": 1,
  "workflows": [
    {
      "id": "5AD22863-2B2B-4ADA-9B29-F5992787EE2D",
      "workflowVersion": 1,
      "name": "Base64 Tools",
      "summary": "Encode or decode text and files",
      "symbolName": "textformat.abc",
      "isEnabled": true,
      "requiresTrustConfirmation": false,
      "sortOrder": 0,
      "mode": "scriptFilter",
      "supportedTypes": [
        "text",
        "fileURL",
        "imageURL",
        "videoURL"
      ],
      "script": {
        "source": "inline",
        "code": "ObjC.import(\"Foundation\");\n\nfunction readJSON() {\n    var data = $.NSFileHandle.fileHandleWithStandardInput.readDataToEndOfFile;\n    var text = $.NSString.alloc.initWithDataEncoding(data, $.NSUTF8StringEncoding);\n    if (!text) throw new Error(\"Workflow input is not valid UTF-8\");\n    return JSON.parse(text.js);\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 textResult(value) {\n    writeJSON({\n        version: 1,\n        result: {\n            type: \"text\",\n            value: String(value)\n        }\n    });\n}\n\nfunction filesResult(paths) {\n    writeJSON({\n        version: 1,\n        result: {\n            type: \"files\",\n            paths: paths\n        }\n    });\n}\n\nfunction preview(value) {\n    var compact = String(value).replace(/\\s+/g, \" \").trim();\n    return compact.length > 90 ? compact.slice(0, 87) + \"...\" : compact;\n}\n\nfunction normalizedLineEndings(value) {\n    return String(value).replace(/\\r\\n?/g, \"\\n\");\n}\n\nfunction fileRepresentations(payload) {\n    var items = (payload.paste && payload.paste.representations) || [];\n    return items.filter(function (item) {\n        return item.path && String(item.path).length > 0;\n    });\n}\n\nfunction inputFileRepresentations(payload, allowedTypes) {\n    var pasteType = payload.paste && payload.paste.type;\n    if (allowedTypes.indexOf(pasteType) < 0) return [];\n    return fileRepresentations(payload);\n}\n\nfunction firstInputFile(payload, allowedTypes) {\n    var files = inputFileRepresentations(payload, allowedTypes);\n    return files.length ? files[0] : null;\n}\n\nfunction readFileData(filePath) {\n    var data = $.NSData.dataWithContentsOfFile($(filePath));\n    if (!data) throw new Error(\"Unable to read input file\");\n    return data;\n}\n\nfunction utf8String(data) {\n    var text = $.NSString.alloc.initWithDataEncoding(data, $.NSUTF8StringEncoding);\n    return text ? text.js : null;\n}\n\nfunction readFileText(filePath) {\n    var text = utf8String(readFileData(filePath));\n    if (text === null) throw new Error(\"Input file is not valid UTF-8 text\");\n    return text;\n}\n\nfunction safeFileName(value) {\n    var name = String(value || \"output\").split(\"/\").pop();\n    name = name.replace(/[:\\u0000-\\u001f]/g, \"-\");\n    return name || \"output\";\n}\n\nfunction uniqueFileName(prefix, extensionName) {\n    var timestamp = Math.floor(Date.now() / 1000);\n    var suffix = $.NSUUID.UUID.UUIDString.js.slice(0, 8).toLowerCase();\n    return prefix + \"-\" + timestamp + \"-\" + suffix + \".\" + extensionName;\n}\n\nfunction outputPath(payload, fileName) {\n    return payload.directories.output + \"/\" + safeFileName(fileName);\n}\n\nfunction runTask(executable, arguments) {\n    var task = $.NSTask.alloc.init;\n    var outputPipe = $.NSPipe.pipe;\n    var errorPipe = $.NSPipe.pipe;\n    task.launchPath = executable;\n    task.arguments = $(arguments);\n    task.standardOutput = outputPipe;\n    task.standardError = errorPipe;\n    task.launch;\n    task.waitUntilExit;\n\n    var stdoutData = outputPipe.fileHandleForReading.readDataToEndOfFile;\n    var stderrData = errorPipe.fileHandleForReading.readDataToEndOfFile;\n    var stdoutText = $.NSString.alloc.initWithDataEncoding(\n        stdoutData,\n        $.NSUTF8StringEncoding\n    );\n    var stderrText = $.NSString.alloc.initWithDataEncoding(\n        stderrData,\n        $.NSUTF8StringEncoding\n    );\n    return {\n        status: Number(task.terminationStatus),\n        stdout: stdoutText ? stdoutText.js : \"\",\n        stderr: stderrText ? stderrText.js : \"\"\n    };\n}\n\nfunction compactBase64(value) {\n    return String(value).replace(/\\s+/g, \"\");\n}\n\nfunction decodedData(value) {\n    var compact = compactBase64(value);\n    if (!compact || compact.length % 4 === 1 ||\n        /[^A-Za-z0-9+/=]/.test(compact) ||\n        !/^[A-Za-z0-9+/]*={0,2}$/.test(compact)) {\n        return null;\n    }\n    return $.NSData.alloc.initWithBase64EncodedStringOptions($(compact), 0);\n}\n\nfunction writeData(data, filePath) {\n    if (!data.writeToFileAtomically($(filePath), true)) {\n        throw new Error(\"Unable to write Base64 output file\");\n    }\n}\n\nfunction encodeText(value) {\n    var data = $(String(value)).dataUsingEncoding($.NSUTF8StringEncoding);\n    return data.base64EncodedStringWithOptions(0).js;\n}\n\nfunction encodeFile(payload, representation) {\n    var data = readFileData(representation.path);\n    var encoded = data.base64EncodedStringWithOptions(0).js;\n    var sourceName = safeFileName(representation.fileName || \"file\");\n    var fileName = sourceName + \".base64.txt\";\n    var output = outputPath(payload, fileName);\n    if (!$(encoded).writeToFileAtomicallyEncodingError(\n        $(output),\n        true,\n        $.NSUTF8StringEncoding,\n        null\n    )) {\n        throw new Error(\"Unable to create Base64 file\");\n    }\n    filesResult([fileName]);\n}\n\nfunction decodeText(payload, value) {\n    var data = decodedData(value);\n    if (!data) throw new Error(\"Input is not valid Base64\");\n    var text = utf8String(data);\n    if (text !== null) {\n        textResult(text);\n        return;\n    }\n    var fileName = \"decoded.bin\";\n    writeData(data, outputPath(payload, fileName));\n    filesResult([fileName]);\n}\n\nfunction decodeFile(payload, representation) {\n    var encoded = readFileText(representation.path);\n    var data = decodedData(encoded);\n    if (!data) throw new Error(\"File content is not valid Base64\");\n    var fileName = \"decoded.bin\";\n    writeData(data, outputPath(payload, fileName));\n    filesResult([fileName]);\n}\n\nfunction main() {\n    var payload = readJSON();\n    var representation = firstInputFile(\n        payload,\n        [\"fileURL\", \"imageURL\", \"videoURL\"]\n    );\n    var hasFile = representation !== null;\n    var text = (payload.paste && payload.paste.text) || \"\";\n\n    if (payload.phase === \"filter\") {\n        var actions = hasFile\n            ? [\n                {\n                    id: \"encodeFile\",\n                    title: \"Encode File as Base64\",\n                    subtitle: representation.fileName || \"Create a .base64.txt file\",\n                    icon: \"doc.text\",\n                    argument: { operation: \"encodeFile\" }\n                },\n                {\n                    id: \"decodeFile\",\n                    title: \"Decode Base64 File\",\n                    subtitle: \"Create a decoded binary file\",\n                    icon: \"doc.badge.arrow.down\",\n                    argument: { operation: \"decodeFile\" }\n                }\n            ]\n            : [\n                {\n                    id: \"encodeText\",\n                    title: \"Paste as Base64\",\n                    subtitle: \"Encode and paste · \" + preview(encodeText(text)),\n                    icon: \"textformat.abc\",\n                    argument: { operation: \"encodeText\" }\n                },\n                {\n                    id: \"decodeText\",\n                    title: \"Decode Base64\",\n                    subtitle: decodedData(text) ? \"Decode text or binary data\" : \"Input must be valid Base64\",\n                    icon: \"textformat\",\n                    argument: { operation: \"decodeText\" }\n                }\n            ];\n        writeJSON({ version: 1, actions: actions });\n        return;\n    }\n\n    if (payload.phase === \"execute\") {\n        var operation = (payload.argument && payload.argument.operation) || payload.actionID;\n        if (operation === \"encodeText\") return textResult(encodeText(text));\n        if (operation === \"decodeText\") return decodeText(payload, text);\n        if (!representation) throw new Error(\"A file input is required\");\n        if (operation === \"encodeFile\") return encodeFile(payload, representation);\n        if (operation === \"decodeFile\") return decodeFile(payload, representation);\n        throw new Error(\"Unknown Base64 operation\");\n    }\n    throw new Error(\"Unsupported workflow phase\");\n}\n\nmain();\n",
        "scriptPath": "",
        "interpreterPath": "/usr/bin/osascript",
        "arguments": [
          "-l",
          "JavaScript"
        ],
        "workingDirectory": "",
        "filterTimeout": 5,
        "executeTimeout": 15
      }
    }
  ]
}
