source-map v0.7.x uses WebAssembly for a remarkably faster parsing of source maps. StackTraceGPS is compatible with it with only very minor changes.
Expected Behavior
StackTraceGPS uses WebAssembly to parse source maps.
Context
Parsing modern web app source maps on the client in JavaScript leads to an unusable user experience, locking up the browser for long enough that the user may suspect the app has crashed, and thus force-quits the browser before we could send the parsed error to our server.
Possible Solution
I'm currently enabling it as follows:
package.json:
{
// …
"resolutions": {
"stacktrace-gps@npm:3.1.2/source-map@npm:0.5.6": "npm:source-map@^0.7.6"
}
}
./stacktrace-gps.ts:
import type * as StackFrame from "stackframe"
import type * as StackTrace from "stacktrace-js"
import { SourceMapConsumer } from "source-map"
import mappingsUrl from "source-map/lib/mappings.wasm?url"
import StackTraceGPS from "stacktrace-gps"
SourceMapConsumer.initialize({ "lib/mappings.wasm": mappingsUrl })
interface _StackTraceGPS extends StackTraceGPS {
sourceMapConsumerCache: {
[sourceMappingUrl: string]: SourceMapConsumer | Promise<SourceMapConsumer>
}
pinpoint(stackframe: StackFrame | StackTrace.StackFrame): Promise<StackFrame>
findFunctionName(
stackframe: StackFrame | StackTrace.StackFrame,
): Promise<StackFrame>
getMappedLocation(
stackframe: StackFrame | StackTrace.StackFrame,
): Promise<StackFrame>
[Symbol.dispose](): void
}
// polyfill for `Symbol.dispose`
Symbol.dispose ??= Symbol("Symbol.dispose")
class _StackTraceGPS extends StackTraceGPS {
[Symbol.dispose]() {
for (const sourceMapConsumerPromise of Object.values(
this.sourceMapConsumerCache,
)) {
Promise.resolve(sourceMapConsumerPromise).then((sourceMapConsumer) => {
return sourceMapConsumer.destroy()
})
}
}
}
export { _StackTraceGPS as StackTraceGPS }
./report-error.ts:
import { StackTraceGPS } from "./stacktrace-gps.ts"
const report = async (error: Error) => {
using gps = new StackTraceGPS()
const stack = await Promise.all(
(await StackTrace.fromError(payload.error, { offline: true })).map(
async (stackframe) => {
try {
return await gps.pinpoint(stackframe)
} catch {
return stackframe
}
},
),
)
// report the error…
}
For users that can't use using, a helper with function, modelled on source-map's SourceMapConsumer.with, or a destroy method could work well.
source-mapv0.7.x uses WebAssembly for a remarkably faster parsing of source maps.StackTraceGPSis compatible with it with only very minor changes.Expected Behavior
StackTraceGPSuses WebAssembly to parse source maps.Context
Parsing modern web app source maps on the client in JavaScript leads to an unusable user experience, locking up the browser for long enough that the user may suspect the app has crashed, and thus force-quits the browser before we could send the parsed error to our server.
Possible Solution
I'm currently enabling it as follows:
package.json:{ // … "resolutions": { "stacktrace-gps@npm:3.1.2/source-map@npm:0.5.6": "npm:source-map@^0.7.6" } }./stacktrace-gps.ts:./report-error.ts:For users that can't use
using, a helperwithfunction, modelled onsource-map'sSourceMapConsumer.with, or adestroymethod could work well.