SkillAgentSearch skills...

Comcat

Share single connection between multiple browser tabs/windows and more.

Install / Use

/learn @afterwind-io/Comcat
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

:cat:Comcat

[![Version][version-badge]][npm] ![License][license-badge]

<!-- ![Downloads][download-badge] -->

Share single connection between multiple browser tabs/windows and more.

Introduction

This library is currently aimed to solve a common problem:

I want to consume some messages pushed by the backend, but ...

  • I don't want to create a new connection every time I open a new tab/window;
  • Or, I want a single tab to receive the messages, and shared them between other tabs.

Comcat offers some critical features around this topic, including:

  • Broadcast messages to all tabs;
  • Keep one and only one connection alive across tabs;

With the unique characteristics of [SharedWorker][mdn-sharedworker], Comcat can automatically reconnect if the tab owns the connection is closed or even crashed. If you are keen on how it is accomplished, please refer to How it works.

Disclaimer

Comcat guarantees eliminating duplicate connections or messages, but does not guarantee the integrity of all incoming messages. That means, messages may be lost in certain edge cases.

If it is a major concern over the message integrity, Comcat may not be suit to your app. Relevant details are discussed in Caveats.

Get Started

Install

npm install comcat

OR

yarn add comcat

Usage

For demonstration purpose, here we implement a minimal example to share time information across tabs.

//examplePump.ts

import { ComcatPump } from 'comcat';

const POLLING_INTERVAL = 60 * 1000;

let intervalHandler = -1;

/**
 * First we create a `pump` to pull messages from the backend.
 */
const pump = new ComcatPump({
  category: 'example',
});

/**
 * Define how we connect to the backend.
 */
pump.onConnect = () => {
  intervalHandler = setInterval(() => {
    fetch('http://worldtimeapi.org/api/ip')
      .then((res) => {
        return res.json();
      })
      .then((data) => {
        /**
         * When messages come, push them to the consumer...
         * ...with a customized topic.
         */
        pump.pump('Time', data.datetime);
        pump.pump('Unix', data.unixtime);
      });
  }, POLLING_INTERVAL);
};

/**
 * Define how we disconnect from it.
 */
pump.onDisconnect = () => {
  clearInterval(intervalHandler);
};

/**
 * Start the engine!
 */
pump.start();
// examplePipe.ts

import { ComcatPipe } from 'comcat';

/**
 * Then we create a `pipe` to receive messages from `pump`s.
 */
const pipe = new ComcatPipe({
  /**
   * Choose the topic we care about...
   */
  topic: 'Time',
});

pipe.onMessage = (topic, data) => {
  /**
   * ...And do something with the messages.
   */
  console.log('The current time is: ', data);
};

/**
 * Start rolling!
 */
pipe.start();

/**
 * Don't forget to dispose the pipe when it is no longer used.
 */
// pipe.stop();

The Concepts

Overview

overview

Pump

Pump behaves like a "Message Provider". It is responsible for creating the connection to the backend and then feed the messages to the further consumer.

You have full control of how to connect, and disconnect, to the source of the message, often the server. Moreover, it is up to you to determine when and what to send. Comcat only manages the timing of connection and disconnection for you.

If needed, you can create multiple Pumps to deal with various sources. Comcat uses category to identify different groups of pumps. There is only one active connection in each group, thus the tabs own the active connections may not be the same.

Pipe

Pipe is the "Message Receiver". It notifies the user when the message arrives, and is meant to be a intermediary between Comcat and the consumer.

Pipe provides basic filtering based on topic, but you can choose whether to accept the incoming message, or even modify the content before it is pushed to the consumer.

Recipes

The repository contains several examples covering the basic usage. Please see [example/README.md][example-readme]

APIs

ComcatPump

The base class for constructing Comcat pumps.

A typical customized pump looks like this:

// myPump.ts

import { ComcatPump } from 'comcat';

const pump = new ComcatPump({
  category: 'MyCategory',
});

pump.onConnect = () => {
  /**
   * Do the connection here.
   *
   * ...and some other works maybe
   */
};
pump.onDisconnect = () => {
  /**
   * Do the disconnection here.
   */
};

new ComcatPump(options)

public constructor(options: ComcatPumpOptions);

interface ComcatPumpOptions {
  category: string;
}

category:

An identifier to classify different message sources.

Each category is coupled with only one type of connection, so you can not create multiple pumps with same category.

ComcatPump.start

public start: () => Promise<boolean>;

Register the pump and try to start the underlying connection.

Because the connection is managed by Comcat, it may be postponed until scheduled.

Returns true if registry succeeds, or vice versa.

ComcatPump.stop

public stop: () => void;

Close the pump and the underlying connection.

In practice, Comcat will close the pump when the current tab is closed, so usually you wont need to trigger this by hand.

If somehow you still want to do it yourself, please note that once the pump is closed, it is fully disposed and cannot be started again. In order to restarting a new pump with the same category, instantiate a new ComcatPump.

ComcatPump.onConnect

public onConnect: () => Promise<boolean> | void;

:warning: The default method is only a placeholder. Always override with your own callback.

Invoked when Comcat tries to connect to your backend. Basically your connection code goes here.

You can fine-tune the inner behavior by returning a flag indicates whether the connection is successful. If the return value is false, or an error is raised, Comcat will either retry the connection after a short period of time, or schedule another tab to do the job. If no value is returned, Comcat will treat the result as successful anyway.

ComcatPump.onDisconnect

public onDisconnect: () => void;

:warning: The default method is only a placeholder. Always override with your own callback.

Invoked when Comcat tries to disconnect to your backend. Basically your disconnection code goes here.

Don't permanently dispose anything here, because your pump may be rescheduled connecting again.

ComcatPump.pump

public pump: (topic: string, data: any) => Promise<void>;

Send the message with a specified topic.

topic:

The category of the message. It is used to help filtering messages in different aspects.

data:

The content of the message. Can be anything that SharedWorker supports, but with some restrictions. Please see [Transferring data to and from workers: further details][mdn-transfer].

ComcatPipe

The base class for constructing Comcat pipes.

A typical customized pipe looks like this:

import { ComcatPipe } from 'comcat';

const pipe = new ComcatPipe({
  topic: 'MyTopic',
});

pipe.onMessage = (topic, data) => {
  /**
   * Do some works with the data.
   */
};

new ComcatPipe(options)

public constructor(options?: ComcatPipeOptions);

interface ComcatPipeOptions {
  topic?: string | RegExp;
}

topic: [optional]

The expected category of the messages. It can be either string or RegExp. If applied, the incoming message is filtered unless its topic exactly matches the provided string, or passes the RegExp test.

ComcatPipe.start

public start: () => Promise<boolean>;

Register the pipe and start listening for the messages from the upstream.

Returns true if registry succeeds, or vice versa.

ComcatPipe.stop

public stop: () => void;

Unregister the pipe and stop listening for the messages.

It is strongly recommended that to prevent potential memory leaks, pipes should be closed immediately when they are no longer in use.

ComcatPipe.onMessage

public onMessage: (topic: string, data: any) => void;

:warning: The default method is only a placeholder. Always override with your own callback.

Invoked when messages arrive.

Note that the messages arrives here have already been filtered by the topic provided in construction options.

topic:

The topic of the message;

data:

The content of the message;

Comcat

Provides global settings that alter how Comcat works.

Comcat.setMode

public setMode: (mode: 'default' | 'direct' = 'default') => void;

Specify the underlying implementation.

By default Comcat uses SharedWebworker to share connection and send messages across tabs/windows. If SharedWebworker is not supported, Comcat will fall back to the direct mode.

When running in direct Mode, all cross-tab features are disabled. The connection activated by pump is created per tab. The messages sent by pump are broadcasted back to the pipes on the same tab. Thus, it behaves just like a normal event bus.

Usually you should just leave it be.

import { Comcat } from 'comcat';

Comcat.setMode('direct');

Comcat.enableDebug

public enableDebug: (flag: boolean) => void;

Determines whether enabling the full debug logging, including inner status and transport information.

Comcat will log every message through the transport, and some basic status information to the console. By default, these output is suppressed.

You can enable the full log like following:

import { Comcat } from 'comcat';

Comcat.enableDebug(true);

Be careful, this may output enormous content. However, the logs from the worker is always com

View on GitHub
GitHub Stars7
CategoryDevelopment
Updated2y ago
Forks0

Languages

TypeScript

Security Score

75/100

Audited on Aug 21, 2023

No findings