Giter Club home page Giter Club logo

ably-js's Introduction

Features

Ably is the platform that powers synchronized digital experiences in realtime. Whether attending an event in a virtual venue, receiving realtime financial information, or monitoring live car performance data – consumers simply expect realtime digital experiences as standard. Ably provides a suite of APIs to build, extend, and deliver powerful digital experiences in realtime for more than 250 million devices across 80 countries each month. Organizations like Bloomberg, HubSpot, Verizon, and Hopin depend on Ably’s platform to offload the growing complexity of business-critical realtime data synchronization at global scale. For more information, see the Ably documentation.

npm version

This is a JavaScript client library for Ably Realtime.

This library currently targets the Ably client library features spec Version 1.2. You can jump to the 'Known Limitations' section to see the features this client library does not yet support or view our client library SDKs feature support matrix to see the list of all the available features.

Supported platforms

This SDK supports the following platforms:

Browsers: All major desktop and mobile browsers, including (but not limited to) Chrome, Firefox, Edge, Safari on iOS and macOS, Opera, and Android browsers. IE is not supported. See compatibility table below for more information on minimum supported versions for major browsers:

Browser Minimum supported version Release date
Chrome 58 Apr 19, 2017
Firefox 52 Mar 7, 2017
Edge 79 Dec 15, 2020
Safari 11 Sep 19, 2017
Opera 45 May 10, 2017

Webpack: see using Webpack in browsers, or our guide for serverside Webpack

Node.js: version 16.x or newer. (1.1.x versions work on Node.js 4.5 or newer, 1.2.x versions work on Node.js 8.17 or newer). We do not currently provide an ESM bundle, please contact us if you would would like to use ably-js in a NodeJS ESM project.

React: We offer a set of React Hooks which make it seamless to use ably-js in your React application. See the React Hooks documentation for more details.

React Native: We aim to support all platforms supported by React Native. If you find any issues please raise an issue or contact us.

NativeScript: see ably-js-nativescript

TypeScript: see below

WebWorkers: The browser bundle supports running in a Web Worker context. You can also use the modular variant of the library in Web Workers.

We test the library against a selection of browsers using their latest versions. Please refer to the test-browser GitHub workflow for the set of browsers that currently undergo CI testing.

We regression-test the library against a selection of Node.js versions, which will change over time. We will always support and test against current LTS Node.js versions, and optionally some older versions that are still supported by upstream dependencies. We reserve the right to drop support for non-LTS versions in a non-major release. We will update the engines field in package.json whenever we change the Node.js versions supported by the project. Please refer to the test-node GitHub workflow for the set of versions that currently undergo CI testing.

However, we aim to be compatible with a much wider set of platforms and browsers than we can possibly test on. That means we'll happily support (and investigate reported problems with) any reasonably-widely-used browser. So if you find any compatibility issues, please do raise an issue in this repository or contact Ably customer support for advice.

If you require support for older browsers and Node.js, you can use the security-maintained version 1 of the library. Install version 1 via CDN link, or from npm with npm install ably@1 --save. It supports IE versions 9 or newer, older versions of major browsers, and Node.js 8.17 or newer. Note that version 1 will only receive security updates and critical bug fixes, and won't include any new features.

For complete API documentation, see the Ably documentation.

Installation

Node.js

npm install ably --save

and require as:

var Ably = require('ably');

For usage, jump to Using the Realtime API or Using the REST API.

Serverside usage with webpack

If you are using a version older than 1.2.5 you will need to add 'ably' to externals in your webpack config to exclude it from webpack processing, and require and use it in as a external module using require('ably') as above.

For browsers

Include the Ably library in your HTML:

<script src="https://cdn.ably.com/lib/ably.min-1.js"></script>

The Ably client library follows Semantic Versioning. To lock into a major or minor version of the client library, you can specify a specific version number such as https://cdn.ably.com/lib/ably.min-1.js for all v1._ versions, or https://cdn.ably.com/lib/ably.min-1.0.js for all v1.0._ versions, or you can lock into a single release with https://cdn.ably.com/lib/ably.min-1.0.9.js. Note you can load the non-minified version by omitting min- from the URL such as https://cdn.ably.com/lib/ably-1.0.js. See https://github.com/ably/ably-js/tags for a list of tagged releases.

For usage, jump to Using the Realtime API or Using the REST API.

Using WebPack

(This applies to using webpack to compile for a browser; for Node.js, see Serverside usage with webpack)

WebPack will search your node_modules folder by default, so if you include ably in your package.json file, when running Webpack the following will allow you to require('ably') (or if using typescript or ES6 modules, import * as Ably from 'ably';). If your webpack target is set to 'browser', this will automatically use the browser commonjs distribution.

If that doesn't work for some reason (e.g. you are using a custom webpack target), you can reference the ably.js static file directly: require('ably/build/ably.js'); (or import * as Ably from 'ably/build/ably.js' for typescript / ES6 modules).

Modular (tree-shakable) variant

Aimed at those who are concerned about their app’s bundle size, the modular variant of the library allows you to create a client which has only the functionality that you choose. Unused functionality can then be tree-shaken by your module bundler.

The modular variant of the library provides:

  • a BaseRealtime class;
  • various plugins that add functionality to a BaseRealtime instance, such as Rest, RealtimePresence, etc.

To use this variant of the library, import the BaseRealtime class from ably/modular, along with the plugins that you wish to use. Then, pass these plugins to the BaseRealtime constructor as shown in the example below:

import { BaseRealtime, WebSocketTransport, FetchRequest, RealtimePresence } from 'ably/modular';

const client = new BaseRealtime({
  key: 'YOUR_ABLY_API_KEY' /* Replace with a real key from the Ably dashboard */,
  plugins: {
    WebSocketTransport,
    FetchRequest,
    RealtimePresence,
  },
});

You must provide:

  • at least one HTTP request implementation; that is, one of FetchRequest or XHRRequest;
  • at least one realtime transport implementation; that is, one of WebSocketTransport or XHRPolling.

BaseRealtime offers the same API as the Realtime class described in the rest of this README. This means that you can develop an application using the default variant of the SDK and switch to the modular version when you wish to optimize your bundle size.

In order to further reduce bundle size, the modular variant of the SDK performs less logging than the default variant. It only logs:

  • messages that have a logLevel of 1 (that is, errors)
  • a small number of other network events

If you need more verbose logging, use the default variant of the SDK.

For more information about the modular variant of the SDK, see the generated documentation (this link points to the documentation for the main branch).

TypeScript

The TypeScript typings are included in the package and so all you have to do is:

import * as Ably from 'ably';

let options: Ably.ClientOptions = { key: 'foo' };
let client = new Ably.Realtime(options); /* inferred type Ably.Realtime */
let channel = client.channels.get('feed'); /* inferred type Ably.RealtimeChannel */

Intellisense in IDEs with TypeScript support is supported:

TypeScript suggestions

If you need to explicitly import the type definitions, see ably.d.ts.

NativeScript

See the ably-js-nativescript repo for NativeScript usage details.

Using the Realtime API

This readme gives some basic examples; for our full API documentation, please go to https://www.ably.com/docs .

Introduction

All examples assume a client has been created as follows:

// basic auth with an API key
var client = new Ably.Realtime(key: string);

// using a Client Options object, see https://www.ably.com/docs/rest/usage#client-options
// which must contain at least one auth option, i.e. at least
// one of: key, token, tokenDetails, authUrl, or authCallback
var client = new Ably.Realtime(options: ClientOptions);

Connection

Successful connection:

client.connection.on('connected', function() {
  # successful connection
});

Failed connection:

client.connection.on('failed', function() {
  # failed connection
});

Subscribing to a channel

Given:

var channel = client.channels.get('test');

Subscribe to all events:

channel.subscribe(function (message) {
  message.name; // 'greeting'
  message.data; // 'Hello World!'
});

Only certain events:

channel.subscribe('myEvent', function (message) {
  message.name; // 'myEvent'
  message.data; // 'myData'
});

Subscribing to a channel with deltas

Subscribing to a channel in delta mode enables delta compression. This is a way for a client to subscribe to a channel so that message payloads sent contain only the difference (ie the delta) between the present message and the previous message on the channel.

Configuring a channel for deltas is detailed in the @ably-forks/vcdiff-decoder documentation.

Beyond specifying channel options, the rest is transparent and requires no further changes to your application. The message.data instances that are delivered to your listening function continue to contain the values that were originally published.

If you would like to inspect the Message instances in order to identify whether the data they present was rendered from a delta message from Ably then you can see if extras.delta.format equals 'vcdiff'.

Publishing to a channel

// Publish a single message with name and data
await channel.publish('greeting', 'Hello World!');

// Publish several messages at once
await channel.publish([{name: 'greeting', data: 'Hello World!'}, ...]);

Querying the History

const messagesPage = channel.history()
messagesPage                                   // PaginatedResult
messagesPage.items                             // array of Message
messagesPage.items[0].data                     // payload for first message
messagesPage.items.length                      // number of messages in the current page of history
messagesPage.hasNext()                         // true if there are further pages
messagesPage.isLast()                          // true if this page is the last page
const nextPage = await messagesPage.next();    // retrieves the next page as PaginatedResult

// Can optionally take an options param, see https://www.ably.com/docs/rest-api/#message-history
const messagesPage = await channel.history({start: ..., end: ..., limit: ..., direction: ...});

Presence on a channel

Getting presence:

const presenceSet = channel.presence.get();
presenceSet; // array of PresenceMessages

Note that presence#get on a realtime channel does not return a PaginatedResult, as the library maintains a local copy of the presence set.

Entering (and leaving) the presence set:

await channel.presence.enter('my status');
// now I am entered

await channel.presence.update('new status');
// my presence data is updated

await channel.presence.leave()
// I've left the presence set

If you are using a client which is allowed to use any clientId -- that is, if you didn't specify a clientId when initializing the client, and are using basic auth or a token witha wildcard clientId (see https://www.ably.com/docs/general/authentication for more information), you can use

await channel.presence.enterClient('myClientId', 'status');
// and similarly, updateClient and leaveClient

Querying the Presence History

const messagesPage = channel.presence.history(); // PaginatedResult
messagesPage.items                               // array of PresenceMessage
messagesPage.items[0].data                       // payload for first message
messagesPage.items.length                        // number of messages in the current page of history
messagesPage.hasNext()                           // true if there are further pages
messagesPage.isLast()                            // true if this page is the last page
const nextPage = await messagesPage.next();      // retrieves the next page as PaginatedResult

// Can optionally take an options param, see https://www.ably.com/docs/rest-api/#message-history
const messagesPage = await channel.presence.history({start: ..., end: ..., limit: ..., direction: ...);

Symmetrical end-to-end encrypted payloads on a channel

When a 128 bit or 256 bit key is provided to the library, the data attributes of all messages are encrypted and decrypted automatically using that key. The secret key is never transmitted to Ably. See https://www.ably.com/docs/realtime/encryption

// Generate a random 256-bit key for demonstration purposes (in
// practice you need to create one and distribute it to clients yourselves)
const key = await Ably.Realtime.Crypto.generateRandomKey();
var channel = client.channels.get('channelName', { cipher: { key: key } });

channel.subscribe(function (message) {
  message.name; // 'name is not encrypted'
  message.data; // 'sensitive data is encrypted'
});

channel.publish('name is not encrypted', 'sensitive data is encrypted');

You can also change the key on an existing channel using setOptions (which completes after the new encryption settings have taken effect):

await channel.setOptions({cipher: {key: <key>}});
// New encryption settings are in effect

Message interactions

Message Interactions allow you to interact with messages previously sent to a channel. Once a channel is enabled with Message Interactions, messages received by that channel will contain a unique timeSerial that can be referenced by later messages.

Example emoji reaction to a message:

function sendReaction(emoji) {
    channel.publish({ name: 'event_name', data: emoji, extras: { ref: { type: "com.ably.reaction", timeserial: "1656424960320-1" } } })
}

See https://www.ably.com/docs/realtime/messages#message-interactions for more detail.

Fallback transport mechanisms

Ably-js has fallback transport mechanisms to ensure its realtime capabilities can function in network conditions (such as firewalls or proxies) that might prevent the client from establishing a WebSocket connection.

The default Ably.Realtime client includes these mechanisms by default. If you are using modular variant of the library, you may wish to provide the BaseRealtime instance with one or more alternative transport modules, namely XHRStreaming and/or XHRPolling, alongside WebSocketTransport, so your connection is less susceptible to these external conditions. For instructions on how to do this, refer to the modular variant of the library section.

Each of these fallback transport mechanisms is supported and tested on all the browsers we test against, even when those browsers do not themselves require those fallbacks.

Using the REST API

This readme gives some basic examples. For our full API documentation, please go to https://www.ably.com/docs .

Introduction

All examples assume a client and/or channel has been created as follows:

// basic auth with an API key
var client = new Ably.Rest(key: string);

// using a Client Options object, see https://www.ably.com/docs/realtime/usage#client-options
// which must contain at least one auth option, i.e. at least
// one of: key, token, tokenDetails, authUrl, or authCallback
var client = new Ably.Rest(options: ClientOptions);

Given:

var channel = client.channels.get('test');

Publishing to a channel

// Publish a single message with name and data
try {
  channel.publish('greeting', 'Hello World!');
  console.log('publish succeeded');
} catch (err) {
  console.log('publish failed with error ' + err);
}

// Publish several messages at once
await channel.publish([{name: 'greeting', data: 'Hello World!'}, ...]);

Querying the History

const messagesPage = await channel.history();
messagesPage                                // PaginatedResult
messagesPage.items                          // array of Message
messagesPage.items[0].data                  // payload for first message
messagesPage.items.length                   // number of messages in the current page of history
messagesPage.hasNext()                      // true if there are further pages
messagesPage.isLast()                       // true if this page is the last page
const nextPage = await messagesPage.next(); // retrieves the next page as PaginatedResult

// Can optionally take an options param, see https://www.ably.com/docs/rest-api/#message-history
await channel.history({start: ..., end: ..., limit: ..., direction: ...});

Presence on a channel

const presencePage = await channel.presence.get() // PaginatedResult
presencePage.items                                // array of PresenceMessage
presencePage.items[0].data                        // payload for first message
presencePage.items.length                         // number of messages in the current page of members
presencePage.hasNext()                            // true if there are further pages
presencePage.isLast()                             // true if this page is the last page
const nextPage = await presencePage.next();       // retrieves the next page as PaginatedResult

Querying the Presence History

const messagesPage = channel.presence.history(); // PaginatedResult
messagesPage.items                               // array of PresenceMessage
messagesPage.items[0].data                       // payload for first message
messagesPage.items.length                        // number of messages in the current page of history
messagesPage.hasNext()                           // true if there are further pages
messagesPage.isLast()                            // true if this page is the last page
const nextPage = await messagesPage.next();      // retrieves the next page as PaginatedResult

// Can optionally take an options param, see https://www.ably.com/docs/rest-api/#message-history
const messagesPage = channel.history({start: ..., end: ..., limit: ..., direction: ...});

Getting the status of a channel

const channelDetails = await channel.status();
channelDetails.channelId        // The name of the channel
channelDetails.status.isActive  // A boolean indicating whether the channel is active
channelDetails.status.occupancy // Contains metadata relating to the occupants of the channel

Generate Token and Token Request

See https://www.ably.com/docs/general/authentication for an explanation of Ably's authentication mechanism.

Requesting a token:

const tokenDetails = await client.auth.requestToken();
// tokenDetails is instance of TokenDetails
// see https://www.ably.com/docs/rest/authentication/#token-details for its properties

// Now we have the token, we can send it to someone who can instantiate a client with it:
var clientUsingToken = new Ably.Realtime(tokenDetails.token);

// requestToken can take two optional params
// tokenParams: https://www.ably.com/docs/rest/authentication/#token-params
// authOptions: https://www.ably.com/docs/rest/authentication/#auth-options
const tokenDetails = await client.auth.requestToken(tokenParams, authOptions);

Creating a token request (for example, on a server in response to a request by a client using the authCallback or authUrl mechanisms):

const tokenRequest = await client.auth.createTokenRequest();
// now send the tokenRequest back to the client, which will
// use it to request a token and connect to Ably

// createTokenRequest can take two optional params
// tokenParams: https://www.ably.com/docs/rest/authentication/#token-params
// authOptions: https://www.ably.com/docs/rest/authentication/#auth-options
const tokenRequest = await client.auth.createTokenRequest(tokenParams, authOptions);

Fetching your application's stats

const statsPage = await client.stats()          // statsPage as PaginatedResult
statsPage.items                                 // array of Stats
statsPage.items[0].inbound.rest.messages.count; // total messages published over REST
statsPage.items.length;                         // number of stats in the current page of history
statsPage.hasNext()                             // true if there are further pages
statsPage.isLast()                              // true if this page is the last page
const nextPage = await statsPage.next();        // retrieves the next page as PaginatedResult

Fetching the Ably service time

const time = await client.time(); // time is in ms since epoch

Push activation

Push activation is supported for browser clients, via the Push plugin. In order to use push activation, you must pass in the plugin via client options.

You also need to provide a path to a service worker which will be registered when the client is activated, and will handle receipt of push notifications.

import * as Ably from 'ably';
import Push from 'ably/push';

const client = new Ably.Rest({
    ...options,
    pushServiceWorkerUrl: '/my_service_worker.js',
    plugins: { Push }
});

Example service worker:

// my_service_worker.js
self.addEventListener("push", async (event) => {
  const { notification } = event.data.json();
  self.registration.showNotification(notification.title, notification);
});

To register the device to receive push notifications, you must call the activate method:

await client.push.activate();

Once the client is activated, you can subscribe to receive push notifcations on a channel:

const channel = client.channels.get('my_push_channel');

// Subscribe the device to receive push notifcations for a channel...
await channel.push.subscribeDevice();

// ...or subscribe all devices associated with the client's cliendId to receive notifcations from the channel
await channel.push.subscribeClient();

// When you no longer need to be subscribed to push notifcations, you can remove the subscription:
await channel.push.unsubscribeDevice();
// Or:
await channel.push.unsubscribeClient();

Push activation works with the Modular variant of the library, but requires you to be using the Rest plugin.

For more information on publishing push notifcations over Ably, see the Ably push documentation.

Delta Plugin

From version 1.2 this client library supports subscription to a stream of Vcdiff formatted delta messages from the Ably service. For certain applications this can bring significant data efficiency savings. This is an optional feature so our

See the @ably-forks/vcdiff-decoder documentation for setup and usage examples.

Support, feedback and troubleshooting

Please visit http://support.ably.com/ for access to our knowledgebase and to ask for any assistance.

You can also view the community reported Github issues.

To see what has changed in recent versions, see the CHANGELOG.

Browser-specific issues

Chrome Extensions

ably-js works out-of-the-box in background scripts for Chrome extensions using manifest v2. However, since manifest v3 background pages are no longer supported so you will need to run ably-js inside a service worker. If you are using an ably-js realtime client in a service worker, note that in versions of Chrome before 116, active WebSockets would not reset the 30s service worker idle timer, resulting in the client being closed prematurely, however, in versions 116 and above, service workers will stay active as long as a client is connected. You can ensure that your extension only runs in versions 116 and above by adding the following to your manifest.json:

{
  ...
  "minimum_chrome_version": "116",
  ...
}

Next.js with App Router and Turbopack

If you are using ably-js in your Next.js project with App Router and Turbopack enabled (via running next dev --turbo), you may encounter Failed to compile Module not found compilation error referencing ./node_modules/keyv/src/index.js file or see Critical dependency: the request of a dependency is an expression warnings for the same keyv module.

To fix this, please add ably to the serverComponentsExternalPackages list in next.config.js (read more about this option here):

const nextConfig = {
  // ...
  experimental: {
    serverComponentsExternalPackages: ['ably'],
  },
};

The issue is coming from the fact that when using App Router specifically dependencies used inside Server Components and Route Handlers will automatically be bundled by Next.js. This causes issues with some packages, usually the ones that have complex require statements, for example, requiring some packages dynamically during runtime. keyv is one of those packages as it uses require statement dynamically when requiring its adapters (see code in repo):

keyv ends up being one of ably-js's upstream dependencies for node.js bundle, which causes the errors above when using it with Next.js App Router.

Using serverComponentsExternalPackages opt-outs from using Next.js bundling for specific packages and uses native Node.js require instead. This is a common problem in App Router for a number of packages (for example, see next.js issue vercel/next.js#52876), and using serverComponentsExternalPackages is the recommended approach here.

Contributing

For guidance on how to contribute to this project, see the CONTRIBUTING.md.

Credits

Automated browser testing supported by

ably-js's People

Contributors

alex-georgiou avatar andydunstall avatar andytwf avatar dependabot[bot] avatar funkyboy avatar kazk avatar kouno avatar lawrence-forooghian avatar lmars avatar matt-esch avatar mattheworiordan avatar mohyour avatar mschristensen avatar nathanaela avatar owenpearson avatar paddybyers avatar peter-kubik avatar quintinwillison avatar rmbellovin avatar rustworthy avatar sacoo7 avatar sapples avatar simonwoolf avatar srushtika avatar tcard avatar tsviatko avatar ttypic avatar vesker avatar vodolaz095 avatar withakay avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ably-js's Issues

npm and buffertools install issue

When I upgrade to Node.js 0.12, I get an error. I suspect the NPM package buffertools is incompatible.

$ grunt nodeunit
Running "nodeunit" task
Running Nodeunit test suite against all tests

module.js:338
    throw err;
          ^
Error: Cannot find module './build/Debug/buffertools.node'
    at Function.Module._resolveFilename (module.js:336:15)
    at Function.Module._load (module.js:278:25)
    at Module.require (module.js:365:17)
    at require (module.js:384:17)
    at Object.<anonymous> (/Users/matthew/Projects/Ably/clients/ably-js/node_modules/buffertools/buffertools.js:26:20)
    at Module._compile (module.js:460:26)
    at Object.Module._extensions..js (module.js:478:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Module.require (module.js:365:17)
>> Nodeunit tests failed!

Connection state recovery is intermittent

If I just refresh my chat client over & over (on a non-persisted client), about a quarter of the time I get the whole message history pushed through on connect, the remaining time I get a clean session.

example timestamp of when I got message history: 1431001293310

example timestamp of when I didn't: 1431001280178

Better error message for an invalid token from the authUrl or callback

When the authUrl requests a token from a URL that returns a token string instead of JSON, yet the client thinks it is JSON, a very unhelpful error message is shown:

Ably: EventEmitter.emit(): Unexpected listener exception: SyntaxError: Unexpected token L; stack = SyntaxError: Unexpected token L

PaginatedResource API change

Whilst our client APIs are consistent, the PaginatedResource API has unfortunately diverged somewhat. Following some discussions, we have decided to implement the PaginatedResource so that it is iterator-like, provides an accessor / method to the items it holds to encourage understanding of the pages of results concept to developers i.e. each next page is a new request for new items, and provides similar synchronous and asynchronous interfaces.

The proposed interface demonstrated in code is therefore as follows:

Async Javascript

function displayPages(currentPage) {
    for (let message in currentPage.items) {
      console.log(message.name);
    }
    if (let next = currentPage.next) {
      next(function(err, nextPage) { displayPages(nextPage); });
    }
}

channel.history(function (err, currentPage) {
  currentPage.isFirst(); // true
  currentPage.isLast(); // false
  displayPages(currentPage);
}

Sync Ruby

current_page = channel.history

current_page.first? # true
current_page.last? # false
current_page.has_next? # true

do 
  current_page.items.each { |message| puts message.name }
  current_page = current_page.next
until current_page.last?

current_page.next # => nil

createTokenRequest without key option

In the Ably Ruby library when calling createTokenRequest, if an API key has been specified in the constructor, then there is no need to specify a key ID in the createTokenRequest method. Given most people will only issue tokens for the set up API key and we treat API keys as opaque (i.e. key ID is not really relevant), I recommend we change the createTokenRequest in the JS client library so that:

  • Passing in an empty hash is valid so long as Basic Auth is being used
  • instead of key_id & key_value options in the hash, we should really just support a key option.

iPad iOS6 timing issues

When running the tests on iPad iOS6, there are timing issues in the logs. We may need to adjust the suite to factor in clocks out of sync when running these types of tests on remote browsers. The test suite never finishes because setupStats fails.

PresenceMessage action

As an Enum is not natively supported in Javascript, I'm trying to figure out what the value for action will be. I am pretty sure it will be the numeric value from the zero-indexed set { ABSENT, PRESENT, ENTER, LEAVE, UPDATE }, however I think this is not a great API for users.

For example, if you a developer called get on all members, there would be no way for the developer to easily work out if they user had an action of leave or enter. In Ruby this is easy because it's an Enum-like object, and in Java it's an Enum, but in Javascript the PresenceMessage.Action object is not public os the user cannot use that to work out what the numeric value means. I feel the user should be shielded from this personally.

I therefore propose that in the Javascript API the PresenceMessage#action field is in fact a string with a value from the set above and it is the responsibility of the library to convert that when sending & receiving messages from Ably. WDYT?

@paddybyers

Comet transport error

Discussion from Slack


Hi guys, I've been having some trouble with the following error: {"statusCode":404,"code":80009,"message":"Connection not established (no transport handle): EEEE-EEEEEE"}

I'm pretty sure I am connected to Ably before receiving the error, as I do receive a "connected" callback before the error. An example URL it 404's on is: click

Here's a part of my log when I receive the error:

Ably: RealtimeChannel.setAttached: activating channel; name = LQMLC; message flags = undefined
ably.js?_=1428610526373:4067 Ably: WebSocketTransport.onWsData(): data received; length = 186; type = string
ably.js?_=1428610526373:4067 Ably: Presence.setPresence(): received presence for 1 participants; syncChannelSerial = undefined
ably.js?_=1428610526373:4067 Ably: PresenceMap.endSync(); channel = LQMLC; syncInProgress = true: undefined
ably.js?_=1428610526373:4067 Ably: WebSocketTransport.onWsData(): data received; length = 186; type = string
ably.js?_=1428610526373:4067 Ably: Presence.setPresence(): received presence for 1 participants; syncChannelSerial = undefined
ably.js?_=1428610526373:4067 Ably: PresenceMap.endSync(); channel = LQMLC; syncInProgress = false: undefined
ably.js?_=1428610526373:4067 Ably: WebSocketTransport.onWsData(): data received; length = 22; type = string
ably.js?_=1428610526373:4067 Ably: ConnectionManager on(ack): serial = undefined; count = 1
ably.js?_=1428610526373:4067 Ably: ConnectionManager.ackMessage(): serial = undefined; count = 1
rest.ably.io/comet/EEEE!EEEEE-FDL7eUqUYtJ/send?access_token=EEEEE.EEEEEEE…qEWgTajYPmaL_JFUhgto0LhFzh_1T2UNorxXf_XWK05_ZRCJTiw&rnd=5785505757667124:1 POST https://rest.ably.io/comet/EEEE!EEEEEEE-FDL7eUqUYtJ/send?access_token=EEEE.…rxqEWgTajYPmaL_JFUhgto0LhFzh_1T2UNorxXf_XWK05_ZRCJTiw&rnd=5785505757667124 404 (Not Found)
ably.js?_=1428610526373:4067 Ably: CometTransport.sendItems(): on complete: err = {"statusCode":404,"code":80009,"message":"Connection not established (no transport handle): EEEEE-EEEEEeUqUYtJ"}
ably.js?_=1428610526373:4067 Ably: WebSocketTransport.onWsData(): data received; length = 12; type = string

Any ideas what might be causing this?

apart from that message in the log, are you experiencing other problems? eg errors returned from the API?

It stops the rest of my application. Which is happening at either channel.subscribe( or channel.presence.enter(. The last thing it does is attaching to a channel.

apologies I was offline earlier, @paddy do you have any idea what could be causing this? @customer any chance you can provide a JSBin we could look at?
obviously you can leave off the API key and we'll test with one of ours
if you could tell me the part of the API key before the : though that would be useful

Partial API key is XXXXX.XXXXX. I'll set up a JSBin some time soon, if necessary.

Ok, thanks @customer, we're looking at it now

Awesome, thanks

can I just confirm that you don't see an issue if you add transports: ['web_socket'] to the Ably init options?

this seems to be the case, no errors after ~30 times connecting. I'll leave the option there and let you know if I receive any.

Thanks @customer, we'll try and see how you are seeing that message, can you confirm what platform you are on?

An Angular web application.

sorry, I meant which browser / OS?

Chrome on Windows

memberKey attribute / function on a presence message

We provide a memberKey method on the presence object where you pass in a PresenceMessage, why not instead provide a memberKey method on the PresenceMessage itself to simplify the API?

Also, if we provide a memberKey that uniquely identifies a member in a presence set, should we not also add a memberKey attribute to the Realtime object so that a developer can easily work out what their memberKey is? If we don't provide this, then they will need to understand how to construct this memberKey themselves negating any usefulness of providing this method elsewhere.

WDYT?

Browser test error: authbase0 - displayError is not defined

Running:
$ grunt nodeunit:webserver

against sanbox I get the following error:

authbase0

Ably: Connection.connect(): 
ably.js:4067 Ably: ConnectionManager.chooseTransport(): 
sandbox-rest.ably.io/keys/Gl1N4Q.lCxqBQ/requestToken?rnd=6598727754317224:1 POST https://sandbox-rest.ably.io/keys/Gl1N4Q.lCxqBQ/requestToken?rnd=6598727754317224 401 (Unauthorized)
ably.js:4067 Ably: Auth.requestToken(): token request API call returned error; err = {"statusCode":401,"code":40101,"message":"No key id specified"}
ably.js:4067 Ably: EventEmitter.emit(): Unexpected listener exception: ReferenceError: displayError is not defined; stack = ReferenceError: displayError is not defined
    at http://localhost:3000/spec/realtime/auth.test.js:36:24
    at Object.<anonymous> (http://localhost:3000/browser/static/ably.js:6632:6)
    at callListener (http://localhost:3000/browser/static/ably.js:4022:19)
    at XHRRequest.EventEmitter.emit (http://localhost:3000/browser/static/ably.js:4039:5)
    at XHRRequest.complete (http://localhost:3000/browser/static/ably.js:8103:9)
    at onEnd (http://localhost:3000/browser/static/ably.js:8211:9)
    at XMLHttpRequest.XHRRequest.exec.xhr.onreadystatechange (http://localhost:3000/browser/static/ably.js:8260:7)

I realise sandbox is not in sync, but the error about displayError seems to be a bug.

TypeError on presence.subscribe()

In the following snippet:

self.channel = self.Ably.channels.get(_channelname);
self.subscription = type;
self.channel.attach();
self.channel.on('attached', function() {
    console.log("ATTACHED TO CHANNEL");
    self.channel.subscribe(self.subscription, function(msg) {
        console.log("received ably msg?");
        console.log(msg);
    });

    self.channel.presence.enter({}, function(dat) {
        console.log("ENTERED");
        //self.channel.presence.get();
        self.channel.presence.get(null, function(dat, dat2) {
            console.log("subscribed");
            resolve(dat2);
        }); 

        self.channel.presence.subscribe(function(presenceMessage) {
            console.log(presenceMessage);
        });
    });
});

Including the self.channel.presence.subscribe function returns me the following 2 errors:

Ably: EventEmitter.emit(): Unexpected listener exception: TypeError: undefined is not a function; stack = TypeError: undefined is not a function

Ably: WebSocketTransport.onWsData(): Unexpected exception handing channel message: TypeError: undefined is not a function

Note that I have tried replacing the parameters with self.channel.presence.subscribe("enter", function(presenceMessage), but this results in the same errors. Am I doing something wrong here?

No transport handle

Got this when connecting with from docs.ably.io against production

Ably: Connection.connect(): 
ably.min.js:65 Ably: ConnectionManager.chooseTransport(): 
ably.min.js:65 Ably: ConnectionManager.chooseTransport(): upgrading ... connectionKey = -yOKSdeBbXXs8I_x
rest.ably.io/comet/fe95d!-yOKSdeBbXXs8I_x/send?access_token=xVLyHw.C5of7sAC…1e9EQKdZhN_j4kyXnjqsgbknSUmxpHRolPOTLBfgei254VGq0og&rnd=1876056483015418:1 POST https://rest.ably.io/comet/fe95d!-yOKSdeBbXXs8I_x/send?access_token=xVLyHw.…9b1e9EQKdZhN_j4kyXnjqsgbknSUmxpHRolPOTLBfgei254VGq0og&rnd=1876056483015418 404 (Not Found)
ably.min.js:65 Ably: CometTransport.sendItems(): on complete: err = {"statusCode":404,"code":80009,"message":"Connection not established (no transport handle): -yOKSdeBbXXs8I_x"}

Setting default environment

We have an issue in our staging environment that resulted in #24. The problem is that our docs repo uses environment production by default which makes sense, see ably/docs@74d21d1#diff-5628feb5438d9fac128ae3d1a9a92298R114 for an example. Whilst we could add a environment: '{{ENVIRONMENT}}' option to the documentation, it would make the documentation unnecessarily verbose and environment is not really something we want people to know / think about.

So what I propose is perhaps in staging to run a command such as:

Ably.Realtime.setGlobalOption({ environment: 'sandbox' });
Ably.Rest.setGlobalOption({ environment: 'sandbox' });

How difficult is this to achieve? Do you have a better solution?

[Object object] in error messages makes it difficult to see what the problem is

On our staging site Rafael was seeing 404 errors coming up. I looked into it and see that a sandbox API key was being used against production end point which we will fix.

However, the issue is that Rafael was unable to see what the error was, see below.

Can we ensure that the error messages are displayed correctly in the console?

voila_capture 2015-03-31_06-26-51_pm

iFrame loading

From what I can tell, the iFrame.html and Javascript associated with it is loaded from the REST / Realtime end point such as https://rest.ably.io/static/iframe.html, see https://github.com/ably/ably-js/blob/master/browser/lib/transport/iframetransport.js#L66-L73.

I believe this approach has the following issues:

  • Versioning is not respected. If you upgrade a client library and change the iFrame code, older versions of the ably-js browser library will now use the newer HTML & Javascript which is not guaranteed to be backwards compatible.
  • We are not using the CDN.

We should perhaps use Grunt to hard code in a version number based on the git tag and link directly to the cdn.

Note all files needed are on the CDN, see http://cdn.ably.io/lib/iframe.js, http://cdn.ably.io/lib/iframe.min.js and http://cdn.ably.io/lib/iframe.html

Sparse stat support

Can you confirm if JSON values will default to zero now that we will roll out sparse stat support?

Whilst sparse stats for efficiency between client & server is probably the right option, I am not sure the client libraries should be providing empty objects to stat queries so do prefer either a complete JSON object with zero values for each stat, or a more typed approach like we have done in https://github.com/ably/ably-java/blob/master/src/io/ably/types/Stats.java or ably/ably-ruby@cec0349

Time out tests

We should really implement a timeout for all tests in the suite so that the test suite does not hang and block other builds should a done() callback not be called.

Incorrect error shown in clients when account limits are hit

I am testing a local server and when I connect with the ably-js client library, the error shown is:

Ably: Rest(): started
Ably: Auth(): anonymous, using basic auth
Ably: Realtime.ConnectionManager(): started
Ably: Connection.connect():
Ably: ConnectionManager.requestState(): requested state: connecting
Ably: ConnectionManager.cancelTransitionTimer():
Ably: ConnectionManager.enactStateChange: setting new state: connecting; reason = undefined
Ably: ConnectionManager.connectImpl(): starting connection
Ably: ConnectionManager.startTransitionTimer(): transitionState: [object Object]
Ably: ConnectionManager.chooseTransport():
Ably: ConnectionManager.chooseTransport(): Transport recovery mode = clean
Ably: CometTransport.connect(): starting
Ably: CometTransport.connect(): uri: https://local-rest.ably.io:443/comet/connect
Ably: CometTransport.connect(): connectParams:?key=hG9k8g.5BC1rQ%3AyzNeBJDVGA1QuAKa
Ably: ConnectionManager.chooseTransport(): Unexpected error establishing transport; err = {"statusCode":404,"code":80000}
Ably: ConnectionManager.connectImpl(): Error: Unable to connect (no available host)
Ably: ConnectionManager.notifyState(): new state: disconnected
Ably: ConnectionManager.cancelTransitionTimer():
Ably: ConnectionManager.enactStateChange: setting new state: disconnected; reason = Unable to connect (no available host)

However, when I connect using a telnet session to see what is actually exchanged, I see the following:

$ telnet localhost 80
Connected to localhost.
Escape character is '^]'.
GET /?access_token=hG9k8g.C8WNMrAhOZ_1n7oNYqKvrzGbGnbySjpz5OPyWcwJHGTq_ZctT-dTNIItzuZ7WK88i7eOo4Yog2dG62-WqmGZ2uHFHOuLY9N0oGgLMOgYwT9c&format=json&echo=true HTTP/1.1
Connection: Upgrade
Upgrade: websocket
Host: localhost:80
Origin: localhost:80
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: MTMtMTQzMzM0Nzg3NTU5OA==

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: 0GFCTfIbbrcN75iYRZrkF7ykWMo=

{"action":9,"error":{"message":"account blocked (message limits exceeded)","code":40112,"statusCode":401}}

I suspect this is really an ably-js issue and not a realtime issue, but I wanted to raise it here first. We need to ensure clients are shown the correct error messages, especially for problems like this.

Possible proxy method enhancements

We discussed having a shortcut method to access channels with the form

var client = new AblyRealtime(key);
var channel = client.channel('test');

Also, if we have a close method on the AblyRealtime client, should we not add a connect as well. There is an argument that connect is implicit, however this is not always the case.

If we do add this, we should do the same for Java & Ruby

token option vs tokenId

@paddybyers when implementing the Ably Ruby library I opted to use a token_id option as opposed to token because I understood that by just passing a token ID the library could authenticate with Ably. However, I see in the Ably JS library that it seems to expect an actual token, whereas if you instance the library with a string token, it is actually a tokenId.

Should we not be consistent and require either a token or a tokenId, or should we instead perhaps simply add another option which is tokenId that allows the tokenId string to be passed into the constructor?

FYI, I noticed that in the Ably lib I was using the option api_key instead of key, so that is changed now, see ably/ably-ruby@8b6cc9c

Swallowing errors

I have come across a similar problem to the EventEmitter swallowing discussion with another callback, see 93e3c4a for an example of the problem.

Here's the result of the Jasmine test being run:

$ grunt test:jasmine --spec spec/realtime/event_emitter.spec.js
Running "jasmine" task
Running Jasmine test suite against spec/realtime/event_emitter.spec.js
Started
Test App rzabrg has been set up
Realtime EventEmitter
  swallows exceptions, does not log the error, and leaves the suite hanging on failure waiting for a timeout ...
{ statusCode: 401,
  code: 40160,
  message: 'Channel denied access based on given capability; channelId = doesNotHavePermission' }
F Failed
    Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
0 of 1 passed (0 skipped, 0 disabled).
Test suite behaviour
  stops immediately when an exception is raised in an async block ...

/Users/matthew/Projects/Ably/clients/ably-js/node_modules/chai/lib/chai/assertion.js:106
      throw new AssertionError(msg, {
            ^
AssertionError: failed
    at null._onTimeout (/Users/matthew/Projects/Ably/clients/ably-js/spec/realtime/event_emitter.spec.js:26:9)
    at Timer.listOnTimeout [as ontimeout] (timers.js:112:15)
>> Browser tests failed!

As you can see, the assert 93e3c4a#diff-611f6d447f261be4dc21af22d9c2348bR15 is simply swallowed by the Ably library, so no test can ever catch this error and fail the test for the right reason, instead it just times out. Equally no log message is shown.

If you look at 93e3c4a#diff-611f6d447f261be4dc21af22d9c2348bR26 however, that error is caught immediately and the test fails quickly.

I really don't like the idea that the client library is swallowing errors like this. I was actually trying to test the EventEmitter behaviour, and spent ages figuring out that the attach() block has the same behaviour. I believe we need a better solution.

IFrameTransport errors when connecting with tls: false

Context:

ably.js:4067 Ably: IframeTransport.connect(): starting
ably.js:4067 Ably: Auth.getToken(): using cached token; expires = 1434461878631
ably.js:4067 Ably: IframeTransport.createIframe(): destUri: https://sandbox-realtime.ably.io:443/static/iframe-0.8.0.html?origin=file%3…YQ3H5DJHvk3InWqSy63gj0gtcLqRsevTjT7644LeOQK7vRw7Jm6iujC153Y7d8ObMRFbTWew1k
ably.js:4067 Ably: IframeTransport.tryConnect(): viable transport IframeTransport; uri=undefined
ably.js:4067 Ably: ConnectionManager.chooseTransport(): transport iframe connecting
ably.js:4067 Ably: ConnectionManager.setTransportPending(): transport = IframeTransport; uri=undefined
ably.js:4067 Ably: ConnectionManager.setTransportPending: on state = disconnected
ably.js:4067 Ably: ConnectionManager.setTransportPending: reason =  Connection to server temporarily unavailable
ably.js:4067 Ably: ConnectionManager.deactivateTransport(): transport = IframeTransport; uri=undefined
ably.js:4067 Ably: IframeTransport.dispose(): 
ably.js:4067 Ably: ConnectionManager.chooseTransport(): Establishing http transport: IframeTransport; uri=undefined

Error:
Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('file://') does not match the recipient window's origin ('null').

Stack:

IframeAgent.postMessageEvent @ iframe-0.8.0.html?origin=file%3A%2F%2F&access_token=rkPwKA.CBL7U5DiBnAAO66Z0qtvkbiMxoL08awwA1YQ3H5D…:1294
IframeAgent.onData @ iframe-0.8.0.html?origin=file%3A%2F%2F&access_token=rkPwKA.CBL7U5DiBnAAO66Z0qtvkbiMxoL08awwA1YQ3H5D…:1290
(anonymous function) @ iframe-0.8.0.html?origin=file%3A%2F%2F&access_token=rkPwKA.CBL7U5DiBnAAO66Z0qtvkbiMxoL08awwA1YQ3H5D…:1147
callListener @ iframe-0.8.0.html?origin=file%3A%2F%2F&access_token=rkPwKA.CBL7U5DiBnAAO66Z0qtvkbiMxoL08awwA1YQ3H5D…:218
EventEmitter.emit @ iframe-0.8.0.html?origin=file%3A%2F%2F&access_token=rkPwKA.CBL7U5DiBnAAO66Z0qtvkbiMxoL08awwA1YQ3H5D…:240
onChunk @ iframe-0.8.0.html?origin=file%3A%2F%2F&access_token=rkPwKA.CBL7U5DiBnAAO66Z0qtvkbiMxoL08awwA1YQ3H5D…:830
onProgress @ iframe-0.8.0.html?origin=file%3A%2F%2F&access_token=rkPwKA.CBL7U5DiBnAAO66Z0qtvkbiMxoL08awwA1YQ3H5D…:817
XHRRequest.exec.xhr.onreadystatechange @ iframe-0.8.0.html?origin=file%3A%2F%2F&access_token=rkPwKA.CBL7U5DiBnAAO66Z0qtvkbiMxoL08awwA1YQ3H5D…:852

Spec validation

For version 0.8 of our client libraries, we have a documented specification at http://docs.ably.io/client-lib-development-guide/features/

It is required that all of our client libraries that use a 0.8.x version number, where possible, adhere to this specification ensuring feature compatibility across all languages and platforms. To ensure that we do not have regressions in future from this specification, all of the features within the 0.8 spec should have a matching test as well.

Please can you review this library against the feature set and do the following:

  • Ensure that all spec requirements are implemented, I believe there are a few small variations
  • Check that the README.md is up to date with the Ruby version with details such as how to install it from a package manager if applicable.

Bug when running `karma`

I am running this against realtime master branch, and the test suite just hangs.

I am surprised because I added timeout checks to all tests.

$ ABLY_ENV=local grunt karma
Running "karma" task
Running Karma tests using browsers 'Firefox' against all specs
INFO [karma]: Karma v0.12.31 server started at http://localhost:9876/
INFO [launcher]: Starting browser Firefox
INFO [preprocessor.env]: Publishing variables:  [ 'ABLY_ENV',
  'ABLY_REALTIME_HOST',
  'ABLY_REST_HOST',
  'ABLY_PORT',
  'ABLY_TLS_PORT',
  'ABLY_USE_TLS' ]
INFO [Firefox 37.0.0 (Mac OS X 10.10)]: Connected on socket eeQcZfVfmr9GOMej830B with id 73998001
INFO: 'Test App 4DyD4A in environment local has been set up'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): upgrading ... connectionKey = mTiBDssAAO3pzecw'
LOG: 'Ably: Connection.close(): connectionKey = mTiBDssAAO3pzecw'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): upgrading ... connectionKey = A9tVyX4PtO4-lVop'
LOG: 'Ably: Connection.close(): connectionKey = A9tVyX4PtO4-lVop'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): upgrading ... connectionKey = MpMhhnttJD3wHsQP'
LOG: 'Ably: Connection.close(): connectionKey = MpMhhnttJD3wHsQP'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): upgrading ... connectionKey = mzkdUyBk-AgPr1kq'
LOG: 'Ably: Connection.close(): connectionKey = mzkdUyBk-AgPr1kq'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): upgrading ... connectionKey = f6Esa5D93ybj-gOm'
LOG: 'Ably: Connection.close(): connectionKey = f6Esa5D93ybj-gOm'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = R9igLxcjg753Bv5m'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = Ny_qBjmOgt30yt3e'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = 4COI60yYYmcSb_MG'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = HEVqgJE7rYCDLXvr'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): upgrading ... connectionKey = bi260iYGXCpKqlnl'
LOG: 'Ably: Connection.close(): connectionKey = bi260iYGXCpKqlnl'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: Connection.close(): connectionKey = undefined'
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: Connection.close(): connectionKey = undefined'
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: Connection.close(): connectionKey = undefined'
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: Connection.close(): connectionKey = undefined'
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.connectImpl(): [ErrorInfo: Connection already closed; statusCode=80017; code=400]'
LOG: 'Ably: ConnectionManager.connectImpl(): [ErrorInfo: Connection already closed; statusCode=80017; code=400]'
LOG: 'Ably: ConnectionManager.connectImpl(): [ErrorInfo: Connection already closed; statusCode=80017; code=400]'
LOG: 'Ably: ConnectionManager.connectImpl(): [ErrorInfo: Connection already closed; statusCode=80017; code=400]'
LOG: 'Ably: Connection.close(): connectionKey = Dgtd856zrbb1LIno'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = CvlY6mPzNptlqbCY'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = v34b_nT-CVuHxqzf'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = qOUlJiLVfjTDxK4w'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = 8fgc-19FsaIR1hAR'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = gjA-fCzz8TCZvKlg'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = gobdgXOUnMCpxEVv'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = lBOLrbJRw6-vlvxC'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = JPy2-9fPWgHJ_fHO'
LOG: 'Ably: Connection.close(): connectionKey = OFzETB9elPwT3gxx'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = 3_sjQ8hz-Yj4Dhti'
LOG: 'Ably: Connection.close(): connectionKey = uZeCoMOmPgdxuHEb'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = GlOG0OZobvIkv_ca'
LOG: 'Ably: Connection.close(): connectionKey = sgY4itwTsmkwLE1r'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = Oyv-IVY1Mfdvf8OR'
LOG: 'Ably: Connection.close(): connectionKey = 2_VhGDkg7AavYQRZ'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = jGZvxsSVslc_p6Ed'
LOG: 'Ably: Connection.close(): connectionKey = 3bf41MIHNwA5PN5f'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = FjjLlqREbWPZ3gUD'
LOG: 'Ably: Connection.close(): connectionKey = iSKS5V6ddqIdedht'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): upgrading ... connectionKey = VUPIUmY3kNYl8nsP'
LOG: 'Ably: Connection.close(): connectionKey = VUPIUmY3kNYl8nsP'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: Connection.close(): connectionKey = undefined'
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: Connection.close(): connectionKey = undefined'
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Transport.onChannelMessage(): error; connectionKey = undefined; err = {"statusCode":404,"code":40400,"message":"No application found with id not_a"}'
LOG: 'Ably: Connection.close(): connectionKey = undefined'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): upgrading ... connectionKey = KLPRmC9GFMXHKFTO'
LOG: 'Ably: Connection.close(): connectionKey = KLPRmC9GFMXHKFTO'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): upgrading ... connectionKey = wP2MzF4zmNVFxb6A'
LOG: 'Ably: Connection.close(): connectionKey = wP2MzF4zmNVFxb6A'
Firefox 37.0.0 (Mac OS X 10.10) SLOW 5.008 secs:  restpublish
Firefox 37.0.0 (Mac OS X 10.10) SLOW 5.008 secs:  restpublish
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): upgrading ... connectionKey = U8mxlAXQIEIFk6jd'
LOG: 'Ably: Connection.close(): connectionKey = U8mxlAXQIEIFk6jd'
Firefox 37.0.0 (Mac OS X 10.10) SLOW 5.006 secs:  wspublish
Firefox 37.0.0 (Mac OS X 10.10) SLOW 5.006 secs:  wspublish
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'sending: 5'
LOG: 'publish callback called'
LOG: 'sending: 4'
LOG: 'publish callback called'
LOG: 'sending: 3'
LOG: 'publish callback called'
LOG: 'sending: 2'
LOG: 'publish callback called'
LOG: 'sending: 1'
LOG: 'publish callback called'
LOG: 'Ably: Connection.close(): connectionKey = MI0UtVLPErQ8a1Wf'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'sending: 5'
LOG: 'publish callback called'
LOG: 'sending: 4'
LOG: 'publish callback called'
LOG: 'sending: 3'
LOG: 'publish callback called'
LOG: 'sending: 2'
LOG: 'publish callback called'
LOG: 'sending: 1'
LOG: 'publish callback called'
LOG: 'Ably: Connection.close(): connectionKey = fpqThPedM3gF5CVd'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = 1Q9XbY7UZ30meroU'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = NpgXHwLYd7iNcyD3'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = xrwp9FMc2d6ekWeh'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = -juXlWCIYv_WnCn0'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = CamOBZQ9s0R5SLcG'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = 79-F7K4ZomUF1ZLt'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = WaU1YDAzF59Kywp0'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = Aayts2gOwBfLPZ75'
LOG: 'Ably: Connection.close(): connectionKey = 54XozhwtbkA6r5jH'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = vM8sl1QI5E4QHVj5'
LOG: 'Ably: Connection.close(): connectionKey = 3USBLm132j4ua14c'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = Tq8Ghkv_lfjb0GsV'
LOG: 'Ably: Connection.close(): connectionKey = Tq8Ghkv_lfjb0GsV'
LOG: 'Ably: Connection.close(): connectionKey = zSIZJ3LgohquvUk3'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'sending (phase 1): 0'
LOG: 'sending (phase 1): 1'
LOG: 'sending (phase 1): 2'
LOG: 'sending (phase 1): 3'
LOG: 'sending (phase 1): 4'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'sending (phase 4): 0'
LOG: 'sending (phase 4): 1'
LOG: 'sending (phase 4): 2'
LOG: 'sending (phase 4): 3'
LOG: 'sending (phase 4): 4'
LOG: 'Ably: Connection.close(): connectionKey = e10FyBtI8c8KeYEY'
LOG: 'Ably: Connection.close(): connectionKey = --nWgYH9mDdC8Mmo'
Firefox 37.0.0 (Mac OS X 10.10) SLOW 15.513 secs:  resume_inactive_ws_text
Firefox 37.0.0 (Mac OS X 10.10) SLOW 15.513 secs:  resume_inactive_ws_text
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'sending (phase 1): 0'
LOG: 'sending (phase 1): 1'
LOG: 'sending (phase 1): 2'
LOG: 'sending (phase 1): 3'
LOG: 'sending (phase 1): 4'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'sending (phase 4): 0'
LOG: 'sending (phase 4): 1'
LOG: 'sending (phase 4): 2'
LOG: 'sending (phase 4): 3'
LOG: 'sending (phase 4): 4'
LOG: 'Ably: Connection.close(): connectionKey = kn6dNnK8G3ev6h_W'
LOG: 'Ably: Connection.close(): connectionKey = y_ZpMgvxMALvYpaM'
Firefox 37.0.0 (Mac OS X 10.10) SLOW 15.525 secs:  resume_inactive_ws_binary
Firefox 37.0.0 (Mac OS X 10.10) SLOW 15.525 secs:  resume_inactive_ws_binary
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'sending (phase 1): 0'
LOG: 'sending (phase 1): 1'
LOG: 'sending (phase 1): 2'
LOG: 'sending (phase 1): 3'
LOG: 'sending (phase 1): 4'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'sending (phase 4): 0'
LOG: 'sending (phase 4): 1'
LOG: 'sending (phase 4): 2'
LOG: 'sending (phase 4): 3'
LOG: 'sending (phase 4): 4'
LOG: 'Ably: Connection.close(): connectionKey = sM0hUVfYNTqwkrq5'
LOG: 'Ably: Connection.close(): connectionKey = 1XG5YtNHYIhf22lP'
Firefox 37.0.0 (Mac OS X 10.10) SLOW 15.499 secs:  resume_inactive_comet_text
Firefox 37.0.0 (Mac OS X 10.10) SLOW 15.499 secs:  resume_inactive_comet_text
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'sending (phase 1): 0'
LOG: 'sending (phase 1): 1'
LOG: 'sending (phase 1): 2'
LOG: 'sending (phase 1): 3'
LOG: 'sending (phase 1): 4'
LOG: 'sending (phase 2): 0'
LOG: 'sending (phase 2): 1'
LOG: 'sending (phase 2): 2'
LOG: 'sending (phase 2): 3'
LOG: 'sending (phase 2): 4'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = SYL6oJhFpWp5EB9Z'
LOG: 'Ably: Connection.close(): connectionKey = c9q3jBliWxr2BvB9'
Firefox 37.0.0 (Mac OS X 10.10) SLOW 13.118 secs:  resume_active_ws_text
Firefox 37.0.0 (Mac OS X 10.10) SLOW 13.118 secs:  resume_active_ws_text
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'sending (phase 1): 0'
LOG: 'sending (phase 1): 1'
LOG: 'sending (phase 1): 2'
LOG: 'sending (phase 1): 3'
LOG: 'sending (phase 1): 4'
LOG: 'sending (phase 2): 0'
LOG: 'sending (phase 2): 1'
LOG: 'sending (phase 2): 2'
LOG: 'sending (phase 2): 3'
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'sending (phase 2): 4'
LOG: 'Ably: Connection.connect(): '
Firefox 37.0.0 (Mac OS X 10.10) SLOW 2 mins 8.865 secs:  resume_active_ws_binary
Firefox 37.0.0 (Mac OS X 10.10)  resume_active_ws_binary FAILED
    Test timed out after 120s
    AssertionError: Test timed out after 120s
    Expected 3 assertions, 2 ran
    Error: Expected 3 assertions, 2 ran
    nodeunit</</exports.test/test.done@/Users/mattheworiordan/Projects/Ably/clients/ably-js/node_modules/karma-nodeunit/lib/nodeunit.js:1601:25
    withTimeout/</exports[needle]/test.done@/Users/mattheworiordan/Projects/Ably/clients/ably-js/spec/common/modules/shared_helper.js:34:17
    withTimeout/</exports[needle]/timer<@/Users/mattheworiordan/Projects/Ably/clients/ably-js/spec/common/modules/shared_helper.js:38:17

Firefox 37.0.0 (Mac OS X 10.10) SLOW 2 mins 8.865 secs:  resume_active_ws_binary
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'sending (phase 1): 0'
LOG: 'sending (phase 1): 1'
LOG: 'sending (phase 1): 2'
LOG: 'sending (phase 1): 3'
LOG: 'sending (phase 1): 4'
LOG: 'sending (phase 2): 0'
Connection to github.com closed by remote host.
LOG: 'sending (phase 2): 1'
LOG: 'sending (phase 2): 2'
LOG: 'sending (phase 2): 3'
LOG: 'sending (phase 2): 4'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: Connection.close(): connectionKey = KLXt6VAwLIykdZoM'
LOG: 'Ably: Connection.close(): connectionKey = YgkdF7XkwUnNXQB6'
Firefox 37.0.0 (Mac OS X 10.10) SLOW 1 min 25.104 secs:  resume_active_comet_text
Firefox 37.0.0 (Mac OS X 10.10) SLOW 1 min 25.104 secs:  resume_active_comet_text
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): upgrading ... connectionKey = nV6gRouDxqBsEsbY'
LOG: 'Ably: Connection.close(): connectionKey = nV6gRouDxqBsEsbY'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): upgrading ... connectionKey = O3ILbfojWJg0Eq-T'
LOG: 'Ably: Connection.close(): connectionKey = O3ILbfojWJg0Eq-T'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): upgrading ... connectionKey = _PZJr2C20Orj5jc4'
LOG: 'Ably: Connection.close(): connectionKey = _PZJr2C20Orj5jc4'
LOG: 'Ably: Connection.connect(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): '
LOG: 'Ably: ConnectionManager.chooseTransport(): upgrading ... connectionKey = XpQL63rk0r1QaG5k'
LOG: 'Ably: Transport.onChannelMessage(): error; connectionKey = XpQL63rk0r1QaG5k; err = {"message":"Message received in failed state","statusCode":410}'

Nodeunit tests in the browser

I was just about to look into extending our browser & device coverage for this library, and unfortunately hit a few snags.

I could not get the tests to run at all at first, managed to fix it with 9f1db79.

Now when run grunt test:webserver to spin up a browser that can run the Nodeunit tests, only a small subset of the test are running, namely:

voila_capture 2015-03-31_12-16-55_am

grunt karma seems to also be effected running only this subset.

Do you know what has caused this issue? If not, I will have to dive in, but was not expecting to have to do that.

REST request timeout

It seems Java has a connection timeout set at https://github.com/ably/ably-java/blob/0275671edd98e7090181696ed6dd34a01cbbd96f/src/io/ably/http/Http.java#L133-L134, but no request timeout and I can't see any request timeout set in this ably-js library for REST requests. I think it would be sensible to agree on a standard default timeout for REST requests and implement in the libraries. If we did this, we may want to make this configurable for the client libraries by the developer as well.

WDYT?

Presence#enterClient errors if not supplied with a data param

channel.presence.enterClient('bob', 'foobar'); works fine, but channel.presence.enterClient('bob'); fails with "Ably: ConnectionManager.sendQueuedMessages(): Unexpected exception in transport.send(): TypeError: ob is undefined"

(Docs say data parameter is optional, but might just be that the docs are out of date. Ruby client works without it.)

Time function on Realtime blocks Node from exiting

@paddybyers, please see https://github.com/ably/ably-js/commits/time-issue-example, and specifically this commit.

In the example test, I can recreate the issue whereby the test suite does not exit under Node.js. To test this, simply uncomment f210378#diff-8e96d4a2d277fa48d367d76e19feade6R56 and run the following command:

grunt nodeunit --test spec/realtime/client_lib_issue.test.js

If you try the same thing with the REST time function it works, and it also works fine if you simply connect to Realtime and then close it. The problem appears to currently be isolated to the time function.

If there is any way you could fix this soon I'd appreciate it as it is a bit of an issue whilst trying to migrate the test suite.

No keyName specified

After getting back to development after approx 2 weeks, my previously working application now receives the following error when trying to connect with the realtime library: {"statusCode":401,"code":40101,"message":"No keyName specified"}

This presumably happens on the following line: new Ably.Realtime({ key: Settings.ABLY_KEY, log: { level: 1 }, clientId: "clientid", transports: ['web_socket']});

Where Settings.ABLY_KEY is ofcourse my valid API key.

Were there same API changes I am now aware of?

Internet Up URL & use of HTTPS

@paddybyers, for the Java & Ably-js libraries, please can you use the correct URL for the Internet Up CDN test which is https://internet-up.ably-realtime.com/is-the-internet-up.txt, see this change in ably/ably-ruby@007e3b4.

Also, in the ably-js library, if the library is loaded over HTTPS and it then needs to do an internet up check it currently fails because it tries to do the request over HTTP, see

request('http://internet-up.ably.io.s3-website-us-east-1.amazonaws.com/is-the-internet-up.js', null, null, null, false, function(err, response) {
and
Http.request('http://internet-up.ably.io.s3-website-us-east-1.amazonaws.com/is-the-internet-up.txt', null, null, null, false, function(err, responseText) {
.

@kouno this is the issue we saw FYI.

PhantomJS issues

I will update this issue with issues as I find them.

  1. iFrameTransport fails frequently, error I see:
PhantomJS 1.9.8 (Mac OS X) ERROR
  TypeError: 'null' is not an object (evaluating 'wrapWindow.destWindow')
  at /Users/matthew/Projects/Ably/clients/ably-js/browser/static/ably.js:8605
PhantomJS 1.9.8 (Mac OS X) ERROR
  TypeError: 'null' is not an object (evaluating 'wrapWindow.destWindow')
  at /Users/matthew/Projects/Ably/clients/ably-js/browser/static/ably.js:8605

this correlates with the following within IframeTransport.prototype.createIframe:

function onload() {
            self.destWindow = wrapWindow.destWindow; # this line throws the exception
            DomEvent.addMessageListener(wrapWindow, self.messageListener = messageListener);
            iframeComplete = true;
            callback(null, wrapIframe);
        };

Logger

Logger needs to log Major issues by default

Possible enhancement: Capybara / QT-webkit support

Whilst running the website test suite against the home page demo, I continued to use the default capybara-webkit driver which is based on QT Webkit. The tests failed every time, however when I changed to PhantomJS it worked. We may want to consider reviewing what the issue is with QT Webkit for other people using this in their CI environments.

Connection issues

@paddybyers I am a bit confused why http://jsbin.com/vuvozisape/1/edit?js,console,output is not working.

Seemingly it will only connect when using the format Ably.Realtime({ key: "key" }), and all other key & token formats fail for different reasons.

I also thought we had changed our libraries to support the use of a a key or token string in place of the constructor options which too doesn't seem to be working.

Can we ensure we add test coverage to this once resolved?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.