Files
skills/docs/foundation/data/serialize/src/adapters/eval-safe/eval-safe.md
2026-05-29 08:20:56 +01:00

3.7 KiB

eval-safe

(no submodules)

Purpose

Provides serializeEvalSafe and deserializeEvalSafe as an explicit opt-in adapter for scenarios where the standard tagged JSON or MessagePack codecs cannot be used — for example, when the payload must include JavaScript Function values, RegExp literals with complex syntax, or other types that cannot be expressed in a JSON-safe intermediate. The adapter wraps the serialize-javascript library, which is purpose-built for this use case (e.g. server-side Redux initial state injection into HTML).

This module lives under the ./unsafe subpath export and must never appear in the main @suaveplan/serialize bundle. Importing it is an explicit acknowledgment of the security trade-off.

Features

  • serializeEvalSafe — produces a JavaScript expression string via serialize-javascript that, when eval'd, reconstructs the original value including Functions and RegExps
  • deserializeEvalSafe — evaluates the produced string via (0, eval)(...) (indirect eval) to reconstruct the value
  • Security warning emitted on every call unless { ignoreSecurityWarning: true } is passed
  • EvalSafeOptions interface for suppressing warnings in controlled environments

Usage: Basic Example

import { serializeEvalSafe, deserializeEvalSafe } from "@suaveplan/serialize/unsafe";

// Suppress warnings in a trusted server-side context
const str = serializeEvalSafe({ fn: () => 42 }, { ignoreSecurityWarning: true });
const restored = deserializeEvalSafe(str, { ignoreSecurityWarning: true });
// (restored as any).fn() === 42

Usage: Advanced Example

import { serializeEvalSafe } from "@suaveplan/serialize/unsafe";

// Inject Redux initial state into an HTML page server-side.
// This is the canonical use case for serialize-javascript.
const html = `
  <script>
    window.__INITIAL_STATE__ = ${serializeEvalSafe(reduxState, { ignoreSecurityWarning: true })};
  </script>
`;

API Reference

serializeEvalSafe(value: unknown, opts?: EvalSafeOptions): string

Serialize value using serialize-javascript. The output is a JavaScript expression string, not JSON.

Parameters:

  • value — any JavaScript value, including Functions and RegExps
  • opts.ignoreSecurityWarning — pass true to suppress the console.warn

Returns: a JavaScript expression string.

deserializeEvalSafe(json: string, opts?: EvalSafeOptions): unknown

Evaluate json as a JavaScript expression and return the result.

Parameters:

  • json — a string produced by serializeEvalSafe; MUST be from a trusted source
  • opts.ignoreSecurityWarning — pass true to suppress the console.warn

Returns: the reconstructed value.

Security: Uses indirect eval ((0, eval)(...)), which runs in the global scope rather than the local closure scope, slightly reducing the attack surface compared to direct eval.

EvalSafeOptions

interface EvalSafeOptions {
  ignoreSecurityWarning?: boolean;
}

Implementation Notes

  • The (0, eval)(...) pattern (indirect eval) is used intentionally: it evaluates in the global scope rather than capturing local variables, which slightly reduces the blast radius if a malicious string is accidentally evaluated.
  • serialize-javascript is listed as a regular dependency (not a dev dependency) because it is shipped as part of the /unsafe subpath. The main bundle's Rollup config marks it as external, so it is never bundled into dist/index.js.
  • The biome lint/security/noEval rule is suppressed with a comment at the call site to document that the eval usage is intentional, reviewed, and isolated to this specific adapter.