> ## Documentation Index
> Fetch the complete documentation index at: https://docs.eigenpal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Script

> Execute a TypeScript function in a QuickJS sandbox. Input keys become the function's parameter list, in declaration order, and the required `: R` return-type annotation IS this step's output schema: `inputs: { items, taxRate }` ⇒ `function script(items: …, taxRate: …): R { … }`.

`transform.script` runs a typed TypeScript function in a secure WebAssembly
(QuickJS) sandbox. Reach for it whenever a transformation is deterministic but
too involved for a LiquidJS template, array `map`/`filter`/`reduce`, multi-step
calculations, conditional branching, date arithmetic, or regex parsing.

## When to use Script vs LiquidJS

| Use case                                         | Tool                   |
| ------------------------------------------------ | ---------------------- |
| Simple field access, basic filters, simple math  | LiquidJS (`{{ ... }}`) |
| Array reduce/map/filter, multi-step calculations | **Script**             |
| Conditional branching, date math, string parsing | **Script**             |

A good rule: if the logic spans more than two or three lines, use a script.

## The contract

The function **must** be named `script`. Its parameters come from the `inputs`
map (each key becomes a parameter, in declaration order), and its return-type
annotation **is** this step's output schema. There is no separate
`outputSchema:` field.

* Parameter names and order must equal `Object.keys(inputs)`. Enforced at push time.
* The `: R` return annotation is mandatory and is derived to JSON Schema, so
  downstream steps autocomplete against it. A too-loose annotation (such as
  `unknown`) leaves downstream field references unresolvable.
* No `async`, `import()`, or `require()`, the sandbox has no module loader and
  the worker calls the function synchronously.

## Example

```yaml theme={null}
- name: calculate-totals
  type: transform.script
  with:
    inputs:
      items: '{{ steps.extract.output.lineItems }}'
      taxRate: '{{ input.taxRate }}'
    function: |
      function script(
        items: { price: number; quantity: number }[],
        taxRate: number,
      ): { subtotal: number; tax: number; total: number } {
        const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
        const tax = subtotal * taxRate;
        return { subtotal, tax, total: subtotal + tax };
      }
```

Here `{ subtotal, tax, total }` is the output schema, so a later step can read
`{{ steps.calculate-totals.output.total }}`.

## Configuration

Configuration goes inside the step's `with:` block.

<ParamField path="inputs" type="record<string, string>">
  Named inputs mapped from template expressions. Keys become the function parameter list in declaration order: `inputs: &#123; items, taxRate }` ⇒ `function script(items: …, taxRate: …): R &#123; … }`.
</ParamField>

<ParamField path="function" type="string" required>
  TypeScript function declaration. Must be `function script(args): R &#123; … }` where the parameter list equals `Object.keys(inputs)` in order and `R` is a return type annotation. The annotation IS this step's output schema.
</ParamField>

<ParamField path="timeout" type="number" default="5000">
  Max execution time in milliseconds (default: 5000)
</ParamField>

<ParamField path="memoryLimit" type="number" default="10485760">
  Max memory in bytes (default: 10MB)
</ParamField>

## Output

Returns `unknown`. Value returned from script. Validated at runtime against the JSON Schema derived from the function's return type annotation.
