feat(webhook): generic type for event emitter
Signed-off-by: Innei <i@innei.in>
This commit is contained in:
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
21
packages/webhook/readme.md
Normal file
21
packages/webhook/readme.md
Normal 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
|
||||
91
packages/webhook/scripts/generate.js
Normal file
91
packages/webhook/scripts/generate.js
Normal 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()
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user