feat(webhook): generic type for event emitter

Signed-off-by: Innei <i@innei.in>
This commit is contained in:
Innei
2023-12-24 22:25:19 +08:00
parent b8a8c35d12
commit cfc3513b92
7 changed files with 144 additions and 7 deletions

View File

@@ -4,11 +4,12 @@
"devDependencies": {
"express": "4.18.2"
},
"type": "module",
"main": "dist/index.cjs",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsup && node scripts/post-build.js"
"build": "node scripts/generate.js && tsup && node scripts/post-build.cjs"
},
"bump": {
"before": [
@@ -34,4 +35,4 @@
},
"./package.json": "./package.json"
}
}
}

View File

@@ -0,0 +1,21 @@
# Mix Space Core Webhook SDK
```bash
pnpm install @mx-space/webhook
```
## Usage
```ts
const handler = createHandler({
secret: 'your_secret',
})
ctx.server.post('/mx/webhook', (req, res) => {
handler(req.raw, res.raw)
})
handler.emitter.on(event, callback)
```
## MIT

View File

@@ -0,0 +1,91 @@
// const fs = require('fs')
// const path = require('path')
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
import prettier from 'prettier'
import ts from 'typescript'
function generateGenericEventType(fileName) {
const program = ts.createProgram([fileName], {})
const sourceFile = program.getSourceFile(fileName)
let genericEventType = 'export type GenericEvent =\n'
ts.forEachChild(sourceFile, (node) => {
if (
ts.isInterfaceDeclaration(node) &&
node.name.text === 'EventPayloadMapping'
) {
node.members.forEach((member) => {
if (ts.isPropertySignature(member) && member.type) {
const key = member.name
let eventType = ''
if (
ts.isComputedPropertyName(key) &&
ts.isPropertyAccessExpression(key.expression)
) {
eventType = key.expression.name.text
}
if (eventType && eventType !== '*') {
const payloadType = member.type.getText(sourceFile)
genericEventType += ` | { type: BusinessEvents.${eventType}; payload: ${payloadType} }\n`
}
}
})
}
})
return genericEventType
}
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const fileName = path.resolve(__dirname, '../src/types.ts')
const newGenericEventType = generateGenericEventType(fileName)
function replaceGenericEventType(fileName, newGenericEventType) {
const fileContent = fs.readFileSync(fileName, 'utf8')
const sourceFile = ts.createSourceFile(
fileName,
fileContent,
ts.ScriptTarget.Latest,
true,
)
let startPos = -1
let endPos = -1
ts.forEachChild(sourceFile, (node) => {
if (ts.isTypeAliasDeclaration(node) && node.name.text === 'GenericEvent') {
startPos = node.pos
endPos = node.end
}
})
if (startPos === -1 || endPos === -1) {
throw new Error('GenericEvent type not found in the file.')
}
// Replace the specified part of the file content with the new type string
return `${fileContent.slice(0, startPos)}\n\n${
// Add a new line before the new type
newGenericEventType
}${fileContent.slice(endPos)}\n`
}
// const newFileContent = replaceGenericEventType(fileName, newGenericEventType)
const newFileContent = replaceGenericEventType(fileName, newGenericEventType)
async function main() {
fs.writeFileSync(
path.resolve(__dirname, '../src/types.ts'),
await prettier.format(newFileContent, {
parser: 'typescript',
semi: false,
tabWidth: 2,
printWidth: 80,
singleQuote: true,
trailingComma: 'all',
}),
)
}
main()

View File

@@ -11,11 +11,7 @@ interface CreateHandlerOptions {
}
type Handler = {
(
req: IncomingMessage,
res: ServerResponse,
callback: (err: Error) => void,
): void
(req: IncomingMessage, res: ServerResponse): void
} & {
emitter: ExtendedEventEmitter
}
@@ -41,6 +37,10 @@ export const createHandler = (options: CreateHandlerOptions): Handler => {
if (isValid) {
handler.emitter.emit(event as BusinessEvents, obj)
handler.emitter.emit('*', {
event,
payload: obj,
})
res.statusCode = 200
res.end()
} else {

View File

@@ -43,7 +43,10 @@ export interface EventPayloadMapping {
[BusinessEvents.COMMENT_CREATE]: Omit<CommentModel, 'ref'> & {
ref: Id | PostModel | PageModel | NoteModel | RecentlyModel
}
'*': GenericEvent
}
export interface IActivityLike {
id: string
type: 'Note' | 'Post'
@@ -53,3 +56,24 @@ export interface IActivityLike {
title: string
}
}
export type GenericEvent =
| { type: BusinessEvents.POST_CREATE; payload: NormalizedPost }
| { type: BusinessEvents.POST_UPDATE; payload: NormalizedPost }
| { type: BusinessEvents.POST_DELETE; payload: PayloadOnlyId }
| { type: BusinessEvents.NOTE_CREATE; payload: NormalizedNote }
| { type: BusinessEvents.NOTE_UPDATE; payload: NormalizedNote }
| { type: BusinessEvents.NOTE_DELETE; payload: PayloadOnlyId }
| { type: BusinessEvents.PAGE_CREATE; payload: PageModel }
| { type: BusinessEvents.PAGE_UPDATE; payload: PageModel }
| { type: BusinessEvents.PAGE_DELETE; payload: PayloadOnlyId }
| { type: BusinessEvents.SAY_CREATE; payload: SayModel }
| { type: BusinessEvents.RECENTLY_CREATE; payload: RecentlyModel }
| { type: BusinessEvents.ACTIVITY_LIKE; payload: IActivityLike }
| { type: BusinessEvents.LINK_APPLY; payload: LinkModel }
| {
type: BusinessEvents.COMMENT_CREATE
payload: Omit<CommentModel, 'ref'> & {
ref: Id | PostModel | PageModel | NoteModel | RecentlyModel
}
}