⚡ Transmute PLAYGROUND Developer Guide
v0.5.1 ▶ Open Playground

Transmute Developer Guide v0.5.1

Transmute is a high-performance, DataWeave-compatible JSON transformation engine with an interactive browser Playground. Write expressive data transformations using a clean DSL — filter, map, reduce, groupBy, and 80+ built-in functions.

DataWeave Compatible 1265 Tests Pass Enterprise Ready

Quickstart

Open the Playground, paste your JSON into the Payload panel, then write a DSL expression in the Script editor.

output application/json
---
payload.users filter ($.age > 25) map $.name
💡
Auto-execution The Script editor re-runs 350ms after every keystroke. Press Ctrl+Enter to run immediately.

▶ Try in Playground

Playground UI Tour

① Input Explorer

Located on the left. Contains the Payload panel (your input JSON) and optional named Variables.

ControlPurpose
Type dropdown (JSON ▾)Input format — JSON, TEXT, XML, CSV, YAML. Real-time validation per type.
✓ / ✗ badgeGreen = valid, red = invalid with inline error message
+ Add (Variables)Named variables (JSON or TEXT) — referenced by name in scripts
ℹ️
Smart validation Selecting CSV while the payload is JSON immediately shows an error — the validator detects JSON structure ({ or [) and rejects it for CSV/XML types.

② Script Editor Updated

A unified code editor in the center with live line numbers and syntax highlighting.

ElementDescription
Line numbersAuto-updated gutter, syncs scroll position
Syntax highlightingKeywords (purple), operators (blue), payload refs (orange), strings (green)
JSON badgeAuto-detected from output application/json in your script
Status dotGray=idle, amber=running, green=OK, red=error
💡
Full-script paste You can paste a complete script (including output header and ---) directly into the editor — it detects and uses it as-is.

③ Output Panel

ControlPurpose
Format dropdownChanging it re-runs the transformation with the new output type
CopyCopy output to clipboard
Pretty toggleIndented vs compact output
Metrics barExecution time, streaming mode, row count

④ Autocomplete New

As you type, a suggestion popup appears. The first item is always pre-selected — press Enter immediately to accept it.

BadgeTypeExamples
fnBuilt-in functionupper(, sizeOf(, flatten(
opInfix operatorfilter, map, groupBy
kwKeywordpayload, if, var, fun
fPayload fieldExtracted from current JSON (payload.users.name)
vScript variableFrom var x = ... in your script

Navigate with , accept with Enter or Tab, dismiss with Esc.

⑤ Log Viewer

Collapsible panel at the bottom — shows output from log() and trace() calls. Click ▶ Logs to expand.

Script Structure

// Header: output directive + variable/function definitions
output application/json
var active = payload.users filter $.age > 25
fun greet(name) = "Hello, " ++ name

---

// Body: the expression to evaluate
{count: sizeOf(active), msg: greet(active[0].name)}
DirectiveDescription
output application/jsonOutput format. Also: text/plain, application/csv, application/xml
import fn from moduleModule imports (all functions pre-registered; accepted and ignored)
var name = exprBind variable — available in body and in later var declarations
fun name(params) = bodyDefine reusable function — supports type annotations & default values
---Separator between header and body

Field Access

payload.name               // dot access
payload["first name"]      // bracket key
payload."first name"       // dot + quoted key
payload?.address?.city     // safe navigation (null if missing)
payload.users[0]           // 0-based index
payload.users[-1]          // last element
payload.users[0 to 2]      // range slice
payload.card[-4 to -1]     // last 4 chars of a string
payload.users[*].name      // wildcard: all names
payload..name              // descendants: all name values at any depth
xml.employee.@id           // XML attribute access (@ prefix)
xml.employee.@id           // Stored as "@id" key in parsed XML map
ℹ️
XML Attributes: When XML input is parsed, attributes become @attrName keys. Access them with .@attr syntax, e.g. payload.employee.@id. Produce XML output with output application/xml in the script header.

Operators

Arithmetic & Concatenation

OperatorDescriptionExample
+Add numbers; concat when non-numeric5 + 3
-Subtract; or remove field from objectpayload - "password"
* / %Multiply, divide, modulo$ mod 2 == 0
++Concat arrays, objects, or strings[1] ++ [2], "a" ++ "b"
--Remove from array or object[1,2,3] -- [2]

Comparison & Logical

OperatorExample
== != > < >= <=Numeric strings auto-coerced: "26" > 25
and or not !$.active and $.age > 18
containspayload contains 3
startsWith / endsWith"file.csv" endsWith ".csv"
~=Type-coercing equality — key ~= "1" matches key "1" or 1
matches$.code matches /^[A-Z]+/
is / as$ is String, "42" as Number
default$.name default "N/A"

Object shorthand New

// IDENTIFIER: expr  creates {IDENTIFIER: expr}
result: payload.users filter $.active
// same as: {result: payload.users filter $.active}

filter

payload filter $.age > 25
payload filter ((item, index) -> item.active and index > 0)
payload filter isEven($)

map

payload map $.name
payload map ($ * 2)
payload map (value, index) -> {id: index, v: value}
payload map (v,k) -> {num: v.num last 3}

reduce

payload reduce ($$ + $)
payload reduce ((item, acc = 0) -> acc + item)
payload reduce ((item, acc = {}) -> acc ++ {(item.name): item.value})

groupBy

payload groupBy $.dept
(payload groupBy $.dept) mapObject (members, dept) ->
  {(dept): members map $.name}

mapObject

$ = value, $$ = key, $$$ = index

payload mapObject (value, key) -> {(key): upper(value)}
payload mapObject {(upper($$)): $}
payload mapObject (v, k, i) -> {(i): {key: k, val: v}}  // $$$ = index

filterObject

payload filterObject ($ != null)
payload filterObject ((value, key) -> key != "password")
payload filterObject ((v, k, i) -> k ~= "id")      // ~= type-coercing equal

let / fun Enhanced

// var in header (preferred)
output application/json
var active = payload.users filter $.active
var total  = sizeOf(active)
---
{total: total, users: active}

// fun with typed params + default + --- separator
fun factorial(n:Number, acc:Number=1) =
  (if (n <= 1) acc else factorial(n-1, acc*n))
---
factorial(payload)

if / else / match

if (payload.status == "OK") 200 else 500
payload.score > 90 ? "A" : "B"

payload match {
  case "A"         -> 1
  case s is String -> upper(s)
  case n is Number -> n * 2
  else             -> null
}

Lambdas

$ -> $ * 2
(x) -> x * 2
(value, index) -> {id: index, v: value}
(item, acc = 0) -> acc + item
⚠️
Nested infix operators Wrap inner map/filter in parens: (cus) -> (cus.orders map {...})

String Functions

fnupper(str)
UPPERCASE
fnlower(str)
lowercase
fncapitalize(str)
Capitalize first
fntrim(str)
Strip whitespace
fnreplace(str, from, to)
Replace pattern
opstr splitBy delim
Split to array
oparr joinBy delim
Join to string
opstr startsWith s
Prefix check
opstr endsWith s
Suffix check
opstr last N
Last N chars
fnsubstringAfter(str, d)
After delimiter
fnsubstringBefore(str, d)
Before delimiter
fnsubstringBeforeLast(s, d)
Before last delimiter
fnunderscore(str)
camelCase to snake_case
fnpadLeft(str, n, c)
Left-pad to length
fnpadRight(str, n, c)
Right-pad to length
fnrepeat(str, n)
Repeat n times
fncharCode(char)
ASCII code
fnfromCharCode(n)
Char from ASCII
fnsizeOf(str)
String length
fnlines(str)
Split into lines
fnwords(str)
Split into words

String interpolation

"Hello, $(payload.name)! Age: $(payload.age)"

Array Functions

fnflatten(arr)
Flatten nested arrays
oparr flatMap fn
Map then flatten
oparr distinctBy fn?
Remove duplicates
oparr orderBy fn?
Sort ascending
fnsum(arr)
Sum of array
fnaverage(arr)
Average of array
fnmax(arr)
Maximum value
fnmin(arr)
Minimum value
fnsizeOf(arr)
Array length
fnfirst(arr)
First element
fnlast(arr)
Last element
oparr maxBy fn
Element with max key
oparr minBy fn
Element with min key
oparr countBy fn
Count matching
fntake(arr, n)
First n elements
fndrop(arr, n)
Skip first n
fnreverse(arr)
Reverse array
fnisEmpty(arr)
True if empty
fnrandomInt(n)
Random 0..n-1

Object Functions

fnkeysOf(obj)
Array of keys
fnvaluesOf(obj)
Array of values
opobj pluck fn
Object to array
fnhasKey(obj, key)
Check key exists
fnmerge(obj1, obj2)
Deep merge
fnpick(obj, keys)
Pick specific keys

Math Functions

FunctionExample
abs(n)abs(-5) → 5
ceil(n)ceil(1.2) → 2
floor(n)floor(1.9) → 1
round(n, scale?)round(3.567, 2) → 3.57
sqrt(n)sqrt(9) → 3
pow(base, exp)pow(2,10) → 1024
sum(arr)sum([1,2,3]) → 6
max(arr)max([5,1,9]) → 9
min(arr)min([5,1,9]) → 1
average(arr)average([2,4]) → 3
💡
Integer output Whole-number results print without .0sum([10,20,30]) outputs 60, not 60.0.

Type & Logic Functions

FunctionDescription
isEven(n) / isOdd(n)Numeric parity check
isAlpha(s) / isNumeric(s)String character type
isNull(v) / isEmpty(v)Null / empty check
isString(v) / isNumber(v) / isArray(v)Runtime type checks
typeof(v)Returns type as string
v as Number / String / BooleanType coercion
v is String / Number / ArrayType check operator
v default fallbackReturn fallback if v is null

Crypto Functions New

Cryptographic hashing, HMAC, and symmetric encryption via dw::Crypto-compatible built-ins.

FunctionDescription
MD5(str)Hex-encoded MD5 hash
SHA1(str)Hex-encoded SHA-1 hash
SHA256(str)Hex-encoded SHA-256 hash
SHA512(str)Hex-encoded SHA-512 hash
HMACWith(content, secret, alg)Hex HMAC — alg: "HmacSHA256", "HmacSHA512", "HmacMD5"
HMACBinary(content, secret, alg)Base64 HMAC (binary output)
aesEncrypt(content, key)AES-GCM encrypt → Base64 (IV prepended, authenticated)
aesDecrypt(ciphertext, key)AES-GCM decrypt → plaintext string
// Hashing
MD5("hello")                        // "5d41402abc4b2a76b9719d911017c592"
SHA256("hello")                     // "2cf24dba5fb0a30e..."

// HMAC
HMACWith("payload", "secret", "HmacSHA256")

// AES-256-GCM round-trip
let cipher = aesEncrypt("hello world", "mySecretKey")
aesDecrypt(cipher, "mySecretKey")   // "hello world"
⚠️
Security: aesEncrypt uses AES-256-GCM with a random IV per call (NIST SP 800-38D). The IV is prepended to the ciphertext — only aesDecrypt with the same key can recover the plaintext.

Tree Functions New

Recursive tree traversal helpers from dw::util::Tree.

FunctionDescription
mapLeafValues(value, mapper)Apply mapper(v) to every scalar leaf in the tree; preserves object/array structure
filterTree(value, criteria)Remove entries/elements where criteria(v, path) returns false; recurses into all nodes
// Uppercase every string leaf
mapLeafValues({a: "hello", b: {c: "world"}}, (v) -> upper(v))
// {a: "HELLO", b: {c: "WORLD"}}

// Remove empty fields at any depth
filterTree(payload, (v, path) -> !isEmpty(v as String))

Date & Time Functions

FunctionDescription
now()Current datetime (ISO 8601)
formatDate(dt, fmt)Format: "yyyy-MM-dd"
dateParse(str, fmt)Parse string to datetime
year(dt) month(dt) day(dt)Extract components
daysBetween(d1, d2)Days between two dates
plusPeriod(dt, n, unit)Add: "days", "months", "years"

Pattern: Data Transformation

{fullName: payload.name, userAge: payload.age}
payload ++ {tax: payload.price * 0.1}
payload - "password" - "token"
(payload filter $.active) map $.email

Pattern: Dynamic Keys

{(payload.key): payload.value}
payload map (value, index) -> {("item" ++ index): value}
(payload groupBy $.customer)
  mapObject (orders, cust) -> {(cust): sizeOf(orders)}

Pattern: Nested Maps

flatten(payload.customers map (cus) ->
  (cus.orders map {customer: cus.name, orderId: $.id}))
ℹ️
Wrap the inner map expression in parentheses when it appears inside a lambda body.

Pattern: Merge & Join

(payload.arr1 ++ payload.arr2) distinctBy $
payload reduce ($$ ++ $)
payload.metrics reduce ((item, acc={}) ->
  acc ++ {(item.name): item.value})

Pattern: Recursion

fun factorial(n:Number, acc:Number=1) =
  (if (n <= 1) acc else factorial(n-1, acc*n))
---
factorial(payload)     // factorial(6) -> 720

fun recsum(arr, total=0) =
  if (isEmpty(arr)) total
  else recsum(arr[1 to -1], total + arr[0])
---
recsum(1 to 10)        // -> 55

Keyboard Shortcuts

Run transformationCtrl+Enter
Accept autocompleteEnter or Tab
Navigate suggestions /
Dismiss autocompleteEsc
Select all in editorCtrl+A
Copy outputCopy button

REST API Reference

POST /api/v1/transform
{
  "dsl":       "output application/json\n---\npayload filter $.age > 25",
  "json":      "<employees><employee id=\"1\"><name>Alice</name></employee></employees>",
  "inputType": "XML",        // JSON (default) | XML | CSV | YAML | TEXT
  "pretty":    true,
  "variables": {"cfg": {"content":"{\"env\":\"prod\"}","type":"JSON"}}
}
EndpointMethodDescription
/api/v1/transformPOSTExecute DSL transformation
/api/v1/validatePOSTValidate DSL syntax only
/api/v1/healthGETFull health check (uptime, memory, JVM)
/api/v1/metricsGETRequest counts, memory, threads
/api/v1/infoGETService info, version, limits

Enterprise Features

FeatureDetail
API versioning/api/v1/ prefix; legacy /api/ still works
Rate limiting120 requests/minute per IP (sliding window)
Request tracingX-Request-ID header — pass your own UUID or one is generated
Security headersX-Frame-Options, X-Content-Type-Options, X-XSS-Protection, CSP, Referrer-Policy
Execution timeout30 000 ms per transformation (configurable)
Payload size limit10 MB maximum JSON
Response compressionGzip for responses ≥ 1 KB
CORSAll origins; Vary: Origin prevents CDN cache poisoning
Error safetyStack traces never exposed; structured error JSON
Cache-Controlno-store, no-cache on all /api/* responses
HSTSStrict-Transport-Security: max-age=31536000 on HTTPS connections
AES encryptionAES-256-GCM with random IV (NIST SP 800-38D) — not ECB
XFF rate-limit bypassX-Forwarded-For ignored by default; opt-in via app.trust-proxy=true
IP counter cleanupRate-limit map purged every 5 min to prevent unbounded memory growth

Transmute Playground — DataWeave-compatible DSL engine — Open Playground ▶