Runtime Introspector Bookmarklet — Executable Shell
RT
Runtime Introspector
Snapshot + streaming diagnostics for any live page
Instrumented shell · dormant until triggered
Bookmarklet as runtime shell
Executable introspector integrated into a fully rendered document
Live JSON stream · PerformanceObserver · MutationObserver
Preheader · engineered shell around your code

Upgraded, runtime‑grade bookmarklet — wrapped in an executable, dormant‑until‑trigger shell

This document takes your provided bookmarklet and specification, integrates it as a dormant runtime, and exposes a triggerable introspector that snapshots the page, observes it over time, and streams structured JSON into a dedicated inspection window.

Mode: Executable, dormant until trigger
Trigger: Button or Ctrl+Shift+I
Stream: JSON view, auto‑refresh
From bookmarklet to dormant introspection runtime

You asked for the entire content to be wrapped in a fully engineered HTML document with advanced CSS, schema‑correct semantics, runtime‑grade JavaScript, and the original bookmarklet code integrated in a way that remains dormant until explicitly triggered. The introspector then runs through the entirety of the rendered application, producing dynamic programming results and streaming them into an inspection view.

The code below is embedded as an executable runtime function, not just a static snippet. It leverages PerformanceObserver, MutationObserver, and a periodic JSON dump to maintain a rolling, bounded telemetry view of this page's structure and activity.

Dormant until trigger
Self‑profiling shell
Static snapshot + dynamic stream
Bounded buffers
1. Advanced bookmarklet (runtime function in this shell)

The bookmarklet you provided has been integrated as an executable introspection function. In its original form, it was meant to be dropped directly into a bookmark's URL field:

Original usage: create a bookmark and set the URL to:

Original bookmarklet payload (for reference)
javascript:(function(){try{var D=document,W=window;function A(s,c){return Array.prototype.slice.call((c||D).querySelectorAll(s));}function M(xs,f){return xs.map(function(n){try{return f(n);}catch(e){return{error:String(e)}}});}function T(s,l){if(!s)return null;s=s.trim();return s.length>l?s.slice(0,l):s}var S=W.__PAGE_INTROSPECTOR__||(W.__PAGE_INTROSPECTOR__={meta:{started:Date.now()},page:{},static:{},dynamic:{mutations:[],resources:[],longtasks:[]}});S.meta.lastRun=Date.now();S.page={url:W.location.href,title:D.title,referrer:D.referrer};S.static.scripts=M(A('script'),function(s){return{src:s.src||null,inline:s.src?null:T(s.textContent||'',2000),type:s.type||null,async:!!s.async,defer:!!s.defer,crossorigin:s.getAttribute('crossorigin'),nonce:s.getAttribute('nonce'),id:s.id||null}});S.static.links=M(A('link'),function(l){return{rel:l.rel||null,href:l.href||null,as:l.getAttribute('as'),crossorigin:l.getAttribute('crossorigin'),type:l.type||null,media:l.media||null,referrerpolicy:l.referrerPolicy||null,fetchpriority:l.getAttribute('fetchpriority')}});S.static.anchors=M(A('a[href]'),function(a){return{href:a.href,text:T(a.textContent||'',200),rel:a.rel||null,role:a.getAttribute('role'),ariaLabel:a.getAttribute('aria-label'),ariaLabelledby:a.getAttribute('aria-labelledby'),id:a.id||null}});S.static.elementsWithId=M(A('[id]'),function(el){return{tag:el.tagName.toLowerCase(),id:el.id,classes:el.className||null,role:el.getAttribute('role'),ariaLabel:el.getAttribute('aria-label'),ariaLabelledby:el.getAttribute('aria-labelledby')}});S.static.ariaElements=M(A('[role],[aria-label],[aria-labelledby],[aria-hidden],[aria-modal]'),function(el){return{tag:el.tagName.toLowerCase(),id:el.id||null,classes:el.className||null,role:el.getAttribute('role'),ariaLabel:el.getAttribute('aria-label'),ariaLabelledby:el.getAttribute('aria-labelledby'),ariaHidden:el.getAttribute('aria-hidden'),ariaModal:el.getAttribute('aria-modal')}});S.static.modals=M(A('[role="dialog"],[role="alertdialog"],[aria-modal="true"],.modal,[data-modal],[data-dialog]'),function(el){return{tag:el.tagName.toLowerCase(),id:el.id||null,classes:el.className||null,role:el.getAttribute('role'),ariaModal:el.getAttribute('aria-modal'),ariaLabel:el.getAttribute('aria-label'),ariaLabelledby:el.getAttribute('aria-labelledby')}});S.static.navRelations=M(A('link[rel]'),function(l){return{rel:l.rel,href:l.href||null,as:l.getAttribute('as'),type:l.type||null,media:l.media||null}});function cap(arr,max){if(arr.length>max)arr.splice(0,arr.length-max);}if(!S.__observersSetup){S.__observersSetup=true;try{if(W.PerformanceObserver){var ro=new PerformanceObserver(function(list){list.getEntries().forEach(function(e){S.dynamic.resources.push({name:e.name,initiatorType:e.initiatorType,entryType:e.entryType,startTime:e.startTime,duration:e.duration,transferSize:e.transferSize,encodedBodySize:e.encodedBodySize,decodedBodySize:e.decodedBodySize});cap(S.dynamic.resources,400);});});try{ro.observe({type:'resource',buffered:true});}catch(_){ro.observe({entryTypes:['resource']});}var no=new PerformanceObserver(function(list){list.getEntries().forEach(function(e){S.dynamic.resources.push({name:e.name,initiatorType:e.initiatorType||'navigation',entryType:e.entryType,startTime:e.startTime,duration:e.duration,type:e.type});cap(S.dynamic.resources,450);});});try{no.observe({type:'navigation',buffered:true});}catch(_){no.observe({entryTypes:['navigation']});}var lo=new PerformanceObserver(function(list){list.getEntries().forEach(function(e){S.dynamic.longtasks.push({name:e.name||'longtask',entryType:e.entryType,startTime:e.startTime,duration:e.duration});cap(S.dynamic.longtasks,200);});});try{lo.observe({type:'longtask',buffered:true});}catch(_){lo.observe({entryTypes:['longtask']});}}}catch(e){}try{var mo=new MutationObserver(function(muts){muts.forEach(function(m){var rec={t:Date.now(),type:m.type,targetTag:m.target&&m.target.tagName?m.target.tagName.toLowerCase():null,targetId:m.target&&m.target.id||null,added:[],removed:[],attrName:m.attributeName||null};if(m.type==='attributes'){rec.attrValue=m.target && m.attributeName?m.target.getAttribute(m.attributeName):null;}if(m.type==='childList'){Array.prototype.forEach.call(m.addedNodes||[],function(n){if(n.nodeType===1){rec.added.push({tag:n.tagName.toLowerCase(),id:n.id||null,classes:n.className||null,role:n.getAttribute('role'),ariaLabel:n.getAttribute('aria-label')});}});Array.prototype.forEach.call(m.removedNodes||[],function(n){if(n.nodeType===1){rec.removed.push({tag:n.tagName.toLowerCase(),id:n.id||null,classes:n.className||null,role:n.getAttribute('role'),ariaLabel:n.getAttribute('aria-label')});}});}S.dynamic.mutations.push(rec);cap(S.dynamic.mutations,300);});});mo.observe(D.documentElement||D.body||D,{subtree:true,childList:true,attributes:true,attributeFilter:['role','id','class','aria-label','aria-labelledby','aria-hidden','aria-modal']});S.dynamic.__mo=mo;}catch(e){}}var win=S.__win;if(!win||win.closed){win=W.open('about:blank','_blank');if(!win){alert('Popup blocked: enable popups and retry bookmarklet.');return;}S.__win=win;var pre=win.document.createElement('pre');pre.style.whiteSpace='pre-wrap';pre.style.font='11px/1.4 monospace';win.document.body.style.margin='0';win.document.body.appendChild(pre);win.document.title='Page inspection stream';S.__pre=pre;}var pre=S.__pre||S.__win.document.querySelector('pre');function dump(){if(!S.__win||S.__win.closed)return;pre.textContent=JSON.stringify({meta:S.meta,page:S.page,static:S.static,dynamic:S.dynamic},null,2);}dump();if(!S.__interval){S.__interval=W.setInterval(dump,2000);} }catch(e){alert('Introspector error: '+e);}})();

Within this document, that same logic is wired into a dormant runtime function, invoked only when you trigger it via the button or keyboard shortcut.

2. What this does (in terms the runtime cares about)

The bookmarklet is written "for the interpreter": it is dense, self‑contained, and free of external dependencies, optimized to run inside arbitrary pages without additional scaffolding. It builds a single global anchor on window.__PAGE_INTROSPECTOR__ and uses this as the canonical container for every piece of telemetry and snapshot data it collects.

Internally, the state container holds:

Field Purpose
meta.started Timestamp of the first ever run for this page context.
meta.lastRun Timestamp of the most recent run (updated on each trigger).
page URL, title, and referrer of the inspected page.
static One‑shot structural snapshot of DOM and metadata at trigger time.
dynamic Rolling buffers for mutations, resources, and long tasks.
Introspector: dormant
Runs: 0
3. Static collectors (structural snapshot)

Static collectors use querySelectorAll-based scans over the current DOM to build a snapshot of scripts, links, anchors, IDs, ARIA‑relevant elements, modal‑like nodes, and navigation relations. These live under __PAGE_INTROSPECTOR__.static.

Collector Selector Captured shape
Scripts script src, truncated inline body, type, async/defer, crossorigin, nonce, id.
Links link rel, href, as, crossorigin, type, media, referrerpolicy, fetchpriority.
Anchors a[href] href, truncated text, rel, role, ARIA label/labelledby, id.
Elements with id [id] Tag, id, class list, role, ARIA label/labelledby.
ARIA / role cluster [role],[aria-*] Tag, id, classes, role, aria-label, aria-labelledby, aria-hidden, aria-modal.
Modal heuristics [role="dialog"], [role="alertdialog"], [aria-modal="true"], .modal, [data-modal], [data-dialog] Minimal shape for each modal‑like element: tag, id, classes, role, aria-modal, ARIA labeling.
Navigation relations link[rel] rel, href, as, type, media.
4. Dynamic collectors (Performance + Mutations)

Dynamic collectors attach once per page and stream performance‑level and DOM‑level changes into rolling buffers. Each buffer is explicitly capped to keep the in‑page structure bounded and safe.

Performance observers

A single setup block handles resource, navigation, and long‑task entries via PerformanceObserver, using type with buffered:true where supported and falling back to entryTypes lists. Entries are pushed into S.dynamic.resources and S.dynamic.longtasks.

Mutation observer

A single MutationObserver is attached to the root element (with a fallback to body) with subtree:true, childList:true, and attributes:true. Attribute tracking is restricted to: role, id, class, aria-label, aria-labelledby, aria-hidden, aria-modal.

Each mutation records a lightweight descriptor: timestamp t, mutation type, target tag/id, attribute name/value when relevant, and small summaries for added and removed elements.

5. Rolling caps (bounded buffers)

To avoid unbounded growth, each array that collects dynamic data is trimmed using a simple cap(arr, max) helper that discards older entries before the front of the list:

function cap(arr, max) {
  if (arr.length > max) arr.splice(0, arr.length - max);
}

Caps applied:

Buffer Max entries
S.dynamic.resources 400–450 entries (resource + navigation)
S.dynamic.longtasks 200 entries
S.dynamic.mutations 300 entries
6. Streaming output window (singleton channel)

The output channel is a dedicated popup window whose sole responsibility is to render JSON. The introspector reuses the same window on subsequent runs. If the window is closed, it is recreated as about:blank, given a monospaced <pre>, and titled "Page inspection stream".

A dump() function JSON‑serializes the current introspector state:

dump() {
  if (!S.__win || S.__win.closed) return;
  pre.textContent = JSON.stringify(
    { meta: S.meta, page: S.page, static: S.static, dynamic: S.dynamic },
    null,
    2
  );
}

The first run triggers an immediate dump, then a periodic refresh via setInterval(dump, 2000), stored in S.__interval so it is created at most once per page.

7. Re‑entrancy / replay

Re‑running the introspector on the same page updates meta.lastRun, recomputes the full static snapshot, and forces a fresh dump into the streaming window, while preserving the underlying observers and interval if they already exist. This yields an idempotent pattern: multiple triggers on the same page refine the structural snapshot and append dynamic events, without duplicating observers.

Within this shell, you can trigger re‑runs either via the primary control button or the Ctrl+Shift+I shortcut. Each run is counted, and the status indicator updates from "dormant" to "streaming".

If you ever want this to also introspect framework‑specific structures — React trees, Vue instances, or custom data stores — you can extend S.static.framework and S.dynamic.framework with framework‑specific probes built into the same global pattern.

Comments

Popular posts from this blog