Strings
The string builtins cover the basics every web app reaches for: case folding, whitespace trimming, splitting and joining, substring search and replace, prefix/suffix checks, padding for column alignment, and a positional format for templated output. Every call works identically on the server (during > server evaluation) and in the browser (inside handlers) — same name, same result.
upper / lower
Case folding, ASCII + Unicode aware
upper(s) and lower(s) return a new string with the case folded. They handle Unicode — accented characters and non-Latin scripts round-trip correctly.
<p> [upper('hello')]
<p> [lower('WORLD')] HELLO
world
trim — strip leading and trailing whitespace
Inner spaces are preserved
trim(s) removes whitespace (spaces, tabs, newlines) from both ends of the string. Useful for cleaning up form input before validation. Inner whitespace is left alone — use regex.replace if you need to collapse runs.
<p> ([trim(' hi ')]) (hi)
split / join — array bridge
Two halves of the same conversion
split(s, sep) cuts a string on a delimiter into an array. join(arr, sep) is the inverse — turns an array of strings into a delimited string. Together they let you reshape "list-as-string" data without touching the loop.
<p> [split('a,b,c', ',')]
<p> [join(items, ' / ')] split: [a, b, c]
join: apples / pears / figs
replace — all occurrences
No regex, no special chars
replace(s, from, to) swaps every occurrence of a literal substring. There are no escape rules — every character is matched literally. For pattern-based replacement, use regex.replace from the regex namespace.
<p> [replace('foo bar foo', 'foo', 'baz')] baz bar baz
starts_with / ends_with / contains
Three substring predicates, return booleans
Each takes the haystack first and the needle second, returning true or false. contains is overloaded — it also works on arrays (element membership) and objects (key membership), which is convenient when the data shape changes between two call sites.
<p> [starts_with('hello world', 'hello')]
<p> [ends_with('hello world', 'world')]
<p> [contains('hello world', 'lo wo')]
<p> [contains(items, 'pears')] starts: true
ends: true
contains string: true
contains array: true
pad_left / pad_right
Fixed-width alignment for tables and IDs
pad_left(s, width, char) prepends the pad character until the result is the requested width; pad_right appends instead. If the input is already at or over the width, it's returned unchanged. Common use: zero-padding numeric IDs for sortable filenames.
<p> ([pad_left('7', 3, '0')])
<p> ([pad_right('7', 3, '.')]) pad_left: (007)
pad_right: (7..)
repeat — concat N times
Quick separators, ASCII art, retry indicators
repeat(s, n) joins n copies of s into one string. Useful for rules in plain-text output, leader dots, or progress bars.
<p> [repeat('ab', 3)]
<p> [repeat('-', 20)] ababab
--------------------
format — positional placeholders
{} consumes args in order; no named slots
format(template, args...) substitutes each {} with the next argument. There are no named or indexed slots — order is the contract. The format spec mirrors the simplest Python f-string subset on purpose; reach for prose concatenation when you need anything richer.
<p> [format('hi {}', 'world')]
<p> [format('{} + {} = {}', 1, 2, 3)] hi world
1 + 2 = 3