Skip to content

Releases: mathematic-inc/smapped-traces

v0.1.2

11 Mar 05:25
f1138c9

Choose a tag to compare

smapped-traces v0.1.2 🗺️

We're thrilled to ship the first public release of smapped-traces — a source map resolution library built from the ground up for OpenTelemetry. No more staring at minified bundle.js:1:98432 stack traces in your error dashboards. smapped-traces intercepts OTLP span exports on the client, attaches bundler debug IDs to every exception event, and resolves those mangled frames back to your original TypeScript source on the server — all transparently, with zero changes to your OpenTelemetry instrumentation code.


✨ New Features

Client-side span exporter with debug ID enrichment

SourceMappedSpanExporter wraps any OpenTelemetry span pipeline and automatically extracts debug IDs from exception stack traces, attaching them as a exception.stacktrace.debug_ids attribute before export. It starts with fetch for the first request, then switches to navigator.sendBeacon so traces are never dropped during page unload.

import { SourceMappedSpanExporter } from 'smapped-traces/client'
import { BatchSpanProcessor, WebTracerProvider } from '@opentelemetry/sdk-trace-web'

const exporter = new SourceMappedSpanExporter('/api/traces')
const provider = new WebTracerProvider({
  spanProcessors: [new BatchSpanProcessor(exporter)],
})
provider.register()

Server-side traces handler with source map resolution

createTracesHandler produces a standard (Request) => Promise<Response> handler you can drop into any framework. It deserializes OTLP/protobuf, resolves every exception stack trace against your source map store, replaces the minified trace in-place (preserving the original as exception.stacktrace.original), and forwards the enriched spans to any downstream OTLP exporter.

// app/api/traces/route.ts (Next.js App Router)
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { createTracesHandler } from 'smapped-traces/route'
import { createSqliteStore } from '@smapped-traces/sqlite'

export const POST = createTracesHandler({
  exporter: new OTLPTraceExporter({ url: 'http://localhost:4318/v1/traces' }),
  store: createSqliteStore('.next/sourcemaps.db'),
})

Pluggable source map store interface

All storage backends implement a simple three-method SourceMapStore interface (get, put, optional close), making it trivial to build your own. Four ready-made implementations ship out of the box:

Package Backend
@smapped-traces/sqlite Local SQLite (great for single-server / Next.js)
@smapped-traces/s3 AWS S3, Google Cloud Storage, Cloudflare R2
smapped-traces/storecreateHttpStore Fetch from a remote store over HTTP
smapped-traces/storecreateStoreHandler Expose any store as an HTTP API

The HTTP store pair lets you run a dedicated source map microservice and point multiple app instances at it:

// Sidecar service (Bun / Deno / Node)
import { createStoreHandler } from 'smapped-traces/store'
import { createSqliteStore } from '@smapped-traces/sqlite'

const store = createSqliteStore('./sourcemaps.db')
Bun.serve({ port: 8081, fetch: createStoreHandler(store) })

// App server
import { createHttpStore } from 'smapped-traces/store'

const store = createHttpStore('http://localhost:8081')

S3-compatible store (@smapped-traces/s3)

One store, three cloud providers. Pass any S3Client — AWS, GCS (with the S3-interop endpoint), or Cloudflare R2 — and smapped-traces handles the rest.

import { S3Client } from '@aws-sdk/client-s3'
import { createS3Store } from '@smapped-traces/s3'

const store = createS3Store({
  client: new S3Client({ region: 'us-east-1' }),
  bucket: 'my-sourcemaps',
  prefix: 'sourcemaps/',
})

Next.js build plugin (@smapped-traces/nextjs)

withSourceMaps wraps your next.config.ts, enables Turbopack debug IDs, turns on production browser source maps, and registers a post-build hook that uploads every *.js.map with a debugId field to your store — then deletes the map files so they never ship to users.

// next.config.ts
import { withSourceMaps } from '@smapped-traces/nextjs'
import { createS3Store } from '@smapped-traces/s3'
import { S3Client } from '@aws-sdk/client-s3'

export default withSourceMaps(
  { /* your existing Next.js config */ },
  {
    // Normalise Turbopack's internal path prefix so source links work in your IDE
    replaceTurbopackSourcePrefix: 'file:///',
    store: () =>
      createS3Store({
        client: new S3Client({ region: 'us-east-1' }),
        bucket: 'my-sourcemaps',
      }),
  }
)

Standalone source map resolver

Need to resolve stack traces outside the built-in handler? createSourceMapResolver gives you direct access — it maintains an LRU cache of parsed SourceMapConsumer instances so repeated lookups stay fast.

import { createSourceMapResolver } from 'smapped-traces/resolve'
import { createSqliteStore } from '@smapped-traces/sqlite'

const resolver = createSourceMapResolver({
  store: createSqliteStore('./sourcemaps.db'),
  maxCacheSize: 100,
})

const readable = await resolver.resolveStackTrace(minifiedStack, debugIds)
console.log(readable)

⚡ Improvements

  • sendBeacon reliability — after the first successful fetch, the client automatically switches to navigator.sendBeacon so spans are never lost when the user navigates away or closes the tab.
  • Graceful degradation — if the server responds with 204 No Content, the exporter disables itself and stops sending, making it easy to turn off collection without redeploying the client.
  • Safe-by-default store handlercreateStoreHandler responds with 405 Method Not Allowed for anything other than GET and PUT, and always returns 400 when no debug ID is present in the path.
  • Automatic Turbopack supportwithSourceMaps handles both flat and sectioned (index map) source map formats, correctly transforming turbopack:///[project]/ prefixes in both.

📦 Installation

# Core library
npm install smapped-traces

# Next.js build plugin
npm install @smapped-traces/nextjs

# S3-compatible store
npm install @smapped-traces/s3

# SQLite store
npm install @smapped-traces/sqlite better-sqlite3

All packages are pure ESM and ship with full TypeScript declarations.

sqlite-v0.1.1

11 Mar 05:25
f1138c9

Choose a tag to compare

@smapped-traces/sqlite v0.1.1 🗺️

We're thrilled to introduce @smapped-traces/sqlite — a brand-new, dedicated SQLite store for smapped-traces that makes zero-infrastructure source map resolution a reality. With a single function call you get a fully persistent, production-ready store that lives right next to your build output: no external services, no network round-trips, no extra containers. This release also moves the entire smapped-traces ecosystem to its permanent home under the mathematic-inc organization on GitHub.


✨ New Features

@smapped-traces/sqlite — local SQLite store for source maps

createSqliteStore provides a SourceMapStore backed by better-sqlite3. It creates the database and table lazily on first write, stores source map JSON in SQLite's native binary JSON format (jsonb) for compact, fast retrieval, and closes cleanly when you're done.

import { createSqliteStore } from '@smapped-traces/sqlite'
import { createTracesHandler } from 'smapped-traces/route'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'

// Next.js: app/api/traces/route.ts
export const POST = createTracesHandler({
  exporter: new OTLPTraceExporter({ url: 'http://localhost:4318/v1/traces' }),
  store: createSqliteStore('.next/sourcemaps.db'),
})

Pair it with the @smapped-traces/nextjs build plugin and your source maps are collected at build time, stored in the SQLite database, and resolved automatically at runtime — all without leaving your server:

// next.config.ts
import { join } from 'node:path'
import { withSourceMaps } from '@smapped-traces/nextjs'
import { createSqliteStore } from '@smapped-traces/sqlite'

export default withSourceMaps(
  { /* ...your Next.js config... */ },
  {
    store: (distDir) => createSqliteStore(join(distDir, 'sourcemaps.db')),
  }
)

Or run a standalone source map microservice with Bun, pairing createStoreHandler from the core with the SQLite backend:

import { createStoreHandler } from 'smapped-traces/store'
import { createSqliteStore } from '@smapped-traces/sqlite'

const store = createSqliteStore('./sourcemaps.db')
Bun.serve({ port: 8081, fetch: createStoreHandler(store) })

🐛 Bug Fixes

  • Repository migrated to mathematic-inc organization — all package repository URLs, issue links, and changelogs now point to the correct github.com/mathematic-inc/smapped-traces home. (#6)

📦 Installation

Install the packages that fit your stack:

# Core library (client exporter + server handler + store primitives)
npm install smapped-traces

# Next.js build plugin — collects & uploads source maps at build time
npm install @smapped-traces/nextjs

# SQLite store — zero-infra local source map persistence
npm install @smapped-traces/sqlite

# S3-compatible store — AWS S3, Google Cloud Storage, or Cloudflare R2
npm install @smapped-traces/s3

All packages require smapped-traces as a peer dependency. The @smapped-traces/sqlite package additionally requires better-sqlite3:

npm install smapped-traces better-sqlite3

s3-v0.1.2

11 Mar 05:25
f1138c9

Choose a tag to compare

smapped-traces s3-v0.1.2 brings cloud-native source map storage to your OpenTelemetry error pipeline — store and serve your debug artifacts directly from AWS S3, Google Cloud Storage, or Cloudflare R2, all through the same familiar S3 API. This release also plants the project firmly in its new home at mathematic-inc/smapped-traces, with polished READMEs to match. Whether you're running a distributed Next.js app or a standalone OTLP collector, your minified stack traces now have a scalable, production-grade place to call home.


✨ New Features

S3-compatible source map store (@smapped-traces/s3)

The new createS3Store function plugs directly into the smapped-traces store interface and works with any S3-compatible service. Pass in a pre-configured S3Client (from @aws-sdk/client-s3), a bucket name, and an optional key prefix — that's it.

AWS S3:

import { S3Client } from '@aws-sdk/client-s3'
import { createS3Store } from '@smapped-traces/s3'

const store = createS3Store({
  client: new S3Client({ region: 'us-east-1' }),
  bucket: 'my-sourcemaps',
  prefix: 'sourcemaps/',
})

Cloudflare R2:

import { S3Client } from '@aws-sdk/client-s3'
import { createS3Store } from '@smapped-traces/s3'

const store = createS3Store({
  client: new S3Client({
    region: 'auto',
    endpoint: 'https://<account-id>.r2.cloudflarestorage.com',
    credentials: { accessKeyId: '...', secretAccessKey: '...' },
  }),
  bucket: 'my-sourcemaps',
})

Wire it up to your Next.js build in next.config.ts:

import { withSourceMaps } from '@smapped-traces/nextjs'
import { createS3Store } from '@smapped-traces/s3'
import { S3Client } from '@aws-sdk/client-s3'

export default withSourceMaps(nextConfig, {
  store: () => createS3Store({
    client: new S3Client({ region: 'us-east-1' }),
    bucket: 'my-sourcemaps',
  }),
})

And use the same store on your server to resolve incoming traces:

// app/api/traces/route.ts
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { createTracesHandler } from 'smapped-traces/route'
import { createS3Store } from '@smapped-traces/s3'
import { S3Client } from '@aws-sdk/client-s3'

export const POST = createTracesHandler({
  exporter: new OTLPTraceExporter({ url: 'http://localhost:4318/v1/traces' }),
  store: createS3Store({
    client: new S3Client({ region: 'us-east-1' }),
    bucket: 'my-sourcemaps',
  }),
})

🐛 Bug Fixes

  • Repository migration — the project has moved to mathematic-inc/smapped-traces. All links, references, and package metadata now point to the new home. (#6)
  • README updates — outdated documentation has been refreshed across all packages to reflect the current API and project structure.

📦 Installation

Install the packages for your setup:

# Core library (client exporter + server handler + store primitives)
npm install smapped-traces

# Next.js build plugin
npm install @smapped-traces/nextjs

# S3-compatible store (AWS S3 · Google Cloud Storage · Cloudflare R2)
npm install @smapped-traces/s3

nextjs-v0.1.2

11 Mar 05:25
f1138c9

Choose a tag to compare

@smapped-traces/nextjs v0.1.2

Say goodbye to cryptic minified stack traces in your OpenTelemetry errors! This release brings smapped-traces to its new home under the mathematic-inc organization, ships refreshed documentation across all packages, and marks a great checkpoint to highlight everything this library can do — automatically enriching OTLP exception spans with debug IDs at build time and resolving them back to your original TypeScript source on the server, all without touching your existing OpenTelemetry pipeline.

🐛 Bug Fixes

  • Repository migrated to mathematic-inc — all packages, links, and metadata now point to github.com/mathematic-inc/smapped-traces. Update any bookmarks or dependency configs accordingly. (#6)
  • Refreshed READMEs — outdated documentation has been corrected across all packages so the examples you copy actually work. (94d0eee)

✨ What smapped-traces Does (The Full Picture)

Client — SourceMappedSpanExporter

Drop in SourceMappedSpanExporter as your OpenTelemetry span exporter. It automatically extracts debug IDs from minified stack traces and attaches them to exception events before forwarding traces to your server endpoint. It uses fetch initially and seamlessly upgrades to sendBeacon for reliable delivery even during page unloads.

import { SourceMappedSpanExporter } from 'smapped-traces/client'
import { BatchSpanProcessor, WebTracerProvider } from '@opentelemetry/sdk-trace-web'

const exporter = new SourceMappedSpanExporter('/api/traces')
const provider = new WebTracerProvider({
  spanProcessors: [new BatchSpanProcessor(exporter)],
})
provider.register()

Server — createTracesHandler

On the server, createTracesHandler receives the enriched OTLP/protobuf traces, looks up source maps by debug ID, resolves every minified frame back to its original file and line number (preserving the original as .original), then forwards the clean spans to your observability backend.

// app/api/traces/route.ts
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { createTracesHandler } from 'smapped-traces/route'
import { createSqliteStore } from '@smapped-traces/sqlite'

export const POST = createTracesHandler({
  exporter: new OTLPTraceExporter({ url: 'http://localhost:4318/v1/traces' }),
  store: createSqliteStore('.next/sourcemaps.db'),
})

Next.js Build Plugin — withSourceMaps

withSourceMaps hooks into Next.js's post-build step to collect every source map with a debug ID (including Turbopack-generated ones), upload them to your store, and then delete them so they never ship to the browser. The replaceTurbopackSourcePrefix option lets you rewrite Turbopack's internal turbopack:///[project]/ paths to a URL scheme your IDE or error viewer will recognize.

// next.config.ts
import { withSourceMaps } from '@smapped-traces/nextjs'
import { createSqliteStore } from '@smapped-traces/sqlite'
import { join } from 'node:path'

export default withSourceMaps(
  { /* your existing Next.js config */ },
  {
    replaceTurbopackSourcePrefix: 'file:///',
    store: (distDir) => createSqliteStore(join(distDir, 'sourcemaps.db')),
  }
)

Remote Source Map Store — HTTP pair

For multi-server or serverless deployments, run a standalone store microservice and point your Next.js build at it:

// Store microservice (Bun / Deno / Node)
import { createStoreHandler } from 'smapped-traces/store'
import { createSqliteStore } from '@smapped-traces/sqlite'

Bun.serve({ port: 8081, fetch: createStoreHandler(createSqliteStore('./sourcemaps.db')) })

// next.config.ts — point build uploads at the microservice
import { createHttpStore } from 'smapped-traces/store'

store: () => createHttpStore('https://sourcemaps.internal')

S3-Compatible Store — @smapped-traces/s3

Store source maps in the cloud with createS3Store. Works with AWS S3, Google Cloud Storage, and Cloudflare R2 — anything that speaks the S3 API.

import { S3Client } from '@aws-sdk/client-s3'
import { createS3Store } from '@smapped-traces/s3'

const store = createS3Store({
  client: new S3Client({ region: 'us-east-1' }),
  bucket: 'my-sourcemaps',
  prefix: 'sourcemaps/',
})

Custom Store — SourceMapStore interface

Have your own storage backend? Implement the two-method SourceMapStore interface and plug it straight in:

import type { SourceMapStore } from 'smapped-traces/store'

const myStore: SourceMapStore = {
  async get(debugId) { /* return JSON string or null */ },
  async put(debugId, content) { /* persist content */ },
  close() { /* optional cleanup */ },
}

📦 Installation

Install the packages that fit your setup:

# Core library (client exporter + server handler + store interface)
npm install smapped-traces

# Next.js build plugin
npm install @smapped-traces/nextjs

# SQLite store (great for local dev and single-server deployments)
npm install @smapped-traces/sqlite

# S3-compatible store (AWS S3, Google Cloud Storage, Cloudflare R2)
npm install @smapped-traces/s3

Full changelog: nextjs-v0.1.1...nextjs-v0.1.2