Skip to content

Commit e949b87

Browse files
Convert @bugsnag/plugin-server-session to TypeScript (#2514)
* convert server session to typescript * update package-lock * add type declaration file for backo * update types * update types * update types and tests * update types
1 parent e3d49c4 commit e949b87

File tree

10 files changed

+129
-33
lines changed

10 files changed

+129
-33
lines changed

package-lock.json

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/plugin-server-session/package.json

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
{
22
"name": "@bugsnag/plugin-server-session",
33
"version": "8.4.0",
4-
"main": "session.js",
4+
"main": "dist/session.js",
5+
"types": "dist/types/session.d.ts",
6+
"exports": {
7+
".": {
8+
"types": "./dist/types/session.d.ts",
9+
"default": "./dist/session.js",
10+
"import": "./dist/session.mjs"
11+
}
12+
},
513
"description": "@bugsnag/js plugin to enable session tracking in server applications",
614
"homepage": "https://www.bugsnag.com/",
715
"repository": {
@@ -12,7 +20,7 @@
1220
"access": "public"
1321
},
1422
"files": [
15-
"*.js"
23+
"dist"
1624
],
1725
"author": "Bugsnag",
1826
"license": "MIT",
@@ -24,5 +32,11 @@
2432
},
2533
"peerDependencies": {
2634
"@bugsnag/core": "^8.0.0"
35+
},
36+
"scripts": {
37+
"build": "npm run build:npm",
38+
"build:npm": "rollup --config rollup.config.npm.mjs",
39+
"clean": "rm -rf dist/*",
40+
"test:types": "tsc -p tsconfig.json"
2741
}
2842
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import createRollupConfig from "../../.rollup/index.mjs";
2+
3+
export default createRollupConfig({
4+
input: "src/session.ts",
5+
external: ["events", "backo"],
6+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
declare module 'backo' {
2+
interface BackoffOptions {
3+
min?: number
4+
max?: number
5+
jitter?: number
6+
factor?: number
7+
}
8+
9+
class Backoff {
10+
attempts: number
11+
12+
constructor(options?: BackoffOptions)
13+
14+
duration(): number
15+
reset(): void
16+
}
17+
18+
export = Backoff
19+
}

packages/plugin-server-session/session.js renamed to packages/plugin-server-session/src/session.ts

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,26 @@
1-
const { intRange, runSyncCallbacks } = require('@bugsnag/core')
2-
const SessionTracker = require('./tracker')
3-
const Backoff = require('backo')
1+
import { App, Client, Config, Device, Notifier, Plugin, Session, intRange, runSyncCallbacks } from '@bugsnag/core'
2+
import SessionTracker from './tracker'
3+
import Backoff from 'backo'
44

5-
module.exports = {
5+
interface PluginConfig extends Config {
6+
sessionSummaryInterval?: number
7+
}
8+
9+
interface SessionCount {
10+
startedAt: string
11+
sessionsStarted: number
12+
}
13+
14+
interface SessionPayload extends Session{
15+
notifier: Notifier
16+
device: Device
17+
app: App
18+
sessionCounts: SessionCount[]
19+
}
20+
21+
const plugin: Plugin<PluginConfig> = {
622
load: (client) => {
7-
const sessionTracker = new SessionTracker(client._config.sessionSummaryInterval)
23+
const sessionTracker = new SessionTracker(client._config.sessionSummaryInterval ?? undefined)
824
sessionTracker.on('summary', sendSessionSummary(client))
925
sessionTracker.start()
1026
client._sessionDelegate = {
@@ -31,8 +47,9 @@ module.exports = {
3147
return client
3248
}
3349

34-
// Otherwise start a new session
35-
return client.startSession()
50+
// Otherwise start a new session and ensure a Client is always returned
51+
const newClient = client.startSession()
52+
return newClient || client
3653
}
3754
}
3855
},
@@ -45,7 +62,7 @@ module.exports = {
4562
}
4663
}
4764

48-
const sendSessionSummary = client => sessionCounts => {
65+
const sendSessionSummary = (client: Client) => (sessionCounts: SessionCount[]): void => {
4966
// exit early if the current releaseStage is not enabled
5067
if (client._config.enabledReleaseStages !== null && !client._config.enabledReleaseStages.includes(client._config.releaseStage)) {
5168
client._logger.warn('Session not sent due to releaseStage/enabledReleaseStages configuration')
@@ -58,7 +75,7 @@ const sendSessionSummary = client => sessionCounts => {
5875
const maxAttempts = 10
5976
req(handleRes)
6077

61-
function handleRes (err) {
78+
function handleRes (err?: Error | null): void {
6279
if (!err) {
6380
const sessionCount = sessionCounts.reduce((accum, s) => accum + s.sessionsStarted, 0)
6481
return client._logger.debug(`${sessionCount} session(s) reported`)
@@ -67,11 +84,11 @@ const sendSessionSummary = client => sessionCounts => {
6784
client._logger.error('Session delivery failed, max retries exceeded', err)
6885
return
6986
}
70-
client._logger.debug('Session delivery failed, retry #' + (backoff.attempts + 1) + '/' + maxAttempts, err)
87+
client._logger.error('Session delivery failed, retry #' + (backoff.attempts + 1) + '/' + maxAttempts, err)
7188
setTimeout(() => req(handleRes), backoff.duration())
7289
}
7390

74-
function req (cb) {
91+
function req (cb: (err?: Error | null) => void) {
7592
const payload = {
7693
notifier: client._notifier,
7794
device: {},
@@ -83,7 +100,7 @@ const sendSessionSummary = client => sessionCounts => {
83100
sessionCounts
84101
}
85102

86-
const ignore = runSyncCallbacks(client._cbs.sp, payload, 'onSessionPayload', client._logger)
103+
const ignore = runSyncCallbacks(client._cbs.sp, payload as SessionPayload, 'onSessionPayload', client._logger)
87104
if (ignore) {
88105
client._logger.debug('Session not sent due to onSessionPayload callback')
89106
return cb(null)
@@ -92,3 +109,5 @@ const sendSessionSummary = client => sessionCounts => {
92109
client._delivery.sendSession(payload, cb)
93110
}
94111
}
112+
113+
export default plugin

packages/plugin-server-session/tracker.js renamed to packages/plugin-server-session/src/tracker.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1+
import { Session } from '@bugsnag/core'
2+
import EventEmitter from 'events'
3+
14
const DEFAULT_SUMMARY_INTERVAL = 10 * 1000
2-
const Emitter = require('events').EventEmitter
35

4-
module.exports = class SessionTracker extends Emitter {
5-
constructor (intervalLength) {
6+
class SessionTracker extends EventEmitter {
7+
private _sessions: Map<string, number>
8+
private _interval: NodeJS.Timeout | null
9+
private _intervalLength?: number
10+
11+
constructor (intervalLength?: number) {
612
super()
713
this._sessions = new Map()
814
this._interval = null
@@ -18,19 +24,21 @@ module.exports = class SessionTracker extends Emitter {
1824
}
1925

2026
stop () {
21-
clearInterval(this._interval)
27+
if (this._interval !== null) {
28+
clearInterval(this._interval)
29+
}
2230
this._interval = null
2331
}
2432

25-
track (session) {
33+
track (session: Session) {
2634
const key = dateToMsKey(session.startedAt)
2735
const cur = this._sessions.get(key)
2836
this._sessions.set(key, typeof cur === 'undefined' ? 1 : cur + 1)
2937
return session
3038
}
3139

3240
_summarize () {
33-
const summary = []
41+
const summary: Array<{ startedAt: string, sessionsStarted: number }> = []
3442
this._sessions.forEach((val, key) => {
3543
summary.push({ startedAt: key, sessionsStarted: val })
3644
this._sessions.delete(key)
@@ -40,9 +48,11 @@ module.exports = class SessionTracker extends Emitter {
4048
}
4149
}
4250

43-
const dateToMsKey = (d) => {
51+
const dateToMsKey = (d: Date) => {
4452
const dk = new Date(d)
4553
dk.setSeconds(0)
4654
dk.setMilliseconds(0)
4755
return dk.toISOString()
4856
}
57+
58+
export default SessionTracker

packages/plugin-server-session/test/session.test.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1+
import { Client, Session } from '@bugsnag/core'
12
import { EventEmitter } from 'events'
2-
import { Client } from '@bugsnag/core'
3-
import _Tracker from '../tracker'
4-
import plugin from '../session'
5-
import { Session } from '@bugsnag/core'
3+
import plugin from '../src/session'
4+
import _Tracker from '../src/tracker'
65

76
const Tracker = _Tracker as jest.MockedClass<typeof _Tracker>
87

9-
jest.mock('../tracker')
8+
jest.mock('../src/tracker')
109

1110
describe('plugin: server sessions', () => {
1211
beforeEach(() => {

packages/plugin-server-session/test/tracker.test.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import Tracker from '../tracker'
21
import { Session } from '@bugsnag/core'
32
import timekeeper from 'timekeeper'
3+
import Tracker from '../src/tracker'
44

55
describe('session tracker', () => {
66
it('should track sessions and summarize per minute', done => {
@@ -28,13 +28,34 @@ describe('session tracker', () => {
2828
})
2929

3030
it('should only start one interval', () => {
31-
const t = new Tracker(5)
31+
jest.useFakeTimers()
32+
const t = new Tracker(100)
33+
let summaryEmissionCount = 0
34+
35+
t.track(new Session())
36+
t.track(new Session())
37+
38+
t.on('summary', () => {
39+
summaryEmissionCount++
40+
})
41+
3242
t.start()
33-
const i0 = t._interval
3443
t.start()
35-
expect(i0).toBe(t._interval)
44+
t.start()
45+
46+
jest.advanceTimersByTime(100)
47+
expect(summaryEmissionCount).toBe(1)
48+
49+
t.track(new Session())
50+
jest.advanceTimersByTime(100)
51+
expect(summaryEmissionCount).toBe(2)
52+
3653
t.stop()
37-
expect(t._interval).toBe(null)
54+
t.track(new Session())
55+
jest.advanceTimersByTime(200)
56+
expect(summaryEmissionCount).toBe(2)
57+
58+
jest.useRealTimers()
3859
})
3960

4061
afterEach(() => timekeeper.reset())
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "../../tsconfig.json",
3+
"include": ["src/**/*.ts"],
4+
"compilerOptions": {
5+
"lib": ["es2017"],
6+
"types": ["node"]
7+
}
8+
}

tsconfig.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
"packages/plugin-aws-lambda",
2222
"packages/plugin-contextualize",
2323
"packages/plugin-navigation-breadcrumbs",
24-
"packages/plugin-server-session",
2524
"packages/plugin-react",
2625
"packages/plugin-vue",
2726
"packages/plugin-express",

0 commit comments

Comments
 (0)