Custom Code in a Workflow
Set a Function node's Operation Type to Run Custom JS (ES5) and it runs a block of JavaScript in a sandbox. The sandbox is deliberately small, and the constraints below are enforced rather than advised.
Reach for it when the value cannot be assembled by interpolating variables into a string: a date converted to another format, a total added up, a list filtered down. Everything else is one field in Update / Override Data mode, which needs no code and cannot hit an execution limit.
ES5 Only#
The code is parsed as ECMAScript 5. const, let, arrow functions, template literals, destructuring, async/await and classes are all syntax errors. Use var and function () {}.
The editor parses as you type and reports the failure, naming the ES6 feature when it can recognise one.
The 50,000-Step Cap#
The sandbox executes one interpreter step at a time and stops at 50,000. Past that it throws Execution limit exceeded. JavaScript ran for more than 50,000 steps and the node takes its Error port.
A step is one interpreter operation, not one line. Measured against the real interpreter:
| Code | Steps |
|---|---|
| A single assignment | 5 |
Round-tripping a 50-item array through JSON | ~1,600 |
| A loop of 100 additions | ~2,000 |
Copying a 100-item array in a for loop | ~5,600 |
| A loop of 1,000 additions | ~20,000 |
| A loop of 5,000 additions | ~100,000, over the cap |
A trivial loop iteration costs roughly 20 steps, so the ceiling is around 2,500 iterations. Reshaping a response, formatting a date or building a string is nowhere near it. Iterating a large collection can reach it.
The One-Second Time Limit#
A Function node also stops after 1 second, however few steps it has taken. It takes its Error port with Execution time exceeded in the execution log. Ordinary code finishes in a few milliseconds.
A regular expression that can match the same text many ways, such as /(a+)+$/ against a long string, can take longer than a second on its own. Rewrite the expression so each character can match only one way, or check the length of the input before matching it.
The Function nodes in a single run share 10 seconds in total, from the trigger to the end, pauses included. After that, every Function node in that run takes its Error port without running its code. The next run starts again with the full 10 seconds.
A Function node that waits more than five seconds for a free sandbox takes its Error port with The code sandbox is busy. The node did not run. Connect the Error port to a Message node that asks the visitor to try again.
The Memory and Export Limits#
A Function node can use 128 MB of memory while its code runs. Past that, the code stops and the node takes its Error port with Memory limit exceeded in the execution log. Code that builds a very large string or array reaches it, so build only as much as the next node needs.
The values one Function node sends out through setExport must total less than 1 MB. Over that, the node takes its Error port with Exports too large in the execution log, and none of its exports reach the workflow. Export the fields the next node needs, not a whole API response.
What Is Available#
The sandbox is a bare ES5 environment. These exist:
Object, Array, String, Number, Boolean, Math, JSON, Date, RegExp, Error, parseInt, parseFloat, isNaN, decodeURIComponent, encodeURIComponent.
Three things are injected by Chatleadr:
| Name | Purpose |
|---|---|
flow | Workflow state, as a read-only snapshot |
setExport(name, value) | The only way to send a value downstream |
console.log(...) | Writes to the server log, for debugging |
Unavailable in the Sandbox#
require, import, fetch, XMLHttpRequest, process, Buffer, Promise, Map, Set, Symbol, and every Node.js and browser API.
Reading State#
flow holds workflow state with dotted keys expanded into nested objects, so a Form node labelled Ask for details with a field id email is readable as flow.Ask_for_details.email.
var email = flow.Ask_for_details ? flow.Ask_for_details.email : '';
var domain = email.indexOf('@') > -1 ? email.split('@')[1] : '';
setExport("domain", domain);Guard every access. A node that has not run yet, or a field the visitor left blank, leaves the property undefined, and reading a property of undefined throws and takes the Error port.
Getting Values Out#
setExport(name, value) is the only route. Each call writes <Label>.<name> into workflow state, namespaced under the node's label.
setExport("isReady", true);In a node labelled Check the input, that produces Check_the_input.isReady.
The editor warns when the code contains no setExport call at all, because a Function node that exports nothing has no effect on the workflow.
When the Code Fails#
The node takes its Error port on a syntax error, a thrown exception, reading a property of undefined, the 50,000-step cap, the time limits, and the memory and export limits. The message is recorded in the execution log.
Connect that port to a Message node or an End node. An unconnected port ends the run silently, and the visitor is left with nothing said.
Variables It Produces#
One per setExport call, as <Label>.<name>. The editor reads the calls out of your code to populate the variable picker downstream, so a name built dynamically will work at runtime but will not be offered in the picker.
The Preview#
The card reports whether the code compiled, not what it does.
| Preview | Meaning |
|---|---|
| Function Configured Successfully | The code parsed and calls setExport |
| Function export missing | It parses but exports nothing |
| A syntax error | It does not parse |
Common Questions#
Why Is My Arrow Function a Syntax Error?#
The sandbox is ES5. Use function () {}.
Can I Use async or Promises?#
No. The sandbox is synchronous and Promise does not exist. Anything asynchronous belongs in an API Request node.
How Do I Format a Date?#
Date is available, and so are getFullYear, getMonth and the rest. There is no locale formatting library, so build the string explicitly.
What Happens If My Code Throws?#
The node takes the Error port and the message is recorded in the execution log. Connect that port so the visitor is told something.
Can I Loop Over an API Response?#
Yes, within the step budget. A hundred items is comfortable; several thousand is not. Where the collection is large, filter it server-side before returning it.
Is There a Way to Raise the Step Cap?#
No. The step cap, the time limits, and the memory and export limits are fixed in the engine. Move heavy work behind an API Request node.