npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@gavant/ember-websockets

v0.0.3

Published

Basic websockets support for ember applications.

Downloads

177

Readme

gavant-ember-websockets

Basic websockets support for ember applications, using sock.js and the STOMP messaging protocol via the stomp.js client.

Installation

ember install @gavant/ember-websockets

Add the settings below (shown with default values) to your app's config/environment.js to configure the addon. At the very least, you will need to set the websockets.baseURL value to point to an API endpoint URL.

let ENV = {

    //other environment configs...

    websockets: {
        //REQUIRED - the endpoint url used to make the socket connection,
        //typically this is an endpoint on the app's main API host
        baseURL: '',
        //the name of the request header sent in all AJAX requests
        //that uniquely identify the originating browser/client
        clientUUIDHeader: 'x-client-uuid',
        //the global socket channel path for app-wide socket events
        //if there is no global channel, set this to false
        globalChannel: '/topic/global',
        //the name of the property in ember-data models that hold
        //the date the record was last modified. used by ModelSocketEventMixin
        //to determine if received model data should be pushed into the store
        //set to false to disable this behavior
        modelDateField: 'dateModified',
        //display verbose sock.js logs in the dev tools console
        debug: true,
        //user must have an authenticated session to connect
        requiresAuth: true,
        //the intervals at which successive reconnect attempts
        //are made when the socket is disconnected
        reconnectDelaySteps: [1000, 2000, 5000, 10000, 30000, 60000]
    }
}

Usage

Socket Service

The primary service provided by the addon, it is used to make a websocket connection, and subscribe to socket channels.

//Basic usage example
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import { get } from '@ember/object';
import { bind } from '@ember/runloop';

export default Route.extend({
    socket: service(),

    setupController() {
        this._super(...arguments);
        //setup a socket channel subscription when entering a route
        const listener = bind(this, 'onSocketEvent');
        get(this, 'socket').subscribe('/topic/some-socket-channel', listener);
    },

    resetController(controller, isExiting) {
        this._super(...arguments);

        if(isExiting) {
            //unsubscribe from the socket channel when leaving the route
            get(this, 'socket').unsubscribe('/topic/some-socket-channel');
        }
    },

    onSocketEvent(message) {
        try {
            //parse the socket event message body
            const body = JSON.parse(message.body);
            console.log(body);
        } catch(err) {

        }
    }
});

The Socket service also mixes in @ember/object/evented, and triggers the following events which can be listened to:

  • connected - a websocket connection is successfully established
  • disconnected - the websocket connection was lost (passes an error object with the event)

ClientIdentity Service

Implements a mechanism for uniquely identifying every app instance/browser client, which can be sent in AJAX request headers to the API. The API can then return this UUID value in socket events, which the addon's ModelSocketEventMixin uses to ignore events that originated from the user that caused them.

The most common places in the app that need to be modified to add the UUID header are:

  • ember-data adapter
    In your application/adapter, define a headers (docs) property that includes the client UUID header:
    export default RESTAdapter.extend({
        clientIdentity: service(),
        headers: readOnly('clientIdentity.uuidHeader'),
    });
  • ember-ajax service
    If your application uses an ajax service extended from the ember-ajax addon to do custom non ember-data AJAX requests (most apps do), you will need to modify the service to include the client UUID header:
    export default AjaxService.extend({
        clientIdentity: service(),
    
        //NOTE: example includes some other common headers,
        //e.g. Content-Type and authorization token from ember-simple-auth
        headers: computed('authorizationHeaders', 'clientIdentity.uuidHeader', function () {
            const headers = assign(
                {'Content-Type': 'application/vnd.api+json'},
                get(this, 'clientIdentity.uuidHeader'),
                get(this, 'authorizationHeaders')
            );
    
            return headers;
        })
    });
  • ember-simple-auth authenticator
    In a custom ember-simple-auth authenticator (e.g. for OAuth 2 authentication), you will need to override the makeRequest() method to be able to add custom headers:
    export default Authenticator.extend({
        clientIdentity: service(),
    
        makeRequest(url, data, headers = {}) {
            assign(
                headers,
                get(this, 'clientIdentity.uuidHeader'),
                {'Content-Type': 'application/x-www-form-urlencoded'}
            );
    
            //rest of method implementation...
        }
    });

ApplicationRouteMixin

This mixin should be applied to the application/route. It sets up the standard boilerplate logic for making a socket connection on app boot/login, and subscribing the the global events channel.

import SocketsApplicationRouteMixin from '@gavant/ember-websockets/mixins/application-route-mixin';

ApplicationControllerMixin

This mixin should be applied to the application/controller. It simply adds a websockets query param, which can be used to disable socket connections when visiting the app with ?websockets=false in the URL.

import SocketsApplicationControllerMixin from '@gavant/ember-websockets/mixins/application-controller-mixin';

GlobalSocketEventsMixin

Subscribes to the global events channel. This is used by the ApplicationRouteMixin and generally should not need to be used directly.

import GlobalSocketEventsMixin from '@gavant/ember-websockets/mixins/socket-events/global';

ModelSocketEventsMixin

Implements common logic used for handling socket events which send changes to ember-data models. It expects socket events to follow a standard REST-like pattern to identify record create/update/delete events. Generally, you will use this in a Route in the following manner:

import ModelSocketEventsMixin from '@gavant/ember-websockets/mixins/socket-events/model';

export default Route.extend(ModelSocketEventsMixin, {
    myChannelName: '/topic/my-channel-name',

    listenForEvents() {
        const channel = get(this, 'myChannelName');
        const listener = bind(this, 'onSocketEvent');
        get(this, 'socket').subscribe(channel, listener);
    },

    stopListeningForEvents() {
        get(this, 'socket').unsubscribe(get(this, 'myChannelName'));
    },

    onSocketEvent(message) {
        this.handleModelEvent(message, 'modelName');
    },

    afterModel() {
        this._super(...arguments);
        this.listenForEvents();
    },

    resetController(controller, isExiting) {
        this._super(...arguments);
        if(isExiting) {
            this.stopListeningForEvents();
        }
    }
});

The ModelSocketEventsMixin also triggers an event, modelReceived on the Socket service when an event is received, so that other parts of your application can execute logic when this happens:

get(this, 'socket').on('modelReceived', this, (method, modelName, modelJson, existingModel) => {
    //do something with the received model event
    console.log(method, modelName, modelJson, existingModel);
    //=>
    //"POST|PUT|DELETE"
    //"someModel"
    //{id: 999, ...}
    //DS.Model instance
});

Contributing

Installation

  • git clone <repository-url>
  • cd gavant-ember-websockets
  • npm install

Linting

  • npm run lint:js
  • npm run lint:js -- --fix

Running tests

  • ember test – Runs the test suite on the current Ember version
  • ember test --server – Runs the test suite in "watch mode"
  • ember try:each – Runs the test suite against multiple Ember versions

Running the dummy application

For more information on using ember-cli, visit https://ember-cli.com/.

License

This project is licensed under the MIT License.