diff --git a/aedes.d.ts b/aedes.d.ts index 0c0413a4..a520f9f3 100644 --- a/aedes.d.ts +++ b/aedes.d.ts @@ -80,6 +80,12 @@ declare namespace aedes { type PublishedHandler = (packet: AedesPublishPacket, client: Client, callback: (error?: Error | null) => void) => void + type LastHearthbeatTimestamp = Date; + + interface Brokers { + [brokerId: string]: LastHearthbeatTimestamp; + } + interface AedesOptions { mq?: any id?: string @@ -101,6 +107,7 @@ declare namespace aedes { id: string connectedClients: number closed: boolean + brokers: Brokers handle: (stream: Connection) => Client diff --git a/package.json b/package.json index 8ee57e24..3436b2e7 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "unit:report": "tap --no-esm -J test/*.js --cov --coverage-report=html --coverage-report=cobertura | tee out.tap", "typescript": "tsc --project ./test/types/tsconfig.json", "test:report": "npm run lint && npm run unit:report && npm run typescript", - "test": "npm run lint && npm run unit && npm run typescript", + "test": "npm run lint && npm run unit && npm run typescript && tsd", + "test:types": "tsd", "test:ci": "npm run lint && npm run unit -- --cov --coverage-report=lcovonly && npm run typescript", "license-checker": "license-checker --production --onlyAllow=\"MIT;ISC;BSD-3-Clause;BSD-2-Clause\"", "release": "read -p 'GITHUB_TOKEN: ' GITHUB_TOKEN && export GITHUB_TOKEN=$GITHUB_TOKEN && release-it --disable-metrics" @@ -38,6 +39,9 @@ "pre-commit": [ "test" ], + "tsd": { + "directory": "test/types" + }, "repository": { "type": "git", "url": "https://github.com/moscajs/aedes.git" @@ -103,6 +107,7 @@ "snazzy": "^9.0.0", "standard": "^16.0.3", "tap": "^14.11.0", + "tsd": "^0.14.0", "typescript": "^4.1.2", "websocket-stream": "^5.5.2" }, diff --git a/test/types/aedes.test-d.ts b/test/types/aedes.test-d.ts new file mode 100644 index 00000000..fd9d599c --- /dev/null +++ b/test/types/aedes.test-d.ts @@ -0,0 +1,163 @@ +/* eslint no-unused-vars: 0 */ +/* eslint no-undef: 0 */ + +import { expectType } from 'tsd' +import type { + Aedes, + Brokers, + Client, + Connection, + ConnackPacket, + AuthenticateError, + AedesPublishPacket, + PublishPacket, + Subscription, + SubscribePacket, + UnsubscribePacket +} from '../../aedes' +import { Server } from '../../aedes' +import type { Packet } from 'mqtt-packet' +import { Socket } from 'net' + +// Aedes server +const broker = Server({ + concurrency: 100, + heartbeatInterval: 60000, + connectTimeout: 30000, + id: 'aedes', + preConnect: (client: Client, packet: Packet, callback) => { + if (client.req) { + callback(new Error('not websocket stream'), false) + } + if (client.conn instanceof Socket && client.conn.remoteAddress === '::1') { + callback(null, true) + } else { + callback(new Error('connection error'), false) + } + }, + authenticate: (client: Client, username: string, password: Buffer, callback) => { + if (username === 'test' && password === Buffer.from('test') && client.version === 4) { + callback(null, true) + } else { + const error = new Error() as AuthenticateError + error.returnCode = 1 + + callback(error, false) + } + }, + authorizePublish: (client: Client, packet: PublishPacket, callback) => { + if (packet.topic === 'aaaa') { + return callback(new Error('wrong topic')) + } + + if (packet.topic === 'bbb') { + packet.payload = Buffer.from('overwrite packet payload') + } + + callback(null) + }, + authorizeSubscribe: (client: Client, sub: Subscription, callback) => { + if (sub.topic === 'aaaa') { + return callback(new Error('wrong topic')) + } + + if (sub.topic === 'bbb') { + // overwrites subscription + sub.qos = 2 + } + + callback(null, sub) + }, + authorizeForward: (client: Client, packet: AedesPublishPacket) => { + if (packet.topic === 'aaaa' && client.id === 'I should not see this') { + return null + // also works with return undefined + } else if (packet.topic === 'aaaa' && client.id === 'I should not see this either') { + return + } + + if (packet.topic === 'bbb') { + packet.payload = Buffer.from('overwrite packet payload') + } + + return packet + } +}) + +expectType(broker) + +expectType(broker.brokers) + +expectType(broker.on('closed', () => {})) +expectType(broker.on('client', (client: Client) => {})) +expectType(broker.on('clientError', (client: Client, error: Error) => {})) +expectType(broker.on('connackSent', (packet: ConnackPacket, client: Client) => {})) + +expectType(broker.publish( + {} as PublishPacket, + (error?: Error) => { + if (error) { + console.error(error) + } + } +)) + +expectType(broker.subscribe( + 'topic', + (packet: AedesPublishPacket, callback: () => void) => {}, + () => {} +)) + +expectType(broker.unsubscribe( + 'topic', + (packet: AedesPublishPacket, callback: () => void) => {}, + () => {} +)) + +expectType(broker.close()) +expectType(broker.close(() => {})) + +// Aedes client +const client = broker.handle({} as Connection) + +expectType(client) + +expectType(client.conn) + +expectType(client.on('connected', () => {})) +expectType(client.on('error', (error: Error) => { + if (error) { + console.error(error) + } +})) + +expectType(client.publish({} as PublishPacket, (error?: Error) => { + if (error) { + console.error(error) + } +})) +expectType(client.publish({} as PublishPacket)) + +expectType(client.subscribe({} as Subscription, (error?: Error) => { + if (error) { + console.error(error) + } +})) +expectType(client.subscribe({} as Subscription)) +expectType(client.subscribe([] as Subscription[])) +expectType(client.subscribe({} as SubscribePacket)) + +expectType(client.unsubscribe({} as Subscription, (error?: Error) => { + if (error) { + console.error(error) + } +})) +expectType(client.unsubscribe({} as Subscription)) +expectType(client.unsubscribe([] as Subscription[])) +expectType(client.unsubscribe({} as UnsubscribePacket)) + +expectType(client.emptyOutgoingQueue()) +expectType(client.emptyOutgoingQueue(() => {})) + +expectType(client.close()) +expectType(client.close(() => {})) diff --git a/test/types/index.ts b/test/types/index.ts index b0dd7e40..fccff60c 100644 --- a/test/types/index.ts +++ b/test/types/index.ts @@ -69,6 +69,8 @@ const broker = Server({ } }) +const { brokers } = broker + const server = createServer(broker.handle) broker.on('closed', () => {