Skip to main content

Sourcemaps

When your TypeScript/JavaScript is bundled and minified for production, stack traces show lines like bundle.js:1:43829 instead of UserProfile.tsx:47:handleClick. CostKey's sourcemap support translates these back to the original source.

When you need sourcemaps

You need sourcemaps if your production code is:

  • Bundled (webpack, Vite, esbuild, Rollup, etc.)
  • Minified (terser, uglify, etc.)
  • Transpiled from TypeScript

If you're running raw .ts files with tsx or ts-node in development, you don't need sourcemaps — stack traces already point to the right files.

Setup

1. Set a release version

Tag your SDK init with a release identifier. This connects events to the right sourcemaps.

import { CostKey } from 'costkey'

CostKey.init({
dsn: 'https://ck_key@app.costkey.dev/proj_id',
release: process.env.GIT_SHA, // e.g., "a1b2c3d" or "v1.2.3"
})

2. Generate sourcemaps in your build

Make sure your build tool outputs .map files. Most bundlers do this by default or with a simple config change.

Vite (vite.config.ts):

export default defineConfig({
build: {
sourcemap: true, // Generates .map files
},
})

webpack (webpack.config.js):

module.exports = {
devtool: 'source-map', // Generates .map files
}

Next.js (next.config.js):

module.exports = {
productionBrowserSourceMaps: true,
// For server-side sourcemaps (API routes, server components):
webpack: (config, { isServer }) => {
if (isServer) {
config.devtool = 'source-map'
}
return config
},
}

Then in your instrumentation file:

// instrumentation.ts (or src/instrumentation.ts)
import { CostKey } from 'costkey'

CostKey.init({
dsn: 'https://ck_key@app.costkey.dev/proj_id',
release: process.env.NEXT_PUBLIC_GIT_SHA || process.env.VERCEL_GIT_COMMIT_SHA,
})

Upload after build:

npx costkey sourcemaps upload \
--release $VERCEL_GIT_COMMIT_SHA \
--auth-key $COSTKEY_AUTH_KEY \
.next/server/**/*.map

esbuild:

esbuild src/index.ts --bundle --sourcemap --outdir=dist

3. Upload sourcemaps

After building, upload the .map files to CostKey using the CLI:

costkey sourcemaps upload \
--release $(git rev-parse --short HEAD) \
--auth-key $COSTKEY_AUTH_KEY \
./dist/*.map

CLI options:

FlagRequiredDescription
--release <version>YesRelease version. Must match the release in your SDK init.
--auth-key <key>YesProject auth key (ck_...). Can also be set via COSTKEY_AUTH_KEY env var.
--host <url>NoCostKey server URL. Default: https://app.costkey.dev

The CLI reads each .map file, parses the file field from the sourcemap JSON to determine which source file it maps, and uploads it to the server.

CI/CD integration

GitHub Actions

name: Deploy

on:
push:
branches: [main]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 20

- run: npm ci
- run: npm run build

# Upload sourcemaps to CostKey
- name: Upload sourcemaps
run: |
npx costkey sourcemaps upload \
--release ${{ github.sha }} \
--auth-key ${{ secrets.COSTKEY_AUTH_KEY }} \
./dist/*.map

# Deploy your app (Railway, Vercel, Fly, etc.)
- name: Deploy
run: # your deploy command
env:
GIT_SHA: ${{ github.sha }}

Vercel (Next.js)

Add a custom build script in package.json:

{
"scripts": {
"build": "next build && npx costkey sourcemaps upload --release $VERCEL_GIT_COMMIT_SHA --auth-key $COSTKEY_AUTH_KEY .next/server/**/*.map"
}
}

Set COSTKEY_AUTH_KEY in your Vercel project environment variables.

GitLab CI

deploy:
stage: deploy
script:
- npm ci
- npm run build
- npx costkey sourcemaps upload
--release $CI_COMMIT_SHA
--auth-key $COSTKEY_AUTH_KEY
./dist/*.map
- # your deploy command

How it works

  1. Build time: Your bundler creates .map files alongside the bundled .js files
  2. Upload: The CLI uploads each .map file to the CostKey server, tagged with the release version
  3. Runtime: The SDK captures stack traces from minified code and tags events with the release version
  4. Server-side: When the server receives an event, it matches the release + filename to the uploaded sourcemap and translates the stack frames back to original source locations

The translation happens on the server at ingest time, so:

  • Translated frames are stored in the database (no re-translation needed)
  • The dashboard always shows original source locations
  • You don't need to serve sourcemaps publicly

Troubleshooting

Stack traces still show minified locations:

  • Make sure the release value in CostKey.init() exactly matches the --release used during upload
  • Verify sourcemaps were generated by your build tool (check for .map files in your dist directory)
  • Check the CLI output for upload errors

"No sourcemap files found" error:

  • Make sure the glob pattern matches your .map file locations (e.g., ./dist/**/*.map for nested directories)
  • Verify your build actually generates sourcemap files

Partial translation (some frames translated, others not):

  • This is normal. Frames from node_modules or Node.js internals won't have sourcemaps. Only your application code is translated.