npm package: @framasoft/socket
Description: Absinthe Socket
Author: Mauro Titimoli
Latest version: 1.0.3

# @absinthe/socket

> Absinthe Socket

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
<!-- END doctoc -->

- [Features](#features)
- [Installation](#installation)
  - [Using npm](#using-npm)
  - [Using yarn](#using-yarn)
- [Types](#types)
- [API](#api)
  - [cancel](#cancel)
    - [Parameters](#parameters)
    - [Examples](#examples)
  - [create](#create)
    - [Parameters](#parameters-1)
    - [Examples](#examples-1)
  - [observe](#observe)
    - [Parameters](#parameters-2)
    - [Examples](#examples-2)
  - [send](#send)
    - [Parameters](#parameters-3)
    - [Examples](#examples-3)
  - [toObservable](#toobservable)
    - [Parameters](#parameters-4)
    - [Examples](#examples-4)
  - [unobserve](#unobserve)
    - [Parameters](#parameters-5)
    - [Examples](#examples-5)
  - [unobserveOrCancel](#unobserveorcancel)
    - [Parameters](#parameters-6)
    - [Examples](#examples-6)
- [License](#license)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

## Features

-   Immutable functional API
    > All received and returned objects with the exception of AbsintheSocket
    >   instances (there are plans to make this immutable too) are treated in an
    >   immutable way. Objects have no methods and instead we provide independant
    >   stateless functions to interact with them.
-   Lazy connect / join
    > If provided phoenix socket instance is not connected, then instead of
    >   connecting at creation time, connection will be established on the next
    >   invocation of [send](#send).
-   Handle pending operations on connection lost
    > Pending mutations will be aborted, queries will be resent, and subscriptions
    >   reestablished.
-   Cancellable requests
    > Calling [cancel](#cancel) removes given notifier from absintheSocket instance
    >   and sends a Cancel event to all its observers and unsubscribes in case it
    >   holds a subscription request.
-   Operations deduplication
    > If an already sent request is given to [send](#send), then instead of sending
    >   it again, the notifier associated with it will be returned.
-   Observer support of recoverable errors
    > Since connection lost is handled, then two events needs to exist to represent
    >   this fact: Error (recoverable), Abort (unrecoverable).
-   Multiple observers per request
    > Calling [send](#send) returns a notifier which allows attaching any number of
    >   observers that will be notified when result arrives.
-   Observer interaction depending on operation type
    > For the case of subscriptions, _Start_ event is dispatched when the
    >   subscription is established, while for the other types
    >   (queries and mutations), when the request is sent.

## Installation

### Using [npm](https://docs.npmjs.com/cli/npm)

    $ npm install --save phoenix @absinthe/socket

### Using [yarn](https://yarnpkg.com)

    $ yarn add phoenix @absinthe/socket

## Types

```flowtype
type RequestStatus = "canceled" | "canceling" | "pending" | "sent" | "sending";

// from @jumpn/utils-graphql
type GqlRequest<Variables: void | Object = void> = {
  operation: string,
  variables?: Variables
};

// from @jumpn/utils-graphql
type GqlResponse<Data> = {
  data?: Data,
  errors?: Array<GqlError>
};

// from @jumpn/utils-graphql
type GqlOperationType = "mutation" | "query" | "subscription";

type Observer<Result, Variables: void | Object = void> = {|
  onAbort?: (error: Error) => any,
  onCancel?: () => any,
  onError?: (error: Error) => any,
  onStart?: (notifier: Notifier<Result, Variables>) => any,
  onResult?: (result: Result) => any
|};

type Notifier<Result, Variables: void | Object = void> = {|
  activeObservers: $ReadOnlyArray<Observer<Result, Variables>>,
  canceledObservers: $ReadOnlyArray<Observer<Result, Variables>>,
  isActive: boolean,
  operationType: GqlOperationType,
  request: GqlRequest<Variables>,
  requestStatus: RequestStatus,
  subscriptionId?: string
|};

type AbsintheSocket = {|
  channel: Channel,
  channelJoinCreated: boolean,
  notifiers: Array<Notifier<any>>,
  phoenixSocket: PhoenixSocket
|};
```

## API

<!-- Generated by documentation.js. Update this documentation by updating the source code. -->

### cancel

Cancels a notifier sending a Cancel event to all its observers and
unsubscribing in case it holds a subscription request

#### Parameters

-   `absintheSocket` **AbsintheSocket** 
-   `notifier` **Notifier&lt;any, any>** 

#### Examples

```javascript
import * as withAbsintheSocket from "@absinthe/socket";

withAbsintheSocket.cancel(absintheSocket, notifier);
```

Returns **AbsintheSocket** 

### create

Creates an Absinthe Socket using the given Phoenix Socket instance

#### Parameters

-   `phoenixSocket` **PhoenixSocket** 

#### Examples

```javascript
import * as withAbsintheSocket from "@absinthe/socket";
import {Socket as PhoenixSocket} from "phoenix";

const absintheSocket = withAbsintheSocket.create(
  new PhoenixSocket("ws://localhost:4000/socket")
);
```

Returns **AbsintheSocket** 

### observe

Observes given notifier using the provided observer

#### Parameters

-   `absintheSocket` **AbsintheSocket** 
-   `notifier` **Notifier&lt;Result, Variables>** 
-   `observer` **Observer&lt;Result, Variables>** 

#### Examples

```javascript
import * as withAbsintheSocket from "@absinthe/socket"

const logEvent = eventName => (...args) => console.log(eventName, ...args);

const updatedNotifier = withAbsintheSocket.observe(absintheSocket, notifier, {
  onAbort: logEvent("abort"),
  onError: logEvent("error"),
  onStart: logEvent("open"),
  onResult: logEvent("result")
});
```

### send

Sends given request and returns an object (notifier) to track its progress
(see observe function)

#### Parameters

-   `absintheSocket` **AbsintheSocket** 
-   `request` **GqlRequest&lt;Variables>** 

#### Examples

```javascript
import * as withAbsintheSocket from "@absinthe/socket";

const operation = `
  subscription userSubscription($userId: ID!) {
    user(userId: $userId) {
      id
      name
    }
  }
`;

// This example uses a subscription, but the functionallity is the same for
// all operation types (queries, mutations and subscriptions)

const notifier = withAbsintheSocket.send(absintheSocket, {
  operation,
  variables: {userId: 10}
});
```

Returns **Notifier&lt;Result, Variables>** 

### toObservable

Creates an Observable that will follow the given notifier

#### Parameters

-   `absintheSocket` **AbsintheSocket** 
-   `notifier` **Notifier&lt;Result, Variables>** 
-   `options` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?**  (optional, default `{}`)
    -   `options.unsubscribe` **function (): [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)?** 
    -   `options.onError` **function (error: [Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)): [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)?** 
    -   `options.onStart` **function (notifier: Notifier&lt;Result, Variables>): [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)?** 
    -   `options.handlers` **...any** 

#### Examples

```javascript
import * as withAbsintheSocket from "@absinthe/socket";

const unobserveOrCancelIfNeeded = (absintheSocket, notifier, observer) => {
  if (notifier && observer) {
    withAbsintheSocket.unobserveOrCancel(absintheSocket, notifier, observer);
  }
};

const logEvent = eventName => (...args) => console.log(eventName, ...args);

const observable = withAbsintheSocket.toObservable(absintheSocket, notifier, {
  onError: logEvent("error"),
  onStart: logEvent("open"),
  unsubscribe: unobserveOrCancelIfNeeded
});
```

Returns **Observable** 

### unobserve

Detaches observer from notifier

#### Parameters

-   `absintheSocket` **AbsintheSocket** 
-   `notifier` **Notifier&lt;any, any>** 
-   `observer` **Observer&lt;any, any>** 

#### Examples

```javascript
import * as withAbsintheSocket from "@absinthe/socket";

withAbsintheSocket.unobserve(absintheSocket, notifier, observer);
```

Returns **AbsintheSocket** 

### unobserveOrCancel

Cancels notifier if there are no more observers apart from the one given, or
detaches given observer from notifier otherwise

#### Parameters

-   `absintheSocket` **AbsintheSocket** 
-   `notifier` **Notifier&lt;Result, Variables>** 
-   `observer` **Observer&lt;Result, Variables>** 

#### Examples

```javascript
import * as withAbsintheSocket from "@absinthe/socket";

withAbsintheSocket.unobserve(absintheSocket, notifier, observer);
```

## License

[MIT](LICENSE.txt) :copyright: Jumpn Limited.