JavaScript Rewrite API (v1)

Use JavaScript body rules when you need conditional JSON or text transformations. This chapter covers MintFlow's $MF API in 2.0.0, then walks through an importable example. For JSON-only transformations, jq remains an alternative.

Body rewriting requires active MintFlow Pro access, including an eligible active trial. A Direct output is enough; you do not need a remote VPN server. Only inspect or modify traffic you are authorized to handle.

1. Try the published JavaScript example

The official examples include separate jq and JavaScript bundles. The JavaScript bundle matches https://jsonplaceholder.typicode.com/posts/1, with optional query parameters, rather than all traffic to the service.

  1. Open HTTP → HTTP Rewrite, select the toolbar import button, then From URL…. Use the raw JavaScript bundle, not the GitHub HTML page. Alternatively download that file and use Choose File….
  2. Import saves the script locally, adds jsonplaceholder.typicode.com to Domain Capture if needed, and creates the rule disabled. Review its source and matcher, then enable it. URL imports are one-time acquisitions, not automatically refreshed subscriptions.
  3. Enable HTTP Processing and ensure TCP port 443 is in scope. For HTTPS, install and fully trust the active profile's current CA. Follow the capture setup guide. If necessary, Disable HTTP/3 can encourage compatible clients to use TCP; it does not decrypt QUIC.
  4. Save the profile and start or restart VPN. Resolve compilation errors or Pro restrictions. If using live capture to inspect the result, start recording before the test.
  5. Open https://jsonplaceholder.typicode.com/posts/1 on your iPhone or iPad. Reload to obtain a new response rather than relying on cached content. Compare the fields below, then disable the demo rule and restart VPN after the experiment.

The bundle uses direction: "response", bodyMode: "json", apiVersion: 1, and language: "javascript". Its source is:

const body = $MF.resp.body;
body.title = "MintFlow JavaScript rewrote this JSON response";
body.mintflowDemo = {
  language: "JavaScript",
  method: $MF.req.method,
  working: true
};
$MF.done({ body });

Because JSON mode supplies a parsed object, do not call JSON.parse on this body or JSON.stringify the replacement. The expected response includes:

{
  "title": "MintFlow JavaScript rewrote this JSON response",
  "mintflowDemo": {
    "language": "JavaScript",
    "method": "GET",
    "working": true
  }
}

This is a partial expected result: original userId, id, and body fields remain unchanged. The separate jq demo uses /todos/1; the two matchers do not overlap. Both imports request the same capture suffix without duplicating it and omit priority, so new rules use 100.

These examples are checked against the bundle format and script contract. That is not a claim of a successful live tunnel test; the public demo service may also change. Verify actual traffic on a real device.

2. API reference

MintFlow supplies one frozen $MF namespace for each invocation.

APIMeaning
$MF.apiVersionAPI version, currently 1.
$MF.req.urlNormalized full request URL.
$MF.req.methodRequest method, such as GET or POST.
$MF.req.bodyCurrent request body, only for request rules. Not supplied to response rules.
$MF.respnull for request rules; a response object for response rules.
$MF.resp.statusResponse status code, such as 200.
$MF.resp.bodyCurrent response body for response rules.
$MF.contextDeeply frozen invocation metadata, described below.
$MF.done({ body: value })Submit a replacement body of the configured type.
$MF.done()Finish without changing the current body.
$MF.fail({ code, message })Report a script failure; not a network rejection action.
$MF.log.debug/info/warn/error(message, fields?)Bounded diagnostics; message is a string, optional fields must be JSON-compatible.

Context fields are url, direction (request or response), method, contentType (string or null), requestID (an invocation identifier string), ruleName, and status (response status or null). Treat identifiers as opaque diagnostic values.

The namespace and request/response containers are frozen. In JSON mode, the body value itself is mutable. Change that value or construct a new one, then explicitly submit it. Assigning a URL, status, or header on these objects is not a supported rewrite operation. The v1 API does not expose a headers collection or the other direction's body.

3. Completion and failure behavior

Every execution path must explicitly call $MF.done(...), $MF.done(), or $MF.fail(...). Mutating JSON alone, returning a value, or allowing the program to finish does not commit a rewrite. The first completion signal wins; put completion at the end of the chosen branch rather than continuing to mutate after it.

Check response status and shape before modifying real data. For a response rule in JSON mode:

const body = $MF.resp.body;
if ($MF.resp.status !== 200 || body === null ||
    typeof body !== "object" || Array.isArray(body)) {
  $MF.log.debug("Unexpected response shape; leaving body unchanged");
  $MF.done();
} else {
  body.title = "MintFlow JavaScript rewrote this JSON response";
  $MF.done({ body });
}

A failed, invalid, or timed-out rewrite does not apply that rule's replacement: processing fails open with the current body. Earlier successful rules may already have changed it. Missing completion or a timeout can also cause later JavaScript rules in the same chain to be skipped. Do not use script failure as a security or ad-blocking mechanism; use the appropriate routing or URL rejection rule instead.

4. JSON mode versus text mode

  • JSON: the body is a parsed JSON-compatible value, not necessarily an object. It may be an array, scalar, or null. Submit JSON-compatible data; cycles, undefined, BigInt, functions, symbols, and non-finite numbers are invalid output.
  • Text: the body and replacement must be strings. Use text operations directly. To edit JSON in text mode, you would need to parse and serialize it yourself; selecting JSON mode is usually simpler.

For a narrowly matched request rule configured with bodyMode: "text", this small example changes one text occurrence:

const text = $MF.req.body;
$MF.done({ body: text.replace("demo=off", "demo=on") });

Select the matching direction and body mode in the rule, not inside the script. In profile text these fields are direction, type:javascript, body-mode, and api-version; MFHRB uses bodyMode and apiVersion. JavaScript profile rules default to text mode, so explicitly select JSON for the JSONPlaceholder demo.

5. Sandbox and limits

This is a bounded body-transformation environment, not Node.js or a general browser scripting API. Do not rely on Node modules, a page DOM, durable state between invocations, or APIs from other proxy apps. Network access is blocked; facilities such as fetch, XMLHttpRequest, and WebSocket are disabled. Supply everything the transformation needs in its source and current invocation.

  • JavaScript source in an MFHRB file: at most 128 KiB; the complete bundle: 1 MiB.
  • Rule timeout: 50–2000 ms, default 500 ms. MFHRB uses timeoutMS; profile text uses timeout-ms. The shared rewrite-chain deadline can leave less time than this per-rule allowance.
  • Replacement body: at most 1 MiB; JSON also has depth and node limits. Keep transformations small and bounded.
  • Logging is bounded: up to 32 entries and 16 KiB of encoded log entries per invocation. Do not log credentials or entire private bodies; logs are diagnostics, not a capture export.

There are no automatically provided bare body, context, or MF bindings, and no $request, $response, or $done aliases. Declaring your own const body = $MF.resp.body is valid.

6. Review, test, and share

Use HTTP Labs to test against a representative body and the correct URL, method, direction, status, and body mode. Include an unexpected-shape case and confirm that unrelated data stays intact. Import validates syntax, not correctness for every response.

If a rule appears ineffective, check Pro access, enabled state, HTTP scope, current CA trust, the actual URL and direction, body mode, explicit completion, and whether VPN restarted after saving. Inspect rewrite diagnostics for failures or size/time limits.

Share one reviewed rule and its complete source as an MFHRB bundle. Keep both rule and script names stable when revising; repeat imports update the owned pair and disable it again for review. Profile text alone does not include a managed script's source.

Profile reference · HTTP analysis and MFHRB

Read this page as Markdown