How PageSpy Recreates Chrome DevTools Console for Remote Web Debugging
This article explains PageSpy’s design and implementation for a remote web debugging console that mirrors Chrome DevTools, detailing how it handles complex objects, serialization challenges, getters, self‑references, and prototype‑chain inspection using a custom Atom data model.
You might not need it now, but you should know this web remote debugging tool! The open‑source PageSpy reached over 100 stars in just two days—what are you waiting for? Click the homepage link to try it out.
Preface
Since PageSpy was open‑sourced, many users have asked how it works. This chapter dives into the implementation of the Console panel, which closely mimics Chrome DevTools and supports remote inspection of variables.
The Console panel reproduces the interaction experience of Chrome’s console, not only printing logs but also debugging variables of a remote web page.
We will explore how PageSpy handles the printing of the window object.
Design Goals
The original goal of the PageSpy Console panel was to match the browser’s Console panel. However, printing complex objects directly faces several problems:
Data types that cannot be serialized are lost.
Getter property values, which are computed dynamically, are lost.
Self‑referencing objects cause serialization errors.
It is impossible to traverse the prototype chain to view detailed data.
Each of these issues is crucial in its own scenario. Currently we can state:
Printing a complex object requires conversion before use.
Because further data inspection is needed, the converted data must be stored somewhere.
In PageSpy, the functionality that converts, stores, queries, and interacts with complex objects is encapsulated in an Atom class. Each complex object becomes an atom‑node with the following structure:
// SpyAtom.Overview
interface Overview {
id: string;
type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | 'null' | 'error' | 'debug-origin' | 'atom';
value: string | PropertyDescriptor;
__atomId?: string;
instanceId?: string;
}This 20‑line type definition is what enables printing of the window object.
Why focus on the window object? Because it exhibits all the aforementioned problems:
It contains non‑serializable properties such as global functions.
It has many Getter properties like outerWidth, innerWidth, location, etc.
It includes self‑references, e.g., window.self.
Its prototype chain needs to be traversed for detailed inspection.
Implementation
Printing simple (non‑complex) data does not encounter these issues; we only need to retrieve the value and its type for rendering.
After distinguishing complex from simple data, we extract the definition for complex types:
// SpyAtom.Overview
interface Overview {
id: string;
type: 'atom';
value: string | PropertyDescriptor;
__atomId: string;
instanceId: string;
}id : unique identifier for the data.
__atomId : when an object is printed, its entity is stored in the Atom’s storeMap with __atomId as the key.
instanceId : derived from __atomId; by default it equals __atomId and points to the current printed object. Child objects receive their own instanceId that points to the parent level.
In addition, a parentId field is used:
parentId : also derived from __atomId; it enables retrieval of Getter property values via
Object.getOwnPropertyDescriptor(store[parentId], key).get.call(store[instanceId]).
Conclusion
Reviewing the challenges of printing the window object, PageSpy provides solutions for each:
Non‑serializable property data?
By classifying data as “complex” and describing how it should be displayed based on its type and value.
Getter property dynamic computation lost?
Getter values are obtained by invoking the underlying function with the correct this context, using the stored instanceId and parentId.
Self‑reference objects cannot be serialized?
PageSpy converts only the current level; self‑references are lazily evaluated during inspection.
Inspecting data along the prototype chain?
Adding a [[Prototype]] descriptor allows users to click through the prototype chain for detailed views.
That concludes this deep dive into PageSpy’s implementation. Feel free to integrate it into your workflow and report issues on GitHub.
Reference Links
[1]Homepage: https://huolalatech.github.io/page-spy-web [2] GitHub Issues: https://github.com/HuolalaTech/page-spy-web/issues
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
