stackframe

A tiny JS object representation of a single stack trace frame, powering stacktrace.js.

Library
npm
v1.3.4
82stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
33/100Needs Attention
Development Activity0
Maintenance20
Community40
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
58/100Fair
Architecture60
Code Quality78
Innovation50
Learning Curve45

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 StackFrame constructor 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-numeric lineNumber)
  • evalOrigin support that recursively wraps nested eval call sites in their own StackFrame instances, so eval-within-eval call chains stay structured
  • A toString() method that renders a frame back into the classic functionName (fileName:line:column) stack-trace-line format, matching the original stacktrace.js output
  • A static StackFrame.fromString() parser that reconstructs a StackFrame from a serialized stack-trace-line string, enabling round-tripping
  • Bundled stackframe.d.ts TypeScript definitions and a prebuilt/minified UMD dist/ 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.stack strings 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.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search