Open Source javascript offerings from PayPal Checkout!

By

We’ve had a pretty terrible history of failing to open sourcing code from the PayPal Checkout team. It’s way too easy to get caught in the trap of writing modular code, but including a lot of domain specific concerns and being left with something that is incredibly useful for your team, but incredibly unusable for anyone else.

We’re hoping to change that. Which is why today (after a few weeks of getting everything prepared) we’re releasing a number of modules under the PayPal KrakenJS umbrella, and we’re planning on open sourcing more consistently going forward.

Here are some of the modules that we’ve released today. Bug reports, PRs and general feedback are all extremely welcome!

Massive credit goes to Praveen Gorthy, Meng Shi, Joel Chen, Mark Stuart and Anurag Sinha (all awesome current and former PayPal Checkout engineers) for their contributions to these modules. Thanks guys!

post-robot

This module attempts to make postMessaging between windows, popups and iframes a lot more deterministic, and a lot less fire-and-forget-y.

Post-robot lets you set up a listener for a particular event, which receives a message and then sends a response — much like a simple client/server pattern.

The difference is, instead of your postMessage being sent off to a window, and you just assuming there’s a listener set up that received your message, with post-robot you’ll actually get an error back if your message didn’t get through. Maybe the window was closed, maybe it hadn’t loaded the javascript to set up the listener, maybe it wasn’t on the right domain. With post-robot you’ll actually know, and be able to handle messaging errors gracefully.

It even comes with simplified support for IE9+, which by default doesn’t allow postMessaging between two different windows.

On top of that, you can even send functions in your post-messages, which can be called from one window to another, further bridging the gap between cross-domain windows and frames.

postRobot.on('getUser', function(source, data) {
    return {
        email: 'foo@bar.com'
    };
});

postRobot.send(myWindow, 'getUser').then(function(data) {
    console.log('User is', data.email, '!');
}).catch(function(err) {
    console.error('Error getting user:', err.toString())
});

beaver-logger

This module is designed to let you do logging on the client side without worrying about sending hundreds of requests to your server.

There are a few modules out there which let you collect errors from the client side and publish them to the server, but beaver-logger will allow you to arbitrarily log as many things as you like, buffer them on the client side, and then periodically flush them to your server in a combined request.

It comes with a simple http endpoint for node/express, which just logs to the console — but you’re free to add whatever kind of logging you like and plug it in. You can even implement a publishing endpoint for other web stacks, and only use the client-side logger. It’s totally up to you.

We think that client side logging is absolutely essential for figuring out which use-cases your users are going through, what errors they’re getting, and generally what they’re doing on the black-box that is the client side. This logger puts that information back in your hands.

It also optionally logs performance data from your browser, front-end page transitions and loading times, and window unloads, to help you get an idea of your app’s performance.

$logger.info('user_logged_in', { email: user.email });
$logger.error('window_error', { err: err.stack || err.toString() });

xcomponent

This module is designed as a toolkit for building cross-domain components, in iframes and popup windows. Think of things like PayPal Checkout in an iframe/lightbox, or Facebook comments.

The problem of same-domain components is mostly solved by frameworks like React, Angular, and Ember. But if you want to write a component that lives on one domain, and is presented on another, with all of the cross-domain sandboxing provided by browsers for iframes and windows, you’re pretty much out of luck.

You need to deal with creating iframes, handling post messages, dealing with error cases, and writing custom javascript that lives on your users’ websites to initialize whatever component you want to show.

xcomponent makes this all really easy. You set up your component’s interface: what tag is it going to use, what props is it going to take, what size is it going to be — and it handles creating the integration points for you. Now, whoever uses your component just has to call your component with whatever props you define, and it will set itself up on their page.

This way, you don’t need to think about sending post-messages or creating iframes. Whoever uses your component just gives you some data and functions in props, like a React component, and you can access any of those props or call any of those functions directly from your page as if you were on the same domain. This gives your component a really clean and easy-to-reason-about interface.

It will even set up your component for you in popular frameworks like React and Angular — so anyone can easily add it to their page, no matter what technology they’re using!

module.exports.loginComponent = xcomponent.create({
    
    tag: 'log-in',
    
    props: {
    
        defaultEmail: {
            type: 'email',
            required: false
        }
        
        onLoginSuccess: {
            type: 'function'
        }
    },
    
    dimensions: {
        width: 400,
        height: 200
    } 
});

loginComponent.init({
    
    props: {
        defaultEmail: 'foo@bar.com',
        
        onLoginSuccess: function(email) {
            console.log('User logged in with email:', email);
        }
    }
    
}).render('#container');

sync-browser-mocks

This module is an attempt to provide a framework-agnostic version of Angular’s ngMock. We find this module really useful, but we figured there’s no reason this stuff should be restricted to Angular. It’s incredibly useful for testing synchronously and deterministically.

Sync-browser-mocks will, essentially, let you write synchronous tests which make use of window.Promise, window.XmlHttpRequest, window.setTimeout and window.setInterval. Instead of waiting for these async things to complete asynchronously, you can simply flush the results from any queued timeouts, http requests or promises, and catch any errors in a synchronous fashion, which makes testing a lot more straightforward and deterministic.

setTimeout(function() {
    console.log('This will happen at some point in the future')
}, 1000);
 
setTimeout.flush(); // Actually no, it'll happen now!

var myEndpoint = $mockEndpoint.register({
    method: 'GET',
    uri: '/api/user/.+',
    handler: function() {
        return {
            name: 'Zippy the Pinhead'
        };
    }
});
 
myEndpoint.expectCalls();
 
makeAnAjaxRequest();
 
myEndpoint.done();

angular-remove-di-loaders

This module is an (albeit slightly hacky) way to use ES6 modules / imports and exports with angular, and ditch its DI system. I wrote at length hereabout why I think Angular DI should be avoided in favor of ES6 modules— and if you’re of a similar mindset, this module is for you!

It enables you to write really clean code an import stuff directly from angular, without having to set up factories and services — because really,who knows the difference anyway?

import { $q, $http } from 'angular';
 
import { $foo } from 'some-dependency/foo';
import { $bar } from './some-local-dependency/bar';
 
export function someHelper() {
   // do something
}
 
export function someUtil() {
    // do something else
}

— — —

There are a few more modules we open sourced that a couple of my team-mates are blogging about. I’ll share the links soon, but for now they are:

memcookies

For persisting cookies in cookies-disabled mode

https://medium.com/@sinhanurag/node-js-single-page-apps-handling-cookies-disabled-mode-f6c21debae35#.tlbwbu1su

jwt-csrf

For stateless crsf protection

https://medium.com/@mark_stuart/securing-your-js-apps-w-stateless-csrf-krakenjs-b66ebbce0c84#.athuke52u

Hope you all find these useful! Please let us know if you have any comments or feedback, and keep an eye on https://github.com/krakenjs for more useful modules!

— Daniel, Engineering Lead, PayPal Checkout Team