stackframe
A tiny JS object representation of a single stack trace frame, powering stacktrace.js.
Repository Health
Technical Analysis
stackframe gives JavaScript a single, well-defined object shape for representing one frame of a stack trace — function name, file name, line/column numbers, eval origin, and native/constructor/toplevel flags — with typed getters and setters for every property. It underlies stacktrace.js and the wider stacktrace.js family of error-parsing tools, and was written to closely mirror the native StackFrame representations used internally by Gecko and V8, so consumers can round-trip between engine-native stack traces and a portable JS object.
The library has zero runtime dependencies, ships as a UMD module (AMD, CommonJS, and browser globals), and includes bundled TypeScript definitions. Beyond simple property storage, it supports serializing a frame back to its canonical string form via toString() and parsing a stack-trace-style string back into a StackFrame via the static fromString() method, making it a small but complete building block for anything that needs to construct, inspect, or reformat stack trace data across browsers and Node.
What You Get
- A
StackFrameconstructor that accepts an options object (functionName,fileName,lineNumber,columnNumber,args,source,isEval,isNative,isConstructor,isToplevel,evalOrigin) and exposes a matching getter/setter for every property - Type-coercing setters that normalize input (numbers, booleans, strings) and throw descriptive
TypeErrors for invalid values (e.g. a non-numericlineNumber) evalOriginsupport that recursively wraps nested eval call sites in their ownStackFrameinstances, soeval-within-evalcall chains stay structured- A
toString()method that renders a frame back into the classicfunctionName (fileName:line:column)stack-trace-line format, matching the original stacktrace.js output - A static
StackFrame.fromString()parser that reconstructs aStackFramefrom a serialized stack-trace-line string, enabling round-tripping - Bundled
stackframe.d.tsTypeScript definitions and a prebuilt/minified UMDdist/build for direct browser<script>use
Common Use Cases
- Acting as the underlying frame data structure for stacktrace.js and other libraries in the stacktrace.js ecosystem that need to normalize stack traces across browsers
- Building custom error-reporting or crash-reporting tooling that needs a consistent, engine-agnostic object shape for stack frames instead of parsing raw
Error.stackstrings ad hoc - Serializing stack frames to a compact string form for transport (e.g. sending client-side error frames to a server) and reconstructing them on the receiving end with
fromString() - Representing synthetic or reconstructed stack frames (e.g. from source-mapped or transpiled code) in a validated, well-typed object rather than a loose plain object
Under The Hood
Architecture
The entire library is a single ~140-line UMD-wrapped module (stackframe.js) with no internal layering: a StackFrame constructor iterates a fixed set of property-name arrays (booleanProps, numericProps, stringProps, arrayProps, objectProps) and, for each, dynamically generates a matching get/set pair on the prototype via small factory functions (_getter, and inline closures for each setter). This metaprogramming approach means adding a new property is a one-line change to a props array rather than hand-written accessor pairs, and it keeps the constructor itself generic — it just walks props and calls the corresponding set* method for any key present on the input object. There are no other modules, no dependency injection, and no data flow beyond “construct → validate/coerce via setters → read via getters or toString()”; changing the core props list is the one change that ripples through the whole file.
Tech Stack
Plain, dependency-free ES5-era JavaScript wrapped in a hand-written UMD shim supporting AMD (define), CommonJS (module.exports), and browser globals (root.StackFrame). There is no bundler for the library itself; the prepare npm script just copies the source into dist/ and runs uglify-es to produce a minified, source-mapped build. Testing runs on Karma against Jasmine specs across real browsers (Chrome, Firefox, IE, Safari, Opera, PhantomJS, plus Sauce Labs for CI), and linting is handled by ESLint. TypeScript consumers get hand-maintained definitions in stackframe.d.ts. GitHub Actions runs the CI workflow on push/PR.
Code Quality
A single Jasmine spec file (spec/stackframe-spec.js) exercises the constructor, every setter’s type-coercion and error-throwing behavior, evalOrigin nesting, fromString() parsing, and toString() output for both empty and fully-populated frames — a comprehensive if compact test suite for a library this small. ESLint is configured via .eslintrc.yml and run via npm run lint. Error handling is explicit: setters throw TypeError with descriptive messages rather than silently coercing or swallowing bad input. Naming is consistent (get/set + capitalized property name) and generated programmatically, which limits any risk of an accessor pair drifting out of sync.
API Design
The getter/setter API deliberately mirrors the shape of native stack frame objects in Gecko and V8, which is the library’s main design decision: it trades a more “modern” plain-object or class-with-fields API for one that self-documents intent (setFunctionName, getIsEval) and validates every write. The zero-dependency, UMD-first packaging means it drops into any JS environment — script tag, AMD loader, or require() — without a build step, and the bundled .d.ts file gives TypeScript users full autocomplete despite the library predating widespread native TS authoring. The tradeoff is a slightly more verbose call style (sf.setFunctionName(x) instead of sf.functionName = x) in exchange for input validation at every mutation.