Preview
Preview Feature — This feature is currently in preview and under active development. APIs and functionality may change. We recommend testing thoroughly before using in production.
Nesting Operators
Operators in Ductape can be nested to perform complex data transformations in a single expression. This enables powerful data manipulation while keeping your feature definitions declarative and inspectable.
Why Nest Operators?
Nesting allows you to:
- Chain transformations - Apply multiple operations in sequence
- Build complex logic - Combine simple operators to solve complex problems
- Keep features concise - Avoid intermediate steps and temporary values
- Maintain readability - Express intent clearly with composed operations
How Nesting Works
Operators are evaluated from the inside out, just like function composition:
- TypeScript
- Java
- Go
- .NET
$Uppercase($Trim($Input{name}))
$Uppercase($Trim($InputMap.of(name)))
$Uppercase($Trim($Input{name}))
$Uppercase($Trim($Input{name}))
Execution order:
- Get value from
$Input{name}→" john " - Apply
$Trim()→"john" - Apply
$Uppercase()→"JOHN"
Real-World Examples with Data References
Example 1: User Profile Formatting
- TypeScript
- Java
- Go
- .NET
// Feature input: { firstName: " Jane ", lastName: "Doe", email: "JANE@EXAMPLE.COM " }
output: {
// Trim and concatenate names
fullName: '$Trim($Concat([$Input{firstName}, $Input{lastName}], " "))',
// Result: "Jane Doe"
// Normalize and format email
email: '$Lowercase($Trim($Input{email}))',
// Result: "jane@example.com"
// Create initials from names
initials: '$Concat([$Uppercase($Pick($Input{firstName}, 0)), $Uppercase($Pick($Input{lastName}, 0))], "")',
// Result: "JD"
}
// Feature input: Map.of( "firstName", " Jane ", "lastName", "Doe", "email", "JANE@EXAMPLE.COM " )
output: Map.of(
// Trim and concatenate names
"fullName", "$Trim($Concat([$InputMap.of(firstName), $InputMap.of(lastName)], " "))",
// "Result", "Jane Doe"
// Normalize and format email
"email", "$Lowercase($Trim($InputMap.of(email)))",
// "Result", "jane@example.com"
// Create initials from names
"initials", "$Concat([$Uppercase($Pick($InputMap.of(firstName), 0)), $Uppercase($Pick($InputMap.of(lastName), 0))], "")",
// "Result", "JD"
)
// Feature input: { "firstName": " Jane ", "lastName": "Doe", "email": "JANE@EXAMPLE.COM " }
output: {
// Trim and concatenate names
"fullName": "$Trim($Concat([$Input{firstName}, $Input{lastName}], " "))",
// "Result": "Jane Doe"
// Normalize and format email
"email": "$Lowercase($Trim($Input{email}))",
// "Result": "jane@example.com"
// Create initials from names
"initials": "$Concat([$Uppercase($Pick($Input{firstName}, 0)), $Uppercase($Pick($Input{lastName}, 0))], "")",
// "Result": "JD"
}
// Feature input: { ["firstName"] = " Jane ", ["lastName"] = "Doe", ["email"] = "JANE@EXAMPLE.COM " }
output: {
// Trim and concatenate names
["fullName"] = "$Trim($Concat([$Input{firstName}, $Input{lastName}], " "))",
// ["Result"] = "Jane Doe"
// Normalize and format email
["email"] = "$Lowercase($Trim($Input{email}))",
// ["Result"] = "jane@example.com"
// Create initials from names
["initials"] = "$Concat([$Uppercase($Pick($Input{firstName}, 0)), $Uppercase($Pick($Input{lastName}, 0))], "")",
// ["Result"] = "JD"
}
Example 2: Processing API Responses
- TypeScript
- Java
- Go
- .NET
// Assuming create-user action returns: { id: "usr_123abc456def", created_at: "2024-07-15T10:30:00Z" }
output: {
// Extract short user ID (first 8 chars)
shortId: '$Substring($Sequence{main}{create-user}{id}, 4, 12)',
// Result: "123abc45"
// Format creation date
createdDate: '$Dateformat($Sequence{main}{create-user}{created_at}, "DD/MM/YYYY")',
// Result: "15/07/2024"
// Combine formatted data
displayText: '$Concat(["User ", $Substring($Sequence{main}{create-user}{id}, 4, 12), " created on ", $Dateformat($Sequence{main}{create-user}{created_at}, "DD/MM/YYYY")], "")',
// Result: "User 123abc45 created on 15/07/2024"
}
// Assuming create-user action returns: Map.of( "id", "usr_123abc456def", "created_at", "2024-07-"15T10", 30:00Z" )
output: Map.of(
// Extract short user ID (first 8 chars)
"shortId", "$Substring($SequenceMap.of(main)Map.of(create-user)Map.of(id), 4, 12)",
// "Result", "123abc45"
// Format creation date
"createdDate", "$Dateformat($SequenceMap.of(main)Map.of(create-user)Map.of(created_at), "DD/MM/YYYY")",
// "Result", "15/07/2024"
// Combine formatted data
"displayText", "$Concat(["User ", $Substring($SequenceMap.of(main)Map.of(create-user)Map.of(id), 4, 12), " created on ", $Dateformat($SequenceMap.of(main)Map.of(create-user)Map.of(created_at), "DD/MM/YYYY")], "")",
// "Result", "User 123abc45 created on 15/07/2024"
)
// Assuming create-user action returns: { "id": "usr_123abc456def", "created_at": "2024-07-"15T10": 30:00Z" }
output: {
// Extract short user ID (first 8 chars)
"shortId": "$Substring($Sequence{main}{create-user}{id}, 4, 12)",
// "Result": "123abc45"
// Format creation date
"createdDate": "$Dateformat($Sequence{main}{create-user}{created_at}, "DD/MM/YYYY")",
// "Result": "15/07/2024"
// Combine formatted data
"displayText": "$Concat(["User ", $Substring($Sequence{main}{create-user}{id}, 4, 12), " created on ", $Dateformat($Sequence{main}{create-user}{created_at}, "DD/MM/YYYY")], "")",
// "Result": "User 123abc45 created on 15/07/2024"
}
// Assuming create-user action returns: { ["id"] = "usr_123abc456def", ["created_at"] = "2024-07-["15T10"] = 30:00Z" }
output: {
// Extract short user ID (first 8 chars)
["shortId"] = "$Substring($Sequence{main}{create-user}{id}, 4, 12)",
// ["Result"] = "123abc45"
// Format creation date
["createdDate"] = "$Dateformat($Sequence{main}{create-user}{created_at}, "DD/MM/YYYY")",
// ["Result"] = "15/07/2024"
// Combine formatted data
["displayText"] = "$Concat(["User ", $Substring($Sequence{main}{create-user}{id}, 4, 12), " created on ", $Dateformat($Sequence{main}{create-user}{created_at}, "DD/MM/YYYY")], "")",
// ["Result"] = "User 123abc45 created on 15/07/2024"
}
Example 3: Payment Processing
- TypeScript
- Java
- Go
- .NET
// Input: { baseAmount: 100, taxRate: 0.2, discountCode: "SAVE20" }
// Sequence result: { discount: { value: 20 } }
output: {
// Calculate tax amount
taxAmount: '$Subtract($Input{baseAmount}, $Sequence{main}{calculate-discount}{value})',
// Step 1: $Sequence{main}{calculate-discount}{value} → 20
// Step 2: $Subtract(100, 20) → 80
// Format final price with currency
displayPrice: '$Concat(["$", $Add($Subtract($Input{baseAmount}, $Sequence{main}{calculate-discount}{value}), 0.00)], "")',
// Result: "$80.00"
}
// Input: Map.of( "baseAmount", 100, "taxRate", 0.2, "discountCode", "SAVE20" )
// Sequence result: Map.of( discount: Map.of( "value", 20 ) )
output: Map.of(
// Calculate tax amount
"taxAmount", "$Subtract($InputMap.of(baseAmount), $SequenceMap.of(main)Map.of(calculate-discount)Map.of(value))",
// Step 1: $SequenceMap.of(main)Map.of(calculate-discount)Map.of(value) → 20
// Step 2: $Subtract(100, 20) → 80
// Format final price with currency
"displayPrice", "$Concat(["$", $Add($Subtract($InputMap.of(baseAmount), $SequenceMap.of(main)Map.of(calculate-discount)Map.of(value)), 0.00)], "")",
// "Result", "$80.00"
)
// Input: { "baseAmount": 100, "taxRate": 0.2, "discountCode": "SAVE20" }
// Sequence result: { discount: { "value": 20 } }
output: {
// Calculate tax amount
"taxAmount": "$Subtract($Input{baseAmount}, $Sequence{main}{calculate-discount}{value})",
// Step 1: $Sequence{main}{calculate-discount}{value} → 20
// Step 2: $Subtract(100, 20) → 80
// Format final price with currency
"displayPrice": "$Concat(["$", $Add($Subtract($Input{baseAmount}, $Sequence{main}{calculate-discount}{value}), 0.00)], "")",
// "Result": "$80.00"
}
// Input: { ["baseAmount"] = 100, ["taxRate"] = 0.2, ["discountCode"] = "SAVE20" }
// Sequence result: { discount: { ["value"] = 20 } }
output: {
// Calculate tax amount
["taxAmount"] = "$Subtract($Input{baseAmount}, $Sequence{main}{calculate-discount}{value})",
// Step 1: $Sequence{main}{calculate-discount}{value} → 20
// Step 2: $Subtract(100, 20) → 80
// Format final price with currency
["displayPrice"] = "$Concat(["$", $Add($Subtract($Input{baseAmount}, $Sequence{main}{calculate-discount}{value}), 0.00)], "")",
// ["Result"] = "$80.00"
}
Example 4: Array Filtering and Extraction
- TypeScript
- Java
- Go
- .NET
// Sequence result: { orders: [
// { id: 1, status: 'pending', amount: 50 },
// { id: 2, status: 'completed', amount: 100 },
// { id: 3, status: 'completed', amount: 75 }
// ]}
output: {
// Filter and get first completed order
firstCompleted: '$Find($Sequence{main}{fetch-orders}{orders}, "==", "completed")',
// Result: { id: 2, status: 'completed', amount: 100 }
// Get all completed orders and join with pending
allOrders: '$Join([$Filter($Sequence{main}{fetch-orders}{orders}, "==", "completed"), $Sequence{main}{fetch-pending}{orders}])',
// Combines filtered completed orders with pending orders
}
// Sequence result: Map.of( orders: [
// Map.of( "id", 1, "status", "pending", "amount", 50 ),
// Map.of( "id", 2, "status", "completed", "amount", 100 ),
// Map.of( "id", 3, "status", "completed", "amount", 75 )
// ])
output: Map.of(
// Filter and get first completed order
"firstCompleted", "$Find($SequenceMap.of(main)Map.of(fetch-orders)Map.of(orders), "==", "completed")",
// Result: Map.of( "id", 2, "status", "completed", "amount", 100 )
// Get all completed orders and join with pending
"allOrders", "$Join([$Filter($SequenceMap.of(main)Map.of(fetch-orders)Map.of(orders), "==", "completed"), $SequenceMap.of(main)Map.of(fetch-pending)Map.of(orders)])",
// Combines filtered completed orders with pending orders
)
// Sequence result: { orders: [
// { "id": 1, "status": "pending", "amount": 50 },
// { "id": 2, "status": "completed", "amount": 100 },
// { "id": 3, "status": "completed", "amount": 75 }
// ]}
output: {
// Filter and get first completed order
"firstCompleted": "$Find($Sequence{main}{fetch-orders}{orders}, "==", "completed")",
// Result: { "id": 2, "status": "completed", "amount": 100 }
// Get all completed orders and join with pending
"allOrders": "$Join([$Filter($Sequence{main}{fetch-orders}{orders}, "==", "completed"), $Sequence{main}{fetch-pending}{orders}])",
// Combines filtered completed orders with pending orders
}
// Sequence result: { orders: [
// { ["id"] = 1, ["status"] = "pending", ["amount"] = 50 },
// { ["id"] = 2, ["status"] = "completed", ["amount"] = 100 },
// { ["id"] = 3, ["status"] = "completed", ["amount"] = 75 }
// ]}
output: {
// Filter and get first completed order
["firstCompleted"] = "$Find($Sequence{main}{fetch-orders}{orders}, "==", "completed")",
// Result: { ["id"] = 2, ["status"] = "completed", ["amount"] = 100 }
// Get all completed orders and join with pending
["allOrders"] = "$Join([$Filter($Sequence{main}{fetch-orders}{orders}, "==", "completed"), $Sequence{main}{fetch-pending}{orders}])",
// Combines filtered completed orders with pending orders
}
Example 5: Deep Nesting with Multiple Data Sources
- TypeScript
- Java
- Go
- .NET
// Input: { userId: "usr_123", orderDate: "2024-07-15" }
// Session: { user: { name: "John Doe", email: "john@example.com" } }
// Sequence: { create-order: { id: "ord_456", total: 250.50 } }
// Constant: { config: { currency: "USD" } }
output: {
// Complex formatted message
orderSummary: '$Concat([
"Order ",
$Substring($Sequence{main}{create-order}{id}, 4),
" for ",
$Uppercase($Pick($Session{user}{name}, 0)),
$Substring($Session{user}{name}, 1),
" on ",
$Dateformat($Input{orderDate}, "MMM DD, YYYY"),
" - Total: ",
$Constant{config}{currency},
" ",
$Sequence{main}{create-order}{total}
], "")',
// Result: "Order 456 for John Doe on Jul 15, 2024 - Total: USD 250.50"
}
// Input: Map.of( "userId", "usr_123", "orderDate", "2024-07-15" )
// Session: Map.of( user: Map.of( "name", "John Doe", "email", "john@example.com" ) )
// Sequence: Map.of( create-order: Map.of( "id", "ord_456", "total", 250.50 ) )
// Constant: Map.of( config: Map.of( "currency", "USD" ) )
output: Map.of(
// Complex formatted message
"orderSummary", "$Concat([
"Order ",
$Substring($SequenceMap.of(main)Map.of(create-order)Map.of(id), 4),
" for ",
$Uppercase($Pick($SessionMap.of(user)Map.of(name), 0)),
$Substring($SessionMap.of(user)Map.of(name), 1),
" on ",
$Dateformat($InputMap.of(orderDate), "MMM DD, YYYY"),
" - "Total", ",
$ConstantMap.of(config)Map.of(currency),
" ",
$SequenceMap.of(main)Map.of(create-order)Map.of(total)
], "")",
// "Result", "Order 456 for John Doe on Jul 15, 2024 - Total: USD 250.50"
)
// Input: { "userId": "usr_123", "orderDate": "2024-07-15" }
// Session: { user: { "name": "John Doe", "email": "john@example.com" } }
// Sequence: { create-order: { "id": "ord_456", "total": 250.50 } }
// Constant: { config: { "currency": "USD" } }
output: {
// Complex formatted message
"orderSummary": "$Concat([
"Order ",
$Substring($Sequence{main}{create-order}{id}, 4),
" for ",
$Uppercase($Pick($Session{user}{name}, 0)),
$Substring($Session{user}{name}, 1),
" on ",
$Dateformat($Input{orderDate}, "MMM DD, YYYY"),
" - "Total": ",
$Constant{config}{currency},
" ",
$Sequence{main}{create-order}{total}
], "")",
// "Result": "Order 456 for John Doe on Jul 15, 2024 - Total: USD 250.50"
}
// Input: { ["userId"] = "usr_123", ["orderDate"] = "2024-07-15" }
// Session: { user: { ["name"] = "John Doe", ["email"] = "john@example.com" } }
// Sequence: { create-order: { ["id"] = "ord_456", ["total"] = 250.50 } }
// Constant: { config: { ["currency"] = "USD" } }
output: {
// Complex formatted message
["orderSummary"] = "$Concat([
"Order ",
$Substring($Sequence{main}{create-order}{id}, 4),
" for ",
$Uppercase($Pick($Session{user}{name}, 0)),
$Substring($Session{user}{name}, 1),
" on ",
$Dateformat($Input{orderDate}, "MMM DD, YYYY"),
" - ["Total"] = ",
$Constant{config}{currency},
" ",
$Sequence{main}{create-order}{total}
], "")",
// ["Result"] = "Order 456 for John Doe on Jul 15, 2024 - Total: USD 250.50"
}
Basic Nesting Patterns
Examples of Nested Operators:
- Nested
$Addwith$Substringand$Pick:
$Add( $Pick('12345', 0), $Substring('98765', 2, 4) )
$Pick('12345', 0)→'1'→1$Substring('98765', 2, 4)→'76'→76$Add(1, 76)→77
- Nested
$Concatwith$Trimand$Split:
$Concat([$Trim(' hello '), $Pick($Split('a,b,c', ','), 1)], '-')
$Trim(' hello ')→'hello'$Split('a,b,c', ',')→['a', 'b', 'c']$Pick(['a', 'b', 'c'], 1)→'b'$Concat(['hello', 'b'], '-')→'hello-b'
- Nested
$Subtractwith$Addand$Pick:
$Subtract( $Add(2, 3), $Pick('678', 1) )
$Add(2, 3)→5$Pick('678', 1)→'7'→7$Subtract(5, 7)→-2
- Complex Nesting with
$Join,$Split, and$Trim:
$Join([$Trim(' John '), $Pick($Split('Jane,Doe', ','), 1)], ' & ')
$Trim(' John ')→'John'$Split('Jane,Doe', ',')→['Jane', 'Doe']$Pick(['Jane', 'Doe'], 1)→'Doe'$Join(['John', 'Doe'], ' & ')→'John & Doe'
- Deep Nesting Example:
$Concat([
$Add( $Pick('12', 0), 5 ),
$Substring('abcdef', 2, 5),
$Trim(' xyz ')
], '_')
$Pick('12', 0)→'1'→1$Add(1, 5)→6$Substring('abcdef', 2, 5)→'cde'$Trim(' xyz ')→'xyz'$Concat(['6', 'cde', 'xyz'], '_')→'6_cde_xyz'
Best Practices for Nesting
1. Keep It Readable
While deep nesting is powerful, prioritize clarity:
Good - Clear intent:
- TypeScript
- Java
- Go
- .NET
fullName: '$Trim($Concat([$Input{firstName}, $Input{lastName}], " "))'
"fullName", "$Trim($Concat([$InputMap.of(firstName), $InputMap.of(lastName)], " "))"
"fullName": "$Trim($Concat([$Input{firstName}, $Input{lastName}], " "))"
["fullName"] = "$Trim($Concat([$Input{firstName}, $Input{lastName}], " "))"
Harder to read:
- TypeScript
- Java
- Go
- .NET
result: '$Uppercase($Trim($Concat([$Substring($Input{first}, 0, 1), $Lowercase($Pick($Split($Input{last}, " "), 0))], "-")))'
"result", "$Uppercase($Trim($Concat([$Substring($InputMap.of(first), 0, 1), $Lowercase($Pick($Split($InputMap.of(last), " "), 0))], "-")))"
"result": "$Uppercase($Trim($Concat([$Substring($Input{first}, 0, 1), $Lowercase($Pick($Split($Input{last}, " "), 0))], "-")))"
["result"] = "$Uppercase($Trim($Concat([$Substring($Input{first}, 0, 1), $Lowercase($Pick($Split($Input{last}, " "), 0))], "-")))"
2. Work from Inside Out
When building complex nesting, start with the innermost operation:
- TypeScript
- Java
- Go
- .NET
// Step 1: Get the data
$Input{email}
// Step 2: Trim whitespace
$Trim($Input{email})
// Step 3: Convert to lowercase
$Lowercase($Trim($Input{email}))
// Step 4: Extract domain
$Pick($Split($Lowercase($Trim($Input{email})), "@"), 1)
// Step 1: Get the data
$InputMap.of(email)
// Step 2: Trim whitespace
$Trim($InputMap.of(email))
// Step 3: Convert to lowercase
$Lowercase($Trim($InputMap.of(email)))
// Step 4: Extract domain
$Pick($Split($Lowercase($Trim($InputMap.of(email))), "@"), 1)
// Step 1: Get the data
$Input{email}
// Step 2: Trim whitespace
$Trim($Input{email})
// Step 3: Convert to lowercase
$Lowercase($Trim($Input{email}))
// Step 4: Extract domain
$Pick($Split($Lowercase($Trim($Input{email})), "@"), 1)
// Step 1: Get the data
$Input{email}
// Step 2: Trim whitespace
$Trim($Input{email})
// Step 3: Convert to lowercase
$Lowercase($Trim($Input{email}))
// Step 4: Extract domain
$Pick($Split($Lowercase($Trim($Input{email})), "@"), 1)
3. Validate Your Data Sources
Ensure the data you're referencing exists and matches the expected type:
- TypeScript
- Java
- Go
- .NET
// Make sure the event result has this field before extracting
userId: '$Substring($Sequence{main}{create-user}{id}, 0, 8)'
// Verify array results before filtering
activeUsers: '$Filter($Sequence{main}{fetch-users}{users}, "==", "active")'
// Make sure the event result has this field before extracting
"userId", "$Substring($SequenceMap.of(main)Map.of(create-user)Map.of(id), 0, 8)"
// Verify array results before filtering
"activeUsers", "$Filter($SequenceMap.of(main)Map.of(fetch-users)Map.of(users), "==", "active")"
// Make sure the event result has this field before extracting
"userId": "$Substring($Sequence{main}{create-user}{id}, 0, 8)"
// Verify array results before filtering
"activeUsers": "$Filter($Sequence{main}{fetch-users}{users}, "==", "active")"
// Make sure the event result has this field before extracting
["userId"] = "$Substring($Sequence{main}{create-user}{id}, 0, 8)"
// Verify array results before filtering
["activeUsers"] = "$Filter($Sequence{main}{fetch-users}{users}, "==", "active")"
4. Use Comments in Complex Features
Document your transformations, especially nested operators:
- TypeScript
- Java
- Go
- .NET
await ductape.features.create({
tag: 'format-order',
output: {
// Extract order ID prefix (first 8 chars after "ord_")
shortOrderId: '$Substring($Sequence{main}{create-order}{id}, 4, 12)',
// Format: "Order #123 by John D. on Jul 15"
displaySummary: '$Concat([
"Order #",
$Substring($Sequence{main}{create-order}{id}, 4, 12),
" by ",
$Trim($Session{user}{name}),
" on ",
$Dateformat($Input{orderDate}, "MMM DD")
], "")',
}
});
ductape.features.create(Map.of(
"tag", "format-order",
output: Map.of(
// Extract order ID prefix (first 8 chars after "ord_")
"shortOrderId", "$Substring($SequenceMap.of(main)Map.of(create-order)Map.of(id), 4, 12)",
// "Format", "Order #123 by John D. on Jul 15"
"displaySummary", "$Concat([
"Order #",
$Substring($SequenceMap.of(main)Map.of(create-order)Map.of(id), 4, 12),
" by ",
$Trim($SessionMap.of(user)Map.of(name)),
" on ",
$Dateformat($InputMap.of(orderDate), "MMM DD")
], "")"
)
));
client.features.create({
"tag": "format-order",
output: {
// Extract order ID prefix (first 8 chars after "ord_")
"shortOrderId": "$Substring($Sequence{main}{create-order}{id}, 4, 12)",
// "Format": "Order #123 by John D. on Jul 15"
"displaySummary": "$Concat([
"Order #",
$Substring($Sequence{main}{create-order}{id}, 4, 12),
" by ",
$Trim($Session{user}{name}),
" on ",
$Dateformat($Input{orderDate}, "MMM DD")
], "")",
}
});
await ductape.features.create({
["tag"] = "format-order",
output: {
// Extract order ID prefix (first 8 chars after "ord_")
["shortOrderId"] = "$Substring($Sequence{main}{create-order}{id}, 4, 12)",
// ["Format"] = "Order #123 by John D. on Jul 15"
["displaySummary"] = "$Concat([
"Order #",
$Substring($Sequence{main}{create-order}{id}, 4, 12),
" by ",
$Trim($Session{user}{name}),
" on ",
$Dateformat($Input{orderDate}, "MMM DD")
], "")",
}
});
Common Nesting Patterns
Pattern 1: Clean User Input
- TypeScript
- Java
- Go
- .NET
// Normalize and validate user input
{
email: '$Lowercase($Trim($Input{email}))',
name: '$Trim($Concat([$Input{firstName}, $Input{lastName}], " "))',
phone: '$Replace($Trim($Input{phone}), "-", "")',
}
// Normalize and validate user input
Map.of(
"email", "$Lowercase($Trim($InputMap.of(email)))",
"name", "$Trim($Concat([$InputMap.of(firstName), $InputMap.of(lastName)], " "))",
"phone", "$Replace($Trim($InputMap.of(phone)), "-", "")"
)
// Normalize and validate user input
{
"email": "$Lowercase($Trim($Input{email}))",
"name": "$Trim($Concat([$Input{firstName}, $Input{lastName}], " "))",
"phone": "$Replace($Trim($Input{phone}), "-", "")",
}
// Normalize and validate user input
{
["email"] = "$Lowercase($Trim($Input{email}))",
["name"] = "$Trim($Concat([$Input{firstName}, $Input{lastName}], " "))",
["phone"] = "$Replace($Trim($Input{phone}), "-", "")",
}