Script node
The Script node runs a small snippet of code against upstream rows and emits the result downstream. Three languages are supported: DuckDB SQL (query rows as a virtual table), JavaScript (an isolated-vm sandbox), and Python (Pyodide with an admin-curated library allowlist).
When to use
- You need a transformation that neither Transform / Compute nor a single Filter rule can express cleanly (multi-row window logic, custom string parsing, algorithmic decisions).
- Your team already has a snippet in one of the three supported languages and porting it to a chain of built-in nodes would be more code, not less.
- The transformation is contained: it operates on the input rows, produces output rows, and does not need outbound HTTP, filesystem access, or long-running I/O. Those escape the sandbox.
Ports
| Port | Direction | Kind | Notes |
|---|---|---|---|
in | in | data | Rows to feed the script. |
out | out | data | Rows the script emitted. |
error | out | control | Fires when the script throws, hits the timeout, or hits maxOutputRows. |
notify | out | control | Notification policy port. |
Configuration
The Configure Action modal is a single scrollable page with five collapsible sections. Section order matches the authoring flow (pick language, write code, pick libraries, declare shape, preview).
- Language. DuckDB SQL, JavaScript, or Python. The choice gates which sections are relevant and drives the Monaco editor's language mode.
- Script. Monaco editor with IntelliSense fed by upstream column names and (for Python) the picked libraries. Press
Ctrl+Spaceto open the completion menu. DuckDB scripts are executed against a virtual table namedrows; JavaScript scripts see a globalrowsarray and anemit(row)helper; Python scripts see arowslist and actxobject with sandbox helpers. - Libraries (Python only). A picker of the admin-curated allowlist. Any
import <lib>in the script must resolve to a library that appears in this list; unlisted imports are flagged by the Validate step and refused at run time. - I/O Schema. A grid of
{ name, type }rows declaring the output columns. Feeds the completion catalog on Step 2 and lets downstream nodes see the emitted shape without running the flow first. Types:string,number,boolean,date,json. - Preview & Validate. The Validate button runs a static hazard scan (client + server) that catches sandbox-escape shapes (
require(),process.*,open(),__import__,eval,new Function, etc.). The Run preview button posts to/api/scripts/previewand renders the returned rows in the ValidationCard.
Runtime behaviour
- DuckDB scripts run through an embedded DuckDB engine over the input rows.
- JavaScript scripts run inside an isolated-vm sandbox. No filesystem, no network, no host globals.
require, top-levelimport,fetch,process,child_process,eval,new Functionare all blocked and Validate rejects them before you can Run. - Python scripts run inside Pyodide with the admin-curated library allowlist. The runtime image bakes the allowlist at build time; a new library requires an admin add + a runtime image rebuild + redeploy (see the Script node runbook).
config.timeoutMs(default 30 000) caps per-execution wall time. Exceeding it routes toerror.config.maxOutputRows(default unset) caps the row count. Exceeding it routes toerror.
Failure modes
- Static hazard fired. The Validate button surfaces the exact rule and line. Fix the code before Save. The server enforces the same rules on the API path so a scripted caller cannot bypass.
- Unlisted Python import.
Python library "X" is not in the picked Libraries set.Add it in the Libraries step (if it's on the admin allowlist) or ask an admin to add it to the account allowlist. - Timeout / row cap.
errorport fires with the reason and the run continues if a downstream branch handles it. - Runtime image missing a library. Pyodide throws
ModuleNotFoundErrorat import time. Rebuild the runtime image with the current allowlist and redeploy.