github-webhook-handler 2.0.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/github-webhook-handler.js +60 -5
- package/package.json +18 -9
- package/tsconfig.json +33 -0
- package/types/github-webhook-handler.d.ts +47 -0
- package/types/github-webhook-handler.d.ts.map +1 -0
- package/github-webhook-handler.d.ts +0 -20
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
## [2.1.1](https://github.com/rvagg/github-webhook-handler/compare/v2.1.0...v2.1.1) (2026-04-02)
|
|
2
|
+
|
|
3
|
+
### Trivial Changes
|
|
4
|
+
|
|
5
|
+
* **deps-dev:** bump typescript from 5.9.3 to 6.0.2 ([864e973](https://github.com/rvagg/github-webhook-handler/commit/864e9739e3f9c7a45aabd806bf6475f267d32e0f))
|
|
6
|
+
* **deps:** update deps and upgrade to typescript 6 ([5a07637](https://github.com/rvagg/github-webhook-handler/commit/5a076370d0a47ee1b680e16228a7aae006297172))
|
|
7
|
+
|
|
8
|
+
## [2.1.0](https://github.com/rvagg/github-webhook-handler/compare/v2.0.0...v2.1.0) (2026-01-28)
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* add types via jsdoc tsc, remove old handrolled types ([#59](https://github.com/rvagg/github-webhook-handler/issues/59)) ([f8f20d8](https://github.com/rvagg/github-webhook-handler/commit/f8f20d864cf9529b2eebd953099967267b068ba4))
|
|
13
|
+
|
|
1
14
|
## [2.0.0](https://github.com/rvagg/github-webhook-handler/compare/v1.0.0...v2.0.0) (2026-01-28)
|
|
2
15
|
|
|
3
16
|
### ⚠ BREAKING CHANGES
|
|
@@ -2,6 +2,29 @@ import { EventEmitter } from 'node:events'
|
|
|
2
2
|
import crypto from 'node:crypto'
|
|
3
3
|
import bl from 'bl'
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Object} CreateHandlerOptions
|
|
7
|
+
* @property {string} path
|
|
8
|
+
* @property {string} secret
|
|
9
|
+
* @property {string | string[]} [events]
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {Object} WebhookEvent
|
|
14
|
+
* @property {string} event - The event type (e.g. 'push', 'issues')
|
|
15
|
+
* @property {string} id - The delivery ID from X-Github-Delivery header
|
|
16
|
+
* @property {any} payload - The parsed JSON payload
|
|
17
|
+
* @property {string} [protocol] - The request protocol
|
|
18
|
+
* @property {string} [host] - The request host header
|
|
19
|
+
* @property {string} url - The request URL
|
|
20
|
+
* @property {string} path - The matched handler path
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {string} url
|
|
25
|
+
* @param {CreateHandlerOptions | CreateHandlerOptions[]} arr
|
|
26
|
+
* @returns {CreateHandlerOptions}
|
|
27
|
+
*/
|
|
5
28
|
function findHandler (url, arr) {
|
|
6
29
|
if (!Array.isArray(arr)) {
|
|
7
30
|
return arr
|
|
@@ -17,6 +40,9 @@ function findHandler (url, arr) {
|
|
|
17
40
|
return ret
|
|
18
41
|
}
|
|
19
42
|
|
|
43
|
+
/**
|
|
44
|
+
* @param {CreateHandlerOptions} options
|
|
45
|
+
*/
|
|
20
46
|
function checkType (options) {
|
|
21
47
|
if (typeof options !== 'object') {
|
|
22
48
|
throw new TypeError('must provide an options object')
|
|
@@ -31,7 +57,12 @@ function checkType (options) {
|
|
|
31
57
|
}
|
|
32
58
|
}
|
|
33
59
|
|
|
60
|
+
/**
|
|
61
|
+
* @param {CreateHandlerOptions | CreateHandlerOptions[]} initOptions
|
|
62
|
+
* @returns {EventEmitter & {(req: import('node:http').IncomingMessage, res: import('node:http').ServerResponse, callback: (err?: Error) => void): void, sign(data: string | Buffer): string, verify(signature: string, data: string | Buffer): boolean}}
|
|
63
|
+
*/
|
|
34
64
|
function create (initOptions) {
|
|
65
|
+
/** @type {CreateHandlerOptions} */
|
|
35
66
|
let options
|
|
36
67
|
if (Array.isArray(initOptions)) {
|
|
37
68
|
for (let i = 0; i < initOptions.length; i++) {
|
|
@@ -41,18 +72,30 @@ function create (initOptions) {
|
|
|
41
72
|
checkType(initOptions)
|
|
42
73
|
}
|
|
43
74
|
|
|
75
|
+
// @ts-ignore - handler is a callable EventEmitter via setPrototypeOf
|
|
44
76
|
Object.setPrototypeOf(handler, EventEmitter.prototype)
|
|
77
|
+
// @ts-ignore
|
|
45
78
|
EventEmitter.call(handler)
|
|
46
79
|
|
|
47
80
|
handler.sign = sign
|
|
48
81
|
handler.verify = verify
|
|
49
82
|
|
|
83
|
+
// @ts-ignore
|
|
50
84
|
return handler
|
|
51
85
|
|
|
86
|
+
/**
|
|
87
|
+
* @param {string | Buffer} data
|
|
88
|
+
* @returns {string}
|
|
89
|
+
*/
|
|
52
90
|
function sign (data) {
|
|
53
91
|
return `sha1=${crypto.createHmac('sha1', options.secret).update(data).digest('hex')}`
|
|
54
92
|
}
|
|
55
93
|
|
|
94
|
+
/**
|
|
95
|
+
* @param {string} signature
|
|
96
|
+
* @param {string | Buffer} data
|
|
97
|
+
* @returns {boolean}
|
|
98
|
+
*/
|
|
56
99
|
function verify (signature, data) {
|
|
57
100
|
const sig = Buffer.from(signature)
|
|
58
101
|
const signed = Buffer.from(sign(data))
|
|
@@ -62,10 +105,16 @@ function create (initOptions) {
|
|
|
62
105
|
return crypto.timingSafeEqual(sig, signed)
|
|
63
106
|
}
|
|
64
107
|
|
|
108
|
+
/**
|
|
109
|
+
* @param {import('node:http').IncomingMessage} req
|
|
110
|
+
* @param {import('node:http').ServerResponse} res
|
|
111
|
+
* @param {(err?: Error) => void} callback
|
|
112
|
+
*/
|
|
65
113
|
function handler (req, res, callback) {
|
|
114
|
+
/** @type {string[] | undefined} */
|
|
66
115
|
let events
|
|
67
116
|
|
|
68
|
-
options = findHandler(req.url, initOptions)
|
|
117
|
+
options = findHandler(/** @type {string} */ (req.url), initOptions)
|
|
69
118
|
|
|
70
119
|
if (typeof options.events === 'string' && options.events !== '*') {
|
|
71
120
|
events = [options.events]
|
|
@@ -77,12 +126,16 @@ function create (initOptions) {
|
|
|
77
126
|
return callback()
|
|
78
127
|
}
|
|
79
128
|
|
|
129
|
+
/**
|
|
130
|
+
* @param {string} msg
|
|
131
|
+
*/
|
|
80
132
|
function hasError (msg) {
|
|
81
133
|
res.writeHead(400, { 'content-type': 'application/json' })
|
|
82
134
|
res.end(JSON.stringify({ error: msg }))
|
|
83
135
|
|
|
84
136
|
const err = new Error(msg)
|
|
85
137
|
|
|
138
|
+
// @ts-ignore - handler has EventEmitter prototype
|
|
86
139
|
handler.emit('error', err, req)
|
|
87
140
|
callback(err)
|
|
88
141
|
}
|
|
@@ -103,7 +156,7 @@ function create (initOptions) {
|
|
|
103
156
|
return hasError('No X-Github-Delivery found on request')
|
|
104
157
|
}
|
|
105
158
|
|
|
106
|
-
if (events && events.indexOf(event) === -1) {
|
|
159
|
+
if (events && events.indexOf(/** @type {string} */ (event)) === -1) {
|
|
107
160
|
return hasError('X-Github-Event is not acceptable')
|
|
108
161
|
}
|
|
109
162
|
|
|
@@ -114,14 +167,14 @@ function create (initOptions) {
|
|
|
114
167
|
|
|
115
168
|
let obj
|
|
116
169
|
|
|
117
|
-
if (!verify(sig, data)) {
|
|
170
|
+
if (!verify(/** @type {string} */ (sig), data)) {
|
|
118
171
|
return hasError('X-Hub-Signature does not match blob signature')
|
|
119
172
|
}
|
|
120
173
|
|
|
121
174
|
try {
|
|
122
175
|
obj = JSON.parse(data.toString())
|
|
123
176
|
} catch (e) {
|
|
124
|
-
return hasError(e)
|
|
177
|
+
return hasError(/** @type {Error} */ (e).message)
|
|
125
178
|
}
|
|
126
179
|
|
|
127
180
|
res.writeHead(200, { 'content-type': 'application/json' })
|
|
@@ -131,13 +184,15 @@ function create (initOptions) {
|
|
|
131
184
|
event,
|
|
132
185
|
id,
|
|
133
186
|
payload: obj,
|
|
134
|
-
protocol: req.protocol,
|
|
187
|
+
protocol: /** @type {any} */ (req).protocol,
|
|
135
188
|
host: req.headers.host,
|
|
136
189
|
url: req.url,
|
|
137
190
|
path: options.path
|
|
138
191
|
}
|
|
139
192
|
|
|
193
|
+
// @ts-ignore - handler has EventEmitter prototype
|
|
140
194
|
handler.emit(event, emitData)
|
|
195
|
+
// @ts-ignore - handler has EventEmitter prototype
|
|
141
196
|
handler.emit('*', emitData)
|
|
142
197
|
}))
|
|
143
198
|
}
|
package/package.json
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "github-webhook-handler",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Web handler / middleware for processing GitHub Webhooks",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "github-webhook-handler.js",
|
|
7
|
-
"exports":
|
|
8
|
-
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./github-webhook-handler.js",
|
|
10
|
+
"types": "./types/github-webhook-handler.d.ts"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"types": "types/github-webhook-handler.d.ts",
|
|
9
14
|
"engines": {
|
|
10
15
|
"node": ">=20"
|
|
11
16
|
},
|
|
12
17
|
"scripts": {
|
|
13
18
|
"lint": "standard",
|
|
14
|
-
"build": "
|
|
19
|
+
"build": "npm run build:types",
|
|
20
|
+
"build:types": "tsc --build",
|
|
21
|
+
"prepublishOnly": "npm run build",
|
|
15
22
|
"test:unit": "node --test test.js",
|
|
16
23
|
"test": "npm run lint && npm run test:unit"
|
|
17
24
|
},
|
|
@@ -32,12 +39,14 @@
|
|
|
32
39
|
"@semantic-release/changelog": "^6.0.3",
|
|
33
40
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
34
41
|
"@semantic-release/git": "^10.0.1",
|
|
35
|
-
"@semantic-release/github": "^12.0.
|
|
36
|
-
"@semantic-release/npm": "^13.1.
|
|
42
|
+
"@semantic-release/github": "^12.0.6",
|
|
43
|
+
"@semantic-release/npm": "^13.1.5",
|
|
37
44
|
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
45
|
+
"@types/node": "^25.5.0",
|
|
46
|
+
"conventional-changelog-conventionalcommits": "^9.3.0",
|
|
47
|
+
"semantic-release": "^25.0.3",
|
|
48
|
+
"standard": "^17.1.2",
|
|
49
|
+
"typescript": "^6.0.2"
|
|
41
50
|
},
|
|
42
51
|
"release": {
|
|
43
52
|
"branches": [
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"allowJs": true,
|
|
4
|
+
"checkJs": true,
|
|
5
|
+
"forceConsistentCasingInFileNames": true,
|
|
6
|
+
"noImplicitReturns": false,
|
|
7
|
+
"noImplicitAny": true,
|
|
8
|
+
"noImplicitThis": true,
|
|
9
|
+
"noFallthroughCasesInSwitch": true,
|
|
10
|
+
"noUnusedLocals": true,
|
|
11
|
+
"noUnusedParameters": true,
|
|
12
|
+
"strictFunctionTypes": false,
|
|
13
|
+
"strictNullChecks": true,
|
|
14
|
+
"strictPropertyInitialization": true,
|
|
15
|
+
"strictBindCallApply": true,
|
|
16
|
+
"strict": true,
|
|
17
|
+
"alwaysStrict": true,
|
|
18
|
+
"esModuleInterop": true,
|
|
19
|
+
"target": "ES2022",
|
|
20
|
+
"module": "NodeNext",
|
|
21
|
+
"moduleResolution": "NodeNext",
|
|
22
|
+
"declaration": true,
|
|
23
|
+
"declarationMap": true,
|
|
24
|
+
"outDir": "types",
|
|
25
|
+
"skipLibCheck": true,
|
|
26
|
+
"stripInternal": true,
|
|
27
|
+
"resolveJsonModule": true,
|
|
28
|
+
"emitDeclarationOnly": true
|
|
29
|
+
},
|
|
30
|
+
"include": ["github-webhook-handler.js"],
|
|
31
|
+
"exclude": ["node_modules"],
|
|
32
|
+
"compileOnSave": false
|
|
33
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export default create;
|
|
2
|
+
export type CreateHandlerOptions = {
|
|
3
|
+
path: string;
|
|
4
|
+
secret: string;
|
|
5
|
+
events?: string | string[] | undefined;
|
|
6
|
+
};
|
|
7
|
+
export type WebhookEvent = {
|
|
8
|
+
/**
|
|
9
|
+
* - The event type (e.g. 'push', 'issues')
|
|
10
|
+
*/
|
|
11
|
+
event: string;
|
|
12
|
+
/**
|
|
13
|
+
* - The delivery ID from X-Github-Delivery header
|
|
14
|
+
*/
|
|
15
|
+
id: string;
|
|
16
|
+
/**
|
|
17
|
+
* - The parsed JSON payload
|
|
18
|
+
*/
|
|
19
|
+
payload: any;
|
|
20
|
+
/**
|
|
21
|
+
* - The request protocol
|
|
22
|
+
*/
|
|
23
|
+
protocol?: string | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* - The request host header
|
|
26
|
+
*/
|
|
27
|
+
host?: string | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* - The request URL
|
|
30
|
+
*/
|
|
31
|
+
url: string;
|
|
32
|
+
/**
|
|
33
|
+
* - The matched handler path
|
|
34
|
+
*/
|
|
35
|
+
path: string;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* @param {CreateHandlerOptions | CreateHandlerOptions[]} initOptions
|
|
39
|
+
* @returns {EventEmitter & {(req: import('node:http').IncomingMessage, res: import('node:http').ServerResponse, callback: (err?: Error) => void): void, sign(data: string | Buffer): string, verify(signature: string, data: string | Buffer): boolean}}
|
|
40
|
+
*/
|
|
41
|
+
declare function create(initOptions: CreateHandlerOptions | CreateHandlerOptions[]): EventEmitter & {
|
|
42
|
+
(req: import("node:http").IncomingMessage, res: import("node:http").ServerResponse, callback: (err?: Error) => void): void;
|
|
43
|
+
sign(data: string | Buffer): string;
|
|
44
|
+
verify(signature: string, data: string | Buffer): boolean;
|
|
45
|
+
};
|
|
46
|
+
import { EventEmitter } from 'node:events';
|
|
47
|
+
//# sourceMappingURL=github-webhook-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-webhook-handler.d.ts","sourceRoot":"","sources":["../github-webhook-handler.js"],"names":[],"mappings":";;UAMc,MAAM;YACN,MAAM;;;;;;;WAMN,MAAM;;;;QACN,MAAM;;;;aACN,GAAG;;;;;;;;;;;;SAGH,MAAM;;;;UACN,MAAM;;AAwCpB;;;GAGG;AACH,qCAHW,oBAAoB,GAAG,oBAAoB,EAAE,GAC3C,YAAY,GAAG;IAAC,CAAC,GAAG,EAAE,OAAO,WAAW,EAAE,eAAe,EAAE,GAAG,EAAE,OAAO,WAAW,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI,CAAC;IAAC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAAC,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;CAAC,CAyIvP;6BAtM4B,aAAa"}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
|
|
3
|
-
import { IncomingMessage, ServerResponse } from 'node:http'
|
|
4
|
-
import { EventEmitter } from 'node:events'
|
|
5
|
-
|
|
6
|
-
interface CreateHandlerOptions {
|
|
7
|
-
path: string
|
|
8
|
-
secret: string
|
|
9
|
-
events?: string | string[]
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
interface Handler extends EventEmitter {
|
|
13
|
-
(req: IncomingMessage, res: ServerResponse, callback: (err?: Error) => void): void
|
|
14
|
-
sign(data: string | Buffer): string
|
|
15
|
-
verify(signature: string, data: string | Buffer): boolean
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
declare function createHandler (options: CreateHandlerOptions | CreateHandlerOptions[]): Handler
|
|
19
|
-
|
|
20
|
-
export default createHandler
|