Elyra
Elyra The coding agent e The native code editor Elyra Grove Native local development environment Askr The real server for Laravel & PHP Elyra Framework Rust + Svelte 5 framework for desktop apps Elyra Conductor Local project conductor Elyra SQL Server MySQL-compatible SQL server in Rust Elyra Félagi Agents as teammates on one board Elyra SQL Client Native desktop SQL workbench Elyra SQL Anywhere Replication-ready SQL engine Elyra Sjá SEO & GEO workspace for macOS Elyra DataGrid Server-driven data grid for Laravel
Release notes
Changelog
Elyra
Frontend runtime — `@elyra/runtime`

Frontend runtime — @elyra/runtime

The npm package the Svelte app imports. It speaks the binary wire format so you don't have to.

import { invoke, channel, CommandError } from "@elyra/runtime";

invoke(command, ...args)

Call a Rust #[command] by name. Arguments are MessagePack-encoded; the result is decoded into the resolved type.

const greeting = await invoke<string>("greet", "world");
const sum = await invoke<number>("add", 2, 3);

If the command returns an error (a Result::Err, or a middleware/decode failure), the promise rejects with a CommandError carrying the command name and message.

try {
  await invoke("checked_div", 1, 0);
} catch (e) {
  if (e instanceof CommandError) console.error(e.message);
}

channel(name)

Subscribe to a server-pushed event channel. The return value is a Svelte-readable store, so $channel(...) works in a component; it's also usable standalone.

<script>
  import { channel } from "@elyra/runtime";
  const cursor = channel("cursor");
</script>
<pre>{JSON.stringify($cursor)}</pre>
const unsub = channel<number>("tick").subscribe((v) => { /* ... */ });
// later: unsub();

All channels are multiplexed over one long-poll connection with automatic reconnect/backoff.

The generated api.* facade

After rata codegen you get bindings.ts with a fully typed facade — prefer it over stringly-typed invoke:

import { api } from "./bindings";
const todos = await api.list_todos();        // Promise<Todo[]>
const todo  = await api.add_todo("milk");    // Promise<Todo>

The facade delegates to invoke under the hood, so error handling is identical.

Origin, CORS, and the IPC token

Everything is same-origin under elyra://localhost (the app is served there, IPC and events too), so fetch needs no CORS handling in production — and the shell sends no Access-Control-Allow-* headers there, so a foreign origin can't read an IPC response. Under rata dev the page loads from Vite's http://localhost:5173; only then does the shell add CORS, and only for that exact origin (from ELYRA_DEV_URL).

On top of the origin rule, every /__* request must carry this run's IPC token. The shell generates a random token per launch and injects it into the webview before any page script runs (globalThis.__ELYRA__.token); @elyra/runtime attaches it as x-elyra-token automatically. Requests without it get 403 and the runtime throws ForbiddenError:

import { invoke, ForbiddenError } from "@elyra/runtime";

try {
  await invoke("greet", "World");
} catch (e) {
  if (e instanceof ForbiddenError) {
    // This document wasn't loaded by the app (e.g. a remote iframe).
  }
}

If you talk to the bridge without @elyra/runtime (a raw fetch), send both x-elyra-token and x-elyra-client-id yourself.

Related