{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://javascriptobfuscator.com/Docs/wire-format.schema.json",
  "title": "JSO HTTP API wire format",
  "description": "Request and response shape for the JavaScript Obfuscator HTTP API (HttpApi.ashx). Use this schema with openapi-generator, quicktype, datamodel-code-generator, or any other JSON-Schema-to-types tool to generate a typed client in a language we don't already ship.",
  "$defs": {
    "ProtectRequest": {
      "type": "object",
      "description": "POST body for HttpApi.ashx. Combines credentials, the file list, and any number of protection options.",
      "required": ["APIKey", "APIPwd", "Items"],
      "additionalProperties": true,
      "properties": {
        "APIKey": {
          "type": "string",
          "minLength": 1,
          "description": "Base64-encoded API key from the JSO dashboard. NOT a separate plaintext key — pass the opaque base-64 string through verbatim."
        },
        "APIPwd": {
          "type": "string",
          "minLength": 1,
          "description": "Base64-encoded API password from the JSO dashboard."
        },
        "Name": {
          "type": "string",
          "description": "Project name surfaced in the dashboard audit log. Default per language client: 'python-session', 'dotnet-session', etc.",
          "default": "default"
        },
        "ReleaseLabel": {
          "type": "string",
          "description": "Optional release tag forwarded with the request. Use the commit SHA for CI runs to group audit entries by commit."
        },
        "Items": {
          "type": "array",
          "minItems": 1,
          "description": "Files to protect. The response Items array is keyed by FileName so order does not matter; filenames are normalized to forward-slashes regardless of how they are sent.",
          "items": { "$ref": "#/$defs/ProtectItem" }
        },
        "Compress": { "type": "boolean", "description": "Standard/balanced/maximum: compact whitespace and structural redundancy in the output." },
        "EncodeStrings": { "type": "boolean", "description": "Standard/balanced/maximum: encode string literals using a per-build encoding." },
        "EncryptStrings": { "type": "boolean", "description": "Balanced/maximum: cryptographically encrypt the hoisted string pool." },
        "MoveStringsIntoArray": { "type": "boolean", "description": "Standard/balanced/maximum: hoist all string literals into a single indexed array." },
        "NameMangling": { "type": "boolean", "description": "Standard/balanced/maximum: rename local identifiers to short opaque tokens." },
        "DeepObfuscate": { "type": "boolean", "description": "Balanced/maximum: deeper transforms on nested scope and identifier shadowing." },
        "FlatTransform": { "type": "boolean", "description": "Balanced/maximum: control-flow flattening — linear code becomes a state-machine loop." },
        "CodeTransposition": { "type": "boolean", "description": "Balanced/maximum: reorder statements while preserving semantics." },
        "ProtectMembers": { "type": "boolean", "description": "Maximum: rename non-public object property names." },
        "RenameGlobals": { "type": "boolean", "description": "Maximum: rename globals (be careful — escapes the bundle by definition)." },
        "MoveMembers": { "type": "boolean", "description": "Maximum: relocate member declarations across scope boundaries." },
        "DeadCodeInsertion": { "type": "boolean", "description": "Maximum: splice in provably-unreachable branches to grow the decompiler's search space." },
        "LockDate": { "type": "boolean", "description": "Lock the protected build to expire after LockDateValue." },
        "LockDateValue": { "type": "string", "pattern": "^[0-9]{8}$", "description": "Expiration date in YYYYMMDD format. Use with LockDate=true." },
        "LockDateMsg": { "type": "string", "description": "Optional message displayed when the runtime detects expiry." },
        "LockDomain": { "type": "boolean", "description": "Lock the protected build to one of LockDomainList." },
        "LockDomainList": { "type": "string", "description": "Newline-separated list of domains the protected output is allowed to run on." },
        "DebugProtection": { "type": "boolean", "description": "Runtime Defense: throw on detectable debugger attach." },
        "DisableConsoleOutput": { "type": "boolean", "description": "Runtime Defense: blackhole console.log / warn / error in the protected scope." },
        "SelfDefending": { "type": "boolean", "description": "Runtime Defense: integrity-heartbeat guard. Trips when the protected source is edited." },
        "SelfDefendingIntervalSeconds": { "type": "integer", "minimum": 1, "description": "Heartbeat interval. Lower = more frequent checks = higher detection rate, slightly higher overhead." },
        "BlockDevToolsKeys": { "type": "boolean", "description": "Block F12, Ctrl+Shift+I, and similar devtools-trigger keystrokes in the protected scope." },
        "DetectHeadlessBrowser": { "type": "boolean", "description": "Trip the Runtime Defense beacon when running under Puppeteer/Playwright/Selenium." }
      }
    },

    "ProtectItem": {
      "type": "object",
      "required": ["FileName", "FileCode"],
      "additionalProperties": false,
      "properties": {
        "FileName": { "type": "string", "minLength": 1, "description": "Logical filename. Forward-slash separators; the server normalizes backslashes." },
        "FileCode": { "type": "string", "description": "Source code as a UTF-8 string. Pass the file contents verbatim — no encoding, no escaping." }
      }
    },

    "ProtectResponse": {
      "type": "object",
      "description": "Body shape returned by HttpApi.ashx. HTTP 2xx; failure modes are signalled in the body via a non-Succeed Type.",
      "required": ["Type"],
      "additionalProperties": true,
      "properties": {
        "Type": {
          "type": "string",
          "enum": ["Succeed", "Error", "SourceError", "Exception", "ServerException", "LoginFailed"],
          "description": "Outcome tag. Only 'Succeed' is success; everything else is a hard error."
        },
        "Items": {
          "type": "array",
          "description": "Protected source per input file. Present iff Type=Succeed.",
          "items": { "$ref": "#/$defs/ProtectItem" }
        },
        "Report": {
          "$ref": "#/$defs/ProtectReport",
          "description": "Audit metadata. Present iff Type=Succeed."
        },
        "Message": { "type": "string", "description": "Human-readable failure message. Present when Type != Succeed." },
        "ErrorCode": { "type": "string", "description": "Stable failure code for programmatic routing. Present when Type != Succeed." },
        "FileName": { "type": "string", "description": "Offending filename when Type=SourceError." },
        "LineNumber": { "type": ["string", "integer"], "description": "Offending line when Type=SourceError." },
        "ExceptionToString": { "type": "string", "description": "Server-side exception trace when Type=Exception/ServerException. Safe to share with JSO support." }
      }
    },

    "ProtectReport": {
      "type": "object",
      "description": "Audit-friendly metadata for a successful protection run.",
      "additionalProperties": true,
      "properties": {
        "BuildId": {
          "type": "string",
          "description": "Stable identifier for this protection run. Inject as a global into your runtime so production crash reports carry the matching BuildId."
        },
        "PolymorphismFingerprint": {
          "type": "string",
          "description": "Short SHA-256-derived fingerprint over the protected output. Two consecutive obfuscations of identical input MUST produce different fingerprints when polymorphism is engaged."
        },
        "EnabledOptions": {
          "type": "array",
          "items": { "type": "string" },
          "description": "List of API option names that the server actually applied."
        },
        "GlobalIdentifierMap": {
          "type": "array",
          "description": "Mangled-to-original mapping for renamed global identifiers. Persist alongside the protected dist; feed to jso-symbolicate when a production crash arrives.",
          "items": { "$ref": "#/$defs/IdentifierMapEntry" }
        },
        "MemberIdentifierMap": {
          "type": "array",
          "description": "Mangled-to-original mapping for renamed object members.",
          "items": { "$ref": "#/$defs/IdentifierMapEntry" }
        },
        "InputBytes": { "type": "integer", "minimum": 0 },
        "OutputBytes": { "type": "integer", "minimum": 0 },
        "InputFiles":  { "type": "integer", "minimum": 0 },
        "OutputFiles": { "type": "integer", "minimum": 0 },
        "InputLines":  { "type": "integer", "minimum": 0 },
        "OutputLines": { "type": "integer", "minimum": 0 },
        "GeneratedUtc": { "type": "string", "format": "date-time" }
      }
    },

    "IdentifierMapEntry": {
      "type": "object",
      "required": ["Mangled", "Original"],
      "additionalProperties": false,
      "properties": {
        "Mangled":  { "type": "string", "description": "Mangled identifier as it appears in the protected source." },
        "Original": { "type": "string", "description": "Original identifier name from the input source." }
      }
    }
  },

  "oneOf": [
    { "$ref": "#/$defs/ProtectRequest" },
    { "$ref": "#/$defs/ProtectResponse" }
  ]
}
