├── .editorconfig ├── .github └── FUNDING.yml ├── .gitignore ├── .vscode ├── settings.json └── tasks.json ├── Gruntfile.js ├── README.md ├── bower.json ├── lib ├── browser.js ├── emitter.d.ts ├── emitter.js ├── emitter.js.map └── emitter.ts ├── package-lock.json ├── package.json ├── sample ├── emitter.js └── index.html ├── tsconfig.json ├── tsd.json └── typings ├── node └── node.d.ts └── tsd.d.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | root = true 3 | 4 | [*] 5 | end_of_line = lf 6 | insert_final_newline = false 7 | charset = utf-8 8 | indent_style = space 9 | indent_size = 4 10 | 11 | [*.json] 12 | indent_size = 2 -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [kelindar] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 28 | node_modules 29 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "**/.git": true, 4 | "conf": true, 5 | "gulp_tasks": true, 6 | "**/gulpfile.js": true, 7 | "**/index.spec.js": true, 8 | "tslint.json": true, 9 | "**/.DS_Store": true, 10 | "**/*.js.map": true, 11 | "**/.*": true, 12 | "node_modules": true, 13 | "**/*.js": { 14 | "when": "$(basename).ts" 15 | }, 16 | "**/*?.js": { 17 | "when": "$(basename).tsx" 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "command": "tsc", 4 | "isShellCommand": true, 5 | "showOutput": "silent", 6 | "args": [], 7 | "problemMatcher": "$tsc" 8 | } -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | // Project configuration. 4 | grunt.initConfig({ 5 | browserify: { 6 | options: { 7 | alias: { 8 | 'emitter': '.' 9 | } 10 | }, 11 | js: { 12 | src: '.', 13 | dest: 'build/emitter.js', 14 | } 15 | }, 16 | 17 | concat: { 18 | dist: { 19 | src: ['build/emitter.js', 'lib/browser.js'], 20 | dest: 'build/emitter.js', 21 | }, 22 | }, 23 | 24 | 25 | uglify: { 26 | options: { 27 | mangle: true, 28 | compress: { 29 | sequences: true, 30 | dead_code: true, 31 | conditionals: true, 32 | booleans: true, 33 | unused: true, 34 | if_return: true, 35 | join_vars: true, 36 | drop_console: true 37 | } 38 | 39 | }, 40 | js: { 41 | files: { 'build/emitter.min.js': ['build/emitter.js'] } 42 | } 43 | }, 44 | 45 | watch: { 46 | scripts: { 47 | files: ['lib/*.js'], 48 | tasks: ['default'], 49 | options: { spawn: false }, 50 | }, 51 | }, 52 | 53 | compress: { 54 | main: { 55 | options: { 56 | mode: 'gzip' 57 | }, 58 | files: [ 59 | 60 | { expand: true, src: ['build/*.min.js'], dest: '', ext: '.gz.js' } 61 | ] 62 | } 63 | }, 64 | 65 | copy: { 66 | js: { 67 | files: [ 68 | { 69 | cwd: 'build/', 70 | src: 'emitter.js', 71 | dest: 'sample/', 72 | expand: true } 73 | ] 74 | } 75 | } 76 | 77 | }); 78 | 79 | grunt.loadNpmTasks('grunt-browserify'); 80 | grunt.loadNpmTasks('grunt-contrib-uglify'); 81 | grunt.loadNpmTasks('grunt-contrib-watch'); 82 | grunt.loadNpmTasks('grunt-contrib-compress'); 83 | grunt.loadNpmTasks('grunt-contrib-concat'); 84 | grunt.loadNpmTasks('grunt-contrib-copy'); 85 | 86 | grunt.registerTask('default', ['browserify', 'concat', 'uglify', 'compress', 'copy:js']); 87 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![NPM](https://nodei.co/npm/emitter-io.png)](https://nodei.co/npm/emitter-io/) 2 | [![NPM](https://nodei.co/npm-dl/emitter-io.png)](https://nodei.co/npm/emitter-io/) 3 | 4 | This repository contains JavaScript client for both NodeJS and the Browser for [Emitter](https://emitter.io) (see also on [Emitter GitHub](https://github.com/emitter-io/emitter)). Emitter is an **open-source** real-time communication service for connecting online devices. At its core, emitter.io is a distributed, scalable and fault-tolerant publish-subscribe messaging platform based on MQTT protocol and featuring message storage. 5 | 6 | * [Installation](#install) 7 | * [Example](#example) 8 | * [API](#api) 9 | * [License](#license) 10 | 11 | 12 | ## Installation 13 | 14 | Emitter for NodeJS: 15 | ``` 16 | npm install emitter-io --save 17 | ``` 18 | 19 | Emitter for the Browser: 20 | * [http://cdn.emitter.io/js/emitter.js](http://cdn.emitter.io/js/emitter.js) 21 | * [http://cdn.emitter.io/js/emitter.min.js](http://cdn.emitter.io/js/emitter.min.js) 22 | 23 | 24 | ## Example 25 | 26 | ``` 27 | // connect to emitter.io and get the client 28 | var client = emitter.connect(); // or: require('emitter-io') on NodeJS 29 | 30 | // once we're connected, subscribe to the 'chat' channel 31 | client.subscribe({ 32 | key: "", 33 | channel: "chat" 34 | }); 35 | 36 | // on every message, print it out 37 | client.on('message', function(msg){ 38 | console.log( msg.asString() ); 39 | }); 40 | 41 | // publish a message to the chat channel 42 | client.publish({ 43 | key: "", 44 | channel: "chat/my_name", 45 | message: "hello, emitter!" 46 | }); 47 | ``` 48 | 49 | ## API 50 | * connect() 51 | * Emitter() 52 | * Emitter#disconnect() 53 | * Emitter#publish() 54 | * Emitter#subscribe() 55 | * Emitter#unsubscribe() 56 | * Emitter#keygen() 57 | * Emitter#link() 58 | * Emitter#me() 59 | * Emitter#presence() 60 | * EmitterMessage() 61 | * EmitterMessage#asString() 62 | * EmitterMessage#asBinary() 63 | * EmitterMessage#asObject() 64 | 65 | ------------------------------------------------------- 66 | 67 | ### connect(host: string, port: number) 68 | 69 | Connects to the emitter api broker specified by the given url and options and returns an [Emitter](#emitter) instance. The URL can be on the following protocols: 'mqtt', 'mqtts', 'tcp', 'tls', 'ws', 'wss'. The URL can also be an object as returned by [`URL.parse()`](http://nodejs.org/api/url.html#url_url_parse_urlstr_parsequerystring_slashesdenotehost), in that case the two objects are merged, i.e. you can pass a single object with both the URL and the connect options. 70 | 71 | ------------------------------------------------------- 72 | 73 | ### Emitter() 74 | 75 | The `Emitter` class wraps a client connection to an emitter.io MQTT broker over an arbitrary transport method (TCP, TLS, WebSocket, ecc). It automatically handles the following by with help of MQTT.js client: 76 | * Regular server pings 77 | * QoS flow 78 | * Automatic reconnections 79 | * Start publishing before being connected 80 | 81 | 82 | #### Event `'connect'` 83 | 84 | `function(connack) {}` 85 | 86 | Emitted on successful (re)connection (i.e. connack rc=0). 87 | * `connack` received connack packet. When `clean` connection option is `false` and server has a previous session 88 | for `clientId` connection option, then `connack.sessionPresent` flag is `true`. When that is the case, 89 | you may rely on stored session and prefer not to send subscribe commands for the client. 90 | 91 | #### Event `'disconnect'` 92 | 93 | `function() {}` 94 | 95 | Emitted after a disconnection. 96 | 97 | #### Event `'offline'` 98 | 99 | `function() {}` 100 | 101 | Emitted when the client goes offline. 102 | 103 | #### Event `'error'` 104 | 105 | `function(error) {}` 106 | 107 | Emitted when the client cannot connect (i.e. connack rc != 0) or when a parsing error occurs. 108 | 109 | #### Event `'keygen'` 110 | 111 | `function(keyJson) {}` 112 | 113 | Emitted when the client generate a key to a channel using Emitter#keygen() function. 114 | 115 | ### Event `'message'` 116 | 117 | `function(message) {}` 118 | 119 | Emitted when the client receives a message packet. The message object will be of [EmitterMessage](#message) class, encapsulating the channel and the payload. 120 | 121 | ------------------------------------------------------- 122 | 123 | ### Emitter#disconnect() 124 | 125 | Disconnects from the remote broker 126 | 127 | ------------------------------------------------------- 128 | 129 | ### Emitter#link({ key: string; channel: string; name: string; private: boolean; message: any; ttl?: number; me?: boolean; }) 130 | 131 | Creates a 2-character link to a channel. The channel may be private. For more information about this feature, see [Emitter: Simplify Client/Server and IoT Apps with Links and Private Links (on YouTube)](https://youtu.be/_FgKiUlEb_s) and the [Emitter Pull Request (on GitHub)](https://github.com/emitter-io/emitter/pull/183). 132 | 133 | * `key` is security key to use for the operation, `String` 134 | * `channel` is the channel string to publish to, `String` 135 | * `name` is the 2-character name of the link, `String` 136 | * `private` requests the creation of a private channel, `Boolean` 137 | * `message` is the message to publish, `Buffer` or `String` 138 | * `ttl` is the time to live of the messages that will be sent through the link, `Number`. 139 | * `me` tells whether the messages sent through the link should be also sent to the publisher, `Boolean`. By default it is set to `true`. 140 | 141 | See also `publishWithLink()`. 142 | 143 | ------------------------------------------------------- 144 | 145 | ### Emitter#publish({ key: string; channel: string; message: any; ttl?: number; me?: boolean; }) 146 | 147 | Publishes a message to a channel 148 | * `key` is security key to use for the operation, `String` 149 | * `channel` is the channel string to publish to, `String` 150 | * `message` is the message to publish, `Buffer` or `String` 151 | * `ttl` is the time to live of the message, `Number` 152 | * `me` tells whether the messages should be also sent to the publisher, `Boolean`. By default it is set to `true`. 153 | 154 | ------------------------------------------------------- 155 | 156 | ### Emitter#publishWithLink({ link: string; message: any; }) 157 | 158 | Publishes a message to a link. 159 | * `link` is the name of the link, `String` 160 | * `message` is the message to publish, `Buffer` or `String` 161 | 162 | See also `link()`. 163 | 164 | ------------------------------------------------------- 165 | 166 | ### Emitter#subscribe({ key: string; channel: string; }) 167 | 168 | Subscribes to a channel 169 | * `key` is security key to use for the operation, `String` 170 | * `channel` is the channel string to subscribe to, `String` 171 | 172 | ------------------------------------------------------- 173 | 174 | ### Emitter#unsubscribe({ key: string; channel: string; }) 175 | 176 | Unsubscribes from a channel 177 | * `key` is security key to use for the operation, `String` 178 | * `channel` is the channel string to unsubscribe from, `String` 179 | 180 | ------------------------------------------------------- 181 | 182 | ### Emitter#keygen({ key: string; channel: string; type: string; ttl: number; }) 183 | 184 | Sends a key generation request to the server. 185 | * `key` is **master/secret key** to use for the operation, `String` 186 | * `channel` is the channel string to generate a key for, `String` 187 | * `type` the type of the key to generate. Possible options include `r` for read-only, `w` for write-only, `p` for presence only and `rw` for read-write keys (In addition to `rw`, you can use any combination of `r`, `w` and `p` for key generation), `String` 188 | * `ttl` is the time-to-live of the key, in seconds. 189 | 190 | ------------------------------------------------------- 191 | 192 | ### Emitter#me() 193 | 194 | Retrieves information about the underlying client connection. Information includes the client ID and the links created by the client. 195 | 196 | ------------------------------------------------------- 197 | 198 | ### Emitter#presence({ key: string; channel: string; status: boolean; changes: boolean; }) 199 | 200 | Requests the presence for a particular channel. 201 | * `key` is **master/secret key** to use for the operation, `String` 202 | * `channel` is the channel string to generate a key for, `String` 203 | * `status` whether the current state should be retrieved or not 204 | * `changes` whether the future changes should be received or not 205 | 206 | ------------------------------------------------------- 207 | 208 | ### EmitterMessage() 209 | 210 | The `EmitterMessage` class wraps a message received from the broker. It contains several properties: 211 | * `channel` is channel the message was published to, `String` 212 | * `binary` is the buffer associated with the payload, `Buffer` 213 | 214 | ------------------------------------------------------- 215 | 216 | ### EmitterMessage#asString() 217 | 218 | Returns the payload as a utf-8 `String`. 219 | 220 | ------------------------------------------------------- 221 | 222 | ### EmitterMessage#asBinary() 223 | 224 | Returns the payload as the `Buffer`. 225 | 226 | ------------------------------------------------------- 227 | 228 | ### EmitterMessage#asObject() 229 | 230 | Returns the payload as JSON-deserialized `Object`. 231 | 232 | 233 | ## License 234 | 235 | The MIT License (MIT) 236 | Copyright (c) 2016 Misakai Ltd. 237 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "emitter-io", 3 | "description": "NodeJS MQTT client to use with emitter.io platform", 4 | "main": "lib/emitter.js", 5 | "authors": [ 6 | "Misakai Ltd." 7 | ], 8 | "license": "MIT", 9 | "homepage": "https://github.com/emitter-io/javascript.git", 10 | "ignore": [ 11 | "**/.*", 12 | "node_modules", 13 | "bower_components", 14 | "test", 15 | "tests" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /lib/browser.js: -------------------------------------------------------------------------------- 1 | var emitter = require('emitter'); -------------------------------------------------------------------------------- /lib/emitter.d.ts: -------------------------------------------------------------------------------- 1 | export declare class Emitter { 2 | private _mqtt; 3 | private _callbacks; 4 | /** 5 | * Connects to the emitter service. 6 | */ 7 | connect(request?: ConnectRequest, handler?: () => void): Emitter; 8 | /** 9 | * Disconnects the client. 10 | */ 11 | disconnect(): Emitter; 12 | /** 13 | * Publishes a message to the currently opened endpoint. 14 | */ 15 | publish(request: PublishRequest): Emitter; 16 | /** 17 | * Publishes a message througth a link. 18 | */ 19 | publishWithLink(request: PublishWithLinkRequest): Emitter; 20 | /** 21 | * Subscribes to a particular channel. 22 | */ 23 | subscribe(request: SubscribeRequest): Emitter; 24 | /** 25 | * Create a link to a particular channel. 26 | */ 27 | link(request: LinkRequest): Emitter; 28 | /** 29 | * Unsubscribes from a particular channel. 30 | */ 31 | unsubscribe(request: UnsubscribeRequest): Emitter; 32 | /** 33 | * Sends a key generation request to the server. 34 | */ 35 | keygen(request: KeyGenRequest): Emitter; 36 | /** 37 | * Sends a presence request to the server. 38 | */ 39 | presence(request: PresenceRequest): Emitter; 40 | /** 41 | * Request information about the connection to the server. 42 | */ 43 | me(): Emitter; 44 | /** 45 | * Hooks an event to the client. 46 | */ 47 | on(event: K, callback: (args: EmitterEventsArgsMap[K]) => void): Emitter; 48 | /** 49 | * Unhooks an event from the client. 50 | */ 51 | off(event: K, callback: (args: EmitterEventsArgsMap[K]) => void): Emitter; 52 | private _checkEvent; 53 | /** 54 | * Invokes the callback with a specific name. 55 | */ 56 | private _tryInvoke; 57 | /** 58 | * Formats a channel for emitter.io protocol. 59 | * 60 | * @private 61 | * @param {string} key The key to use. 62 | * @param {string} channel The channel name. 63 | * @param {...Option[]} options The list of options to apply. 64 | * @returns 65 | */ 66 | private _formatChannel; 67 | /** 68 | * Checks if a string starts with a prefix. 69 | */ 70 | private _startsWith; 71 | /** 72 | * Checks whether a string ends with a suffix. 73 | */ 74 | private _endsWith; 75 | /** 76 | * Logs the error and throws it 77 | */ 78 | private _throwError; 79 | } 80 | /** 81 | * Represents a message send througn emitter.io 82 | * 83 | * @class EmitterMessage 84 | */ 85 | export declare class EmitterMessage { 86 | channel: string; 87 | binary: any; 88 | /** 89 | * Creates an instance of EmitterMessage. 90 | * 91 | * @param {*} m The message 92 | */ 93 | constructor(m: IMqttMessage); 94 | /** 95 | * Returns the payload as string. 96 | */ 97 | asString(): string; 98 | /** 99 | * Returns the payload as binary. 100 | */ 101 | asBinary(): any; 102 | /** 103 | * Returns the payload as JSON-deserialized object. 104 | */ 105 | asObject(): any; 106 | } 107 | /** 108 | * Represents the available events. 109 | */ 110 | export declare enum EmitterEvents { 111 | connect = "connect", 112 | disconnect = "disconnect", 113 | message = "message", 114 | offline = "offline", 115 | error = "error", 116 | keygen = "keygen", 117 | presence = "presence", 118 | me = "me" 119 | } 120 | /** 121 | * Represents the argument type of each event callback. 122 | */ 123 | export declare interface EmitterEventsArgsMap { 124 | [EmitterEvents.connect]: Emitter, 125 | [EmitterEvents.disconnect]: Emitter, 126 | [EmitterEvents.message]: EmitterMessage, 127 | [EmitterEvents.offline]: Emitter, 128 | [EmitterEvents.error]: unknown, 129 | [EmitterEvents.keygen]: KeyGenEvent, 130 | [EmitterEvents.presence]: PresenceEvent, 131 | [EmitterEvents.me]: MeEvent 132 | } 133 | /** 134 | * Represents connection options. 135 | * 136 | * @interface IConnectOptions 137 | */ 138 | export interface ConnectRequest { 139 | /** 140 | * Whether the connection should be MQTT over TLS or not. 141 | * 142 | * @type {boolean} 143 | */ 144 | secure?: boolean; 145 | /** 146 | * The hostname to connect to. 147 | * 148 | * @type {string} 149 | */ 150 | host?: string; 151 | /** 152 | * The port number to connect to. 153 | * 154 | * @type {number} 155 | */ 156 | port?: number; 157 | /** 158 | * The number of seconts to wait between keepalive packets. Set to 0 to disable. 159 | * 160 | * @type {number} Keepalive in seconds. 161 | */ 162 | keepalive?: number; 163 | /** 164 | * The username required by your broker, if any 165 | * 166 | * @type {string} 167 | */ 168 | username?: string; 169 | /** 170 | * The password required by your broker, if any 171 | * 172 | * @type {string} 173 | */ 174 | password?: string; 175 | } 176 | export interface PublishRequest { 177 | key: string; 178 | channel: string; 179 | message: any; 180 | ttl?: number; 181 | me?: boolean; 182 | } 183 | export interface PublishWithLinkRequest { 184 | link: string; 185 | message: any; 186 | } 187 | export interface SubscribeRequest { 188 | key: string; 189 | channel: string; 190 | last?: number; 191 | } 192 | export interface LinkRequest { 193 | key: string; 194 | channel: string; 195 | name: string; 196 | private: boolean; 197 | subscribe: boolean; 198 | ttl?: number; 199 | me?: boolean; 200 | } 201 | export interface UnsubscribeRequest { 202 | key: string; 203 | channel: string; 204 | } 205 | export interface KeyGenRequest { 206 | key: string; 207 | channel: string; 208 | type: string; 209 | ttl: number; 210 | } 211 | /** 212 | * Represents a presence request. 213 | * 214 | * @interface PresenceRequest 215 | */ 216 | export interface PresenceRequest { 217 | /** 218 | * The key to use for this request. The key should match the channel and have presence flag associated. 219 | * 220 | * @type {string} 221 | */ 222 | key: string; 223 | /** 224 | * The target channel for the presence request. 225 | * 226 | * @type {string} 227 | */ 228 | channel: string; 229 | /** 230 | * Whether a full status should be sent back in the response. 231 | * 232 | * @type {boolean} 233 | */ 234 | status?: boolean; 235 | /** 236 | * Whether we should subscribe this client to presence notification events. 237 | * 238 | * @type {boolean} 239 | */ 240 | changes?: boolean; 241 | } 242 | /** 243 | * Represents a presence response message or a join/leave notification. 244 | * 245 | * @interface PresenceEvent 246 | */ 247 | export interface PresenceEvent { 248 | /** 249 | * The event, can be "status", "join" or "leave". 250 | * 251 | * @type {string} 252 | */ 253 | event: string; 254 | /** 255 | * The channel for this event. 256 | * 257 | * @type {string} 258 | */ 259 | channel: string; 260 | /** 261 | * The current channel occupancy (the number of subscribers). 262 | * 263 | * @type {number} 264 | */ 265 | occupancy: number; 266 | /** 267 | * The UNIX timestamp of this event. 268 | * 269 | * @type {number} 270 | */ 271 | time: number; 272 | /** 273 | * The list of clients or the client id. 274 | * 275 | * @type {(Array | PresenceInfo)} 276 | */ 277 | who: Array | PresenceInfo; 278 | } 279 | export interface PresenceInfo { 280 | /** 281 | * The id of the connection. 282 | * 283 | * @type {string} 284 | */ 285 | id: string; 286 | /** 287 | * The MQTT username associated with the connection. 288 | * 289 | * @type {string} 290 | */ 291 | username: string; 292 | } 293 | export interface UnsubscribeRequest { 294 | key: string; 295 | channel: string; 296 | } 297 | export interface KeyGenEvent { 298 | key: string; 299 | channel: string; 300 | status: number; 301 | } 302 | export interface MeEvent { 303 | id: string; 304 | username: string; 305 | } 306 | /** 307 | * Represents an MQTT message. 308 | * 309 | * @interface IMqttMessage 310 | */ 311 | export interface IMqttMessage { 312 | topic: string; 313 | payload: any; 314 | } 315 | /** 316 | * Represents an option (key/value pair) for a subscribe or publish operation. 317 | */ 318 | export interface Option { 319 | key: string; 320 | value: string; 321 | } 322 | /** 323 | * Connect creates a new instance of emitter client and connects to it. 324 | */ 325 | export declare function connect(request?: ConnectRequest, connectCallback?: any): Emitter; 326 | -------------------------------------------------------------------------------- /lib/emitter.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var mqtt = require('mqtt'); 4 | var Emitter = /** @class */ (function () { 5 | function Emitter() { 6 | } 7 | /** 8 | * Connects to the emitter service. 9 | */ 10 | Emitter.prototype.connect = function (request, handler) { 11 | var _this = this; 12 | request = request || {}; 13 | // auto-resolve the security level 14 | if (request.secure == null) { 15 | if (typeof window !== "undefined" && window != null && window.location != null && window.location.protocol != null) { 16 | request.secure = window.location.protocol == "https:"; 17 | } 18 | else { 19 | request.secure = false; 20 | } 21 | } 22 | // default options 23 | var defaultConnectOptions = { 24 | host: "api.emitter.io", 25 | port: request.secure ? 443 : 8080, 26 | keepalive: 30, 27 | secure: false 28 | }; 29 | // apply defaults 30 | for (var k in defaultConnectOptions) { 31 | request[k] = "undefined" === typeof request[k] ? defaultConnectOptions[k] : request[k]; 32 | } 33 | request.host = request.host.replace(/.*?:\/\//g, ""); 34 | var brokerUrl = "" + (request.secure ? "wss://" : "ws://") + request.host + ":" + request.port; 35 | this._callbacks = { "connect": [handler] }; 36 | this._mqtt = mqtt.connect(brokerUrl, request); 37 | this._mqtt.on(EmitterEvents.connect, function () { return _this._tryInvoke(EmitterEvents.connect, _this); }); 38 | this._mqtt.on("close", function () { return _this._tryInvoke(EmitterEvents.disconnect, _this); }); 39 | this._mqtt.on("offline", function () { return _this._tryInvoke(EmitterEvents.offline, _this); }); 40 | this._mqtt.on("error", function (error) { return _this._tryInvoke(EmitterEvents.error, error); }); 41 | this._mqtt.on("message", function (topic, msg, packet) { 42 | var message = new EmitterMessage(packet); 43 | if (_this._startsWith(message.channel, "emitter/keygen")) { 44 | // This is keygen message. 45 | _this._tryInvoke(EmitterEvents.keygen, message.asObject()); 46 | } 47 | else if (_this._startsWith(message.channel, "emitter/presence")) { 48 | // This is presence message. 49 | _this._tryInvoke(EmitterEvents.presence, message.asObject()); 50 | } 51 | else if (_this._startsWith(message.channel, "emitter/me")) { 52 | // This is a message requesting info on the connection. 53 | _this._tryInvoke(EmitterEvents.me, message.asObject()); 54 | } 55 | else { 56 | // Do we have a message callback? 57 | _this._tryInvoke(EmitterEvents.message, message); 58 | } 59 | }); 60 | return this; 61 | }; 62 | /** 63 | * Disconnects the client. 64 | */ 65 | Emitter.prototype.disconnect = function () { 66 | this._mqtt.end(); 67 | return this; 68 | }; 69 | /** 70 | * Publishes a message to the currently opened endpoint. 71 | */ 72 | Emitter.prototype.publish = function (request) { 73 | if (typeof request.key !== "string") 74 | this._throwError("emitter.publish: request object does not contain a 'key' string."); 75 | if (typeof request.channel !== "string") 76 | this._throwError("emitter.publish: request object does not contain a 'channel' string."); 77 | if (typeof request.message !== "object" && typeof request.message !== "string") 78 | this._throwError("emitter.publish: request object does not contain a 'message' object."); 79 | var options = new Array(); 80 | // The default server's behavior when 'me' is absent, is to send the publisher its own messages. 81 | // To avoid any ambiguity, this parameter is always set here. 82 | if (request.me == null || request.me == true) { 83 | options.push({ key: "me", value: '1' }); 84 | } 85 | else { 86 | options.push({ key: "me", value: '0' }); 87 | } 88 | if (request.ttl) { 89 | options.push({ key: "ttl", value: request.ttl.toString() }); 90 | } 91 | var topic = this._formatChannel(request.key, request.channel, options); 92 | this._mqtt.publish(topic, request.message); 93 | return this; 94 | }; 95 | /** 96 | * Publishes a message througth a link. 97 | */ 98 | Emitter.prototype.publishWithLink = function (request) { 99 | if (typeof request.link !== "string") 100 | this._throwError("emitter.publishWithLink: request object does not contain a 'link' string."); 101 | if (typeof request.message !== "object" && typeof request.message !== "string") 102 | this._throwError("emitter.publishWithLink: request object does not contain a 'message' object."); 103 | this._mqtt.publish(request.link, request.message); 104 | return this; 105 | }; 106 | /** 107 | * Subscribes to a particular channel. 108 | */ 109 | Emitter.prototype.subscribe = function (request) { 110 | if (typeof request.key !== "string") 111 | this._throwError("emitter.subscribe: request object does not contain a 'key' string."); 112 | if (typeof request.channel !== "string") 113 | this._throwError("emitter.subscribe: request object does not contain a 'channel' string."); 114 | var options = new Array(); 115 | if (request.last != null) { 116 | options.push({ key: "last", value: request.last.toString() }); 117 | } 118 | // Send MQTT subscribe 119 | var topic = this._formatChannel(request.key, request.channel, options); 120 | this._mqtt.subscribe(topic); 121 | return this; 122 | }; 123 | /** 124 | * Create a link to a particular channel. 125 | */ 126 | Emitter.prototype.link = function (request) { 127 | if (typeof request.key !== "string") 128 | this._throwError("emitter.link: request object does not contain a 'key' string."); 129 | if (typeof request.channel !== "string") 130 | this._throwError("emitter.link: request object does not contain a 'channel' string."); 131 | if (typeof request.name !== "string") 132 | this._throwError("emitter.link: request object does not contain a 'name' string."); 133 | if (typeof request.private !== "boolean") 134 | this._throwError("emitter.link: request object does not contain 'private'."); 135 | if (typeof request.subscribe !== "boolean") 136 | this._throwError("emitter.link: request object does not contain 'subscribe'."); 137 | var options = new Array(); 138 | // The default server's behavior when 'me' is absent, is to send the publisher its own messages. 139 | // To avoid any ambiguity, this parameter is always set here. 140 | if (request.me == null || request.me == true) { 141 | options.push({ key: "me", value: '1' }); 142 | } 143 | else { 144 | options.push({ key: "me", value: '0' }); 145 | } 146 | if (request.ttl != null) { 147 | options.push({ key: "ttl", value: request.ttl.toString() }); 148 | } 149 | var formattedChannel = this._formatChannel(null, request.channel, options); 150 | request = { 151 | "key": request.key, 152 | "channel": formattedChannel, 153 | "name": request.name, 154 | "private": request.private, 155 | "subscribe": request.subscribe 156 | }; 157 | console.log(JSON.stringify(request)); 158 | this._mqtt.publish('emitter/link/', JSON.stringify(request)); 159 | return this; 160 | }; 161 | /** 162 | * Unsubscribes from a particular channel. 163 | */ 164 | Emitter.prototype.unsubscribe = function (request) { 165 | if (typeof request.key !== "string") 166 | this._throwError("emitter.unsubscribe: request object does not contain a 'key' string."); 167 | if (typeof request.channel !== "string") 168 | this._throwError("emitter.unsubscribe: request object does not contain a 'channel' string."); 169 | // Send MQTT unsubscribe 170 | var topic = this._formatChannel(request.key, request.channel, []); 171 | this._mqtt.unsubscribe(topic); 172 | return this; 173 | }; 174 | /** 175 | * Sends a key generation request to the server. 176 | */ 177 | Emitter.prototype.keygen = function (request) { 178 | if (typeof request.key !== "string") 179 | this._throwError("emitter.keygen: request object does not contain a 'key' string."); 180 | if (typeof request.channel !== "string") 181 | this._throwError("emitter.keygen: request object does not contain a 'channel' string."); 182 | // Publish the request 183 | this._mqtt.publish("emitter/keygen/", JSON.stringify(request)); 184 | return this; 185 | }; 186 | /** 187 | * Sends a presence request to the server. 188 | */ 189 | Emitter.prototype.presence = function (request) { 190 | if (typeof request.key !== "string") 191 | this._throwError("emitter.presence: request object does not contain a 'key' string."); 192 | if (typeof request.channel !== "string") 193 | this._throwError("emitter.presence: request object does not contain a 'channel' string."); 194 | // Publish the request 195 | this._mqtt.publish("emitter/presence/", JSON.stringify(request)); 196 | return this; 197 | }; 198 | /** 199 | * Request information about the connection to the server. 200 | */ 201 | Emitter.prototype.me = function () { 202 | // Publish the request 203 | this._mqtt.publish("emitter/me/", ""); 204 | return this; 205 | }; 206 | /** 207 | * Hooks an event to the client. 208 | */ 209 | Emitter.prototype.on = function (event, callback) { 210 | this._checkEvent('off', event); 211 | if (!this._callbacks) { 212 | this._throwError("emitter.on: called before connecting"); 213 | } 214 | // Set the callback 215 | if (!this._callbacks[event]) { 216 | this._callbacks[event] = []; 217 | } 218 | if (this._callbacks[event].indexOf(callback) === -1) { 219 | this._callbacks[event].push(callback); 220 | } 221 | return this; 222 | }; 223 | /** 224 | * Unhooks an event from the client. 225 | */ 226 | Emitter.prototype.off = function (event, callback) { 227 | this._checkEvent('off', event); 228 | if (!this._callbacks) { 229 | this._throwError("emitter.off: called before connecting"); 230 | } 231 | var eventCallbacks = this._callbacks[event]; 232 | if (eventCallbacks) { 233 | var index = eventCallbacks.indexOf(callback); 234 | if (index >= 0) { 235 | eventCallbacks.splice(index, 1); 236 | } 237 | } 238 | return this; 239 | }; 240 | Emitter.prototype._checkEvent = function (method, event) { 241 | if (!EmitterEvents[event]) { 242 | var names = Object.keys(EmitterEvents); 243 | var values = names 244 | .map(function (name, index) { return (index === names.length - 1 ? 'or ' : '') + "'" + name + "'"; }) 245 | .join(", "); 246 | this._throwError("emitter." + method + ": unknown event type, supported values are " + values + "."); 247 | } 248 | }; 249 | /** 250 | * Invokes the callback with a specific name. 251 | */ 252 | Emitter.prototype._tryInvoke = function (name, args) { 253 | var callbacks = this._callbacks[name]; 254 | if (callbacks) { 255 | callbacks 256 | .filter(function (callback) { return callback; }) 257 | .forEach(function (callback) { return callback(args); }); 258 | } 259 | }; 260 | /** 261 | * Formats a channel for emitter.io protocol. 262 | * 263 | * @private 264 | * @param {string} key The key to use. 265 | * @param {string} channel The channel name. 266 | * @param {...Option[]} options The list of options to apply. 267 | * @returns 268 | */ 269 | Emitter.prototype._formatChannel = function (key, channel, options) { 270 | // Prefix with the key if any 271 | var formatted = channel; 272 | if (key && key.length > 0) 273 | formatted = this._endsWith(key, "/") ? key + channel : key + "/" + channel; 274 | // Add trailing slash 275 | if (!this._endsWith(formatted, "/")) 276 | formatted += "/"; 277 | // Add options 278 | if (options != null && options.length > 0) { 279 | formatted += "?"; 280 | for (var i = 0; i < options.length; ++i) { 281 | formatted += options[i].key + "=" + options[i].value; 282 | if (i + 1 < options.length) 283 | formatted += "&"; 284 | } 285 | } 286 | // We're done compiling the channel name 287 | return formatted; 288 | }; 289 | /** 290 | * Checks if a string starts with a prefix. 291 | */ 292 | Emitter.prototype._startsWith = function (text, prefix) { 293 | return text.slice(0, prefix.length) == prefix; 294 | }; 295 | /** 296 | * Checks whether a string ends with a suffix. 297 | */ 298 | Emitter.prototype._endsWith = function (text, suffix) { 299 | return text.indexOf(suffix, text.length - suffix.length) !== -1; 300 | }; 301 | /** 302 | * Logs the error and throws it 303 | */ 304 | Emitter.prototype._throwError = function (message) { 305 | console.error(message); 306 | throw new Error(message); 307 | }; 308 | return Emitter; 309 | }()); 310 | exports.Emitter = Emitter; 311 | /** 312 | * Represents a message send througn emitter.io 313 | * 314 | * @class EmitterMessage 315 | */ 316 | var EmitterMessage = /** @class */ (function () { 317 | /** 318 | * Creates an instance of EmitterMessage. 319 | * 320 | * @param {*} m The message 321 | */ 322 | function EmitterMessage(m) { 323 | this.channel = m.topic; 324 | this.binary = m.payload; 325 | } 326 | /** 327 | * Returns the payload as string. 328 | */ 329 | EmitterMessage.prototype.asString = function () { 330 | return this.binary.toString(); 331 | }; 332 | /** 333 | * Returns the payload as binary. 334 | */ 335 | EmitterMessage.prototype.asBinary = function () { 336 | return this.binary; 337 | }; 338 | /** 339 | * Returns the payload as JSON-deserialized object. 340 | */ 341 | EmitterMessage.prototype.asObject = function () { 342 | var object = {}; 343 | try { 344 | object = JSON.parse(this.asString()); 345 | } 346 | catch (err) { 347 | console.error(err); 348 | } 349 | return object; 350 | }; 351 | return EmitterMessage; 352 | }()); 353 | exports.EmitterMessage = EmitterMessage; 354 | /** 355 | * Represents the available events. 356 | */ 357 | var EmitterEvents; 358 | (function (EmitterEvents) { 359 | EmitterEvents["connect"] = "connect"; 360 | EmitterEvents["disconnect"] = "disconnect"; 361 | EmitterEvents["message"] = "message"; 362 | EmitterEvents["offline"] = "offline"; 363 | EmitterEvents["error"] = "error"; 364 | EmitterEvents["keygen"] = "keygen"; 365 | EmitterEvents["presence"] = "presence"; 366 | EmitterEvents["me"] = "me"; 367 | })(EmitterEvents = exports.EmitterEvents || (exports.EmitterEvents = {})); 368 | /** 369 | * Connect creates a new instance of emitter client and connects to it. 370 | */ 371 | function connect(request, connectCallback) { 372 | var client = new Emitter(); 373 | client.connect(request, connectCallback); 374 | return client; 375 | } 376 | exports.connect = connect; 377 | //# sourceMappingURL=emitter.js.map -------------------------------------------------------------------------------- /lib/emitter.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"emitter.js","sourceRoot":"","sources":["emitter.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /lib/emitter.ts: -------------------------------------------------------------------------------- 1 | const mqtt = require('mqtt'); 2 | 3 | export class Emitter { 4 | 5 | private _mqtt: any; 6 | private _callbacks: { [key: string]: ((args?: any) => void)[] }; 7 | 8 | /** 9 | * Connects to the emitter service. 10 | */ 11 | public connect(request?: ConnectRequest, handler?: () => void): Emitter { 12 | request = request || {}; 13 | 14 | // auto-resolve the security level 15 | if (request.secure == null) { 16 | if (typeof window !== "undefined" && window != null && window.location != null && window.location.protocol != null) { 17 | request.secure = window.location.protocol == "https:"; 18 | } else { 19 | request.secure = false; 20 | } 21 | } 22 | 23 | // default options 24 | var defaultConnectOptions = { 25 | host: "api.emitter.io", 26 | port: request.secure ? 443 : 8080, 27 | keepalive: 30, 28 | secure: false 29 | }; 30 | 31 | // apply defaults 32 | for (var k in defaultConnectOptions) { 33 | request[k] = "undefined" === typeof request[k] ? defaultConnectOptions[k] : request[k]; 34 | } 35 | 36 | request.host = request.host.replace(/.*?:\/\//g, ""); 37 | var brokerUrl = `${request.secure ? "wss://" : "ws://"}${request.host}:${request.port}`; 38 | 39 | this._callbacks = {"connect": [handler]}; 40 | this._mqtt = mqtt.connect(brokerUrl, request); 41 | 42 | this._mqtt.on("connect", () => this._tryInvoke(EmitterEvents.connect, this)); 43 | this._mqtt.on("close", () => this._tryInvoke(EmitterEvents.disconnect, this)); 44 | this._mqtt.on("offline", () => this._tryInvoke(EmitterEvents.offline, this)); 45 | this._mqtt.on("error", error => this._tryInvoke(EmitterEvents.error, error)); 46 | this._mqtt.on("message", (topic, msg, packet) => { 47 | var message = new EmitterMessage(packet); 48 | if (this._startsWith(message.channel, "emitter/keygen")) { 49 | // This is keygen message. 50 | this._tryInvoke(EmitterEvents.keygen, message.asObject()) 51 | } else if (this._startsWith(message.channel, "emitter/presence")) { 52 | // This is presence message. 53 | this._tryInvoke(EmitterEvents.presence, message.asObject()) 54 | } else if (this._startsWith(message.channel, "emitter/me")) { 55 | // This is a message requesting info on the connection. 56 | this._tryInvoke(EmitterEvents.me, message.asObject()); 57 | } else { 58 | // Do we have a message callback? 59 | this._tryInvoke(EmitterEvents.message, message); 60 | } 61 | }); 62 | return this; 63 | } 64 | 65 | /** 66 | * Disconnects the client. 67 | */ 68 | public disconnect(): Emitter { 69 | this._mqtt.end(); 70 | return this; 71 | } 72 | 73 | /** 74 | * Publishes a message to the currently opened endpoint. 75 | */ 76 | public publish(request: PublishRequest): Emitter { 77 | if (typeof request.key !== "string") 78 | this._throwError("emitter.publish: request object does not contain a 'key' string."); 79 | if (typeof request.channel !== "string") 80 | this._throwError("emitter.publish: request object does not contain a 'channel' string."); 81 | if (typeof request.message !== "object" && typeof request.message !== "string") 82 | this._throwError("emitter.publish: request object does not contain a 'message' object."); 83 | 84 | var options = new Array