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

@ruby184/adonis-socket.io

v0.2.0

Published

AdonisJs 5 websocket provider using socket.io under the hood

Downloads

584

Readme

@ruby184/adonis-socket.io

AdonisJs 5 websocket provider using socket.io under the hood

github-actions-image npm-image license-image typescript-image

This package is trying to implement main ideas from this Adonis RFC. Package is not production ready until v1.0. Use it at your own risk.

Currently implemented features:

  • define socket.io namespaces including dynamic ones with adonis-like routes syntax
  • add adonis-like class middlewares similar to route ones to namespaces
  • handle events on namespaces with controllers and allow to ack events with value returned from event handler
  • centralized exception handling similar to http
  • working with adonis auth middleware
  • more to come...

Installation

Install it from npm

npm i @ruby184/adonis-socket.io

and then configure it using adonis

node ace configure @ruby184/adonis-socket.io

TODO

  • [x] allow .where regex definition for namespace dynamic parameters
  • [x] allow to define controller namespace for socket.io namespace
  • [x] define static namespaces directly as socket.io namespaces and use matching only for dynamic ones (perf)
  • [ ] test everything
  • [ ] we should not create and use response, but return Proxy to intercept and throw error when user tries to use response in websocket context
  • [x] extract errors handling to dedicated exception handler to report and handle
  • [ ] look at how to make easy integration of socket.io multi server support with adonis
  • [ ] look how we can make use of socket middleware which is a function that gets executed for every incoming Packet
  • [ ] handle transformaton of adonis cors config to socket.io as they are not 100% compatible

Usage

Examples

Here is an example of tracking users online status using this package inspired by default examples from socket.io

  1. Currently package supports authentication by api tokens. Update middleware created by @adonisjs/auth in app/Middleware/Auth.ts and add wsHandle method to support websockets
import type { WsContextContract } from '@ioc:Ruby184/Socket.IO/WsContext'
  /**
   * Handle ws namespace connection
   */
  public async wsHandle(
    { auth }: WsContextContract,
    next: () => Promise<void>,
    customGuards: (keyof GuardsList)[]
  ) {
    /**
     * Uses the user defined guards or the default guard mentioned in
     * the config file
     */
    const guards = customGuards.length ? customGuards : [auth.name]
    await this.authenticate(auth, guards)
    await next()
  }
  1. Update start/wsKernel.ts to add authentication middleware updated in previous step. We will add global middleware but you can also use named one and just add it to required nameespace in next step.
Ws.middleware.register([() => import('App/Middleware/Auth')])
  1. Add events listeners in start/socket.ts
Ws.namespace('/')
  .connected('ActivityController.onConnected')
  .disconnected('ActivityController.onDisconnected')
  1. Create a websocket controller in app/Controllers/Ws/ActivityController.ts
import type { WsContextContract } from '@ioc:Ruby184/Socket.IO/WsContext'
import User from 'App/Models/User'

export default class ActivityController {
  private getUserRoom(user: User): string {
    return `user:${user.id}`
  }

  public async onConnected({ socket, auth, logger }: WsContextContract) {
    // all connections for the same authenticated user will be in the room
    const room = this.getUserRoom(auth.user!)
    const userSockets = await socket.in(room).allSockets()

    // this is first connection for given user
    if (userSockets.size === 0) {
      socket.broadcast.emit('user:online', auth.user)
    }

    // add this socket to user room
    socket.join(room)
    // add userId to data shared between Socket.IO servers
    // https://socket.io/docs/v4/server-api/#namespacefetchsockets
    socket.data.userId = auth.user!.id

    const allSockets = await socket.nsp.except(room).fetchSockets()
    const onlineIds = new Set<number>()

    for (const remoteSocket of allSockets) {
      onlineIds.add(remoteSocket.data.userId)
    }

    const onlineUsers = await User.findMany([...onlineIds])

    socket.emit('user:list', onlineUsers)

    logger.info('user connected: %d', auth.user!.id)
  }

  // see https://socket.io/get-started/private-messaging-part-2/#disconnection-handler
  public async onDisconnected({ socket, auth, logger }: WsContextContract, reason: string) {
    const room = this.getUserRoom(auth.user!)
    const userSockets = await socket.in(room).allSockets()

    // user is disconnected
    if (userSockets.size === 0) {
      // notify other users
      socket.broadcast.emit('user:offline', auth.user)
    }

    logger.info('user disconnected (%s): %d', reason, auth.user!.id)
  }
}