@kloudmate/sourcemaps
v0.1.3
Published
Upload source maps to KloudMate so RUM stack traces show your original code
Maintainers
Readme
@kloudmate/sourcemaps
Uploads your source maps to KloudMate during a production build, so RUM stack
traces show src/cart/applyCoupon.ts:42:7 instead of index-4f2a.js:1:84210.
On each build it does three things:
- Injects a debug ID into every script. The
@kloudmate/rum-webSDK reports it with each error, so a frame finds its map even when the release does not match. - Uploads the maps that KloudMate does not already hold.
- Removes the maps from the build output, so your source is never deployed.
Install
Add it as a dev dependency, so every build runs the version you pinned:
npm i -D @kloudmate/sourcemapsToken
Create an Ingest Key - Backend (sk_…) in KloudMate under Settings → Ingest Keys, and
expose it to your CI build as KM_SOURCEMAPS_TOKEN. Without it, nothing is
uploaded, but a production build still loses its .map files, so a build made
without the token never deploys them either. Set deleteAfterUpload: false to
keep them.
Create the key in the workspace your app sends RUM data to. Maps are stored per
workspace, so a key from another workspace uploads them where your errors never
look. Personal API Keys (uk_) and the public pk_ key the SDK uses are refused.
# GitHub Actions
- run: npm run build
env:
KM_SOURCEMAPS_TOKEN: ${{ secrets.KM_SOURCEMAPS_TOKEN }}Vite and Rollup
// vite.config.ts
import { kloudmateSourcemaps } from '@kloudmate/sourcemaps/vite';
export default defineConfig({
build: { sourcemap: 'hidden' },
plugins: [kloudmateSourcemaps({ app: 'shop-web' })],
});The same plugin works in a Rollup config. Use sourcemap: 'hidden' so your
scripts never point at a map that is not deployed.
webpack and Rspack
// webpack.config.mjs or rspack.config.mjs
import { kloudmateSourcemaps } from '@kloudmate/sourcemaps/webpack';
export default {
mode: 'production',
devtool: 'hidden-source-map',
plugins: [kloudmateSourcemaps({ app: 'shop-web' })],
};The package is ESM-only. A CommonJS config can require() it on Node 20.19 or
22.12 and later. In development mode or watch mode the plugin does nothing.
Both plugins act on browser bundles only. Vite SSR builds, library builds and non-client environments are left alone, and so are webpack and Rspack builds for a Node or Electron-main target, library builds and Next.js's server compilers.
Plugin options
Both plugins take the same options.
| Option | Default | Notes |
|---|---|---|
| app | — | Required. Must equal the SDK's applicationName exactly. |
| release | KM_RELEASE | Should equal the SDK's version. Debug IDs match without it, but a frame with no debug ID falls back to release and path. |
| urlPrefix | Vite's base or webpack's output.publicPath, else ~/ | The URL path the output directory is served from. /shop/, ~/shop and https://cdn.example.com/shop/ all mean the same; ~ stands for any origin. |
| deleteAfterUpload | true | Drops every .map file (and any .map.gz or .map.br copy) from the output, whether or not it was uploaded. |
| failOnError | false | Fails the build when KloudMate is unreachable or refuses the upload. Otherwise it logs and carries on. |
| token | KM_SOURCEMAPS_TOKEN | Prefer the environment variable over putting a key in config. |
| url | KM_SOURCEMAPS_URL, else https://api.kloudmate.com | The KloudMate API base URL. |
Other bundlers: the CLI
Install the package first (see Install), then run the CLI as a post-build step,
with inject before upload:
npx @kloudmate/sourcemaps inject ./distnpx @kloudmate/sourcemaps upload ./dist --app shop-web --release "$APP_VERSION" --delete-after-uploadAlways call it by its scoped name. npx runs the version installed in your
project; any other name could resolve to an unrelated package on npm, running
in your CI next to your token and your maps.
inject changes the built scripts. Run it before any step that hashes them,
such as subresource integrity or a service worker manifest.
Next.js
This works with both webpack and Turbopack builds. Turn on browser source maps:
// next.config.js
module.exports = { productionBrowserSourceMaps: true };Then run the CLI on .next/static, which is served under /_next/static:
// package.json
"build": "next build && npx @kloudmate/sourcemaps inject .next/static && npx @kloudmate/sourcemaps upload .next/static --app shop-web --url-prefix /_next/static --delete-after-upload"With a basePath or a path in assetPrefix, add it to the prefix, for example
--url-prefix /shop/_next/static. Do not use experimental.sri, because its
hashes are computed before inject changes the files.
Angular
In angular.json, turn on hidden script maps for the production configuration:
"configurations": {
"production": {
"sourceMap": { "scripts": true, "styles": false, "hidden": true, "vendor": true }
}
}Then run the CLI on the browser output. Projects still on the older browser
builder output to dist/shop-web, with no browser folder.
// package.json
"build": "ng build && npx @kloudmate/sourcemaps inject dist/shop-web/browser && npx @kloudmate/sourcemaps upload dist/shop-web/browser --app shop-web --release \"$APP_VERSION\" --delete-after-upload"If the app is served under a baseHref such as /shop/, pass
--url-prefix /shop/.
If you use @angular/service-worker, regenerate its manifest after inject.
Otherwise its hashes no longer match the changed files and the service worker
rejects them. Run this from the project root, adding the baseHref as a third
argument if the app has one:
ngsw-config dist/shop-web/browser ngsw-config.jsonesbuild
Build with sourcemap: 'external'. That writes the maps without pointing the
scripts at them:
await esbuild.build({
entryPoints: ['src/main.ts'],
bundle: true,
minify: true,
sourcemap: 'external',
outdir: 'dist',
});Then run inject and upload on dist as above.
CLI flags
npx @kloudmate/sourcemaps -- --help prints them; a bare --help goes to npx
itself.
| Flag | Notes |
|---|---|
| --app | Required for upload. Must equal the SDK's applicationName exactly. |
| --release | Defaults to KM_RELEASE. Must equal the SDK's version for path matching. |
| --url-prefix | The URL path <dir> is served from, such as /shop/ or https://cdn.example.com/shop/. Defaults to ~/, the site root. |
| --delete-after-upload | Deletes every .map file under <dir>, whether or not it was uploaded, even when no token is set. |
| --url | The KloudMate API base URL. Defaults to KM_SOURCEMAPS_URL, else https://api.kloudmate.com. |
| --strict | Exits non-zero on failure. Without it, failures are logged and the build carries on. |
upload pairs each script with its map through its sourceMappingURL comment,
or a <file>.map file next to it.
inject adds its code at the end of each script. An error thrown while that
script's own top-level code first runs is reported before the debug ID is
registered, so it resolves by release and path instead.
