DraftThis article is currently in draft mode

Tight Javascriptify: Extensible Value Serialization

Serialize any JavaScript value to a readable string with hooks for custom transformations. Handles circular refs, Maps, Sets, Errors, and more.

Basic Usage

import { tightJavaScriptify } from "@phosphor/utils";

tightJavaScriptify({ user: "alice", id: 42 });
// → {user:alice,id:42}

tightJavaScriptify(new Map([["key", "value"]]));
// → {$Map(1):[["key","value"]]}

tightJavaScriptify(new Error("failed"));
// → {name:Error,message:failed,stack:...}

Built-in Type Support

function builtInPreHookForJsTypes(key: string, value: unknown): null | { value: unknown } {
  // Convert Map/Set to a standard object structure so that JSON.stringify can handle them
  if (value instanceof Map) {
    const entries = Array.from(value.entries());
    return { value: { [`$Map(${value.size})`]: entries } };
  }
  if (value instanceof Set) {
    const values = Array.from(value);
    return { value: { [`$Set(${value.size})`]: values } };
  }
  if (value instanceof Uint8Array) {
    return { value: { [`$Uint8Array(${value.length})`]: uint8ArrayToUtf8String(value) } };
  }
  if (value instanceof Date) {
    return { value: { $Date: value.toISOString() } };
  }
  return null;
}

Automatically handles:

  • Map/Set{$Map(n): entries} / {$Set(n): values}
  • Date{$Date: "2025-10-21T..."}
  • Uint8Array{$Uint8Array(n): "utf8 string"}
  • Error{name, message, stack}
  • Function"functionName" or truncated toString()
  • Circular refs[Circular? key: Object]

Extensible Hooks

Add custom transformations with preHooks, hooks, or postHooks:

/**
 * Applies each hook in sequence. The first hook that returns a non-null object
 * wins. If none return anything, returns undefined.
 */
function applyHooks(key: string, value: unknown, hooks: TightJavaScriptifyHook[], seen = new WeakMap()): unknown {
  let current = value;
  if (isObject(current)) {
    if (seen.has(current)) return `[Circular? ${seen.get(current)}]`;
    seen.set(current, `${key}: ${toString(current)}`);
  }
  if (hooks.length > 0) {
    for (const hook of hooks) {
      const result = hook.replacer(key, current);
      if (result != null) {
        current = result.value;
      }
    }
  }
  current = toJSON(current);
  if (Array.isArray(current)) {
    for (let i = 0; i < current.length; i++) {
      current[i] = applyHooks(String(i), current[i], hooks, seen);
    }
  } else if (current && typeof current === "object") {
    const result: Record<string, unknown> = {};
    for (const [subKey, subValue] of Object.entries(current)) {
      result[subKey] = applyHooks(subKey, subValue, hooks, seen);
    }
    return result;
  }
  return current;
}

Example: Custom Class

class UserId {
  constructor(public id: string) {}
}

tightJavaScriptify.hooks.push({
  name: "UserId",
  replacer: (key, value) => {
    if (value instanceof UserId) {
      return { value: { $UserId: value.id } };
    }
    return null;
  },
});

tightJavaScriptify({ user: new UserId("u123") });
// → {user:{$UserId:u123}}

Example: Redact Secrets

tightJavaScriptify.preHooks.push({
  name: "RedactAPIKeys",
  replacer: (key, value) => {
    if (key === "apiKey" && typeof value === "string") {
      return { value: value.slice(0, 4) + "..." };
    }
    return null;
  },
});

tightJavaScriptify({ apiKey: "sk_live_abc123xyz" });
// → {apiKey:sk_l...}

Hook Execution Order

// Single-pass traversal applies hooks in order:
[
  ...tightJavaScriptify.preHooks, // Built-in JS types (Map/Set/Date)
  ...extraHooks, // Per-call custom hooks
  ...tightJavaScriptify.hooks, // Global user hooks
  ...tightJavaScriptify.postHooks, // Error/Function fallbacks
];

Per-Call Hooks

Pass one-off hooks without mutating global state:

const redactEmail = {
  name: "RedactEmail",
  replacer: (key: string, value: unknown) => {
    if (key === "email") return { value: "***" };
    return null;
  },
};

tightJavaScriptify({ email: "alice@example.com" }, [redactEmail]);
// → {email:***}

Full Source

/**
 * Serializes a value to a "tight" JavaScript-ish string.
 * By default, it handles circular objects, Errors, etc. If you wish to add
 * custom transformations, push a new hook to `tightJavaScriptify.hooks`.
 *
 * Performance optimization: This implementation performs a single traversal of the data
 * structure while maintaining the order of hook application (preHooks -> extraHooks ->
 * hooks -> postHooks). This avoids multiple recursive traversals of the same structure.
 *
 * Example usage:
 * ```ts
 * tightJavaScriptify.hooks.push({
 *   name: "myHook",
 *   replacer: (key, value) => {
 *     if (isMySpecialObject(value)) {
 *       return { value: transformMyObject(value) };
 *     }
 *     return null;
 *   }
 * })
 * const result = tightJavaScriptify(myData);
 *
 * // For custom preHooks:
 * tightJavaScriptify.preHooks.push({
 *   name: "replaceDates",
 *   replacer: (key, value) => {
 *     if (value instanceof Date) {
 *       return { value: { $Date: value.toISOString() } };
 *     }
 *     return null;
 *   }
 * });
 *
 * // We also have a default builtInPreHookForMapAndSet that handles Map/Set
 * // You can remove it if you wish to override map or set transformations:
 * tightJavaScriptify.preHooks = tightJavaScriptify.preHooks.filter(h => h.name !== "builtInMapSetHook");
 * ```
 */
function tightJavaScriptify(value: unknown, extraHooks?: (Falsey | TightJavaScriptifyHook)[]): string {
  let traversed = toJSON(value);

  const combinedHooks = [
    ...tightJavaScriptify.preHooks,
    ...(extraHooks ?? []),
    ...tightJavaScriptify.hooks,
    ...tightJavaScriptify.postHooks,
  ].filter(isTruthy);
  // Single traversal applying all hooks in sequence
  traversed = applyHooks("", traversed, combinedHooks);
  // try {
  //   traversed.hooked = combinedHooks.map((h) => h.name).join(" -> ");
  // } catch {}

  if (traversed === undefined) return "undefined";
  if (traversed === null) return "null";

  const result = (tightJsonStringify(traversed) ?? "undefined")
    // Convert quoted property keys to unquoted if valid
    .replace(/(\\?")([^"]+)\1:/g, "$2:")
    // Turn our placeholder UNDEFINED back into "undefined"
    .replace(UNDEFINED_RE, "undefined");

  return result;
}

Performance

Single-pass traversal with hook composition avoids multiple recursive walks. Hooks execute sequentially until one returns a transformed value.

Use Cases

  • DevString serialization – Capture complex context in dev messages
  • Logging – Readable structured logs without circular ref errors
  • Debugging – Quick object inspection with custom formatters
  • Test snapshots – Consistent serialization across types
Tera Template Context (Zola data-loc)
	

Tera Documentation

{
  "config": {
    "base_url": "/",
    "mode": "build",
    "title": "Phosphor",
    "description": "Things we learned while building Phosphor",
    "languages": {},
    "default_language": "en",
    "generate_feed": true,
    "generate_feeds": true,
    "feed_filenames": [
      "atom.xml"
    ],
    "taxonomies": [],
    "author": null,
    "build_search_index": true,
    "extra": {
      "logo_svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"108\" height=\"26\" fill=\"none\" viewBox=\"0 0 174 42\">\n<g fill=\"currentColor\">\n<path d=\"M.4 32.6c1.9 0 3-.9 3.8-3.9l4.6-22c.7-3-.7-4-2.5-4v-.5H19c9.2 0 11 5.5 10 10.1-1 4.8-4.8 9.5-16.5 9l.1-.5c7.3.4 10.5-3.9 11.3-8.4.9-4.3-.3-9.4-6.2-9.4h-3L9.2 28.7c-.7 3 1.2 3.9 3.7 3.9v.6H.5v-.6Z\"/><path d=\"M34.5 30.5c0-1.3.9-2.7 2.5-2.7 1.5 0 2.5 1.2 2.3 3-.2 1.2.3 1.6 1 1.5 1.7-.2 4.6-4.2 4.7-10.7 0-6.4-2-8.6-3.6-8.6-2.9 0-7.8 8-9.8 17.9l-.5 2.3h-4.7l5.7-27c.6-2.6 0-3.4-2-3.4v-.6c2.6-.2 5-.5 7.7-2.2l.3.3-.8 4-4 18.1C36 16 40 10 43.9 10c3.4 0 5.4 3.5 5.1 8.4-.3 6.2-4.8 15.2-10.8 15.2-2.4 0-3.6-1.6-3.6-3Zm15.9-9.2c1.6-7 7.4-11 12.6-11 5.2 0 9 5 7.3 12-1.6 7.2-7.4 11.3-12.5 11.3-5.5 0-9-5.2-7.4-12.3Zm7.9 11.5c2.7 0 5.6-4.1 7-11.1 1.4-7 .3-10.8-2.7-10.7-3 0-6 4.3-7.3 11.1-1.4 7 0 10.8 3 10.8Zm11.4-3.3c-.4-1.6.2-3 1.4-3.4a2.2 2.2 0 0 1 3 1.3c.5 1.4-.7 2.4-.6 3.8.2 1 1.2 1.9 3 1.8 1.8 0 3.5-1 4-3 .4-2-1-3.8-3.7-6.5-3-3-4.4-5.2-3.9-8.1.7-3 3.5-5.1 7.3-5.2 3-.1 5.9 1.2 6.5 3.6.5 1.5 0 3-1.4 3.4a2.1 2.1 0 0 1-2.8-1.3c-.5-1.6.8-2.5.6-3.5-.2-.9-1.1-1.7-2.6-1.6a3.8 3.8 0 0 0-3.7 3.2c-.2 1.7.5 3 3.1 5.5 2.7 2.6 5.2 5 4.5 8.5-.7 3.8-4 5.6-7.7 5.6-2.8 0-6.2-1.3-7-4.2Zm17.4 12c1.7 0 3-1 3-4v-22c0-2-.8-2.8-2.9-2.8V12c2.4-.2 5-.7 7.3-1.7l.4.2v4.6a7.7 7.7 0 0 1 7.1-4.8c5.3 0 9 5 9 11.4s-4.5 11.8-10.3 11.8A7.4 7.4 0 0 1 95 31v6.5c0 3 1.6 4 3.7 4v.5H87.1v-.6Zm13.3-8.7c3.3 0 5.7-3.7 5.7-9.4 0-6.9-3-10.5-6.5-10.5-2.4 0-4 1.6-4.7 3.7v7.6c0 4.8 2 8.6 5.5 8.6Z\"/><path d=\"M125 32.6c2.2 0 3.8-.9 3.4-3.9l-1.4-10c-.5-3.6-1.9-5.7-4.6-5.7-2.3 0-4 2.2-4.3 4.8v10.9c0 3 1.3 3.9 3 3.9v.6h-10.9v-.6c1.8 0 3.1-.8 3.1-3.9v-23c0-2.2-.9-3-2.9-3v-.5c2.3-.2 5-.6 7.3-1.7l.4.2V16c.8-3 3.3-5.6 6.7-5.6 3.7 0 6.2 2.5 7 8.2l1.5 10.2c.4 3 1.7 3.9 3.5 3.9v.5h-11.7v-.5ZM134.4 21.8a11 11 0 0 1 11.1-11.5c6 0 11.2 4.9 11.2 11.6 0 6.8-5 11.7-11 11.7-6.2 0-11.3-5.1-11.3-11.8Zm12.6 11c3.7-.6 5.3-5.7 4.5-12-.8-6-3.8-10.1-7.4-9.6-3.7.5-5.3 5.6-4.5 11.6.8 6.2 3.7 10.4 7.4 10Z\"/><path d=\"M155.9 32.6c1.8 0 3-.9 3-4v-13c0-2.2-.8-2.9-2.8-3v-.5a21.2 21.2 0 0 0 7.3-1.7l.4.2v4.8c.7-2.5 2.6-5 5.4-5 2.1 0 3.5 1.5 3.5 3.1 0 1.4-1 2.7-2.6 2.7s-2-1.4-2.4-2.3c-.3-.7-.5-1-1-1-1.4 0-2.7 2-3 4.2v11.6c0 3 1.8 3.9 4 3.9v.6H156v-.6Z\"/>\n</g>\n</svg>\n",
      "drafts": true,
      "sections": [
        "Getting Started",
        "Managing Complexity",
        "Moving Quickly",
        "Understanding Quickly",
        "Architecture Patterns",
        "Interactive Demos",
        "Developer Tools",
        "Advanced Topics"
      ],
      "author": "Phosphor"
    },
    "markdown": {
      "highlight_code": true,
      "error_on_missing_highlight": false,
      "highlight_theme": "base16-ocean-dark",
      "highlight_themes_css": [],
      "render_emoji": false,
      "external_links_class": null,
      "external_links_target_blank": false,
      "external_links_no_follow": false,
      "external_links_no_referrer": false,
      "smart_punctuation": false,
      "definition_list": false,
      "bottom_footnotes": false,
      "extra_syntaxes_and_themes": [],
      "lazy_async_image": false,
      "insert_anchor_links": "none",
      "github_alerts": false
    },
    "search": {
      "index_format": "elasticlunr_javascript"
    },
    "generate_sitemap": true,
    "generate_robots_txt": true,
    "exclude_paginated_pages_in_sitemap": "none"
  },
  "current_path": "/tight-javascriptify/",
  "current_url": "/tight-javascriptify/",
  "lang": "en",
  "page": {
    "relative_path": "tight-javascriptify.md",
    "colocated_path": null,
    "content": "<h1 id=\"tight-javascriptify-extensible-value-serialization\">Tight Javascriptify: Extensible Value Serialization <div data-loc=\"content/tight-javascriptify.md:7\" class=\"heading-src\">⋅</div></h1>\n<p>Serialize any JavaScript value to a readable string with hooks for custom transformations. Handles circular refs, Maps, Sets, Errors, and more.</p>\n<h2 id=\"basic-usage\">Basic Usage <div data-loc=\"content/tight-javascriptify.md:11\" class=\"heading-src\">⋅</div></h2>\n<pre data-lang=\"typescript\" style=\"background-color:#2b303b;color:#c0c5ce;\" class=\"language-typescript \"><code class=\"language-typescript\" data-lang=\"typescript\"><span style=\"color:#b48ead;\">import </span><span>{ </span><span style=\"color:#bf616a;\">tightJavaScriptify </span><span>} </span><span style=\"color:#b48ead;\">from </span><span>&quot;</span><span style=\"color:#a3be8c;\">@phosphor/utils</span><span>&quot;;\n</span><span>\n</span><span style=\"color:#8fa1b3;\">tightJavaScriptify</span><span>({ user: &quot;</span><span style=\"color:#a3be8c;\">alice</span><span>&quot;, id: </span><span style=\"color:#d08770;\">42 </span><span>});\n</span><span style=\"color:#65737e;\">// → {user:alice,id:42}\n</span><span>\n</span><span style=\"color:#8fa1b3;\">tightJavaScriptify</span><span>(new Map([[&quot;</span><span style=\"color:#a3be8c;\">key</span><span>&quot;, &quot;</span><span style=\"color:#a3be8c;\">value</span><span>&quot;]]));\n</span><span style=\"color:#65737e;\">// → {$Map(1):[[&quot;key&quot;,&quot;value&quot;]]}\n</span><span>\n</span><span style=\"color:#8fa1b3;\">tightJavaScriptify</span><span>(new Error(&quot;</span><span style=\"color:#a3be8c;\">failed</span><span>&quot;));\n</span><span style=\"color:#65737e;\">// → {name:Error,message:failed,stack:...}\n</span></code></pre>\n<h2 id=\"built-in-type-support\">Built-in Type Support <div data-loc=\"content/tight-javascriptify.md:26\" class=\"heading-src\">⋅</div></h2>\n<div class=\"codeblock\" data-loc=\"content&#x2F;tight-javascriptify.md:28\">\n  \n  <div class=\"codeblock-content\" data-id=\"builtInPreHookForJsTypes\">\n    <pre class=\"shiki rose-pine\" style=\"background-color:#191724;color:#e0def4\" tabindex=\"0\" data-loc=\"scripts/lib/tight-javascriptify.ts:16\"><code><span class=\"line\"><span style=\"color:#31748F\">function</span><span style=\"color:#EBBCBA\"> builtInPreHookForJsTypes</span><span style=\"color:#908CAA\">(</span><span style=\"color:#C4A7E7;font-style:italic\">key</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> string</span><span style=\"color:#908CAA\">,</span><span style=\"color:#C4A7E7;font-style:italic\"> value</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> unknown</span><span style=\"color:#908CAA\">)</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> null</span><span style=\"color:#31748F\"> |</span><span style=\"color:#908CAA\"> {</span><span style=\"color:#EBBCBA;font-style:italic\"> value</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> unknown</span><span style=\"color:#908CAA\"> }</span><span style=\"color:#908CAA\"> {</span></span>\n<span class=\"line\"><span style=\"color:#908CAA;font-style:italic\">  //</span><span style=\"color:#6E6A86;font-style:italic\"> Convert Map/Set to a standard object structure so that JSON.stringify can handle them</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  if</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#E0DEF4;font-style:italic\">value</span><span style=\"color:#31748F\"> instanceof</span><span style=\"color:#9CCFD8\"> Map</span><span style=\"color:#E0DEF4\">) </span><span style=\"color:#908CAA\">{</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    const</span><span style=\"color:#E0DEF4;font-style:italic\"> entries</span><span style=\"color:#31748F\"> =</span><span style=\"color:#E0DEF4;font-style:italic\"> Array</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">from</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">value</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">entries</span><span style=\"color:#E0DEF4\">())</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    return</span><span style=\"color:#908CAA\"> {</span><span style=\"color:#E0DEF4\"> value</span><span style=\"color:#908CAA\">:</span><span style=\"color:#908CAA\"> {</span><span style=\"color:#E0DEF4\"> [</span><span style=\"color:#F6C177\">`$Map(</span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">value</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">size</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\">)`</span><span style=\"color:#E0DEF4\">]</span><span style=\"color:#908CAA\">:</span><span style=\"color:#E0DEF4;font-style:italic\"> entries</span><span style=\"color:#908CAA\"> }</span><span style=\"color:#908CAA\"> };</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">  }</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  if</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#E0DEF4;font-style:italic\">value</span><span style=\"color:#31748F\"> instanceof</span><span style=\"color:#9CCFD8\"> Set</span><span style=\"color:#E0DEF4\">) </span><span style=\"color:#908CAA\">{</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    const</span><span style=\"color:#E0DEF4;font-style:italic\"> values</span><span style=\"color:#31748F\"> =</span><span style=\"color:#E0DEF4;font-style:italic\"> Array</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">from</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">value</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    return</span><span style=\"color:#908CAA\"> {</span><span style=\"color:#E0DEF4\"> value</span><span style=\"color:#908CAA\">:</span><span style=\"color:#908CAA\"> {</span><span style=\"color:#E0DEF4\"> [</span><span style=\"color:#F6C177\">`$Set(</span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">value</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">size</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\">)`</span><span style=\"color:#E0DEF4\">]</span><span style=\"color:#908CAA\">:</span><span style=\"color:#E0DEF4;font-style:italic\"> values</span><span style=\"color:#908CAA\"> }</span><span style=\"color:#908CAA\"> };</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">  }</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  if</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#E0DEF4;font-style:italic\">value</span><span style=\"color:#31748F\"> instanceof</span><span style=\"color:#9CCFD8\"> Uint8Array</span><span style=\"color:#E0DEF4\">) </span><span style=\"color:#908CAA\">{</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    return</span><span style=\"color:#908CAA\"> {</span><span style=\"color:#E0DEF4\"> value</span><span style=\"color:#908CAA\">:</span><span style=\"color:#908CAA\"> {</span><span style=\"color:#E0DEF4\"> [</span><span style=\"color:#F6C177\">`$Uint8Array(</span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">value</span><span style=\"color:#31748F\">.</span><span style=\"color:#9CCFD8\">length</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\">)`</span><span style=\"color:#E0DEF4\">]</span><span style=\"color:#908CAA\">:</span><span style=\"color:#EBBCBA\"> uint8ArrayToUtf8String</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">value</span><span style=\"color:#E0DEF4\">) </span><span style=\"color:#908CAA\">}</span><span style=\"color:#908CAA\"> };</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">  }</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  if</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#E0DEF4;font-style:italic\">value</span><span style=\"color:#31748F\"> instanceof</span><span style=\"color:#9CCFD8\"> Date</span><span style=\"color:#E0DEF4\">) </span><span style=\"color:#908CAA\">{</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    return</span><span style=\"color:#908CAA\"> {</span><span style=\"color:#E0DEF4\"> value</span><span style=\"color:#908CAA\">:</span><span style=\"color:#908CAA\"> {</span><span style=\"color:#E0DEF4\"> $Date</span><span style=\"color:#908CAA\">:</span><span style=\"color:#E0DEF4;font-style:italic\"> value</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">toISOString</span><span style=\"color:#E0DEF4\">() </span><span style=\"color:#908CAA\">}</span><span style=\"color:#908CAA\"> };</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">  }</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  return</span><span style=\"color:#EBBCBA\"> null</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">}</span></span></code></pre>\n  </div>\n  \n</div>\n<p>Automatically handles:</p>\n<ul>\n<li><strong>Map/Set</strong> → <code>{$Map(n): entries}</code> / <code>{$Set(n): values}</code></li>\n<li><strong>Date</strong> → <code>{$Date: \"2025-10-21T...\"}</code></li>\n<li><strong>Uint8Array</strong> → <code>{$Uint8Array(n): \"utf8 string\"}</code></li>\n<li><strong>Error</strong> → <code>{name, message, stack}</code></li>\n<li><strong>Function</strong> → <code>\"functionName\"</code> or truncated toString()</li>\n<li><strong>Circular refs</strong> → <code>[Circular? key: Object]</code></li>\n</ul>\n<h2 id=\"extensible-hooks\">Extensible Hooks <div data-loc=\"content/tight-javascriptify.md:39\" class=\"heading-src\">⋅</div></h2>\n<p>Add custom transformations with <strong>preHooks</strong>, <strong>hooks</strong>, or <strong>postHooks</strong>:</p>\n<div class=\"codeblock\" data-loc=\"content&#x2F;tight-javascriptify.md:43\">\n  \n  <div class=\"codeblock-content\" data-id=\"applyHooks\">\n    <pre class=\"shiki rose-pine\" style=\"background-color:#191724;color:#e0def4\" tabindex=\"0\" data-loc=\"scripts/lib/tight-javascriptify.ts:49\"><code><span class=\"line\"><span style=\"color:#908CAA;font-style:italic\">/**</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * Applies each hook in sequence. The first hook that returns a non-null object</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * wins. If none return anything, returns undefined.</span></span>\n<span class=\"line\"><span style=\"color:#908CAA;font-style:italic\"> */</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">function</span><span style=\"color:#EBBCBA\"> applyHooks</span><span style=\"color:#908CAA\">(</span><span style=\"color:#C4A7E7;font-style:italic\">key</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> string</span><span style=\"color:#908CAA\">,</span><span style=\"color:#C4A7E7;font-style:italic\"> value</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> unknown</span><span style=\"color:#908CAA\">,</span><span style=\"color:#C4A7E7;font-style:italic\"> hooks</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> TightJavaScriptifyHook</span><span style=\"color:#E0DEF4\">[]</span><span style=\"color:#908CAA\">,</span><span style=\"color:#C4A7E7;font-style:italic\"> seen</span><span style=\"color:#31748F\"> =</span><span style=\"color:#31748F\"> new</span><span style=\"color:#EBBCBA\"> WeakMap</span><span style=\"color:#E0DEF4\">()</span><span style=\"color:#908CAA\">)</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> unknown</span><span style=\"color:#908CAA\"> {</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  let</span><span style=\"color:#E0DEF4;font-style:italic\"> current</span><span style=\"color:#31748F\"> =</span><span style=\"color:#E0DEF4;font-style:italic\"> value</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  if</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#EBBCBA\">isObject</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">current</span><span style=\"color:#E0DEF4\">)) </span><span style=\"color:#908CAA\">{</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    if</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#E0DEF4;font-style:italic\">seen</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">has</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">current</span><span style=\"color:#E0DEF4\">)) </span><span style=\"color:#31748F\">return</span><span style=\"color:#F6C177\"> `[Circular? </span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">seen</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">get</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">current</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\">]`</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#E0DEF4;font-style:italic\">    seen</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">set</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">current</span><span style=\"color:#908CAA\">,</span><span style=\"color:#F6C177\"> `</span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">key</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\">: </span><span style=\"color:#908CAA\">${</span><span style=\"color:#EBBCBA\">toString</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">current</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\">`</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">  }</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  if</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#E0DEF4;font-style:italic\">hooks</span><span style=\"color:#31748F\">.</span><span style=\"color:#9CCFD8\">length</span><span style=\"color:#31748F\"> ></span><span style=\"color:#EBBCBA\"> 0</span><span style=\"color:#E0DEF4\">) </span><span style=\"color:#908CAA\">{</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    for</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#31748F\">const</span><span style=\"color:#E0DEF4;font-style:italic\"> hook</span><span style=\"color:#31748F\"> of</span><span style=\"color:#E0DEF4;font-style:italic\"> hooks</span><span style=\"color:#E0DEF4\">) </span><span style=\"color:#908CAA\">{</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">      const</span><span style=\"color:#E0DEF4;font-style:italic\"> result</span><span style=\"color:#31748F\"> =</span><span style=\"color:#E0DEF4;font-style:italic\"> hook</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">replacer</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">key</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> current</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">      if</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#E0DEF4;font-style:italic\">result</span><span style=\"color:#31748F\"> !=</span><span style=\"color:#EBBCBA\"> null</span><span style=\"color:#E0DEF4\">) </span><span style=\"color:#908CAA\">{</span></span>\n<span class=\"line\"><span style=\"color:#E0DEF4;font-style:italic\">        current</span><span style=\"color:#31748F\"> =</span><span style=\"color:#E0DEF4;font-style:italic\"> result</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">value</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">      }</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">    }</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">  }</span></span>\n<span class=\"line\"><span style=\"color:#E0DEF4;font-style:italic\">  current</span><span style=\"color:#31748F\"> =</span><span style=\"color:#EBBCBA\"> toJSON</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">current</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  if</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#E0DEF4;font-style:italic\">Array</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">isArray</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">current</span><span style=\"color:#E0DEF4\">)) </span><span style=\"color:#908CAA\">{</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    for</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#31748F\">let</span><span style=\"color:#E0DEF4;font-style:italic\"> i</span><span style=\"color:#31748F\"> =</span><span style=\"color:#EBBCBA\"> 0</span><span style=\"color:#908CAA\">;</span><span style=\"color:#E0DEF4;font-style:italic\"> i</span><span style=\"color:#31748F\"> &#x3C;</span><span style=\"color:#E0DEF4;font-style:italic\"> current</span><span style=\"color:#31748F\">.</span><span style=\"color:#9CCFD8\">length</span><span style=\"color:#908CAA\">;</span><span style=\"color:#E0DEF4;font-style:italic\"> i</span><span style=\"color:#31748F\">++</span><span style=\"color:#E0DEF4\">) </span><span style=\"color:#908CAA\">{</span></span>\n<span class=\"line\"><span style=\"color:#E0DEF4;font-style:italic\">      current</span><span style=\"color:#E0DEF4\">[</span><span style=\"color:#E0DEF4;font-style:italic\">i</span><span style=\"color:#E0DEF4\">] </span><span style=\"color:#31748F\">=</span><span style=\"color:#EBBCBA\"> applyHooks</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#EBBCBA\">String</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">i</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> current</span><span style=\"color:#E0DEF4\">[</span><span style=\"color:#E0DEF4;font-style:italic\">i</span><span style=\"color:#E0DEF4\">]</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> hooks</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> seen</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">    }</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">  }</span><span style=\"color:#31748F\"> else</span><span style=\"color:#31748F\"> if</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#E0DEF4;font-style:italic\">current</span><span style=\"color:#31748F\"> &#x26;&#x26;</span><span style=\"color:#31748F\"> typeof</span><span style=\"color:#E0DEF4;font-style:italic\"> current</span><span style=\"color:#31748F\"> ===</span><span style=\"color:#F6C177\"> \"object\"</span><span style=\"color:#E0DEF4\">) </span><span style=\"color:#908CAA\">{</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    const</span><span style=\"color:#E0DEF4;font-style:italic\"> result</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> Record</span><span style=\"color:#908CAA\">&#x3C;</span><span style=\"color:#9CCFD8\">string</span><span style=\"color:#908CAA\">,</span><span style=\"color:#9CCFD8\"> unknown</span><span style=\"color:#908CAA\">></span><span style=\"color:#31748F\"> =</span><span style=\"color:#908CAA\"> {};</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    for</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#31748F\">const</span><span style=\"color:#908CAA\"> [</span><span style=\"color:#E0DEF4;font-style:italic\">subKey</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> subValue</span><span style=\"color:#908CAA\">]</span><span style=\"color:#31748F\"> of</span><span style=\"color:#E0DEF4;font-style:italic\"> Object</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">entries</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">current</span><span style=\"color:#E0DEF4\">)) </span><span style=\"color:#908CAA\">{</span></span>\n<span class=\"line\"><span style=\"color:#E0DEF4;font-style:italic\">      result</span><span style=\"color:#E0DEF4\">[</span><span style=\"color:#E0DEF4;font-style:italic\">subKey</span><span style=\"color:#E0DEF4\">] </span><span style=\"color:#31748F\">=</span><span style=\"color:#EBBCBA\"> applyHooks</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">subKey</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> subValue</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> hooks</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> seen</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">    }</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    return</span><span style=\"color:#E0DEF4;font-style:italic\"> result</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">  }</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  return</span><span style=\"color:#E0DEF4;font-style:italic\"> current</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">}</span></span></code></pre>\n  </div>\n  \n</div>\n<h3 id=\"example-custom-class\">Example: Custom Class <div data-loc=\"content/tight-javascriptify.md:45\" class=\"heading-src\">⋅</div></h3>\n<pre data-lang=\"typescript\" style=\"background-color:#2b303b;color:#c0c5ce;\" class=\"language-typescript \"><code class=\"language-typescript\" data-lang=\"typescript\"><span style=\"color:#b48ead;\">class </span><span style=\"color:#ebcb8b;\">UserId </span><span style=\"color:#eff1f5;\">{\n</span><span style=\"color:#eff1f5;\">  </span><span style=\"color:#b48ead;\">constructor</span><span>(</span><span style=\"color:#b48ead;\">public </span><span style=\"color:#bf616a;\">id</span><span>: </span><span style=\"color:#eff1f5;\">string</span><span>) </span><span style=\"color:#eff1f5;\">{}\n</span><span style=\"color:#eff1f5;\">}\n</span><span>\n</span><span style=\"color:#bf616a;\">tightJavaScriptify</span><span>.</span><span style=\"color:#bf616a;\">hooks</span><span>.</span><span style=\"color:#96b5b4;\">push</span><span>({\n</span><span>  name: &quot;</span><span style=\"color:#a3be8c;\">UserId</span><span>&quot;,\n</span><span>  </span><span style=\"color:#8fa1b3;\">replacer</span><span>: (</span><span style=\"color:#bf616a;\">key</span><span>, </span><span style=\"color:#bf616a;\">value</span><span>) </span><span style=\"color:#b48ead;\">=&gt; </span><span>{\n</span><span>    </span><span style=\"color:#b48ead;\">if </span><span>(</span><span style=\"color:#bf616a;\">value </span><span>instanceof UserId) {\n</span><span>      </span><span style=\"color:#b48ead;\">return </span><span>{ value: { $UserId: </span><span style=\"color:#bf616a;\">value</span><span>.id } };\n</span><span>    }\n</span><span>    </span><span style=\"color:#b48ead;\">return </span><span style=\"color:#d08770;\">null</span><span>;\n</span><span>  },\n</span><span>});\n</span><span>\n</span><span style=\"color:#8fa1b3;\">tightJavaScriptify</span><span>({ user: new UserId(&quot;</span><span style=\"color:#a3be8c;\">u123</span><span>&quot;) });\n</span><span style=\"color:#65737e;\">// → {user:{$UserId:u123}}\n</span></code></pre>\n<h3 id=\"example-redact-secrets\">Example: Redact Secrets <div data-loc=\"content/tight-javascriptify.md:66\" class=\"heading-src\">⋅</div></h3>\n<pre data-lang=\"typescript\" style=\"background-color:#2b303b;color:#c0c5ce;\" class=\"language-typescript \"><code class=\"language-typescript\" data-lang=\"typescript\"><span style=\"color:#bf616a;\">tightJavaScriptify</span><span>.</span><span style=\"color:#bf616a;\">preHooks</span><span>.</span><span style=\"color:#96b5b4;\">push</span><span>({\n</span><span>  name: &quot;</span><span style=\"color:#a3be8c;\">RedactAPIKeys</span><span>&quot;,\n</span><span>  </span><span style=\"color:#8fa1b3;\">replacer</span><span>: (</span><span style=\"color:#bf616a;\">key</span><span>, </span><span style=\"color:#bf616a;\">value</span><span>) </span><span style=\"color:#b48ead;\">=&gt; </span><span>{\n</span><span>    </span><span style=\"color:#b48ead;\">if </span><span>(</span><span style=\"color:#bf616a;\">key </span><span>=== &quot;</span><span style=\"color:#a3be8c;\">apiKey</span><span>&quot; &amp;&amp; typeof </span><span style=\"color:#bf616a;\">value </span><span>=== &quot;</span><span style=\"color:#a3be8c;\">string</span><span>&quot;) {\n</span><span>      </span><span style=\"color:#b48ead;\">return </span><span>{ value: </span><span style=\"color:#bf616a;\">value</span><span>.</span><span style=\"color:#96b5b4;\">slice</span><span>(</span><span style=\"color:#d08770;\">0</span><span>, </span><span style=\"color:#d08770;\">4</span><span>) + &quot;</span><span style=\"color:#a3be8c;\">...</span><span>&quot; };\n</span><span>    }\n</span><span>    </span><span style=\"color:#b48ead;\">return </span><span style=\"color:#d08770;\">null</span><span>;\n</span><span>  },\n</span><span>});\n</span><span>\n</span><span style=\"color:#8fa1b3;\">tightJavaScriptify</span><span>({ apiKey: &quot;</span><span style=\"color:#a3be8c;\">sk_live_abc123xyz</span><span>&quot; });\n</span><span style=\"color:#65737e;\">// → {apiKey:sk_l...}\n</span></code></pre>\n<h3 id=\"hook-execution-order\">Hook Execution Order <div data-loc=\"content/tight-javascriptify.md:83\" class=\"heading-src\">⋅</div></h3>\n<pre data-lang=\"typescript\" style=\"background-color:#2b303b;color:#c0c5ce;\" class=\"language-typescript \"><code class=\"language-typescript\" data-lang=\"typescript\"><span style=\"color:#65737e;\">// Single-pass traversal applies hooks in order:\n</span><span>[\n</span><span>  ...</span><span style=\"color:#bf616a;\">tightJavaScriptify</span><span>.</span><span style=\"color:#bf616a;\">preHooks</span><span>, </span><span style=\"color:#65737e;\">// Built-in JS types (Map/Set/Date)\n</span><span>  ...</span><span style=\"color:#bf616a;\">extraHooks</span><span>, </span><span style=\"color:#65737e;\">// Per-call custom hooks\n</span><span>  ...</span><span style=\"color:#bf616a;\">tightJavaScriptify</span><span>.</span><span style=\"color:#bf616a;\">hooks</span><span>, </span><span style=\"color:#65737e;\">// Global user hooks\n</span><span>  ...</span><span style=\"color:#bf616a;\">tightJavaScriptify</span><span>.</span><span style=\"color:#bf616a;\">postHooks</span><span>, </span><span style=\"color:#65737e;\">// Error/Function fallbacks\n</span><span>];\n</span></code></pre>\n<h2 id=\"per-call-hooks\">Per-Call Hooks <div data-loc=\"content/tight-javascriptify.md:95\" class=\"heading-src\">⋅</div></h2>\n<p>Pass one-off hooks without mutating global state:</p>\n<pre data-lang=\"typescript\" style=\"background-color:#2b303b;color:#c0c5ce;\" class=\"language-typescript \"><code class=\"language-typescript\" data-lang=\"typescript\"><span style=\"color:#b48ead;\">const </span><span style=\"color:#bf616a;\">redactEmail </span><span>= {\n</span><span>  name: &quot;</span><span style=\"color:#a3be8c;\">RedactEmail</span><span>&quot;,\n</span><span>  </span><span style=\"color:#8fa1b3;\">replacer</span><span>: (</span><span style=\"color:#bf616a;\">key</span><span>: string, </span><span style=\"color:#bf616a;\">value</span><span>: unknown) </span><span style=\"color:#b48ead;\">=&gt; </span><span>{\n</span><span>    </span><span style=\"color:#b48ead;\">if </span><span>(</span><span style=\"color:#bf616a;\">key </span><span>=== &quot;</span><span style=\"color:#a3be8c;\">email</span><span>&quot;) </span><span style=\"color:#b48ead;\">return </span><span>{ value: &quot;</span><span style=\"color:#a3be8c;\">***</span><span>&quot; };\n</span><span>    </span><span style=\"color:#b48ead;\">return </span><span style=\"color:#d08770;\">null</span><span>;\n</span><span>  },\n</span><span>};\n</span><span>\n</span><span style=\"color:#8fa1b3;\">tightJavaScriptify</span><span>({ email: &quot;</span><span style=\"color:#a3be8c;\">alice@example.com</span><span>&quot; }, [</span><span style=\"color:#bf616a;\">redactEmail</span><span>]);\n</span><span style=\"color:#65737e;\">// → {email:***}\n</span></code></pre>\n<h2 id=\"full-source\">Full Source <div data-loc=\"content/tight-javascriptify.md:112\" class=\"heading-src\">⋅</div></h2>\n<div class=\"codeblock\" data-loc=\"content&#x2F;tight-javascriptify.md:114\">\n  \n  <div class=\"codeblock-content\" data-id=\"tight-javascriptify\">\n    <pre class=\"shiki rose-pine\" style=\"background-color:#191724;color:#e0def4\" tabindex=\"0\" data-loc=\"scripts/lib/tight-javascriptify.ts:84\"><code><span class=\"line\"><span style=\"color:#908CAA;font-style:italic\">/**</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * Serializes a value to a \"tight\" JavaScript-ish string.</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * By default, it handles circular objects, Errors, etc. If you wish to add</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * custom transformations, push a new hook to `tightJavaScriptify.hooks`.</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> *</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * Performance optimization: This implementation performs a single traversal of the data</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * structure while maintaining the order of hook application (preHooks -> extraHooks -></span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * hooks -> postHooks). This avoids multiple recursive traversals of the same structure.</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> *</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * Example usage:</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * ```ts</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * tightJavaScriptify.hooks.push({</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> *   name: \"myHook\",</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> *   replacer: (key, value) => {</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> *     if (isMySpecialObject(value)) {</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> *       return { value: transformMyObject(value) };</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> *     }</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> *     return null;</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> *   }</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * })</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * const result = tightJavaScriptify(myData);</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> *</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * // For custom preHooks:</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * tightJavaScriptify.preHooks.push({</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> *   name: \"replaceDates\",</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> *   replacer: (key, value) => {</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> *     if (value instanceof Date) {</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> *       return { value: { $Date: value.toISOString() } };</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> *     }</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> *     return null;</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> *   }</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * });</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> *</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * // We also have a default builtInPreHookForMapAndSet that handles Map/Set</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * // You can remove it if you wish to override map or set transformations:</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * tightJavaScriptify.preHooks = tightJavaScriptify.preHooks.filter(h => h.name !== \"builtInMapSetHook\");</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * ```</span></span>\n<span class=\"line\"><span style=\"color:#908CAA;font-style:italic\"> */</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">function</span><span style=\"color:#EBBCBA\"> tightJavaScriptify</span><span style=\"color:#908CAA\">(</span><span style=\"color:#C4A7E7;font-style:italic\">value</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> unknown</span><span style=\"color:#908CAA\">,</span><span style=\"color:#C4A7E7;font-style:italic\"> extraHooks</span><span style=\"color:#31748F\">?:</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#9CCFD8\">Falsey</span><span style=\"color:#31748F\"> |</span><span style=\"color:#9CCFD8\"> TightJavaScriptifyHook</span><span style=\"color:#E0DEF4\">)[]</span><span style=\"color:#908CAA\">)</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> string</span><span style=\"color:#908CAA\"> {</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  let</span><span style=\"color:#E0DEF4;font-style:italic\"> traversed</span><span style=\"color:#31748F\"> =</span><span style=\"color:#EBBCBA\"> toJSON</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">value</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#31748F\">  const</span><span style=\"color:#E0DEF4;font-style:italic\"> combinedHooks</span><span style=\"color:#31748F\"> =</span><span style=\"color:#E0DEF4\"> [</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    ...</span><span style=\"color:#E0DEF4;font-style:italic\">tightJavaScriptify</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">preHooks</span><span style=\"color:#908CAA\">,</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    ...</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">extraHooks</span><span style=\"color:#31748F\"> ??</span><span style=\"color:#E0DEF4\"> [])</span><span style=\"color:#908CAA\">,</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    ...</span><span style=\"color:#E0DEF4;font-style:italic\">tightJavaScriptify</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">hooks</span><span style=\"color:#908CAA\">,</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    ...</span><span style=\"color:#E0DEF4;font-style:italic\">tightJavaScriptify</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">postHooks</span><span style=\"color:#908CAA\">,</span></span>\n<span class=\"line\"><span style=\"color:#E0DEF4\">  ]</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">filter</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">isTruthy</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA;font-style:italic\">  //</span><span style=\"color:#6E6A86;font-style:italic\"> Single traversal applying all hooks in sequence</span></span>\n<span class=\"line\"><span style=\"color:#E0DEF4;font-style:italic\">  traversed</span><span style=\"color:#31748F\"> =</span><span style=\"color:#EBBCBA\"> applyHooks</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#F6C177\">\"\"</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> traversed</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> combinedHooks</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA;font-style:italic\">  //</span><span style=\"color:#6E6A86;font-style:italic\"> try {</span></span>\n<span class=\"line\"><span style=\"color:#908CAA;font-style:italic\">  //</span><span style=\"color:#6E6A86;font-style:italic\">   traversed.hooked = combinedHooks.map((h) => h.name).join(\" -> \");</span></span>\n<span class=\"line\"><span style=\"color:#908CAA;font-style:italic\">  //</span><span style=\"color:#6E6A86;font-style:italic\"> } catch {}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#31748F\">  if</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#E0DEF4;font-style:italic\">traversed</span><span style=\"color:#31748F\"> ===</span><span style=\"color:#EBBCBA\"> undefined</span><span style=\"color:#E0DEF4\">) </span><span style=\"color:#31748F\">return</span><span style=\"color:#F6C177\"> \"undefined\"</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  if</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#E0DEF4;font-style:italic\">traversed</span><span style=\"color:#31748F\"> ===</span><span style=\"color:#EBBCBA\"> null</span><span style=\"color:#E0DEF4\">) </span><span style=\"color:#31748F\">return</span><span style=\"color:#F6C177\"> \"null\"</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#31748F\">  const</span><span style=\"color:#E0DEF4;font-style:italic\"> result</span><span style=\"color:#31748F\"> =</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#EBBCBA\">tightJsonStringify</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">traversed</span><span style=\"color:#E0DEF4\">) </span><span style=\"color:#31748F\">??</span><span style=\"color:#F6C177\"> \"undefined\"</span><span style=\"color:#E0DEF4\">)</span></span>\n<span class=\"line\"><span style=\"color:#908CAA;font-style:italic\">    //</span><span style=\"color:#6E6A86;font-style:italic\"> Convert quoted property keys to unquoted if valid</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    .</span><span style=\"color:#EBBCBA\">replace</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#F6C177\">/</span><span style=\"color:#908CAA\">(</span><span style=\"color:#31748F\">\\\\?</span><span style=\"color:#F6C177\">\"</span><span style=\"color:#908CAA\">)([</span><span style=\"color:#31748F\">^\"</span><span style=\"color:#908CAA\">]</span><span style=\"color:#31748F\">+</span><span style=\"color:#908CAA\">)</span><span style=\"color:#31748F\">\\1</span><span style=\"color:#F6C177\">:/</span><span style=\"color:#31748F\">g</span><span style=\"color:#908CAA\">,</span><span style=\"color:#F6C177\"> \"$2:\"</span><span style=\"color:#E0DEF4\">)</span></span>\n<span class=\"line\"><span style=\"color:#908CAA;font-style:italic\">    //</span><span style=\"color:#6E6A86;font-style:italic\"> Turn our placeholder UNDEFINED back into \"undefined\"</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    .</span><span style=\"color:#EBBCBA\">replace</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">UNDEFINED_RE</span><span style=\"color:#908CAA\">,</span><span style=\"color:#F6C177\"> \"undefined\"</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#31748F\">  return</span><span style=\"color:#E0DEF4;font-style:italic\"> result</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">}</span></span></code></pre>\n  </div>\n  \n</div>\n<h2 id=\"performance\">Performance <div data-loc=\"content/tight-javascriptify.md:116\" class=\"heading-src\">⋅</div></h2>\n<p>Single-pass traversal with hook composition avoids multiple recursive walks. Hooks execute sequentially until one returns a transformed value.</p>\n<h2 id=\"use-cases\">Use Cases <div data-loc=\"content/tight-javascriptify.md:120\" class=\"heading-src\">⋅</div></h2>\n<ul>\n<li><strong>DevString serialization</strong> – Capture complex context in dev messages</li>\n<li><strong>Logging</strong> – Readable structured logs without circular ref errors</li>\n<li><strong>Debugging</strong> – Quick object inspection with custom formatters</li>\n<li><strong>Test snapshots</strong> – Consistent serialization across types</li>\n</ul>\n",
    "permalink": "/tight-javascriptify/",
    "slug": "tight-javascriptify",
    "ancestors": [
      "_index.md"
    ],
    "title": "Tight Javascriptify: Extensible Value Serialization",
    "description": null,
    "updated": null,
    "date": "2025-10-21",
    "year": 2025,
    "month": 10,
    "day": 21,
    "taxonomies": {},
    "authors": [],
    "extra": {},
    "path": "/tight-javascriptify/",
    "components": [
      "tight-javascriptify"
    ],
    "summary": null,
    "toc": [
      {
        "level": 1,
        "id": "tight-javascriptify-extensible-value-serialization",
        "permalink": "/tight-javascriptify/#tight-javascriptify-extensible-value-serialization",
        "title": "Tight Javascriptify: Extensible Value Serialization ⋅",
        "children": [
          {
            "level": 2,
            "id": "basic-usage",
            "permalink": "/tight-javascriptify/#basic-usage",
            "title": "Basic Usage ⋅",
            "children": []
          },
          {
            "level": 2,
            "id": "built-in-type-support",
            "permalink": "/tight-javascriptify/#built-in-type-support",
            "title": "Built-in Type Support ⋅",
            "children": []
          },
          {
            "level": 2,
            "id": "extensible-hooks",
            "permalink": "/tight-javascriptify/#extensible-hooks",
            "title": "Extensible Hooks ⋅",
            "children": [
              {
                "level": 3,
                "id": "example-custom-class",
                "permalink": "/tight-javascriptify/#example-custom-class",
                "title": "Example: Custom Class ⋅",
                "children": []
              },
              {
                "level": 3,
                "id": "example-redact-secrets",
                "permalink": "/tight-javascriptify/#example-redact-secrets",
                "title": "Example: Redact Secrets ⋅",
                "children": []
              },
              {
                "level": 3,
                "id": "hook-execution-order",
                "permalink": "/tight-javascriptify/#hook-execution-order",
                "title": "Hook Execution Order ⋅",
                "children": []
              }
            ]
          },
          {
            "level": 2,
            "id": "per-call-hooks",
            "permalink": "/tight-javascriptify/#per-call-hooks",
            "title": "Per-Call Hooks ⋅",
            "children": []
          },
          {
            "level": 2,
            "id": "full-source",
            "permalink": "/tight-javascriptify/#full-source",
            "title": "Full Source ⋅",
            "children": []
          },
          {
            "level": 2,
            "id": "performance",
            "permalink": "/tight-javascriptify/#performance",
            "title": "Performance ⋅",
            "children": []
          },
          {
            "level": 2,
            "id": "use-cases",
            "permalink": "/tight-javascriptify/#use-cases",
            "title": "Use Cases ⋅",
            "children": []
          }
        ]
      }
    ],
    "word_count": 295,
    "reading_time": 2,
    "assets": [],
    "draft": true,
    "lang": "en",
    "lower": {
      "relative_path": "devstate-extensible-debug-renderer.md",
      "colocated_path": null,
      "content": "<h1 id=\"devstate-extensible-debug-renderer\">DevState: Extensible Debug Renderer <div data-loc=\"content/devstate-extensible-debug-renderer.md:7\" class=\"heading-src\">⋅</div></h1>\n<p>Render complex runtime data structures with type-specific handlers. Register custom renderers to format atoms, queries, and domain objects without touching the core.</p>\n<h2 id=\"registry-pattern\">Registry Pattern <div data-loc=\"content/devstate-extensible-debug-renderer.md:11\" class=\"heading-src\">⋅</div></h2>\n<p>Define custom renderers with type guards:</p>\n<div class=\"codeblock\" data-loc=\"content&#x2F;devstate-extensible-debug-renderer.md:15\">\n  \n  <div class=\"codeblock-content\" data-id=\"define-renderer\">\n    <pre class=\"shiki rose-pine\" style=\"background-color:#191724;color:#e0def4\" tabindex=\"0\" data-loc=\"scripts/lib/dev/DevState.tsx:198\"><code><span class=\"line\"><span style=\"color:#31748F\">export</span><span style=\"color:#31748F\"> function</span><span style=\"color:#EBBCBA\"> defineRenderer</span><span style=\"color:#908CAA\">&#x3C;</span><span style=\"color:#9CCFD8\">T</span><span style=\"color:#31748F\"> extends</span><span style=\"color:#9CCFD8\"> UnknownObject</span><span style=\"color:#908CAA\">>(</span></span>\n<span class=\"line\"><span style=\"color:#EBBCBA\">  test</span><span style=\"color:#31748F\">:</span><span style=\"color:#908CAA\"> (</span><span style=\"color:#C4A7E7;font-style:italic\">data</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> UnknownObject</span><span style=\"color:#908CAA\">)</span><span style=\"color:#31748F\"> =></span><span style=\"color:#C4A7E7;font-style:italic\"> data</span><span style=\"color:#31748F\"> is</span><span style=\"color:#9CCFD8\"> T</span><span style=\"color:#908CAA\">,</span></span>\n<span class=\"line\"><span style=\"color:#C4A7E7;font-style:italic\">  render</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> Renderer</span><span style=\"color:#908CAA\">&#x3C;</span><span style=\"color:#9CCFD8\">T</span><span style=\"color:#908CAA\">>,</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">)</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> RegistryItem</span><span style=\"color:#908CAA\">&#x3C;</span><span style=\"color:#9CCFD8\">any</span><span style=\"color:#908CAA\">></span><span style=\"color:#908CAA\"> {</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  return</span><span style=\"color:#908CAA\"> {</span><span style=\"color:#E0DEF4;font-style:italic\"> test</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> render</span><span style=\"color:#908CAA\"> };</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">}</span></span></code></pre>\n  </div>\n  \n</div>\n<pre data-lang=\"tsx\" style=\"background-color:#2b303b;color:#c0c5ce;\" class=\"language-tsx \"><code class=\"language-tsx\" data-lang=\"tsx\"><span style=\"color:#65737e;\">// Match by type predicate\n</span><span style=\"color:#8fa1b3;\">defineRenderer</span><span>(\n</span><span>  (</span><span style=\"color:#bf616a;\">data</span><span>) </span><span style=\"color:#b48ead;\">=&gt; </span><span style=\"color:#8fa1b3;\">isAtom</span><span>(</span><span style=\"color:#bf616a;\">data</span><span>),\n</span><span>  ({ </span><span style=\"color:#bf616a;\">data</span><span>, </span><span style=\"color:#bf616a;\">showPath</span><span>, </span><span style=\"color:#bf616a;\">r </span><span>}) </span><span style=\"color:#b48ead;\">=&gt; </span><span>{\n</span><span>    </span><span style=\"color:#b48ead;\">const </span><span style=\"color:#bf616a;\">value </span><span>= </span><span style=\"color:#8fa1b3;\">useAtomValue</span><span>(</span><span style=\"color:#bf616a;\">data</span><span>);\n</span><span>    </span><span style=\"color:#b48ead;\">return </span><span>&lt;</span><span style=\"color:#ebcb8b;\">r.Unknown </span><span style=\"color:#d08770;\">name</span><span>=</span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">data</span><span>.</span><span style=\"color:#96b5b4;\">toString</span><span>()</span><span style=\"color:#ab7967;\">} </span><span style=\"color:#d08770;\">data</span><span>=</span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">value</span><span style=\"color:#ab7967;\">} </span><span style=\"color:#d08770;\">showPath</span><span>=</span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">showPath</span><span style=\"color:#ab7967;\">} </span><span>/&gt;;\n</span><span>  },\n</span><span>);\n</span></code></pre>\n<p>String-based renderers parse and transform:</p>\n<div class=\"codeblock\" data-loc=\"content&#x2F;devstate-extensible-debug-renderer.md:30\">\n  \n  <div class=\"codeblock-content\" data-id=\"define-string-renderer\">\n    <pre class=\"shiki rose-pine\" style=\"background-color:#191724;color:#e0def4\" tabindex=\"0\" data-loc=\"scripts/lib/dev/DevState.tsx:207\"><code><span class=\"line\"><span style=\"color:#31748F\">export</span><span style=\"color:#31748F\"> function</span><span style=\"color:#EBBCBA\"> defineStringRenderer</span><span style=\"color:#908CAA\">&#x3C;</span><span style=\"color:#9CCFD8\">T</span><span style=\"color:#908CAA\">>(</span></span>\n<span class=\"line\"><span style=\"color:#EBBCBA\">  parse</span><span style=\"color:#31748F\">:</span><span style=\"color:#908CAA\"> (</span><span style=\"color:#C4A7E7;font-style:italic\">data</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> string</span><span style=\"color:#908CAA\">)</span><span style=\"color:#31748F\"> =></span><span style=\"color:#9CCFD8\"> null</span><span style=\"color:#31748F\"> |</span><span style=\"color:#9CCFD8\"> undefined</span><span style=\"color:#31748F\"> |</span><span style=\"color:#9CCFD8\"> T</span><span style=\"color:#908CAA\">,</span></span>\n<span class=\"line\"><span style=\"color:#C4A7E7;font-style:italic\">  render</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> Renderer</span><span style=\"color:#908CAA\">&#x3C;</span><span style=\"color:#9CCFD8\">T</span><span style=\"color:#908CAA\">>,</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">)</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> StringRegistryItem</span><span style=\"color:#908CAA\">&#x3C;</span><span style=\"color:#9CCFD8\">any</span><span style=\"color:#908CAA\">></span><span style=\"color:#908CAA\"> {</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  return</span><span style=\"color:#908CAA\"> {</span><span style=\"color:#E0DEF4;font-style:italic\"> parse</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> render</span><span style=\"color:#908CAA\"> };</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">}</span></span></code></pre>\n  </div>\n  \n</div>\n<pre data-lang=\"tsx\" style=\"background-color:#2b303b;color:#c0c5ce;\" class=\"language-tsx \"><code class=\"language-tsx\" data-lang=\"tsx\"><span style=\"color:#65737e;\">// Parse UIDs from strings\n</span><span style=\"color:#8fa1b3;\">defineStringRenderer</span><span>(\n</span><span>  (</span><span style=\"color:#bf616a;\">data</span><span>) </span><span style=\"color:#b48ead;\">=&gt; </span><span>(</span><span style=\"color:#bf616a;\">World</span><span>.</span><span style=\"color:#8fa1b3;\">isUID</span><span>(</span><span style=\"color:#bf616a;\">data</span><span>) ? </span><span style=\"color:#bf616a;\">data </span><span>: </span><span style=\"color:#d08770;\">null</span><span>),\n</span><span>  ({ </span><span style=\"color:#bf616a;\">data</span><span>, </span><span style=\"color:#bf616a;\">r</span><span>, </span><span style=\"color:#bf616a;\">showPath </span><span>}) </span><span style=\"color:#b48ead;\">=&gt; </span><span>(\n</span><span>    &lt;</span><span style=\"color:#ebcb8b;\">r.Custom </span><span style=\"color:#d08770;\">rawValue</span><span>=</span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">data</span><span style=\"color:#ab7967;\">} </span><span style=\"color:#d08770;\">icon</span><span>=</span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">Ta</span><span>.</span><span style=\"color:#bf616a;\">IconBox</span><span style=\"color:#ab7967;\">} </span><span style=\"color:#d08770;\">showPath</span><span>=</span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">showPath</span><span style=\"color:#ab7967;\">}</span><span>&gt;\n</span><span>      &lt;</span><span style=\"color:#ebcb8b;\">UIDElement </span><span style=\"color:#d08770;\">uid</span><span>=</span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">data</span><span style=\"color:#ab7967;\">} </span><span>/&gt;\n</span><span>    &lt;/</span><span style=\"color:#ebcb8b;\">r.Custom</span><span>&gt;\n</span><span>  ),\n</span><span>);\n</span></code></pre>\n<h2 id=\"renderer-components\">Renderer Components <div data-loc=\"content/devstate-extensible-debug-renderer.md:44\" class=\"heading-src\">⋅</div></h2>\n<p>Built-in primitives handle common cases:</p>\n<pre data-lang=\"tsx\" style=\"background-color:#2b303b;color:#c0c5ce;\" class=\"language-tsx \"><code class=\"language-tsx\" data-lang=\"tsx\"><span style=\"color:#b48ead;\">const </span><span style=\"color:#bf616a;\">r</span><span>: RendererComponents = {\n</span><span>  Array: &lt;</span><span style=\"color:#ebcb8b;\">Array </span><span style=\"color:#d08770;\">data</span><span>=</span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">items</span><span style=\"color:#ab7967;\">} </span><span style=\"color:#d08770;\">name</span><span>=&quot;</span><span style=\"color:#a3be8c;\">Items</span><span>&quot; /&gt;,\n</span><span>  Object: &lt;</span><span style=\"color:#ebcb8b;\">Object </span><span style=\"color:#d08770;\">data</span><span>=</span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">obj</span><span style=\"color:#ab7967;\">} </span><span style=\"color:#d08770;\">name</span><span>=&quot;</span><span style=\"color:#a3be8c;\">Config</span><span>&quot; </span><span style=\"color:#d08770;\">commaSeparatedOmitKeys</span><span>=&quot;</span><span style=\"color:#a3be8c;\">internal,_private</span><span>&quot; /&gt;,\n</span><span>  Unknown: &lt;</span><span style=\"color:#ebcb8b;\">Unknown </span><span style=\"color:#d08770;\">data</span><span>=</span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">value</span><span style=\"color:#ab7967;\">} </span><span style=\"color:#d08770;\">name</span><span>=&quot;</span><span style=\"color:#a3be8c;\">Result</span><span>&quot; /&gt;,\n</span><span>  Placeholder: &lt;</span><span style=\"color:#ebcb8b;\">Placeholder </span><span style=\"color:#d08770;\">name</span><span>=&quot;</span><span style=\"color:#a3be8c;\">WeakMap</span><span>&quot; /&gt;,\n</span><span>  </span><span style=\"color:#8fa1b3;\">Custom</span><span>: (\n</span><span>    &lt;</span><span style=\"color:#ebcb8b;\">Custom </span><span style=\"color:#d08770;\">icon</span><span>=</span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">Ta</span><span>.</span><span style=\"color:#bf616a;\">Icon</span><span style=\"color:#ab7967;\">} </span><span style=\"color:#d08770;\">name</span><span>=&quot;</span><span style=\"color:#a3be8c;\">Type</span><span>&quot;&gt;\n</span><span>      ...\n</span><span>    &lt;/</span><span style=\"color:#ebcb8b;\">Custom</span><span>&gt;\n</span><span>  ),\n</span><span>};\n</span></code></pre>\n<p>Use <code>commaSeparatedOmitKeys</code> to filter object keys without memoizing the data object.</p>\n<h2 id=\"integration\">Integration <div data-loc=\"content/devstate-extensible-debug-renderer.md:64\" class=\"heading-src\">⋅</div></h2>\n<p>Register renderers at app root:</p>\n<div class=\"codeblock\" data-loc=\"content&#x2F;devstate-extensible-debug-renderer.md:68\">\n  \n  <div class=\"codeblock-content\" data-id=\"provide-renderers\">\n    <pre class=\"shiki rose-pine\" style=\"background-color:#191724;color:#e0def4\" tabindex=\"0\" data-loc=\"scripts/lib/dev/app-dev-state-renderers.tsx:156\"><code><span class=\"line\"><span style=\"color:#31748F\">export</span><span style=\"color:#31748F\"> const</span><span style=\"color:#EBBCBA;font-style:italic\"> ProvideAppDevStateRenderers</span><span style=\"color:#31748F\"> =</span><span style=\"color:#908CAA\"> ({</span><span style=\"color:#C4A7E7;font-style:italic\"> children</span><span style=\"color:#908CAA\"> }</span><span style=\"color:#31748F\">:</span><span style=\"color:#908CAA\"> {</span><span style=\"color:#EBBCBA;font-style:italic\"> children</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> React</span><span style=\"color:#31748F\">.</span><span style=\"color:#9CCFD8\">ReactNode</span><span style=\"color:#908CAA\"> })</span><span style=\"color:#31748F\"> =></span><span style=\"color:#908CAA\"> {</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  return</span><span style=\"color:#E0DEF4\"> (</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86\">    &#x3C;</span><span style=\"color:#9CCFD8\">ProvideDevStateRenderers</span><span style=\"color:#C4A7E7;font-style:italic\"> registry</span><span style=\"color:#31748F\">=</span><span style=\"color:#908CAA\">{</span><span style=\"color:#E0DEF4;font-style:italic\">RENDERERS</span><span style=\"color:#908CAA\">}</span><span style=\"color:#C4A7E7;font-style:italic\"> stringRegistry</span><span style=\"color:#31748F\">=</span><span style=\"color:#908CAA\">{</span><span style=\"color:#E0DEF4;font-style:italic\">STRING_RENDERERS</span><span style=\"color:#908CAA\">}</span><span style=\"color:#C4A7E7;font-style:italic\"> orderedKeys</span><span style=\"color:#31748F\">=</span><span style=\"color:#908CAA\">{</span><span style=\"color:#E0DEF4;font-style:italic\">KEY_ORDER_PREFERENCE</span><span style=\"color:#908CAA\">}</span><span style=\"color:#6E6A86\">></span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">      {</span><span style=\"color:#E0DEF4;font-style:italic\">children</span><span style=\"color:#908CAA\">}</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86\">    &#x3C;/</span><span style=\"color:#9CCFD8\">ProvideDevStateRenderers</span><span style=\"color:#6E6A86\">></span></span>\n<span class=\"line\"><span style=\"color:#E0DEF4\">  )</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">};</span></span></code></pre>\n  </div>\n  \n</div>\n<pre data-lang=\"tsx\" style=\"background-color:#2b303b;color:#c0c5ce;\" class=\"language-tsx \"><code class=\"language-tsx\" data-lang=\"tsx\"><span style=\"color:#b48ead;\">const </span><span style=\"color:#bf616a;\">RENDERERS</span><span>: RegistryItem&lt;any&gt;[] = [\n</span><span>  </span><span style=\"color:#8fa1b3;\">defineRenderer</span><span>(\n</span><span>    (</span><span style=\"color:#bf616a;\">thing</span><span>) </span><span style=\"color:#b48ead;\">=&gt; </span><span style=\"color:#bf616a;\">thing </span><span>instanceof DevString,\n</span><span>    ({ </span><span style=\"color:#bf616a;\">data</span><span>, </span><span style=\"color:#bf616a;\">r </span><span>}) </span><span style=\"color:#b48ead;\">=&gt; </span><span>&lt;</span><span style=\"color:#ebcb8b;\">r.Custom </span><span style=\"color:#d08770;\">name</span><span>=&quot;</span><span style=\"color:#a3be8c;\">DevString</span><span>&quot;&gt;...&lt;/</span><span style=\"color:#ebcb8b;\">r.Custom</span><span>&gt;,\n</span><span>  ),\n</span><span>  </span><span style=\"color:#8fa1b3;\">defineRenderer</span><span>(\n</span><span>    </span><span style=\"color:#8fa1b3;\">objectPredicateMacro</span><span>&lt;Queryable&gt;([&quot;</span><span style=\"color:#a3be8c;\">label</span><span>&quot;, &quot;</span><span style=\"color:#a3be8c;\">hash</span><span>&quot;], (</span><span style=\"color:#bf616a;\">d</span><span>) </span><span style=\"color:#b48ead;\">=&gt; </span><span style=\"color:#bf616a;\">d</span><span>.</span><span style=\"color:#bf616a;\">_tag </span><span>=== &quot;</span><span style=\"color:#a3be8c;\">db</span><span>&quot;),\n</span><span>    ({ </span><span style=\"color:#bf616a;\">data</span><span>, </span><span style=\"color:#bf616a;\">r </span><span>}) </span><span style=\"color:#b48ead;\">=&gt; </span><span>&lt;</span><span style=\"color:#ebcb8b;\">LiveQueryRenderer </span><span style=\"color:#d08770;\">query</span><span>=</span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">data</span><span style=\"color:#ab7967;\">} </span><span style=\"color:#d08770;\">r</span><span>=</span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">r</span><span style=\"color:#ab7967;\">} </span><span>/&gt;,\n</span><span>  ),\n</span><span>];\n</span><span>\n</span><span style=\"color:#b48ead;\">export const </span><span style=\"color:#8fa1b3;\">ProvideAppDevStateRenderers </span><span>= ({ </span><span style=\"color:#bf616a;\">children </span><span>}) </span><span style=\"color:#b48ead;\">=&gt; </span><span>(\n</span><span>  &lt;</span><span style=\"color:#ebcb8b;\">ProvideDevStateRenderers\n</span><span>    </span><span style=\"color:#d08770;\">registry</span><span>=</span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">RENDERERS</span><span style=\"color:#ab7967;\">}\n</span><span>    </span><span style=\"color:#d08770;\">stringRegistry</span><span>=</span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">STRING_RENDERERS</span><span style=\"color:#ab7967;\">}\n</span><span>    </span><span style=\"color:#d08770;\">orderedKeys</span><span>=</span><span style=\"color:#ab7967;\">{</span><span>[&quot;</span><span style=\"color:#a3be8c;\">devInfo</span><span>&quot;, &quot;</span><span style=\"color:#a3be8c;\">name</span><span>&quot;, &quot;</span><span style=\"color:#a3be8c;\">source</span><span>&quot;]</span><span style=\"color:#ab7967;\">}\n</span><span>  &gt;\n</span><span>    </span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">children</span><span style=\"color:#ab7967;\">}\n</span><span>  &lt;/</span><span style=\"color:#ebcb8b;\">ProvideDevStateRenderers</span><span>&gt;\n</span><span>);\n</span></code></pre>\n<h2 id=\"real-examples\">Real Examples <div data-loc=\"content/devstate-extensible-debug-renderer.md:93\" class=\"heading-src\">⋅</div></h2>\n<p><strong>Jotai Atoms</strong>: Read atom value and render live</p>\n<pre data-lang=\"tsx\" style=\"background-color:#2b303b;color:#c0c5ce;\" class=\"language-tsx \"><code class=\"language-tsx\" data-lang=\"tsx\"><span style=\"color:#8fa1b3;\">defineRenderer</span><span>(\n</span><span>  (</span><span style=\"color:#bf616a;\">thing</span><span>) </span><span style=\"color:#b48ead;\">=&gt; </span><span style=\"color:#8fa1b3;\">isAtom</span><span>(</span><span style=\"color:#bf616a;\">thing</span><span>),\n</span><span>  ({ </span><span style=\"color:#bf616a;\">data</span><span>, </span><span style=\"color:#bf616a;\">r </span><span>}) </span><span style=\"color:#b48ead;\">=&gt; </span><span>{\n</span><span>    </span><span style=\"color:#b48ead;\">const </span><span style=\"color:#bf616a;\">value </span><span>= </span><span style=\"color:#8fa1b3;\">useAtomValue</span><span>(</span><span style=\"color:#bf616a;\">data</span><span>);\n</span><span>    </span><span style=\"color:#b48ead;\">return </span><span>&lt;</span><span style=\"color:#ebcb8b;\">r.Unknown </span><span style=\"color:#d08770;\">name</span><span>=</span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">data</span><span>.</span><span style=\"color:#bf616a;\">debugLabel</span><span style=\"color:#ab7967;\">} </span><span style=\"color:#d08770;\">data</span><span>=</span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">value</span><span style=\"color:#ab7967;\">} </span><span>/&gt;;\n</span><span>  },\n</span><span>);\n</span></code></pre>\n<p><strong>DevString with location links</strong>:</p>\n<pre data-lang=\"tsx\" style=\"background-color:#2b303b;color:#c0c5ce;\" class=\"language-tsx \"><code class=\"language-tsx\" data-lang=\"tsx\"><span style=\"color:#8fa1b3;\">defineRenderer</span><span>(\n</span><span>  (</span><span style=\"color:#bf616a;\">thing</span><span>) </span><span style=\"color:#b48ead;\">=&gt; </span><span style=\"color:#bf616a;\">thing </span><span>instanceof DevString,\n</span><span>  ({ </span><span style=\"color:#bf616a;\">data</span><span>, </span><span style=\"color:#bf616a;\">r</span><span>, </span><span style=\"color:#bf616a;\">showPath </span><span>}) </span><span style=\"color:#b48ead;\">=&gt; </span><span>{\n</span><span>    </span><span style=\"color:#b48ead;\">const </span><span>{ </span><span style=\"color:#bf616a;\">context</span><span>, </span><span style=\"color:#bf616a;\">message </span><span>} = </span><span style=\"color:#bf616a;\">data</span><span>.</span><span style=\"color:#8fa1b3;\">toJSON</span><span>();\n</span><span>    </span><span style=\"color:#b48ead;\">return </span><span>(\n</span><span>      &lt;</span><span style=\"color:#ebcb8b;\">r.Custom </span><span style=\"color:#d08770;\">name</span><span>=&quot;</span><span style=\"color:#a3be8c;\">DevString</span><span>&quot; </span><span style=\"color:#d08770;\">nameChild</span><span>=</span><span style=\"color:#ab7967;\">{</span><span>&lt;</span><span style=\"color:#ebcb8b;\">DevStringLink </span><span style=\"color:#d08770;\">reason</span><span>=</span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">data</span><span style=\"color:#ab7967;\">} </span><span>/&gt;</span><span style=\"color:#ab7967;\">}</span><span>&gt;\n</span><span>        </span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">message</span><span>.</span><span style=\"color:#8fa1b3;\">map</span><span>((</span><span style=\"color:#bf616a;\">part</span><span>, </span><span style=\"color:#bf616a;\">i</span><span>) </span><span style=\"color:#b48ead;\">=&gt;\n</span><span>          typeof </span><span style=\"color:#bf616a;\">part </span><span>=== &quot;</span><span style=\"color:#a3be8c;\">string</span><span>&quot; ? &lt;</span><span style=\"color:#bf616a;\">span</span><span>&gt;</span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">part</span><span style=\"color:#ab7967;\">}</span><span>&lt;/</span><span style=\"color:#bf616a;\">span</span><span>&gt; : &lt;</span><span style=\"color:#ebcb8b;\">r.Unknown </span><span style=\"color:#d08770;\">data</span><span>=</span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">part</span><span style=\"color:#ab7967;\">} </span><span style=\"color:#d08770;\">showPath</span><span>=</span><span style=\"color:#ab7967;\">{</span><span>[&quot;</span><span style=\"color:#a3be8c;\">msg</span><span>&quot;]</span><span style=\"color:#ab7967;\">} </span><span>/&gt;,\n</span><span>        )</span><span style=\"color:#ab7967;\">}\n</span><span>        </span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">data</span><span>.</span><span style=\"color:#bf616a;\">cause </span><span>&amp;&amp; &lt;</span><span style=\"color:#ebcb8b;\">r.Unknown </span><span style=\"color:#d08770;\">name</span><span>=&quot;</span><span style=\"color:#a3be8c;\">Reason</span><span>&quot; </span><span style=\"color:#d08770;\">data</span><span>=</span><span style=\"color:#ab7967;\">{</span><span style=\"color:#bf616a;\">data</span><span>.</span><span style=\"color:#bf616a;\">cause</span><span style=\"color:#ab7967;\">} </span><span>/&gt;</span><span style=\"color:#ab7967;\">}\n</span><span>      &lt;/</span><span style=\"color:#ebcb8b;\">r.Custom</span><span>&gt;\n</span><span>    );\n</span><span>  },\n</span><span>);\n</span></code></pre>\n<h2 id=\"key-features\">Key Features <div data-loc=\"content/devstate-extensible-debug-renderer.md:126\" class=\"heading-src\">⋅</div></h2>\n<ul>\n<li><strong>Type-safe matching</strong>: Type guards ensure correct data shape</li>\n<li><strong>Composable</strong>: Renderers use <code>r.*</code> components to recurse</li>\n<li><strong>React hooks</strong>: Use atoms, queries, state inside renderers</li>\n<li><strong>Depth tracking</strong>: Automatic collapse at configurable depth</li>\n<li><strong>Circular detection</strong>: Prevents infinite recursion</li>\n<li><strong>Ordered keys</strong>: Control property display order globally</li>\n</ul>\n",
      "permalink": "/devstate-extensible-debug-renderer/",
      "slug": "devstate-extensible-debug-renderer",
      "ancestors": [
        "_index.md"
      ],
      "title": "DevState: Extensible Debug Renderer",
      "description": null,
      "updated": null,
      "date": "2025-10-21",
      "year": 2025,
      "month": 10,
      "day": 21,
      "taxonomies": {},
      "authors": [],
      "extra": {},
      "path": "/devstate-extensible-debug-renderer/",
      "components": [
        "devstate-extensible-debug-renderer"
      ],
      "summary": null,
      "toc": [
        {
          "level": 1,
          "id": "devstate-extensible-debug-renderer",
          "permalink": "/devstate-extensible-debug-renderer/#devstate-extensible-debug-renderer",
          "title": "DevState: Extensible Debug Renderer ⋅",
          "children": [
            {
              "level": 2,
              "id": "registry-pattern",
              "permalink": "/devstate-extensible-debug-renderer/#registry-pattern",
              "title": "Registry Pattern ⋅",
              "children": []
            },
            {
              "level": 2,
              "id": "renderer-components",
              "permalink": "/devstate-extensible-debug-renderer/#renderer-components",
              "title": "Renderer Components ⋅",
              "children": []
            },
            {
              "level": 2,
              "id": "integration",
              "permalink": "/devstate-extensible-debug-renderer/#integration",
              "title": "Integration ⋅",
              "children": []
            },
            {
              "level": 2,
              "id": "real-examples",
              "permalink": "/devstate-extensible-debug-renderer/#real-examples",
              "title": "Real Examples ⋅",
              "children": []
            },
            {
              "level": 2,
              "id": "key-features",
              "permalink": "/devstate-extensible-debug-renderer/#key-features",
              "title": "Key Features ⋅",
              "children": []
            }
          ]
        }
      ],
      "word_count": 237,
      "reading_time": 2,
      "assets": [],
      "draft": true,
      "lang": "en",
      "lower": null,
      "higher": null,
      "translations": [],
      "backlinks": []
    },
    "higher": {
      "relative_path": "click-to-source.md",
      "colocated_path": null,
      "content": "<h1 id=\"click-to-source-embedding-runtime-locations\">Click to Source: Embedding Runtime Locations <div data-loc=\"content/click-to-source.md:10\" class=\"heading-src\">⋅</div></h1>\n\n<p>Build-time transforms inject source locations into runtime code. Alt+Click any element to jump to its source file.</p>\n<h2 id=\"try-it\">Try It <div data-loc=\"content/click-to-source.md:19\" class=\"heading-src\">⋅</div></h2>\n<div data-loc=\"content/click-to-source.md:21\" id=\"click-to-source-demo\">\nHold Alt and hover over any button, then click to view its source. The mock console shows what file would open, and the viewer displays the source code.\n</div>\n<script src=\"/js/click-to-source-demo.entrypoint.js\" type=\"module\"></script>\n<h2 id=\"react-component-locations\">React Component Locations <div data-loc=\"content/click-to-source.md:26\" class=\"heading-src\">⋅</div></h2>\n<p>Transform <code>$</code> prop shorthand into <code>data-loc</code> attributes:</p>\n<div class=\"codeblock\" data-loc=\"content&#x2F;click-to-source.md:30\">\n  \n  <div class=\"codeblock-content\" data-id=\"react-loc-transform\">\n    <pre class=\"shiki rose-pine\" style=\"background-color:#191724;color:#e0def4\" tabindex=\"0\" data-loc=\"tools/bun-dev-and-react-$-className-loc.mts:35\"><code><span class=\"line\"><span style=\"color:#31748F\">if</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#E0DEF4;font-style:italic\">enableReact$Loc</span><span style=\"color:#31748F\"> &#x26;&#x26;</span><span style=\"color:#E0DEF4;font-style:italic\"> path</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">endsWith</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#F6C177\">\".tsx\"</span><span style=\"color:#E0DEF4\">)) </span><span style=\"color:#908CAA\">{</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  for</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#31748F\">const</span><span style=\"color:#E0DEF4;font-style:italic\"> match</span><span style=\"color:#31748F\"> of</span><span style=\"color:#E0DEF4;font-style:italic\"> code</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">matchAll</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">CLASSNAME_$_RE</span><span style=\"color:#E0DEF4\">)) </span><span style=\"color:#908CAA\">{</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    const</span><span style=\"color:#E0DEF4;font-style:italic\"> index</span><span style=\"color:#31748F\"> =</span><span style=\"color:#E0DEF4;font-style:italic\"> match</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">index</span><span style=\"color:#31748F\">!</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    const</span><span style=\"color:#908CAA\"> [</span><span style=\"color:#E0DEF4;font-style:italic\">_</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> pre</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> post</span><span style=\"color:#908CAA\">]</span><span style=\"color:#31748F\"> =</span><span style=\"color:#E0DEF4;font-style:italic\"> match</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    const</span><span style=\"color:#908CAA\"> {</span><span style=\"color:#E0DEF4;font-style:italic\"> line</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> column</span><span style=\"color:#908CAA\"> }</span><span style=\"color:#31748F\"> =</span><span style=\"color:#EBBCBA\"> indexToLineAndColumn</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">code</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> index</span><span style=\"color:#31748F\"> +</span><span style=\"color:#E0DEF4;font-style:italic\"> pre</span><span style=\"color:#31748F\">.</span><span style=\"color:#9CCFD8\">length</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#31748F\">    const</span><span style=\"color:#E0DEF4;font-style:italic\"> link</span><span style=\"color:#31748F\"> =</span><span style=\"color:#F6C177\"> `</span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">path</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">slice</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">inCodebase</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">index</span><span style=\"color:#31748F\"> +</span><span style=\"color:#E0DEF4;font-style:italic\"> IN_CODEBASE_SLICE</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\">:</span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">line</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\">:</span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">column</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\">`</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    let</span><span style=\"color:#E0DEF4;font-style:italic\"> dataAttrs</span><span style=\"color:#31748F\"> =</span><span style=\"color:#F6C177\"> `data-loc=</span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">JSON</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">stringify</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">link</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\">`</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    if</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#E0DEF4;font-style:italic\">pre</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">startsWith</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#F6C177\">\"&#x3C;\"</span><span style=\"color:#E0DEF4\">)) </span><span style=\"color:#908CAA\">{</span></span>\n<span class=\"line\"><span style=\"color:#E0DEF4;font-style:italic\">      dataAttrs</span><span style=\"color:#31748F\"> =</span><span style=\"color:#F6C177\"> `</span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">dataAttrs</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\"> data-name=</span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">JSON</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">stringify</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">pre</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">split</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#F6C177\">\" \"</span><span style=\"color:#E0DEF4\">)[</span><span style=\"color:#EBBCBA\">0</span><span style=\"color:#E0DEF4\">]</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">slice</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#EBBCBA\">1</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">trim</span><span style=\"color:#E0DEF4\">())</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\">`</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#31748F\">    if</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#E0DEF4;font-style:italic\">post</span><span style=\"color:#31748F\"> ===</span><span style=\"color:#F6C177\"> \"=\"</span><span style=\"color:#E0DEF4\">) </span><span style=\"color:#908CAA\">{</span></span>\n<span class=\"line\"><span style=\"color:#E0DEF4;font-style:italic\">      string</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">overwrite</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">index</span><span style=\"color:#31748F\"> +</span><span style=\"color:#E0DEF4;font-style:italic\"> pre</span><span style=\"color:#31748F\">.</span><span style=\"color:#9CCFD8\">length</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> index</span><span style=\"color:#31748F\"> +</span><span style=\"color:#E0DEF4;font-style:italic\"> _</span><span style=\"color:#31748F\">.</span><span style=\"color:#9CCFD8\">length</span><span style=\"color:#31748F\"> -</span><span style=\"color:#E0DEF4;font-style:italic\"> post</span><span style=\"color:#31748F\">.</span><span style=\"color:#9CCFD8\">length</span><span style=\"color:#908CAA\">,</span><span style=\"color:#F6C177\"> `</span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">dataAttrs</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\"> className`</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">    }</span><span style=\"color:#31748F\"> else</span><span style=\"color:#908CAA\"> {</span></span>\n<span class=\"line\"><span style=\"color:#E0DEF4;font-style:italic\">      string</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">overwrite</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">index</span><span style=\"color:#31748F\"> +</span><span style=\"color:#E0DEF4;font-style:italic\"> pre</span><span style=\"color:#31748F\">.</span><span style=\"color:#9CCFD8\">length</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> index</span><span style=\"color:#31748F\"> +</span><span style=\"color:#E0DEF4;font-style:italic\"> _</span><span style=\"color:#31748F\">.</span><span style=\"color:#9CCFD8\">length</span><span style=\"color:#31748F\"> -</span><span style=\"color:#E0DEF4;font-style:italic\"> post</span><span style=\"color:#31748F\">.</span><span style=\"color:#9CCFD8\">length</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> dataAttrs</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">    }</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">  }</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">}</span></span></code></pre>\n  </div>\n  \n</div>\n<pre data-lang=\"tsx\" style=\"background-color:#2b303b;color:#c0c5ce;\" class=\"language-tsx \"><code class=\"language-tsx\" data-lang=\"tsx\"><span>  </span><span style=\"color:#65737e;\">// Input:\n</span><span>  &lt;</span><span style=\"color:#bf616a;\">div </span><span style=\"color:#d08770;\">$</span><span>=&quot;</span><span style=\"color:#a3be8c;\">card</span><span>&quot;&gt;Content&lt;/</span><span style=\"color:#bf616a;\">div</span><span>&gt;\n</span><span>\n</span><span>  </span><span style=\"color:#65737e;\">// Output (at build):\n</span><span>  &lt;</span><span style=\"color:#bf616a;\">div </span><span style=\"color:#d08770;\">data-loc</span><span>=&quot;</span><span style=\"color:#a3be8c;\">components/Card.tsx:26:7</span><span>&quot; </span><span style=\"color:#d08770;\">className</span><span>=&quot;</span><span style=\"color:#a3be8c;\">card</span><span>&quot;&gt;Content&lt;/</span><span style=\"color:#bf616a;\">div</span><span>&gt;\n</span></code></pre>\n<p>Runtime handler walks React fiber tree and opens the file:</p>\n<div class=\"codeblock\" data-loc=\"content&#x2F;click-to-source.md:42\">\n  \n  <div class=\"codeblock-content\" data-id=\"click-to-source-handler\">\n    <pre class=\"shiki rose-pine\" style=\"background-color:#191724;color:#e0def4\" tabindex=\"0\" data-loc=\"scripts/lib/dev/click-to-source.client.ts:156\"><code><span class=\"line\"><span style=\"color:#31748F\">const</span><span style=\"color:#EBBCBA;font-style:italic\"> getPath</span><span style=\"color:#31748F\"> =</span><span style=\"color:#908CAA\"> (</span><span style=\"color:#C4A7E7;font-style:italic\">fiber</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> Fiber</span><span style=\"color:#908CAA\">,</span><span style=\"color:#C4A7E7;font-style:italic\"> element</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> HTMLElement</span><span style=\"color:#31748F\"> |</span><span style=\"color:#9CCFD8\"> undefined</span><span style=\"color:#908CAA\">)</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> string</span><span style=\"color:#31748F\"> |</span><span style=\"color:#9CCFD8\"> undefined</span><span style=\"color:#31748F\"> =></span><span style=\"color:#908CAA\"> {</span></span>\n<span class=\"line\"><span style=\"color:#908CAA;font-style:italic\">  //</span><span style=\"color:#6E6A86;font-style:italic\"> First check for data-loc attribute if element is provided</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  if</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#E0DEF4;font-style:italic\">element</span><span style=\"color:#31748F\">?.</span><span style=\"color:#E0DEF4;font-style:italic\">dataset</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">loc</span><span style=\"color:#E0DEF4\">) </span><span style=\"color:#908CAA\">{</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    return</span><span style=\"color:#E0DEF4;font-style:italic\"> element</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">dataset</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">loc</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">  }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#31748F\">  const</span><span style=\"color:#E0DEF4;font-style:italic\"> source</span><span style=\"color:#31748F\"> =</span><span style=\"color:#E0DEF4;font-style:italic\"> fiber</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">_debugSource</span><span style=\"color:#31748F\"> ??</span><span style=\"color:#E0DEF4;font-style:italic\"> fiber</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">_debugInfo</span><span style=\"color:#31748F\"> ??</span><span style=\"color:#E0DEF4;font-style:italic\"> fiber</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">_source</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#31748F\">  if</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#31748F\">!</span><span style=\"color:#E0DEF4;font-style:italic\">source</span><span style=\"color:#E0DEF4\">) </span><span style=\"color:#31748F\">return</span><span style=\"color:#EBBCBA\"> undefined</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#31748F\">  const</span><span style=\"color:#908CAA\"> {</span><span style=\"color:#E0DEF4;font-style:italic\"> fileName</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> lineNumber</span><span style=\"color:#31748F\"> =</span><span style=\"color:#EBBCBA\"> 1</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> columnNumber</span><span style=\"color:#31748F\"> =</span><span style=\"color:#EBBCBA\"> 1</span><span style=\"color:#908CAA\"> }</span><span style=\"color:#31748F\"> =</span><span style=\"color:#E0DEF4;font-style:italic\"> source</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  return</span><span style=\"color:#F6C177\"> `</span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">fileName</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\">:</span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">lineNumber</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\">:</span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">columnNumber</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\">`</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">};</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#31748F\">const</span><span style=\"color:#EBBCBA;font-style:italic\"> getLayersForElement</span><span style=\"color:#31748F\"> =</span><span style=\"color:#908CAA\"> (</span><span style=\"color:#C4A7E7;font-style:italic\">element</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> HTMLElement</span><span style=\"color:#908CAA\">,</span><span style=\"color:#C4A7E7;font-style:italic\"> root</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> string</span><span style=\"color:#908CAA\">)</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> ComponentLayer</span><span style=\"color:#E0DEF4\">[] </span><span style=\"color:#31748F\">=></span><span style=\"color:#908CAA\"> {</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  let</span><span style=\"color:#E0DEF4;font-style:italic\"> instance</span><span style=\"color:#31748F\"> =</span><span style=\"color:#EBBCBA\"> getReactInstanceForElement</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">element</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  const</span><span style=\"color:#E0DEF4;font-style:italic\"> layers</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> ComponentLayer</span><span style=\"color:#E0DEF4\">[] </span><span style=\"color:#31748F\">=</span><span style=\"color:#E0DEF4\"> []</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#31748F\">  while</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#E0DEF4;font-style:italic\">instance</span><span style=\"color:#E0DEF4\">) </span><span style=\"color:#908CAA\">{</span></span>\n<span class=\"line\"><span style=\"color:#908CAA;font-style:italic\">    //</span><span style=\"color:#6E6A86;font-style:italic\"> Try to find the DOM element for this fiber to check for data-loc</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    const</span><span style=\"color:#E0DEF4;font-style:italic\"> fiberElement</span><span style=\"color:#31748F\"> =</span><span style=\"color:#E0DEF4;font-style:italic\"> instance</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">stateNode</span><span style=\"color:#31748F\"> instanceof</span><span style=\"color:#9CCFD8\"> HTMLElement</span><span style=\"color:#31748F\"> ?</span><span style=\"color:#E0DEF4;font-style:italic\"> instance</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">stateNode</span><span style=\"color:#31748F\"> :</span><span style=\"color:#EBBCBA\"> undefined</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    const</span><span style=\"color:#E0DEF4;font-style:italic\"> path</span><span style=\"color:#31748F\"> =</span><span style=\"color:#EBBCBA\"> getPath</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">instance</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> fiberElement</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    if</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#E0DEF4;font-style:italic\">path</span><span style=\"color:#E0DEF4\">) </span><span style=\"color:#908CAA\">{</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">      const</span><span style=\"color:#E0DEF4;font-style:italic\"> name</span><span style=\"color:#31748F\"> =</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">        typeof</span><span style=\"color:#E0DEF4;font-style:italic\"> instance</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">type</span><span style=\"color:#31748F\"> ===</span><span style=\"color:#F6C177\"> \"string\"</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">          ?</span><span style=\"color:#E0DEF4;font-style:italic\"> instance</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">type</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">          :</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#E0DEF4;font-style:italic\">instance</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">type</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">displayName</span><span style=\"color:#31748F\"> ??</span><span style=\"color:#E0DEF4;font-style:italic\"> instance</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">type</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">name</span><span style=\"color:#31748F\"> ??</span><span style=\"color:#E0DEF4;font-style:italic\"> instance</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">type</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">render</span><span style=\"color:#31748F\">?.</span><span style=\"color:#E0DEF4;font-style:italic\">name</span><span style=\"color:#31748F\"> ??</span><span style=\"color:#F6C177\"> \"undefined\"</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#E0DEF4;font-style:italic\">      layers</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">push</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#908CAA\">{</span><span style=\"color:#E0DEF4;font-style:italic\"> name</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4\"> path</span><span style=\"color:#908CAA\">:</span><span style=\"color:#E0DEF4;font-style:italic\"> path</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">replace</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#F6C177\">`</span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">root</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\">/`</span><span style=\"color:#908CAA\">,</span><span style=\"color:#F6C177\"> \"\"</span><span style=\"color:#E0DEF4\">) </span><span style=\"color:#908CAA\">}</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">    }</span></span>\n<span class=\"line\"><span style=\"color:#E0DEF4;font-style:italic\">    instance</span><span style=\"color:#31748F\"> =</span><span style=\"color:#E0DEF4;font-style:italic\"> instance</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">_debugOwner</span><span style=\"color:#31748F\"> ??</span><span style=\"color:#EBBCBA\"> undefined</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">  }</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  return</span><span style=\"color:#E0DEF4;font-style:italic\"> layers</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">};</span></span></code></pre>\n  </div>\n  \n</div>\n<h2 id=\"devstring-locations\">DevString Locations <div data-loc=\"content/click-to-source.md:44\" class=\"heading-src\">⋅</div></h2>\n<p>Tagged templates automatically capture call-site locations:</p>\n<div class=\"codeblock\" data-loc=\"content&#x2F;click-to-source.md:48\">\n  \n  <div class=\"codeblock-content\" data-id=\"devstring-transform\">\n    <pre class=\"shiki rose-pine\" style=\"background-color:#191724;color:#e0def4\" tabindex=\"0\" data-loc=\"tools/bun-dev-and-react-$-className-loc.mts:23\"><code><span class=\"line\"><span style=\"color:#31748F\">if</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#E0DEF4;font-style:italic\">enableDevLoc</span><span style=\"color:#E0DEF4\">) </span><span style=\"color:#908CAA\">{</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  for</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#31748F\">const</span><span style=\"color:#E0DEF4;font-style:italic\"> match</span><span style=\"color:#31748F\"> of</span><span style=\"color:#E0DEF4;font-style:italic\"> code</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">matchAll</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">DEV_RE</span><span style=\"color:#E0DEF4\">)) </span><span style=\"color:#908CAA\">{</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    const</span><span style=\"color:#E0DEF4;font-style:italic\"> index</span><span style=\"color:#31748F\"> =</span><span style=\"color:#E0DEF4;font-style:italic\"> match</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">index</span><span style=\"color:#31748F\">!</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    const</span><span style=\"color:#908CAA\"> {</span><span style=\"color:#E0DEF4;font-style:italic\"> line</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> column</span><span style=\"color:#908CAA\"> }</span><span style=\"color:#31748F\"> =</span><span style=\"color:#EBBCBA\"> indexToLineAndColumn</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">code</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> index</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    const</span><span style=\"color:#908CAA\"> [</span><span style=\"color:#E0DEF4;font-style:italic\">_</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> fn</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> message</span><span style=\"color:#908CAA\">]</span><span style=\"color:#31748F\"> =</span><span style=\"color:#E0DEF4;font-style:italic\"> match</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">    const</span><span style=\"color:#E0DEF4;font-style:italic\"> link</span><span style=\"color:#31748F\"> =</span><span style=\"color:#F6C177\"> `</span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">path</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">slice</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">inCodebase</span><span style=\"color:#31748F\">.</span><span style=\"color:#E0DEF4;font-style:italic\">index</span><span style=\"color:#31748F\"> +</span><span style=\"color:#E0DEF4;font-style:italic\"> IN_CODEBASE_SLICE</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\">:</span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">line</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\">:</span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">column</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\">`</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#E0DEF4;font-style:italic\">    string</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">overwrite</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">index</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> index</span><span style=\"color:#31748F\"> +</span><span style=\"color:#E0DEF4;font-style:italic\"> _</span><span style=\"color:#31748F\">.</span><span style=\"color:#9CCFD8\">length</span><span style=\"color:#908CAA\">,</span><span style=\"color:#F6C177\"> `</span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">fn</span><span style=\"color:#908CAA\">}</span><span style=\"color:#31748F\">\\`</span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">message</span><span style=\"color:#908CAA\">}</span><span style=\"color:#31748F\">\\`</span><span style=\"color:#F6C177\">.ctx({ loc: </span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">JSON</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">stringify</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#E0DEF4;font-style:italic\">link</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\"> })`</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">  }</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">}</span></span></code></pre>\n  </div>\n  \n</div>\n<pre data-lang=\"typescript\" style=\"background-color:#2b303b;color:#c0c5ce;\" class=\"language-typescript \"><code class=\"language-typescript\" data-lang=\"typescript\"><span style=\"color:#65737e;\">// Input:\n</span><span style=\"color:#8fa1b3;\">handler</span><span>(</span><span style=\"color:#8fa1b3;\">dev</span><span>`</span><span style=\"color:#a3be8c;\">User pressed X</span><span>`);\n</span><span>\n</span><span style=\"color:#65737e;\">// Output (at build):\n</span><span style=\"color:#8fa1b3;\">handler</span><span>(</span><span style=\"color:#8fa1b3;\">dev</span><span>`</span><span style=\"color:#a3be8c;\">User pressed X</span><span>`.</span><span style=\"color:#8fa1b3;\">ctx</span><span>({ loc: &quot;</span><span style=\"color:#a3be8c;\">Card.tsx:83:12</span><span>&quot; }));\n</span></code></pre>\n<p><strong>Usage:</strong></p>\n<pre data-lang=\"typescript\" style=\"background-color:#2b303b;color:#c0c5ce;\" class=\"language-typescript \"><code class=\"language-typescript\" data-lang=\"typescript\"><span style=\"color:#b48ead;\">function </span><span style=\"color:#8fa1b3;\">deleteCard</span><span>(</span><span style=\"color:#bf616a;\">reason</span><span>: DevString) {\n</span><span>  </span><span style=\"color:#ebcb8b;\">console</span><span>.</span><span style=\"color:#96b5b4;\">log</span><span>(&quot;</span><span style=\"color:#a3be8c;\">Called from:</span><span>&quot;, </span><span style=\"color:#bf616a;\">reason</span><span>.</span><span style=\"color:#8fa1b3;\">toJSON</span><span>().</span><span style=\"color:#bf616a;\">context</span><span>.</span><span style=\"color:#bf616a;\">loc</span><span>);\n</span><span>}\n</span><span>\n</span><span style=\"color:#8fa1b3;\">deleteCard</span><span>(</span><span style=\"color:#8fa1b3;\">dev</span><span>`</span><span style=\"color:#a3be8c;\">User clicked delete</span><span>`);\n</span><span style=\"color:#65737e;\">// Automatically knows source location\n</span></code></pre>\n<p>DevStrings compose with <code>.because()</code> to build audit trails:</p>\n<pre data-lang=\"typescript\" style=\"background-color:#2b303b;color:#c0c5ce;\" class=\"language-typescript \"><code class=\"language-typescript\" data-lang=\"typescript\"><span style=\"color:#b48ead;\">const </span><span style=\"color:#bf616a;\">rootEvent </span><span>= </span><span style=\"color:#8fa1b3;\">dev</span><span>`</span><span style=\"color:#a3be8c;\">keydown from root</span><span>`;\n</span><span style=\"color:#8fa1b3;\">handler</span><span>(</span><span style=\"color:#bf616a;\">rootEvent</span><span>.</span><span style=\"color:#8fa1b3;\">because</span><span>(</span><span style=\"color:#8fa1b3;\">dev</span><span>`</span><span style=\"color:#a3be8c;\">Looking for action handler</span><span>`));\n</span><span style=\"color:#65737e;\">// Creates chain: &quot;keydown from root → Looking for action handler&quot;\n</span></code></pre>\n<h2 id=\"editor-integration\">Editor Integration <div data-loc=\"content/click-to-source.md:77\" class=\"heading-src\">⋅</div></h2>\n<p>Local HTTP endpoint opens files:</p>\n<div class=\"codeblock\" data-loc=\"content&#x2F;click-to-source.md:81\">\n  \n  <div class=\"codeblock-content\" data-id=\"open-in-editor\">\n    <pre class=\"shiki rose-pine\" style=\"background-color:#191724;color:#e0def4\" tabindex=\"0\" data-loc=\"scripts/lib/dev/openInDevEditor.ts:1\"><code><span class=\"line\"><span style=\"color:#31748F\">let</span><span style=\"color:#E0DEF4;font-style:italic\"> lastOpenedFile</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> string</span><span style=\"color:#31748F\"> |</span><span style=\"color:#9CCFD8\"> null</span><span style=\"color:#31748F\"> =</span><span style=\"color:#EBBCBA\"> null</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA;font-style:italic\">/**</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * uses our launch-editor endpoint to open the file in the dev's editor</span></span>\n<span class=\"line\"><span style=\"color:#6E6A86;font-style:italic\"> * this has been set up as a vite plugin.</span></span>\n<span class=\"line\"><span style=\"color:#908CAA;font-style:italic\"> */</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">export</span><span style=\"color:#31748F\"> const</span><span style=\"color:#EBBCBA;font-style:italic\"> openInDevEditor</span><span style=\"color:#31748F\"> =</span><span style=\"color:#908CAA\"> (</span><span style=\"color:#C4A7E7;font-style:italic\">loc</span><span style=\"color:#31748F\">:</span><span style=\"color:#9CCFD8\"> string</span><span style=\"color:#908CAA\">)</span><span style=\"color:#31748F\"> =></span><span style=\"color:#908CAA\"> {</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  if</span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#E0DEF4;font-style:italic\">lastOpenedFile</span><span style=\"color:#31748F\"> ===</span><span style=\"color:#E0DEF4;font-style:italic\"> loc</span><span style=\"color:#E0DEF4\">) </span><span style=\"color:#31748F\">return</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#E0DEF4;font-style:italic\">  lastOpenedFile</span><span style=\"color:#31748F\"> =</span><span style=\"color:#E0DEF4;font-style:italic\"> loc</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#EBBCBA\">  setTimeout</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#908CAA\">()</span><span style=\"color:#31748F\"> =></span><span style=\"color:#E0DEF4\"> (</span><span style=\"color:#E0DEF4;font-style:italic\">lastOpenedFile</span><span style=\"color:#31748F\"> =</span><span style=\"color:#EBBCBA\"> null</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">,</span><span style=\"color:#EBBCBA\"> 500</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#31748F\">  void</span><span style=\"color:#EBBCBA\"> fetch</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#F6C177\">`http://localhost:5090/__open-in-editor?file=</span><span style=\"color:#908CAA\">${</span><span style=\"color:#E0DEF4;font-style:italic\">loc</span><span style=\"color:#908CAA\">}</span><span style=\"color:#F6C177\">`</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">catch</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#908CAA\">(</span><span style=\"color:#C4A7E7;font-style:italic\">error</span><span style=\"color:#908CAA\">)</span><span style=\"color:#31748F\"> =></span><span style=\"color:#908CAA\"> {</span></span>\n<span class=\"line\"><span style=\"color:#E0DEF4;font-style:italic\">    console</span><span style=\"color:#31748F\">.</span><span style=\"color:#EBBCBA\">error</span><span style=\"color:#E0DEF4\">(</span><span style=\"color:#F6C177\">\"Failed to open in editor\"</span><span style=\"color:#908CAA\">,</span><span style=\"color:#E0DEF4;font-style:italic\"> error</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">  }</span><span style=\"color:#E0DEF4\">)</span><span style=\"color:#908CAA\">;</span></span>\n<span class=\"line\"><span style=\"color:#908CAA\">};</span></span></code></pre>\n  </div>\n  \n</div>\n<pre data-lang=\"typescript\" style=\"background-color:#2b303b;color:#c0c5ce;\" class=\"language-typescript \"><code class=\"language-typescript\" data-lang=\"typescript\"><span style=\"color:#8fa1b3;\">openInDevEditor</span><span>(&quot;</span><span style=\"color:#a3be8c;\">Card.tsx:26:7</span><span>&quot;);\n</span><span>  ↓\n</span><span style=\"color:#8fa1b3;\">fetch</span><span>(&quot;</span><span style=\"color:#a3be8c;\">http://localhost:5090/__open-in-editor?file=Card.tsx:26:7</span><span>&quot;);\n</span><span>  ↓\n</span><span style=\"color:#65737e;\">// Editor opens at that line\n</span></code></pre>\n<h2 id=\"setup\">Setup <div data-loc=\"content/click-to-source.md:91\" class=\"heading-src\">⋅</div></h2>\n<p><strong>Build plugin:</strong></p>\n<pre data-lang=\"typescript\" style=\"background-color:#2b303b;color:#c0c5ce;\" class=\"language-typescript \"><code class=\"language-typescript\" data-lang=\"typescript\"><span style=\"color:#b48ead;\">import </span><span>{ </span><span style=\"color:#bf616a;\">bunDevStringAndReact$ClassNameLoc </span><span>} </span><span style=\"color:#b48ead;\">from </span><span>&quot;</span><span style=\"color:#a3be8c;\">./bun-dev-and-react-$-className-loc.mts</span><span>&quot;;\n</span><span>\n</span><span style=\"color:#b48ead;\">await </span><span style=\"color:#bf616a;\">Bun</span><span>.</span><span style=\"color:#8fa1b3;\">build</span><span>({\n</span><span>  plugins: [\n</span><span>    </span><span style=\"color:#8fa1b3;\">bunDevStringAndReact$ClassNameLoc</span><span>({\n</span><span>      enableReact$Loc: </span><span style=\"color:#d08770;\">true</span><span>,\n</span><span>      enableDevLoc: </span><span style=\"color:#d08770;\">true</span><span>,\n</span><span>    }),\n</span><span>  ],\n</span><span>});\n</span></code></pre>\n<p><strong>Runtime:</strong></p>\n<pre data-lang=\"typescript\" style=\"background-color:#2b303b;color:#c0c5ce;\" class=\"language-typescript \"><code class=\"language-typescript\" data-lang=\"typescript\"><span style=\"color:#b48ead;\">import </span><span>{ </span><span style=\"color:#bf616a;\">initClickToSource </span><span>} </span><span style=\"color:#b48ead;\">from </span><span>&quot;</span><span style=\"color:#a3be8c;\">./click-to-source.client.ts</span><span>&quot;;\n</span><span>\n</span><span style=\"color:#b48ead;\">if </span><span>(</span><span style=\"color:#b48ead;\">import</span><span>.meta.</span><span style=\"color:#bf616a;\">env</span><span>.</span><span style=\"color:#bf616a;\">DEV</span><span>) {\n</span><span>  </span><span style=\"color:#8fa1b3;\">initClickToSource</span><span>();\n</span><span>}\n</span></code></pre>\n",
      "permalink": "/click-to-source/",
      "slug": "click-to-source",
      "ancestors": [
        "_index.md"
      ],
      "title": "Click to Source: Embedding Runtime Locations",
      "description": null,
      "updated": null,
      "date": "2025-10-20",
      "year": 2025,
      "month": 10,
      "day": 20,
      "taxonomies": {},
      "authors": [],
      "extra": {
        "nav_section": "Moving Quickly",
        "nav_order": 1
      },
      "path": "/click-to-source/",
      "components": [
        "click-to-source"
      ],
      "summary": null,
      "toc": [
        {
          "level": 1,
          "id": "click-to-source-embedding-runtime-locations",
          "permalink": "/click-to-source/#click-to-source-embedding-runtime-locations",
          "title": "Click to Source: Embedding Runtime Locations ⋅",
          "children": [
            {
              "level": 2,
              "id": "try-it",
              "permalink": "/click-to-source/#try-it",
              "title": "Try It ⋅",
              "children": []
            },
            {
              "level": 2,
              "id": "react-component-locations",
              "permalink": "/click-to-source/#react-component-locations",
              "title": "React Component Locations ⋅",
              "children": []
            },
            {
              "level": 2,
              "id": "devstring-locations",
              "permalink": "/click-to-source/#devstring-locations",
              "title": "DevString Locations ⋅",
              "children": []
            },
            {
              "level": 2,
              "id": "editor-integration",
              "permalink": "/click-to-source/#editor-integration",
              "title": "Editor Integration ⋅",
              "children": []
            },
            {
              "level": 2,
              "id": "setup",
              "permalink": "/click-to-source/#setup",
              "title": "Setup ⋅",
              "children": []
            }
          ]
        }
      ],
      "word_count": 255,
      "reading_time": 2,
      "assets": [],
      "draft": false,
      "lang": "en",
      "lower": null,
      "higher": null,
      "translations": [],
      "backlinks": []
    },
    "translations": [],
    "backlinks": []
  },
  "zola_version": "0.21.0"
}