Skip to content

Script API

Use this reference to look up noodle.* methods and their constraints. For a walkthrough, start with Scripting. Cookbooks contains copyable recipes; Scripted tests defines test() and the expect() matchers. Script YAML belongs in Collection YAML.

Script source is never variable-substituted. Pre completes after folder overrides and one substitution pass, but before HTTP. Request and RunScope changes are staged and all are discarded on an uncaught script failure. On complete success, request mutations apply only to the in-memory prepared copy and RunScope changes commit before HTTP. Those RunScope changes remain available to later requests in the same collection run even when HTTP, transport, capture, post, or assertion handling subsequently fails. A later capture can overwrite a script value. Manual sends and request run use fresh scopes.

The complete order is folder merge, environment/RunScope overlay, one substitution pass, pre, HTTP, capture commits, post, assertions, tests. Assertion expectations keep the original substitution pass, not a second pass after post. Post runs once for every completed response, including HTTP and capture errors, but never for intermediate redirects/auth challenges or transport failures. It sees successful captures. A post error preserves the response, captures, and earlier successful writes, retains logs, rolls back only that invocation’s staged RunScope/cookie changes, and still evaluates assertions and tests. Successful post RunScope writes reach later collection requests even if assertions fail. Capture persistence remains manual/request run only and still applies after later post/assertion failures. Script writes are transient unless they explicitly request persistence on a manual send or request run.

Declare scripts.pre, scripts.post, and tests as inline JavaScript or external file references in collection settings.yml, nested folder.yml, or request YAML.

  • Pre: collection → outermost folder → nearest folder → request.
  • Post, after captures: collection → outermost folder → nearest folder → request.
  • Tests, after assertions: collection → outermost folder → nearest folder → request.

All three phases use the same scope order. The most specific successful post write wins, including persisted writes. Versions 0.9.4 and 0.9.5 ran post blocks in reverse order; update collection or folder post blocks that depend on values produced by request post blocks when upgrading.

Collection and folder blocks run once per request, including each dataset row. Root folder.yml is ignored. An empty block is a no-op and does not disable inherited blocks.

Every block has a fresh QuickJS invocation. JavaScript locals are isolated; successful RunScope writes are visible to later blocks. A pre failure stops remaining pre blocks and HTTP. A post failure rolls back only that block and continues later posts, assertions, and tests. A top-level test error stops only its block. Successful persistence intents keep execution order; collection runs and F5 suppress persistence as before. Saved noodle.runRequest() calls use the same inheritance and cycle protections.

Executed blocks identify their phase, scope, scope ID, and source kind. source.path is the declaring YAML path; source.scopeId identifies the collection, folder path, or request ID. source.sourceKind is inline or external; external blocks add the collection-relative source.sourcePath. Test groups retain ordered invocations, including blocks declaring zero tests, and errors for multiple block errors; legacy error remains the first error. CLI, Results, and history show origins and bounded, redacted diagnostics and logs without retaining source code or RunScope values.

Inherited blocks share a 64 KiB console-text budget and a 256 KiB test-record budget per request. Logs become [TRUNCATED] at the limit; exhausted test records produce a test script error, and later blocks are still invoked.

The same random catalog, except seed, is available as $random.method in supported body fields. Time methods use $time.method. Calls accept JSON literals; current-time placeholders share one instant per request execution. Script noodle.time calls retain their live-clock behavior and script random seeds do not control body generation. See body template syntax and limits.

Noodle APIs are properties of the frozen, null-prototype noodle global. console and JavaScript built-ins remain global. Existing scripts must prefix API accesses with noodle.; the old bare API globals are unavailable.

API Members
noodle.request url: string and method: Method; read/write in pre, read-only in post and tests
noodle.request.headers get(name), has(name), set(name, value), delete(name)
noodle.request.params get(name), getAll(name), set(name, value), append(name, value), delete(name)
noodle.request.body text(), json(), setText(value), setJson(value), clear()
noodle.request.auth clear(), setBearer(token), setBasic(username, password), setApiKey(key, value, placement)
noodle.iteration Read-only { index, count, data } for a dataset row, otherwise null; shared with children
noodle.env get(name)
noodle.run get(name), set(name, value, options?), unset(name, options?); options: { persist: "environment" | "secret" }
noodle.crypto sha256(value, encoding), hmacSha256(secret, value, encoding), randomBytes(size, encoding)
noodle.random Frozen synchronous English test-data generators; see catalog and options
noodle.time Frozen date/time helpers; see methods and formats
console log(...values), info(...values), warn(...values), error(...values)
noodle.response (post and tests) Read-only status, statusText, timeMs; headers.get(name), headers.has(name), text(), json()
noodle.cookies (when available) get(name) in post and tests; set(input) and delete(name) in post only

In post and tests, request readers reflect the final Noodle-prepared HTTP leg after signing and cookie/header preparation: effective URL, method, query parameters and headers. Every request mutator, including URL/method assignment, centrally throws a clear read-only API error. Host objects, streams, Bun types and upload buffers are never exposed.

Both phases may pass { persist: "environment" } or { persist: "secret" } to noodle.run.set and noodle.run.unset. Set creates or updates a stored value using capture serialization; unset removes an ordinary entry or both a secret’s vault value and declaration. CLI secret delete still retains its declaration. Missing deletions are harmless. Environment operations reject declared secrets; secret set may promote ordinary variables, but secret unset cannot delete them. Empty secret values and reserved _color names are rejected.

Manual sends and request run require an existing active environment and honor persistence. Collection runs and the TUI Runner apply only runtime changes and report persistence as transient. No options retain existing behavior. noodle.env.get keeps its initial snapshot, including resolved secrets; noodle.run.get sees current staged/committed scope writes. Persistent unset suppresses the baseline variable for the remaining run until a successful set or capture; plain unset removes only the override. There is no second substitution pass.

Script success commits runtime changes before asynchronous host persistence. Pre persistence precedes HTTP; existing capture saves precede post persistence, so an explicit post intent wins durably. The latest explicit intent per key within a phase wins, and later transient writes do not change its saved-value snapshot. VM failure discards the phase’s intents. Persistence failure attempts storage rollback, retains successful runtime/request/cookie changes, reports redacted per-operation errors, and makes automation fail in the script category without skipping remaining phases. Secrets never use plaintext fallback. Staging permits 100 distinct keys and a 256 KiB combined serialized intent batch per invocation; host storage runs outside the 500 ms VM deadline.

Response header reads are case-insensitive and missing headers return null. Timing is milliseconds. Binary responses keep status/header/time expressions available, while JSON body captures and assertions fail with a binary-response error. Explicit noodle.response.text() decodes binary bytes as UTF-8 under the existing 5 MiB raw/decoded UTF-8 limits; json() retains its existing parser and caching behavior. There is no binary scripting API.

noodle.response.text() lazily transfers the original VM string without truncation, capped at 5 MiB of UTF-8 before copying. noodle.response.json() uses the captured native VM JSON parser and caches success or failure per invocation; JSON null is preserved. The text cap applies when either body reader is called, not to metadata-only post processing. Invalid JSON is a structured ScriptApiValidationError. Cached JSON objects belong only to that invocation, not the host response or the capture/assertion resolver. In tests, cached response JSON is recursively frozen. Ordinary bridge and RunScope values still obey the 256 KiB/depth-32 limits, so extract small fields instead of copying an entire large response into noodle.run.set. VM allocation and deadline failures remain resource errors rather than invalid-JSON API errors; later invocations use fresh runtimes.

noodle.cookies is absent when the jar is disabled/unavailable or sendCookies: false. Response Set-Cookie processing happens before post, even under request suppression. get(name) returns the first applicable matching value in tough-cookie order or null. delete(name) removes all applicable same-name cookies and preserves inaccessible matches. set(input) requires string name and value, optional applicable string path, optional ISO 8601 date-time string expires, optional boolean secure and httpOnly, and optional sameSite: strict|lax|none. Unknown attributes, including domain, are invalid. Cookies are host-only for the final effective URL; omitted path uses normal default-path rules, omitted expiry means session lifetime, and past expiry is allowed. Prefix validation and secure-origin rules reuse tough-cookie, including localhost treatment. There is no browser-navigation SameSite context. A complete batch is revalidated against the current jar before synchronous publication with RunScope writes. Successful operations enter the existing journal and retain deferred saves, locking, encryption, and storage warnings. Success does not promise immediate disk durability. Applicable, received, read, staged, overwritten, and deleted values are registered for redaction even on failure; short values can over-mask otherwise public output.

Header names are case-insensitive. set preserves the first matching key’s casing and position while removing duplicate case variants; delete removes all case variants. Parameter names are case-sensitive. Parameter operations affect only enabled declarations, preserve duplicate order, leave disabled declarations untouched, and append new entries at the end.

noodle.request.body.text() returns JSON, XML, or raw text, and returns null for absent, multipart, URL-encoded, or binary bodies. noodle.request.body.json() parses the current textual body and throws for absent or invalid JSON. setText, setJson, and clear replace incompatible body, form-data, and file fields. setJson stores compact JSON with body_type: json; clear sets body_type: none.

Auth helpers replace the complete prepared auth config. setApiKey placement is header or query. Auth arguments, HMAC secrets, noodle.crypto.randomBytes outputs and generated passwords are known secrets. Other noodle.random data is visible by default. noodle.env.get reads only the selected environment and never RunScope overrides. noodle.env and noodle.run names match ^\w+$ and reject unsafe prototype names. noodle.run.set accepts only bounded JSON-compatible values. Crypto inputs are UTF-8 strings; encoding is exactly hex or base64; random size is an integer from 0 through 4096.

Frozen noodle.time and its methods are available in both pre and post scripts. Helpers return strings or numbers, not Date objects. JavaScript Date and noodle.random.timestamp() / isoTimestamp() remain available.

Method Result
now() Current Unix milliseconds
unix(value?) Unix seconds, rounded down; defaults to now
fromUnix(seconds) Convert numeric Unix seconds to milliseconds
parse(text) Parse an ISO date or timestamp into milliseconds
iso(value?) UTC ISO timestamp; defaults to now
format(value, pattern, options?) Format with optional { timeZone }; defaults to UTC
add(value, amount, unit) Add an elapsed duration and return milliseconds
subtract(value, amount, unit) Subtract an elapsed duration and return milliseconds
diff(a, b, unit?) Signed elapsed difference a - b; defaults to milliseconds

value, a, and b accept finite epoch milliseconds or ISO strings. Date-only YYYY-MM-DD values mean midnight UTC. Date-times use YYYY-MM-DDTHH:mm:ss[.fraction]Z or an explicit +HH:mm / -HH:mm offset. Signed six-digit expanded years are also accepted. ISO input is limited to 64 characters. Invalid calendar dates, ambiguous local date-times, numeric strings, and values outside JavaScript’s Date range are rejected. Sub-millisecond precision is truncated.

Units are exactly milliseconds, seconds, minutes, hours, days, and weeks. Amounts may be fractional or negative. A day is always 24 hours and a week is seven days, including across daylight-saving changes. Arithmetic returns milliseconds with any sub-millisecond result truncated toward zero; diff preserves fractional units. Months, years, and local-calendar arithmetic are not supported.

Patterns contain 1 through 512 characters and support:

Token Meaning
YYYY Four-digit year, or signed expanded year outside 0000 through 9999
MM, DD Two-digit month and day
HH, mm, ss Two-digit 24-hour clock, minutes, and seconds
SSS Three-digit milliseconds
Z, ZZ UTC offset with or without colons, such as -03:00 or -0300
[text] Literal text, such as [T] in an ISO-like pattern

Punctuation is literal. Unsupported letter tokens and unmatched or nested brackets are errors. Historical timezone offsets retain seconds when needed, for example -04:56:02 / -045602. Such formatted strings are display values; use iso() for a UTC timestamp that parse() can read back. options accepts only timeZone, a name of at most 128 characters such as UTC, America/Santiago, or Asia/Kathmandu. Unknown zones and numeric offset zone identifiers are rejected. Formatting uses Gregorian dates and Latin digits, independent of the machine’s locale and default timezone. Zone rules come from the bundled runtime’s timezone data.

const now = noodle.time.now();
const expiresAt = noodle.time.add(now, 15, "minutes");
noodle.request.headers.set("X-Timestamp", noodle.time.iso(now));
noodle.run.set("expiresAt", expiresAt);
const local = noodle.time.format(now, "YYYY-MM-DD HH:mm:ss Z", {
timeZone: "America/Santiago",
});

Apply generated values directly to the current request. Values stored with noodle.run.set can be read in the same request’s post script and substituted into later requests in the collection run. Calls without a timestamp read the current machine clock each time; capture now() once when values must agree. Invalid inputs use the existing script API errors and rollback behavior. Locale formatting, custom-format parsing, calendar boundaries, and JSON placeholders are not part of this API.

noodle.random is a frozen synchronous API in both pre and post. Every generator works without arguments; seed(value) and pick(values) require one argument. Configurable methods accept one optional options object. Unknown fields, invalid types and extra arguments throw ScriptApiValidationError before generation.

All names below use the noodle.random. prefix:

Category Methods
Identifiers uuid(), id(), nanoId()
Primitives number(), float(), boolean(), alphaNumeric(), abbreviation()
Names name(), firstName(), lastName(), namePrefix(), nameSuffix()
Contact email(), exampleEmail(), username(), password(), phone(), phoneWithExtension()
Location address(), streetName(), city(), country(), countryCode(), latitude(), longitude()
Internet ipv4(), ipv6(), macAddress(), url(), domainName(), domainSuffix(), domainWord(), userAgent(), protocol()
Language/version locale(), semver()
Dates/time datePast(), dateFuture(), dateRecent(), weekday(), month(), timestamp(), isoTimestamp()
Colors color(), hexColor()
Words/grammar word(), words(), noun(), verb(), ingVerb(), adjective(), phrase()
Lorem ipsum loremWord(), loremWords(), loremSentence(), loremSentences(), loremParagraph(), loremParagraphs(), loremText(), loremSlug(), loremLines()
Companies companyName(), companySuffix()
Business language businessPhrase(), businessAdjective(), businessBuzzword(), businessNoun(), catchPhrase(), catchPhraseAdjective(), catchPhraseDescriptor(), catchPhraseNoun()
Jobs jobTitle(), jobArea(), jobDescriptor(), jobType()
Commerce product(), productName(), productAdjective(), productMaterial(), department(), price()
Finance bankAccount(), bankAccountName(), creditCardMask(), bic(), iban(), transactionType(), currencyCode(), currencyName(), currencySymbol(), bitcoinAddress()
Database metadata databaseColumn(), databaseType(), databaseCollation(), databaseEngine()
Files fileName(), fileExtension(), fileType(), commonFileName(), commonFileExtension(), commonFileType(), filePath(), directoryPath(), mimeType()
Images avatarUrl(), imageUrl(), imageDataUri()
Utilities seed(value), pick(values)

number, float, latitude, longitude and timestamp return numbers. boolean returns a boolean, pick returns a copied JSON value, and seed returns nothing. All other methods return strings, including decimal prices and ISO dates. uuid generates UUID v4, name generates a full name, address generates a street address, and locale generates a two-letter language code. timestamp returns current Unix seconds; isoTimestamp returns current ISO UTC time. hexColor is lowercase RGB hex. Images use current Faker URL providers or SVG data URIs; generation performs no network or filesystem operations.

Methods Options and defaults
number { min, max }; inclusive safe integers, default 0..1000
float { min, max, fractionDigits }; default 0..1, max exclusive unless precision is supplied, following Faker
id, alphaNumeric, nanoId, password, bankAccount { length }; defaults 12, 1, 21, 15, 8 respectively
words, loremWords, loremSentence, loremSentences, loremParagraph, loremParagraphs, loremSlug, loremLines { count }; corresponding words, sentences, paragraphs or lines; unspecified counts follow Faker 10.6.0
datePast, dateFuture { years, refDate }; default one year, reference defaults to invocation start
dateRecent { days, refDate }; default one day, reference defaults to invocation start
price { min, max, fractionDigits }; default 0..1000, two decimal places
imageUrl, imageDataUri { width, height }; default 640 × 480

Other methods accept no options. Lengths and image dimensions must be integers 1..4096; counts 1..100; fraction digits 0..15; years 1..100; days 1..36500. Numeric bounds must be finite and ordered, and float ranges must not overflow. refDate must be a valid ISO timestamp with a timezone, for example 2026-01-01T00:00:00Z. pick requires a non-empty JSON-compatible array of at most 1,000 elements, within the usual 256 KiB/depth-32 bridge limits; unsafe keys, cycles, non-finite numbers, accessors and unsupported values are rejected.

Each invocation lazily creates an independent English/base Faker 10.6.0 instance with a fresh random seed. seed accepts an integer 0..4294967295 and resets only that invocation’s sequence. Pre, post, different requests and concurrent runs never share generator state. Share generated values with noodle.run.set instead. Seeded results are reproducible within the pinned Faker version. Relative dates also need an explicit refDate; current timestamps remain clock-based.

noodle.random.seed(42);
noodle.run.set("testUser", {
id: noodle.random.uuid(),
name: noodle.random.name(),
email: noodle.random.exampleEmail(),
age: noodle.random.number({ min: 18, max: 80 }),
status: noodle.random.pick(["pending", "active"]),
});
noodle.run.set("createdAt", noodle.random.dateRecent({
days: 7,
refDate: "2026-01-01T00:00:00Z",
}));

IDs and passwords are seeded alphanumeric test data, without cryptographic security or guaranteed uniqueness. Passwords are registered as known secrets immediately, even if a later operation fails; ordinary generated data stays visible by default. Faker remains on the host; the guest receives only frozen methods and bounded JSON results. Body placeholders reuse the same methods; locale selection and image category options remain unavailable.

The sandbox uses a fresh QuickJS runtime and context for each script with these fixed limits:

Limit Value
VM execution across resumptions 500 ms
Wall time including child calls 30 seconds, bounded by ancestor deadline
Script-initiated calls per top-level request 10
Child nesting levels 4
Outstanding calls per script 1
QuickJS runtime memory 32 MiB
QuickJS stack 512 KiB
UTF-8 source 256 KiB
Console entries 100
Combined console text 64 KiB
Random bytes per call 4 KiB
One bridged value 256 KiB
Response text, UTF-8 5 MiB
Bridged JSON depth 32
Console serialization depth 4

The shared WASM memory is fixed at 64 MiB. Bun, process, filesystem, shell, raw network, timer, worker, module-loader, and other host APIs are absent. Top-level await and Promises are supported through controlled VM jobs; use only noodle.runRequest and noodle.sendRequest for network operations in pre/post. Imports and background work remain unsupported. Bridged objects must be plain or null-prototype JSON without cycles, unsafe keys, non-finite numbers, or unsupported members.

Treat collections containing scripts as trusted code. noodle.env.get can read selected-environment secrets, and a script can place them in the prepared URL, headers, or body that Noodle sends immediately afterward.

Requests with scripts return scripts: { evaluated, results } containing only executed results in pre/post order with phase: pre|post, scope: collection|folder|request, sourceKind: inline|external, success, durationMs, redacted logs, an optional normalized error, and optional persistence outcomes with variable, target, operation, status (saved|transient|failed), and redacted errors only. success records VM execution; persistence failures also fail the overall request. Preparation failures before script execution use evaluated: false; requests without a script omit the group. Human run output distinguishes Pre-script/Post-script and never prints logs. TUI Results uses Pre-request/Post-response rows with phase-specific error locations; Console displays redacted messages with phase, level, and request-relative timing. Script failures participate in the fixed failure-category order and collection continuation/fail-fast only after available response diagnostics finish. Script and test editors provide completion and diagnostics; TUI-only syntax preflight checks applicable sources before HTTP. Manual .timeline entries retain bounded, redacted pre/post results, logs, errors, and persistence outcomes; individual diagnostic text and each serialized log array are limited to 10,000 bytes with [TRUNCATED] markers. Script source, capture results, and RunScope values are excluded. Successful manual request snapshots reflect prepared request mutations. Automation does not create history.

Existing synchronous scripts and helpers keep working. Pre and post also accept top-level await. Network operations return Promises; await every call before the script finishes. Tests accept async callbacks and top-level await, but remain read-only and cannot call either network API.

Method Behavior
await noodle.runRequest(id) Runs an exact saved request ID in the current collection snapshot, including folder overrides, scripts, captures, assertions, and tests. Use auth/login, without .yml. Invalid, missing, cross-collection, and recursive IDs fail.
await noodle.sendRequest(options) Sends literal script values. Required url; optional method (default GET), string-valued headers, string body, and non-negative integer timeout in milliseconds. Use JSON.stringify and an explicit Content-Type for JSON. No additional variable substitution occurs.

Both return a frozen response with status, statusText, timeMs, case-insensitive headers.get/has, and synchronous text()/json() readers. Readers use the same 5 MiB UTF-8 limit as noodle.response; JSON is frozen. Saved responses also expose bounded execution diagnostics. Validation, transport, and HTTP status 400 or higher reject the call. Saved calls also reject on script, capture, assertion, or test failures. Catch the error to read failureCategories, execution, and response when available.

const login = await noodle.runRequest("auth/login")
const token = login.json().token
noodle.run.set("TOKEN", token)
noodle.request.headers.set("Authorization", `Bearer ${token}`)
const profile = await noodle.sendRequest({
url: "https://api.example.com/me",
headers: { Authorization: `Bearer ${token}` },
})
noodle.run.set("USER_ID", profile.json().id)

Children see the parent’s staged RunScope writes. Successful child captures and script writes immediately become available to the parent and subsequent child calls. Failed children discard their variable changes. The enclosing script commits the combined changes only when it succeeds; the latest successful write wins. Known secrets remain registered for redaction after rollback. The current request was substituted before pre: use request setters to apply new values to it. Post cannot modify the completed request.

Child persistence instructions are reported as transient. To persist a selected result from a manual send or request run, explicitly call noodle.run.set("TOKEN", token, { persist: "secret" }) in the parent. Collection runs and the TUI Runner suppress all persistence. HTTP effects, received cookies, and successful child cookie edits cannot be undone by variable rollback; parent cookie edits remain staged until that parent invocation succeeds.

Calls inherit collection proxy, TLS, cookie, and cancellation policies. Direct calls do not copy the parent’s credentials or headers. Saved calls use their own authentication. Nested OAuth uses cached credentials and cannot open a browser.

Only one network call may be outstanding per script. Calls can nest sequentially, up to four child levels and ten script-initiated calls per top-level request, shared across phases and descendants. Each script has a 30-second wall deadline including children; descendants inherit any earlier ancestor deadline. Shorter request timeouts still apply. The separate 500 ms VM execution budget counts all resumptions, excluding network waits. Unresolved Promises, pending calls at script completion, and limit failures stop the invocation and cancel pending work. There are no timers, raw fetch, imports, host access, or background tasks.

Each script result may contain a flat requests list with call kind, saved ID, depth, method, redacted URL, HTTP status, duration, success, failure categories, and normalized error. Results, Runner details, CLI JSON, and concise human output show child calls, including caught failures. A caught failure does not fail the parent. Manual history retains bounded, redacted summaries without child bodies, variable values, or separate child timeline entries.