Skip to content

Scripting

Fetch API

Make web-compatible, abortable HTTP requests through Varc's permission-gated and quota-bounded network bridge.

Saved manual scripts and command macros can use the global Fetch API after Network is enabled for that script. Transformers and dashboard scripts cannot use Fetch.

const response = await fetch("https://api.example.test/items?limit=10");

if (!response.ok) {
  throw new Error(`HTTP ${response.status} ${response.statusText}`);
}

return await response.json();

The API follows familiar web shapes while Varc enforces permission, quota, deadline, and cancellation boundaries around every request.

Grant Network

Save the script, then enable Network from the editor’s Permissions control or Settings → Scripting → Permissions. A new script and a newly saved copy start denied.

Network is permission to contact any URL. Varc does not provide a domain allowlist or private-address block, so review scripts that combine env secrets with Fetch. See environments and permissions.

Send a request

fetch(input, init?) accepts a URL string, URL, or Request:

const response = await fetch(new URL("/v1/events", env.API_ORIGIN), {
  method: "POST",
  headers: {
    authorization: `Bearer ${env.API_TOKEN}`,
    "content-type": "application/json",
  },
  body: JSON.stringify({ type: "script.check", active: true }),
});

return { status: response.status, body: await response.json() };

RequestInit supports method, headers, body, redirect, and signal. A body can be a string, Uint8Array, ArrayBuffer, array-buffer view, Blob, FormData, or URLSearchParams.

Varc normalizes the request before sending it. Forbidden framing and routing headers are rejected rather than rewritten silently.

Request and Headers

Use Request to normalize or clone a reusable request description:

const base = new Request("https://api.example.test/report", {
  headers: new Headers({ accept: "application/json" }),
});

const response = await fetch(base.clone());
return await response.json();

Headers provides append, delete, get, getSetCookie, has, set, entries, keys, values, iteration, and forEach. Header names are handled case-insensitively.

Request exposes url, method, headers, redirect, signal, body, and bodyUsed, plus clone, text, json, arrayBuffer, bytes, blob, and formData.

Read a response body

Body readers are asynchronous and consume the body once:

Reader Return
await response.text() string
await response.json() unknown
await response.arrayBuffer() ArrayBuffer
await response.bytes() Uint8Array
await response.blob() Blob
await response.formData() FormData

bodyUsed becomes true after consumption. Clone first when two independent readers are required:

const response = await fetch("https://api.example.test/config");
const copy = response.clone();

return {
  raw: await response.text(),
  parsed: await copy.json(),
};

Response also exposes status, statusText, ok, headers, url, redirected, type, and body. Static helpers are Response.error(), Response.json(data, init?), and Response.redirect(url, status?).

Blob and FormData

Create and inspect bounded binary bodies with Blob:

const blob = new Blob(["prefix:", new Uint8Array([1, 2, 3])], {
  type: "application/octet-stream",
});

return {
  size: blob.size,
  type: blob.type,
  bytes: await blob.bytes(),
};

Blob provides size, type, slice, text, arrayBuffer, bytes, and stream.

Build multipart data with FormData:

const form = new FormData();
form.set("name", "Ada");
form.append("report", new Blob(["ready"], { type: "text/plain" }), "report.txt");

const response = await fetch("https://api.example.test/upload", {
  method: "POST",
  body: form,
});

return response.status;

FormData supports append, delete, get, getAll, has, set, entries, and iteration.

Abort an in-flight request

An AbortSignal crosses the host bridge. Aborting cancels the active request future rather than merely ignoring the response:

const controller = new AbortController();
const timeout = AbortSignal.timeout(2_000);
const signal = AbortSignal.any([controller.signal, timeout]);

const response = await fetch("https://api.example.test/slow", { signal });
return await response.text();

AbortController.abort(reason?), AbortSignal.abort(reason?), AbortSignal.timeout(milliseconds), AbortSignal.any(signals), throwIfAborted, and abort event listeners are available. Cancelling the whole script also cancels its request.

Consume a stream

Responses are completely buffered and bounded before a script can read them. response.body is therefore a Web Streams-shaped view over those bytes, not a live unbounded socket.

const response = await fetch("https://api.example.test/export");
const reader = response.body?.getReader();
const chunks: Uint8Array[] = [];

if (reader) {
  for (;;) {
    const part = await reader.read();
    if (part.done) break;
    if (part.value) chunks.push(part.value);
  }
  reader.releaseLock();
}

return chunks;

ReadableStream provides locked, getReader, cancel, and tee. A reader provides read, cancel, and releaseLock.

Prefer bytes, text, or another direct reader unless a library specifically expects a stream.

URL and text utilities

The Fetch environment includes:

  • URL and its live searchParams;
  • iterable URLSearchParams with append, delete, get, getAll, has, set, sort, and toString;
  • UTF-8 TextEncoder and TextDecoder;
  • crypto.getRandomValues and crypto.randomUUID.

These are available as globals; you do not import them from a package.

Redirects

The supported request policy is redirect: "follow", with at most ten redirects. "manual" and "error" are rejected rather than accepted with browser-dependent behavior.

The final response reports its URL and whether it was redirected. Varc does not maintain a cookie jar or attach ambient credentials while following redirects.

Request limits

Boundary Limit
URL 8 KiB
Request body 1 MiB
Request headers 64
Aggregate request header bytes 16 KiB
Response body 8 MiB
Redirects 10
Transport backstop 30 seconds

The execution class’s own deadline may stop the request earlier. A quota breach, permission denial, abort, cancellation, malformed body, or network failure produces a bounded error without including secret request bodies or credentials.

There is no cookie store, ambient credential store, filesystem access, browser cache, service worker, arbitrary TCP/UDP, or process-global HTTP client. Browser navigation and DOM APIs are not installed.