{
  "version": 1,
  "workflows": [
    {
      "id": "F2A7DA67-8A92-46E8-A7F5-7C89101D1C62",
      "defaultIntroducedVersion": 11,
      "name": "Generate Data Model",
      "nameLocalizationKey": "generate_data_model",
      "summary": "Swift · TypeScript · Kotlin · Go · Python",
      "symbolName": "curlybraces.square",
      "isEnabled": true,
      "requiresTrustConfirmation": false,
      "sortOrder": 12,
      "mode": "scriptFilter",
      "supportedTypes": [
        "json"
      ],
      "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 words(value) {\n    var normalized = String(value)\n        .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n        .replace(/[^A-Za-z0-9]+/g, \" \")\n        .trim();\n    return normalized ? normalized.split(/\\s+/) : [];\n}\n\nfunction pascalCase(value) {\n    var parts = words(value);\n    var result = parts.map(function (part) {\n        return part.charAt(0).toUpperCase() + part.slice(1);\n    }).join(\"\");\n    if (!result) {\n        result = \"Value\";\n    }\n    return /^[0-9]/.test(result) ? \"Value\" + result : result;\n}\n\nfunction camelCase(value) {\n    var result = pascalCase(value);\n    return result.charAt(0).toLowerCase() + result.slice(1);\n}\n\nfunction snakeCase(value) {\n    var result = words(value).map(function (part) {\n        return part.toLowerCase();\n    }).join(\"_\");\n    if (!result) {\n        result = \"value\";\n    }\n    return /^[0-9]/.test(result) ? \"value_\" + result : result;\n}\n\nfunction quote(value) {\n    return JSON.stringify(String(value));\n}\n\nfunction infer(value) {\n    if (value === null) return { kind: \"null\" };\n    if (Array.isArray(value)) {\n        if (value.length === 0) return { kind: \"array\", item: { kind: \"any\" } };\n        var item = infer(value[0]);\n        for (var index = 1; index < value.length; index++) {\n            item = merge(item, infer(value[index]));\n        }\n        return { kind: \"array\", item: item };\n    }\n    if (typeof value === \"boolean\") return { kind: \"bool\" };\n    if (typeof value === \"number\") {\n        return { kind: Number.isInteger(value) ? \"integer\" : \"number\" };\n    }\n    if (typeof value === \"string\") return { kind: \"string\" };\n    if (typeof value === \"object\") {\n        var properties = {};\n        Object.keys(value).forEach(function (key) {\n            properties[key] = { schema: infer(value[key]), optional: false };\n        });\n        return { kind: \"object\", properties: properties };\n    }\n    return { kind: \"any\" };\n}\n\nfunction merge(left, right) {\n    if (left.kind === \"null\") return right;\n    if (right.kind === \"null\") return left;\n    if (left.kind === right.kind) {\n        if (left.kind === \"array\") {\n            return { kind: \"array\", item: merge(left.item, right.item) };\n        }\n        if (left.kind === \"object\") {\n            var properties = {};\n            var keys = {};\n            Object.keys(left.properties).forEach(function (key) { keys[key] = true; });\n            Object.keys(right.properties).forEach(function (key) { keys[key] = true; });\n            Object.keys(keys).forEach(function (key) {\n                var lhs = left.properties[key];\n                var rhs = right.properties[key];\n                if (lhs && rhs) {\n                    properties[key] = {\n                        schema: merge(lhs.schema, rhs.schema),\n                        optional: lhs.optional || rhs.optional\n                    };\n                } else {\n                    var property = lhs || rhs;\n                    properties[key] = { schema: property.schema, optional: true };\n                }\n            });\n            return { kind: \"object\", properties: properties };\n        }\n        return left;\n    }\n    if ((left.kind === \"integer\" && right.kind === \"number\") ||\n        (left.kind === \"number\" && right.kind === \"integer\")) {\n        return { kind: \"number\" };\n    }\n    return { kind: \"any\" };\n}\n\nfunction buildDefinitions(schema) {\n    var definitions = [];\n    var usedNames = {};\n\n    function uniqueName(requested) {\n        var base = pascalCase(requested);\n        var name = base;\n        var suffix = 2;\n        while (usedNames[name]) {\n            name = base + suffix;\n            suffix += 1;\n        }\n        usedNames[name] = true;\n        return name;\n    }\n\n    function visit(current, requestedName) {\n        if (current.kind === \"array\") {\n            return visit(current.item, requestedName);\n        }\n        if (current.kind !== \"object\") {\n            return null;\n        }\n\n        var name = uniqueName(requestedName);\n        var properties = Object.keys(current.properties).sort().map(function (key) {\n            var property = current.properties[key];\n            var nestedName = null;\n            var nestedSchema = property.schema.kind === \"array\"\n                ? property.schema.item\n                : property.schema;\n            if (nestedSchema.kind === \"object\") {\n                nestedName = visit(nestedSchema, pascalCase(key));\n            }\n            return {\n                key: key,\n                schema: property.schema,\n                optional: property.optional,\n                nestedName: nestedName\n            };\n        });\n        definitions.push({ name: name, properties: properties });\n        return name;\n    }\n\n    var rootName = visit(schema, \"Root\");\n    if (!rootName) {\n        definitions.push({\n            name: \"Root\",\n            properties: [{\n                key: \"value\",\n                schema: schema,\n                optional: false,\n                nestedName: null\n            }]\n        });\n    }\n    return definitions;\n}\n\nfunction typeName(schema, nestedName, language, optional) {\n    var base;\n    if (schema.kind === \"array\") {\n        base = typeName(schema.item, nestedName, language, false);\n        if (language === \"swift\") base = \"[\" + base + \"]\";\n        else if (language === \"typescript\") base += \"[]\";\n        else if (language === \"kotlin\") base = \"List<\" + base + \">\";\n        else if (language === \"go\") base = \"[]\" + base;\n        else base = \"List[\" + base + \"]\";\n    } else if (schema.kind === \"object\") {\n        base = nestedName || \"JSONValue\";\n    } else {\n        var maps = {\n            swift: {\n                null: \"JSONValue\", bool: \"Bool\", integer: \"Int\",\n                number: \"Double\", string: \"String\", any: \"JSONValue\"\n            },\n            typescript: {\n                null: \"null\", bool: \"boolean\", integer: \"number\",\n                number: \"number\", string: \"string\", any: \"unknown\"\n            },\n            kotlin: {\n                null: \"Any\", bool: \"Boolean\", integer: \"Long\",\n                number: \"Double\", string: \"String\", any: \"Any\"\n            },\n            go: {\n                null: \"any\", bool: \"bool\", integer: \"int\",\n                number: \"float64\", string: \"string\", any: \"any\"\n            },\n            python: {\n                null: \"Any\", bool: \"bool\", integer: \"int\",\n                number: \"float\", string: \"str\", any: \"Any\"\n            }\n        };\n        base = maps[language][schema.kind] || maps[language].any;\n    }\n\n    if (!optional) return base;\n    if (language === \"swift\") return base + \"?\";\n    if (language === \"kotlin\") return base + \"?\";\n    if (language === \"go\") return \"*\" + base;\n    if (language === \"python\") return \"Optional[\" + base + \"]\";\n    return base;\n}\n\nfunction generateSwift(definitions) {\n    var needsJSONValue = false;\n    var blocks = definitions.map(function (definition) {\n        var lines = definition.properties.map(function (property) {\n            var type = typeName(property.schema, property.nestedName, \"swift\", property.optional);\n            if (type.indexOf(\"JSONValue\") >= 0) needsJSONValue = true;\n            return \"    let \" + camelCase(property.key) + \": \" + type;\n        });\n        var coding = definition.properties.filter(function (property) {\n            return camelCase(property.key) !== property.key;\n        });\n        if (coding.length) {\n            lines.push(\"\", \"    enum CodingKeys: String, CodingKey {\");\n            definition.properties.forEach(function (property) {\n                var name = camelCase(property.key);\n                var alias = name === property.key ? \"\" : \" = \" + quote(property.key);\n                lines.push(\"        case \" + name + alias);\n            });\n            lines.push(\"    }\");\n        }\n        return \"struct \" + definition.name + \": Codable {\\n\" + lines.join(\"\\n\") + \"\\n}\";\n    });\n    if (needsJSONValue) {\n        blocks.push([\n            \"enum JSONValue: Codable {\",\n            \"    case string(String)\",\n            \"    case number(Double)\",\n            \"    case bool(Bool)\",\n            \"    case object([String: JSONValue])\",\n            \"    case array([JSONValue])\",\n            \"    case null\",\n            \"\",\n            \"    init(from decoder: Decoder) throws {\",\n            \"        let container = try decoder.singleValueContainer()\",\n            \"        if container.decodeNil() { self = .null }\",\n            \"        else if let value = try? container.decode(Bool.self) { self = .bool(value) }\",\n            \"        else if let value = try? container.decode(Double.self) { self = .number(value) }\",\n            \"        else if let value = try? container.decode(String.self) { self = .string(value) }\",\n            \"        else if let value = try? container.decode([JSONValue].self) { self = .array(value) }\",\n            \"        else { self = .object(try container.decode([String: JSONValue].self)) }\",\n            \"    }\",\n            \"\",\n            \"    func encode(to encoder: Encoder) throws {\",\n            \"        var container = encoder.singleValueContainer()\",\n            \"        switch self {\",\n            \"        case .string(let value): try container.encode(value)\",\n            \"        case .number(let value): try container.encode(value)\",\n            \"        case .bool(let value): try container.encode(value)\",\n            \"        case .object(let value): try container.encode(value)\",\n            \"        case .array(let value): try container.encode(value)\",\n            \"        case .null: try container.encodeNil()\",\n            \"        }\",\n            \"    }\",\n            \"}\"\n        ].join(\"\\n\"));\n    }\n    return blocks.join(\"\\n\\n\");\n}\n\nfunction generateTypeScript(definitions) {\n    return definitions.map(function (definition) {\n        var lines = definition.properties.map(function (property) {\n            return \"  \" + quote(property.key) + (property.optional ? \"?\" : \"\") + \": \" +\n                typeName(property.schema, property.nestedName, \"typescript\", false) + \";\";\n        });\n        return \"export interface \" + definition.name + \" {\\n\" + lines.join(\"\\n\") + \"\\n}\";\n    }).join(\"\\n\\n\");\n}\n\nfunction generateKotlin(definitions) {\n    var body = definitions.map(function (definition) {\n        var lines = definition.properties.map(function (property) {\n            var name = camelCase(property.key);\n            var annotation = name === property.key ? \"\" : \"    @SerialName(\" + quote(property.key) + \")\\n\";\n            return annotation + \"    val \" + name + \": \" +\n                typeName(property.schema, property.nestedName, \"kotlin\", property.optional);\n        });\n        return \"@Serializable\\ndata class \" + definition.name + \"(\\n\" +\n            lines.join(\",\\n\") + \"\\n)\";\n    }).join(\"\\n\\n\");\n    return \"import kotlinx.serialization.SerialName\\n\" +\n        \"import kotlinx.serialization.Serializable\\n\\n\" + body;\n}\n\nfunction generateGo(definitions) {\n    return definitions.map(function (definition) {\n        var lines = definition.properties.map(function (property) {\n            return \"\\t\" + pascalCase(property.key) + \" \" +\n                typeName(property.schema, property.nestedName, \"go\", property.optional) +\n                \" `json:\" + quote(property.key) + \"`\";\n        });\n        return \"type \" + definition.name + \" struct {\\n\" + lines.join(\"\\n\") + \"\\n}\";\n    }).join(\"\\n\\n\");\n}\n\nfunction generatePython(definitions) {\n    var blocks = definitions.map(function (definition) {\n        var lines = definition.properties.map(function (property) {\n            var type = typeName(property.schema, property.nestedName, \"python\", property.optional);\n            return \"    \" + snakeCase(property.key) + \": \" + type +\n                (property.optional ? \" = None\" : \"\");\n        });\n        return \"class \" + definition.name + \"(BaseModel):\\n\" +\n            (lines.length ? lines.join(\"\\n\") : \"    pass\");\n    });\n    return \"from typing import Any, List, Optional\\nfrom pydantic import BaseModel\\n\\n\\n\" +\n        blocks.join(\"\\n\\n\\n\");\n}\n\nfunction orderedActions(payload) {\n    var actions = [\n        { id: \"swift\", title: \"Generate Swift Codable Model\", subtitle: \"Swift\", icon: \"swift\" },\n        { id: \"typescript\", title: \"Generate TypeScript Interface\", subtitle: \"TypeScript\", icon: \"curlybraces\" },\n        { id: \"kotlin\", title: \"Generate Kotlin Data Class\", subtitle: \"Kotlin\", icon: \"k.square.fill\" },\n        { id: \"go\", title: \"Generate Go Struct\", subtitle: \"Go\", icon: \"g.square.fill\" },\n        { id: \"python\", title: \"Generate Python Pydantic Model\", subtitle: \"Python\", icon: \"p.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 = \"swift\";\n    else if (/visual studio code|vscode|cursor|windsurf|webstorm/.test(description)) preferred = \"typescript\";\n    else if (/android studio|intellij|com\\.jetbrains\\.intellij/.test(description)) preferred = \"kotlin\";\n    else if (/goland|go land|com\\.jetbrains\\.goland/.test(description)) preferred = \"go\";\n    else if (/pycharm|com\\.jetbrains\\.pycharm/.test(description)) preferred = \"python\";\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 text = payload.paste && payload.paste.text ? payload.paste.text : \"\";\n    var value;\n    try {\n        value = JSON.parse(text);\n    } catch (error) {\n        throw new Error(\"Invalid JSON input\");\n    }\n\n    var actions = orderedActions(payload);\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 definitions = buildDefinitions(infer(value));\n    var result;\n    switch (payload.actionID) {\n    case \"swift\":\n        result = generateSwift(definitions);\n        break;\n    case \"typescript\":\n        result = generateTypeScript(definitions);\n        break;\n    case \"kotlin\":\n        result = generateKotlin(definitions);\n        break;\n    case \"go\":\n        result = generateGo(definitions);\n        break;\n    case \"python\":\n        result = generatePython(definitions);\n        break;\n    default:\n        throw new Error(\"Unknown data model action\");\n    }\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
    }
  ]
}
