Jyro 0.7.5

Jyro 0.7.5 is a bumper release, with one breaking change to the behaviour of else..if constructs (with a migration of find-and-replace else if with elseif), a host of new standard library functions, a rate limiter for InvokeRestMethod and an enhancement to the executor for allowing host-side functions to save a state per-execution context.

else if -> elseif

As discussed, we replaced else..if with elseif and changed the behaviour of else..if. We think it’s this conditional structure is more intuitive and predictable now.

New standard library functions

Ten library functions covering arrays, strings and more were added. You can check out the standard library here.

InvokeRestMethod rate limiter

Jyro scripts run fast, and the risk of hammering API endpoints with repeated calls is a real issue. We've introduced a rate limiter for this function. The new optional fifth parameter, delayMs, specifies the minimum interval between requests. Rather than blindly sleeping before each call, the implementation uses smart interval tracking: it measures how long the previous request took and only sleeps the remaining time. If a request takes 800ms and you've specified a 1000ms delay, the next call waits just 200ms.

# Fetch paginated data with 500ms between requests
foreach page in pages
    var response = InvokeRestMethod(page.url, "GET", null, null, 500)
    results = Concat(results, response.content.items)
end

The rate limiting state is stored per-execution in the new FunctionState dictionary on JyroExecutionContext, ensuring complete tenant isolation. One customer's script cannot observe or influence another's timing. Hosts can cap the maximum allowed delay via RestApiOptions.MaxRequestDelayMs (default 10 seconds) to prevent scripts from using rate limiting as a covert sleep mechanism.

Saving function state between calls

Host functions may occasionally need to save their state between calls within a single script execution. The naive approach-storing state in instance fields on the function class-creates serious problems in multi-tenant environments. Function instances are typically shared across executions for performance, so one tenant's state could leak to another, or worse, create timing side-channels that expose information about other tenants' activity. The FunctionState dictionary on JyroExecutionContext solves this by providing a key-value store that's scoped to a single execution and automatically disposed when the script completes.

public override JyroValue Execute(IReadOnlyList<JyroValue> arguments, JyroExecutionContext context)
{
    const string cacheKey = "MyFunction.Cache";

    if (!context.FunctionState.TryGetValue(cacheKey, out var cached))
    {
        cached = ExpensiveComputation();
        context.FunctionState[cacheKey] = cached;
    }

    return (JyroValue)cached;
}

Use namespaced keys (e.g., "MyFunction.StateName") to avoid collisions with other functions. Since Jyro executes synchronously on a single thread per execution, no locking is required when accessing FunctionState.

Previous
Previous

Jyro 0.7.6, Now with Schema Validation

Next
Next

Why We Replaced else if with elseif in Jyro