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

@hanseltime/janus-simple-command

v1.4.0

Published

Initial Public Offering of Janus Simple Command Protocol Client

Downloads

10,969

Readme

Janus Simple Command Library

This library provides both a "Client" and "Server" class for following the Janus Simple Command Protocol.

This protocol prioritizes human readable message passing and JSON formats over compact message structures. This is meant as a library for plugging into any number of bi-directional connections that you can define, and provides a means of multi-plexing multiple "senders" on the same connection.

Usage

Please see Protocol for an in depth explanation of the protocol as it stands.

In short, on a bidirectional connection either party can initiate a "sender" or a server. The server exists to simply respond to the requests of a sender. The sender talks with the server by commands and then waits for a status message that returns.

From the standpoint of this library, you only need to worry yourself with commands, their send payloads, and the corresponding status payloads that could come back.

import { CommandMap, CommandMessage, StatusMessage, IntermediateStatusMap, StatusMap } from '@hanseltime/janus-simple-command'
// Strongly type your expected commands
type Commands = 'fly' | 'eat'

// Define the command schemas using our built-in CommandMessage type
type CommandMap: CommandMap<Commands> = {
  fly: CommandMessage<'fly', {
    captain: string,
    plane: 'cessna' | '737'
  }>,
  eat: CommandMessage<'eat', {
    eat: boolean
  }>
}
// Define the status schema using our built-in StatusMessage type
type FlySuccess = {
  mph: number
  elevation: number
}
type FlyErrors = 'crashed' | 'failureToLaunch'
type EatSuccess = {
  food: string
}
type StatusMap: StatusMap<Commands> = {
  fly: StatusMessage<FlySuccess, FlyErrors>
  eat: StatusMessage<EatSuccess>
}
// Optional - you can define intermediate statuses for a command
type InterMap: IntermediateStatusMap<Commands> = {
  fly: IntermediateStatusMessage<{
    phase: 'lift-off' | 'grounded'
  }>
}

// Create your connection
const connection: Connection = someConnectionFunction()

const server = new Server<Commands, CommandMap, StatusMap, InterMap>({
  maxSenderInactivity: 10000, // the amount of time we allow between commands
  maxAckRetries: 4, // If we drop an ack, the amount of times we retry a status
  ackRetryDelay: 500, // The amount we wait before retrying on a dropped ack
  connection,
  debug: (msg) => console.log(msg),
})

server.addMessageHandler('fly', async (msg: CommandMap['fly'], sendIntermediateStatus ): Promise<HandlerReturn<StatusMap['fly']>> => {
  // Indicate that we're taking off while we do other stuff
  const sent = await sendIntermediateStatus({
    phase: 'lift-off'
  })

  // If we don't verify it was sent we don't want to keep flying
  if (!sent) {
    console.error('pilot did not acknowledge, canceling flight')
    return {
      isError: true,
      data: {
        type: 'failureToLaunch',
        message: 'Pilot Failed to acknowledge',
      },
    }
  }

  return {
    isError: false,
    data: {
      mph: msg.data.plane === 'cessna' ? 100 : 200,
      elevation: 30000,
    },
  },
})
// Handler for eat command
server.addMessageHandler('eat', async (msg: CommandMap['eat']): Promise<HandlerReturn<StatusMap['eat']>> => {
  // Return for eat
})

await server.open()

// At the end of the server
await server.close()


// For the client connection
const connectionForClient: Connection = someConnectionFunction()

const client = new Client<Commands, CommandMap, StatusMap>({
    commands: ['fly', 'eat'],
    ackRetryDelay: 1000,
    maxAckRetries: 3,
    connection: clientConnection,
    debug: (...args) => {
      console.log(...args)
    },
  })

await client.open()

// Create a sender
const sender = await client.createSender()
const result = sender.command('fly', () => ({
  captain: 'jack',
  plane: '737',
}))

// { mph: 200, elevation: 30000 }
console.log(result)

Creating a Connection

In order to agnosticize our library's dependence on a transport layer, this library relies on a simplified connection interface. You can either look for other implementations of the connection or you can instantiate your own interface and pass it to the connection.

One-to-One Relationship

The Connection interface that you implement is meant to be 1-to-1 to either a Client or Server instance. When noting the setter methods, we only allow one set of listeners on the connection. This does not mean that you cannot run multiple Servers over the same underlying connection (like a websocket), but it does mean that you will provide a Connection that interacts with the websocket for each Server or Client that you want to declare.