docs(stdlib): reference doc covering Rhai built-ins + new namespaces
A script author opening docs/stdlib-reference.md should see every function they can call without imports: the Rhai built-in stdlib (math, string, array, map, blob) plus the seven new PiCloud namespaces. Tight tables over prose — scannable rather than exhaustive. CLAUDE.md current-focus paragraph picks up a pointer to the new doc. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
215
docs/stdlib-reference.md
Normal file
215
docs/stdlib-reference.md
Normal file
@@ -0,0 +1,215 @@
|
||||
# Rhai stdlib reference
|
||||
|
||||
Everything in this document is callable from any user script without
|
||||
imports — Rhai's built-in standard library plus the seven PiCloud
|
||||
utility modules added in v1.1.0. Stateful service modules (KV, docs,
|
||||
HTTP, …) ship in subsequent v1.1.x releases and are documented
|
||||
separately.
|
||||
|
||||
For the architectural shape (why some modules are stateless and
|
||||
register at engine build, why others are per-call), see
|
||||
[sdk-shape.md](sdk-shape.md).
|
||||
|
||||
## Conventions
|
||||
|
||||
- **Throw on failure.** Every function throws a Rhai runtime error on
|
||||
bad input (invalid pattern, invalid encoding, out-of-range arg). Use
|
||||
`try { ... } catch (e) { ... }` if you want to handle it.
|
||||
- **`()` for absent.** Functions that semantically may have no result
|
||||
(e.g. `regex::find` when nothing matches) return `()`. Test with
|
||||
`if v == () { ... }`.
|
||||
- **`bool` for predicates.** Yes/no questions return `bool`.
|
||||
- **UTC, milliseconds, lowercase hex, RFC 3986.** Defaults chosen once,
|
||||
not per call.
|
||||
|
||||
---
|
||||
|
||||
## Rhai built-ins (free with every script)
|
||||
|
||||
These come with the Rhai engine itself. See the
|
||||
[Rhai book](https://rhai.rs/book/lib/index.html) for full signatures.
|
||||
|
||||
**Math:** `+ - * / %`, `min`, `max`, `abs`, `sqrt`, `pow`, `floor`,
|
||||
`ceil`, `round`, `to_int`, `to_float`, `sin`, `cos`, `tan`, `asin`,
|
||||
`acos`, `atan`, `exp`, `ln`, `log`, `PI()`, `E()`.
|
||||
|
||||
**String:** `len`, `is_empty`, `contains`, `starts_with`, `ends_with`,
|
||||
`index_of`, `split`, `trim`, `to_lower`, `to_upper`, `replace`, `chars`,
|
||||
`pad`, `sub_string`, `crop`, `+` (concatenation).
|
||||
|
||||
**Array:** `push`, `pop`, `shift`, `insert`, `remove`, `len`, `clear`,
|
||||
`truncate`, `extend`, `filter`, `map`, `reduce`, `reduce_rev`, `find`,
|
||||
`find_map`, `any`, `all`, `index_of`, `contains`, `sort`, `reverse`,
|
||||
`dedup`, `chunks`, `splice`, `[]` indexing.
|
||||
|
||||
**Map:** `len`, `is_empty`, `contains`, `keys`, `values`, `mixin`,
|
||||
`remove`, `clear`, `fill_with`, `+` (merge), `[]` and `.` access.
|
||||
|
||||
**Blob:** `len`, `push`, `pop`, `clear`, `as_string`, `parse_le_int`,
|
||||
`write_*`, `[]` indexing. Blobs are `Vec<u8>` at the Rust layer.
|
||||
|
||||
**Logging:** `log::trace`, `log::info`, `log::warn`, `log::error` —
|
||||
each takes a message and optionally a structured-data map. (Documented
|
||||
with the SDK contract; mentioned here for completeness.)
|
||||
|
||||
---
|
||||
|
||||
## `regex::` — regular expressions
|
||||
|
||||
Linear-time, no backtracking (powered by the Rust `regex` crate).
|
||||
Patterns compile per call.
|
||||
|
||||
| Function | Description |
|
||||
|---|---|
|
||||
| `regex::is_match(pattern, text) -> bool` | Whether `text` contains a match. |
|
||||
| `regex::find(pattern, text) -> String \| ()` | First match or `()` if none. |
|
||||
| `regex::find_all(pattern, text) -> Array` | All matches as `String` array. |
|
||||
| `regex::replace(pattern, text, replacement) -> String` | Replace first match only. |
|
||||
| `regex::replace_all(pattern, text, replacement) -> String` | Replace every match. |
|
||||
| `regex::split(pattern, text) -> Array` | Split `text` on matches. |
|
||||
| `regex::captures(pattern, text) -> Array \| ()` | `[full, group1, group2, ...]` from the first match; unmatched optional groups appear as `()`. |
|
||||
|
||||
Invalid patterns throw. Use `\\` to escape inside Rhai string literals
|
||||
(`"\\d+"`) or backtick strings to skip escaping (`` `\d+` ``).
|
||||
|
||||
```rhai
|
||||
if regex::is_match(`^/api/v\d+/`, ctx.request.path) {
|
||||
let cap = regex::captures(`/api/v(\d+)/(.+)`, ctx.request.path);
|
||||
let version = cap[1]; // "1"
|
||||
let rest = cap[2]; // "users"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `random::` — cryptographically-secure randomness
|
||||
|
||||
All randomness comes from `OsRng`. There is deliberately no "fast
|
||||
non-crypto" variant — scripts shouldn't have to pick.
|
||||
|
||||
| Function | Description |
|
||||
|---|---|
|
||||
| `random::int(min, max) -> i64` | Uniform integer in `[min, max]` (inclusive). Throws if `min > max`. |
|
||||
| `random::float() -> f64` | Uniform float in `[0.0, 1.0)`. |
|
||||
| `random::bytes(n) -> Blob` | `n` random bytes. `n` in `0..=65536`. |
|
||||
| `random::string(n) -> String` | `n` random alphanumeric chars (`A-Za-z0-9`). `n` in `0..=4096`. |
|
||||
| `random::uuid() -> String` | UUID v4 in canonical 8-4-4-4-12 form. |
|
||||
|
||||
```rhai
|
||||
let token = random::uuid();
|
||||
let salt = random::bytes(16);
|
||||
let pin = random::int(100000, 999999);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `time::` — UTC time
|
||||
|
||||
Canonical time value is **milliseconds since the Unix epoch** as `i64`.
|
||||
ISO 8601 / RFC 3339 strings are for I/O. UTC only — no timezone support.
|
||||
|
||||
| Function | Description |
|
||||
|---|---|
|
||||
| `time::now() -> String` | Current UTC time as ISO 8601 with ms (e.g. `"2026-05-30T20:15:00.123Z"`). |
|
||||
| `time::now_ms() -> i64` | Current ms since Unix epoch. |
|
||||
| `time::parse(iso) -> i64` | Parse RFC 3339 / ISO 8601 string to ms. Throws on bad input. |
|
||||
| `time::format(ms) -> String` | Format ms-since-epoch as ISO 8601 with ms precision. |
|
||||
| `time::add_seconds(ms, secs) -> i64` | `ms + secs*1000`, with overflow check. |
|
||||
| `time::diff_seconds(a_ms, b_ms) -> i64` | `(b_ms - a_ms) / 1000`, truncated. |
|
||||
|
||||
```rhai
|
||||
let started_at = time::now_ms();
|
||||
// ... do work ...
|
||||
let elapsed = time::diff_seconds(started_at, time::now_ms());
|
||||
|
||||
let deadline = time::format(time::add_seconds(time::now_ms(), 3600));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `json::` — JSON parse and stringify
|
||||
|
||||
| Function | Description |
|
||||
|---|---|
|
||||
| `json::parse(s) -> Dynamic` | Parse a JSON string. Returns Rhai maps, arrays, scalars, or `()` for null. Throws on invalid JSON. |
|
||||
| `json::stringify(v) -> String` | Compact JSON. |
|
||||
| `json::stringify_pretty(v) -> String` | Pretty-printed (2-space indent). |
|
||||
|
||||
```rhai
|
||||
let payload = json::parse(ctx.request.body); // if body came in as a string
|
||||
let body_str = json::stringify(#{ ok: true, items: [1, 2, 3] });
|
||||
```
|
||||
|
||||
Note: `ctx.request.body` is *already* parsed when the request body is
|
||||
`Content-Type: application/json` — only call `json::parse` on raw
|
||||
strings.
|
||||
|
||||
---
|
||||
|
||||
## `base64::` — standard and URL-safe Base64
|
||||
|
||||
Two alphabets: standard (with `=` padding) and URL-safe (no padding).
|
||||
Encoders accept both `String` and `Blob`; decoders always return `Blob`.
|
||||
|
||||
| Function | Description |
|
||||
|---|---|
|
||||
| `base64::encode(input) -> String` | Standard alphabet, padded. `input` is `String` or `Blob`. |
|
||||
| `base64::decode(s) -> Blob` | Decode standard alphabet. Throws on invalid. |
|
||||
| `base64::encode_url(input) -> String` | URL-safe alphabet, **no padding**. |
|
||||
| `base64::decode_url(s) -> Blob` | Decode URL-safe alphabet. Throws on invalid. |
|
||||
|
||||
```rhai
|
||||
let token = base64::encode_url(random::bytes(32)); // URL-safe session token
|
||||
let raw = base64::decode("aGVsbG8=");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `hex::` — hexadecimal
|
||||
|
||||
Encode produces lowercase. Decode accepts mixed case.
|
||||
|
||||
| Function | Description |
|
||||
|---|---|
|
||||
| `hex::encode(input) -> String` | Lowercase hex. `input` is `String` or `Blob`. |
|
||||
| `hex::decode(s) -> Blob` | Decode hex (case-insensitive). Throws on invalid. |
|
||||
|
||||
```rhai
|
||||
let fingerprint = hex::encode(random::bytes(20));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `url::` — percent-encoding
|
||||
|
||||
Unreserved set per RFC 3986 (`A-Z`, `a-z`, `0-9`, `-`, `_`, `.`, `~`)
|
||||
is preserved; everything else is percent-encoded.
|
||||
|
||||
| Function | Description |
|
||||
|---|---|
|
||||
| `url::encode(s) -> String` | Percent-encode a component value. |
|
||||
| `url::decode(s) -> String` | Percent-decode. Throws on invalid UTF-8 in the decoded output. |
|
||||
| `url::encode_query(map) -> String` | Build `k1=v1&k2=v2` from a Map. Both keys and values are percent-encoded. Non-string values are coerced via `to_string()`. |
|
||||
|
||||
`url::encode_query` emits keys in the Map's natural order, which is
|
||||
alphabetical (Rhai's `Map` is a `BTreeMap`). RFC 3986 leaves query
|
||||
parameter ordering unspecified, so this is fine for any conforming
|
||||
consumer; if you need a specific ordering, build the string by hand.
|
||||
|
||||
```rhai
|
||||
let qs = url::encode_query(#{ q: "rust regex", page: 2 });
|
||||
// → "page=2&q=rust%20regex"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What's not here
|
||||
|
||||
- **Crypto** (sha256/hmac/argon2/encryption) — deferred to a focused
|
||||
later PR.
|
||||
- **Timezones** — UTC only in v1.1.0. Format with an offset upstream
|
||||
if you need local time.
|
||||
- **JWT, YAML, XML, CSV, Markdown** — not planned for v1.1.x.
|
||||
- **Stateful services** (KV, docs, HTTP, cron, files, pubsub, secrets,
|
||||
email, users, queue, invoke) — land per the v1.1.x roadmap in the
|
||||
[blueprint §12](../serverless_cloud_blueprint.md).
|
||||
Reference in New Issue
Block a user