Nuptial Predicament Mediation

    @extend-chrome/messages
    TypeScript icon, indicating that this package has built-in type declarations

    1.2.2 • Public • Published

    @extend-chrome/messages logo

    @extend-chrome/messages

    npm (scoped) GitHub last commit License TypeScript Declarations Included

    Fiverr: We make Chrome extensions ko-fi


    An API for Chrome extension messaging that makes sense. Uses Promises and Observables for convenience.

    Table of Contents

    Getting started

    You will need to use a bundler like Rollup, Parcel, or Webpack to include this library in the build of Chrome extension.

    See rollup-plugin-chrome-extension for an easy way use Rollup to build your Chrome extension!

    Installation

    $ npm i @extend-chrome/messages

    Usage

    Send and receive messages using isomorphic message wrappers, or with a traditional messages object.

    // messages.js, used in both the background and content script
    import { getMessage } from '@extend-chrome/messages'
    
    // getMessage returns [Function, Observable, Function]
    export const [sendNumber, numberStream, waitForNumber] = getMessage(
      // String to be used as a greeting
      'NUMBER',
    )
    // background.js, a background script
    import { numberStream } from './messages'
    
    // numberStream is an RxJs Observable
    numberStream.subscribe(([n, sender]) => {
      console.log('the data passed to sendNumber', n)
      // Sender is a Chrome runtime MessageSender
      console.log('the message sender', sender)
    })
    // content.ts, a content script
    import { sendNumber } from './messages'
    
    document.body.onclick = () => {
      sendNumber(42) // 42 is logged in the background
    }

    getMessage has great TypeScript support!

    If you're into TypeScript, getMessage is a generic function. It shines when you define the message data type. No more message data type mistakes! Intellisense has you covered.

    // messages.ts
    import { getMessage } from '@extend-chrome/messages'
    
    interface Stats {
      hi: number
      low: number
      date: string
    }
    
    export const [sendStats, statsStream, waitForStats] = getMessage<Stats>('STATS')
    
    // If you have a message type with no data, use void rather than undefined
    // This way you can call it with zero arguments
    export const [sendReady, readyStream, waitForReady] = getMessage<void>('READY')
    // background.ts
    import { statsStream } from './messages'
    
    statsStream.subscribe(([{ hi, lo, date }, sender]) => {
      // Intellisense knows this is an Observable of
      // [Stats, chrome.runtime.MessageSender]
    })
    
    waitForReady().then(() => {
      console.log('content.ts is ready')
    })
    // content.ts
    import { sendStats } from './messages'
    
    sendStats({ hi: 30, low: 14, date: '11/12/2019' })
    
    // Throws a TS error
    sendStats('not a Stats object')
    
    sendReady()

    Features

    TypeScript Definitions

    This library is written in TypeScript, extensively typed, and definitions are included, so no need to install an additional @types library!

    RxJs Observables

    Version 0.5.0 introduces an RxJs Observable as messages.stream.

    Scopes

    Version 0.5.0 introduces getScope, a way to use a separate messaging space.

    This is useful if you are writing a library for Chrome extensions that uses messages internally, but you don't want to pollute the global messaging space.

    API

    getMessage(greeting)

    import { getMessage } from '@extend-chrome/messages'
    
    const [sendMessage, messageStream, waitForMessage] = getMessage('greeting')

    Use this function to create an isomorphic message system. Import it into both the message sender and receiver context (ie, the background page and a content script). getMessage is a TypeScript generic function. See the Usage section for more information, including TypeScript support!

    greeting

    Type: string

    A unique string to identify the message.

    Returns: [messageSender, messageStream]

    Type: [Function, Observable]

    Import the messageSender into the context you wish to send a message. Call the sender with the data you want to send.

    messageStream is an Observable of a [data, MessageSender] tuple. Import the messageStream into the context you wish to recieve messages. Subscribe to it with a message handler function.

    The messages Namespace

    If you're more comfortable with a traditional messages namespace, import the messages object.

    messages.send(data, [options])

    Sending one-way messages is simple: just call messages.send with an object that includes at least a greeting property.

    // content-script.js
    import { messages } from '@extend-chrome/messages'
    
    // Simple message with no data
    messages.send({ greeting: 'hello' }).then(() => {
      console.log('The message was sent.')
    })
    
    // Message with data
    messages
      .send({
        greeting: 'with-data',
        // You can use any prop name or value
        data: { x: 1 },
      })
      .then(() => {
        console.log('The message was sent.')
      })

    Actually, you can send any data type as a message, but an object with a greeting prop is a nice, flexible pattern.

    Get a response with options.async

    Set the optional options.async to true to receive a response. Only message listeners with the third sendResponse argument will receive async messages.

    // content-script.js
    import { messages } from '@extend-chrome/messages'
    
    messages
      .send(
        // Message
        { greeting: 'hello' },
        // Options
        { async: true },
      )
      .then((response) => {
        console.log('They said', response.greeting)
      })

    messages.on(handler)

    To receive one way messages, use a message handler function with 0 or 1 arguments. This handler will only receive messages sent without the async option.

    The return value of the handler is unused.

    // background.js
    import * as messages from '@extend-chrome/messages'
    
    // Listener should have 2, 1, or 0 arguments
    messages.on((message, sender) => {
      if (message.greeting === 'hello') {
        console.log(sender.id, 'said hello')
      }
    })
    Async Messages

    I've found relying on async messages to be a bit of an anti-pattern. Chrome is pretty aggressive about closing the response port, so unless you're doing something synchronous, it's better to use a separate message and use a listener to handle responses.

    To receive async messages, use a message handler with 3 arguments. This handler will only receive messages sent with the async option.

    The third argument is a sendResponse function, which must be called very quickly, or Chrome will throw an error. Even a single await may make the extension unreliable.

    // Async functions are OK!
    messages.on(async (message, sender, sendResponse) => {
      if (message.greeting === 'hello') {
        console.log(sender.id, 'said hello')
    
        await somethingAsync()
    
        // Still need to call sendResponse
        sendResponse({ greeting: 'goodbye' })
      }
    })

    messages.off(handler)

    Call this with the message handler function you wish to stop using.

    messages.stream

    Type: Observable

    An Observable of all messages in its scope.

    import { messages } from '@extend-chrome/messages'
    
    // Receives all messages in the default scope
    messages.stream.subscribe(([message, sender, sendResponse]) => {
      if (typeof sendResponse !== 'undefined') {
        // If sendResponse is defined, it must be called
        sendResponse({ greeting: 'message received!' })
      }
    })

    getScope

    This is useful if you are writing a library for Chrome extensions that uses messages internally, but you don't want to pollute the global messaging space.

    import { messages, getScope } from '@extend-chrome/messages'
    
    const myScope = getScope('my-library')
    
    // `messages.on` will not receive this message
    myScope.send({ greeting: 'hey' })
    
    // `myScope.on` will not receive this message
    messages.send({ greeting: 'hello?' })

    Note: The Chrome API Event chrome.runtime.onMessage will still receive all messages, but projects using @extend-chrome/messages will not receive messages from other scopes.

    Install

    npm i @extend-chrome/messages

    DownloadsWeekly Downloads

    180

    Version

    1.2.2

    License

    MIT

    Unpacked Size

    83.2 kB

    Total Files

    16

    Last publish

    Collaborators

    • jacksteamdev