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:
| Flag | Required | Description |
|---|---|---|
--release <version> | Yes | Release version. Must match the release in your SDK init. |
--auth-key <key> | Yes | Project auth key (ck_...). Can also be set via COSTKEY_AUTH_KEY env var. |
--host <url> | No | CostKey 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
- Build time: Your bundler creates
.mapfiles alongside the bundled.jsfiles - Upload: The CLI uploads each
.mapfile to the CostKey server, tagged with the release version - Runtime: The SDK captures stack traces from minified code and tags events with the release version
- 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
releasevalue inCostKey.init()exactly matches the--releaseused during upload - Verify sourcemaps were generated by your build tool (check for
.mapfiles in your dist directory) - Check the CLI output for upload errors
"No sourcemap files found" error:
- Make sure the glob pattern matches your
.mapfile locations (e.g.,./dist/**/*.mapfor nested directories) - Verify your build actually generates sourcemap files
Partial translation (some frames translated, others not):
- This is normal. Frames from
node_modulesor Node.js internals won't have sourcemaps. Only your application code is translated.