diff --git a/packages/plugin-browser-device/package.json b/packages/plugin-browser-device/package.json index d55eea23e6..d43d99005b 100644 --- a/packages/plugin-browser-device/package.json +++ b/packages/plugin-browser-device/package.json @@ -2,6 +2,14 @@ "name": "@bugsnag/plugin-browser-device", "version": "8.0.0", "main": "device.js", + "types": "dist/types/device.d.ts", + "exports": { + ".": { + "types": "./dist/types/device.d.ts", + "default": "./dist/device.js", + "import": "./dist/device.mjs" + } + }, "description": "@bugsnag/js plugin to set device info in browsers", "homepage": "https://www.bugsnag.com/", "repository": { @@ -24,5 +32,10 @@ }, "peerDependencies": { "@bugsnag/core": "^8.0.0" + }, + "scripts": { + "build": "npm run build:npm", + "build:npm": "rollup --config rollup.config.npm.mjs", + "clean": "rm -rf dist/*" } } diff --git a/packages/plugin-browser-device/rollup.config.npm.mjs b/packages/plugin-browser-device/rollup.config.npm.mjs new file mode 100644 index 0000000000..5a577d0e74 --- /dev/null +++ b/packages/plugin-browser-device/rollup.config.npm.mjs @@ -0,0 +1,6 @@ +import createRollupConfig from '../../.rollup/index.mjs' + +export default createRollupConfig({ + input: 'src/device.ts', + external: ['@bugsnag/cuid'], +}) diff --git a/packages/plugin-browser-device/device.js b/packages/plugin-browser-device/src/device.ts similarity index 54% rename from packages/plugin-browser-device/device.js rename to packages/plugin-browser-device/src/device.ts index bd1437b970..f0ca9245f2 100644 --- a/packages/plugin-browser-device/device.js +++ b/packages/plugin-browser-device/src/device.ts @@ -1,35 +1,24 @@ -const assign = require('@bugsnag/core/lib/es-utils/assign') -const BUGSNAG_ANONYMOUS_ID_KEY = 'bugsnag-anonymous-id' +import type { Device, Plugin } from '@bugsnag/core' +import assign from '@bugsnag/core/lib/es-utils/assign' +import setDefaultUserId from './set-default-user-id' +import getDeviceId from './get-device-id' -const getDeviceId = (win) => { - try { - const storage = win.localStorage - - let id = storage.getItem(BUGSNAG_ANONYMOUS_ID_KEY) - - // If we get an ID, make sure it looks like a valid cuid. The length can - // fluctuate slightly, so some leeway is built in - if (id && /^c[a-z0-9]{20,32}$/.test(id)) { - return id - } - - const cuid = require('@bugsnag/cuid') - id = cuid() - - storage.setItem(BUGSNAG_ANONYMOUS_ID_KEY, id) - - return id - } catch (err) { - // If localStorage is not available (e.g. because it's disabled) then give up +// Declare deprecated navigator values +declare global { + interface Navigator { + browserLanguage?: string + systemLanguage?: string + userLanguage?: string } } /* * Automatically detects browser device details */ -module.exports = (nav = navigator, win = window) => ({ +export default (nav = navigator, win = window): Plugin => ({ + name: 'device', load: (client) => { - const device = { + const device: Device = { locale: nav.browserLanguage || nav.systemLanguage || nav.userLanguage || nav.language, userAgent: nav.userAgent } @@ -43,6 +32,7 @@ module.exports = (nav = navigator, win = window) => ({ : 'portrait' } + // @ts-expect-error _config is private API if (client._config.generateAnonymousId) { device.id = getDeviceId(win) } @@ -50,6 +40,7 @@ module.exports = (nav = navigator, win = window) => ({ client.addOnSession(session => { session.device = assign({}, session.device, device) // only set device id if collectUserIp is false + // @ts-expect-error _config is private API if (!client._config.collectUserIp) setDefaultUserId(session) }) @@ -60,22 +51,17 @@ module.exports = (nav = navigator, win = window) => ({ device, { time: new Date() } ) + // @ts-expect-error _config is private API if (!client._config.collectUserIp) setDefaultUserId(event) + // @ts-expect-error second parameter is private API }, true) }, + // @ts-expect-error _config is private API configSchema: { generateAnonymousId: { - validate: value => value === true || value === false, + validate: (value: unknown): value is boolean => value === true || value === false, defaultValue: () => true, message: 'should be true|false' } } }) - -const setDefaultUserId = (eventOrSession) => { - // device id is also used to populate the user id field, if it's not already set - const user = eventOrSession.getUser() - if (!user || !user.id) { - eventOrSession.setUser(eventOrSession.device.id) - } -} diff --git a/packages/plugin-browser-device/src/get-device-id.ts b/packages/plugin-browser-device/src/get-device-id.ts new file mode 100644 index 0000000000..1bdcdbedef --- /dev/null +++ b/packages/plugin-browser-device/src/get-device-id.ts @@ -0,0 +1,27 @@ +import cuid from '@bugsnag/cuid' + +const BUGSNAG_ANONYMOUS_ID_KEY = 'bugsnag-anonymous-id' + +const getDeviceId = (win: Window) => { + try { + const storage = win.localStorage + + let id = storage.getItem(BUGSNAG_ANONYMOUS_ID_KEY) + + // If we get an ID, make sure it looks like a valid cuid. The length can + // fluctuate slightly, so some leeway is built in + if (id && /^c[a-z0-9]{20,32}$/.test(id)) { + return id + } + + id = cuid() + + storage.setItem(BUGSNAG_ANONYMOUS_ID_KEY, id) + + return id + } catch (err) { + // If localStorage is not available (e.g. because it's disabled) then give up + } +} + +export default getDeviceId diff --git a/packages/plugin-browser-device/src/set-default-user-id.ts b/packages/plugin-browser-device/src/set-default-user-id.ts new file mode 100644 index 0000000000..abc08d9af2 --- /dev/null +++ b/packages/plugin-browser-device/src/set-default-user-id.ts @@ -0,0 +1,11 @@ +import type { Event, Session } from '@bugsnag/core' + +const setDefaultUserId = (eventOrSession: Event | Session) => { + // device id is also used to populate the user id field, if it's not already set + const user = eventOrSession.getUser() + if ((!user || !user.id) && eventOrSession.device) { + eventOrSession.setUser(eventOrSession.device.id) + } +} + +export default setDefaultUserId diff --git a/packages/plugin-browser-device/test/device.test.ts b/packages/plugin-browser-device/test/device.test.ts index 819a208243..35cf600f0a 100644 --- a/packages/plugin-browser-device/test/device.test.ts +++ b/packages/plugin-browser-device/test/device.test.ts @@ -1,4 +1,4 @@ -import plugin from '../device' +import plugin from '../src/device' import Client, { SessionDeliveryPayload, diff --git a/packages/plugin-browser-device/tsconfig.json b/packages/plugin-browser-device/tsconfig.json new file mode 100644 index 0000000000..c76cdab7cd --- /dev/null +++ b/packages/plugin-browser-device/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": { + "target": "ES2020" + } +} + \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 2253bf436b..ca14ba1a2a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -68,7 +68,6 @@ "packages/in-flight", "packages/plugin-aws-lambda", "packages/plugin-browser-context", - "packages/plugin-browser-device", "packages/plugin-contextualize", "packages/plugin-navigation-breadcrumbs", "packages/plugin-network-breadcrumbs",