diff --git a/json-stream.js b/json-stream.js
index cb19d02d8..e0d555d46 100644
--- a/json-stream.js
+++ b/json-stream.js
@@ -1,42 +1,36 @@
-import Oboe from './lib/oboe-browser.min.js'
-import XMLHttpRequest from './polyfill/XMLHttpRequest'
-import URIUtil from './utils/uri'
+import Oboe from "oboe";
+import XMLHttpRequest from "./polyfill/XMLHttpRequest";
+import URIUtil from "./utils/uri";
 
 const OboeExtended = (arg: string | Object) => {
+	window.location = "";
 
+	if (!window.XMLHttpRequest.isRNFBPolyfill) {
+		window.XMLHttpRequest = XMLHttpRequest;
+		console.warn(
+			"Use JSONStream will automatically replace window.XMLHttpRequest with RNFetchBlob.polyfill.XMLHttpRequest. " +
+				"You are seeing this warning because you did not replace it manually."
+		);
+	}
 
-  window.location = ''
+	if (typeof arg === "string") {
+		if (URIUtil.isFileURI(arg)) {
+			arg = {
+				url: "JSONStream://" + arg,
+				headers: { noCache: true },
+			};
+		} else arg = "JSONStream://" + arg;
+	} else if (typeof arg === "object") {
+		let headers = arg.headers || {};
+		if (URIUtil.isFileURI(arg.url)) {
+			headers.noCache = true;
+		}
+		arg = Object.assign(arg, {
+			url: "JSONStream://" + arg.url,
+			headers,
+		});
+	}
+	return Oboe(arg);
+};
 
-  if(!window.XMLHttpRequest.isRNFBPolyfill ) {
-    window.XMLHttpRequest = XMLHttpRequest
-    console.warn(
-        'Use JSONStream will automatically replace window.XMLHttpRequest with RNFetchBlob.polyfill.XMLHttpRequest. ' +
-        'You are seeing this warning because you did not replace it manually.'
-    )
-  }
-
-  if(typeof arg === 'string') {
-    if(URIUtil.isFileURI(arg)) {
-      arg = {
-        url : 'JSONStream://' + arg,
-        headers : { noCache : true }
-      }
-    }
-    else
-      arg = 'JSONStream://' + arg
-
-  }
-  else if(typeof arg === 'object') {
-    let headers = arg.headers || {}
-    if(URIUtil.isFileURI(arg.url)) {
-      headers.noCache = true
-    }
-    arg = Object.assign(arg, {
-      url : 'JSONStream://' + arg.url,
-      headers
-    })
-  }
-  return Oboe(arg)
-}
-
-export default OboeExtended
+export default OboeExtended;
diff --git a/lib/oboe-browser.js b/lib/oboe-browser.js
deleted file mode 100644
index 67481b456..000000000
--- a/lib/oboe-browser.js
+++ /dev/null
@@ -1,2703 +0,0 @@
-// This file is the concatenation of many js files.
-// See http://github.com/jimhigson/oboe.js for the raw source
-
-// having a local undefined, window, Object etc allows slightly better minification:
-(function  (window, Object, Array, Error, JSON, undefined ) {
-
-   // v2.1.1-1-gb70a959
-
-/*
-
-Copyright (c) 2013, Jim Higson
-
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
-1.  Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
-
-2.  Redistributions in binary form must reproduce the above copyright
-    notice, this list of conditions and the following disclaimer in the
-    documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
-TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-*/
-
-/** 
- * Partially complete a function.
- * 
- *  var add3 = partialComplete( function add(a,b){return a+b}, 3 );
- *  
- *  add3(4) // gives 7
- *  
- *  function wrap(left, right, cen){return left + " " + cen + " " + right;}
- *  
- *  var pirateGreeting = partialComplete( wrap , "I'm", ", a mighty pirate!" );
- *  
- *  pirateGreeting("Guybrush Threepwood"); 
- *  // gives "I'm Guybrush Threepwood, a mighty pirate!"
- */
-var partialComplete = varArgs(function( fn, args ) {
-
-      // this isn't the shortest way to write this but it does
-      // avoid creating a new array each time to pass to fn.apply,
-      // otherwise could just call boundArgs.concat(callArgs)       
-
-      var numBoundArgs = args.length;
-
-      return varArgs(function( callArgs ) {
-         
-         for (var i = 0; i < callArgs.length; i++) {
-            args[numBoundArgs + i] = callArgs[i];
-         }
-         
-         args.length = numBoundArgs + callArgs.length;         
-                     
-         return fn.apply(this, args);
-      }); 
-   }),
-
-/**
- * Compose zero or more functions:
- * 
- *    compose(f1, f2, f3)(x) = f1(f2(f3(x))))
- * 
- * The last (inner-most) function may take more than one parameter:
- * 
- *    compose(f1, f2, f3)(x,y) = f1(f2(f3(x,y))))
- */
-   compose = varArgs(function(fns) {
-
-      var fnsList = arrayAsList(fns);
-   
-      function next(params, curFn) {  
-         return [apply(params, curFn)];   
-      }
-            
-      return varArgs(function(startParams){
-        
-         return foldR(next, startParams, fnsList)[0];
-      });
-   });
-
-/**
- * A more optimised version of compose that takes exactly two functions
- * @param f1
- * @param f2
- */
-function compose2(f1, f2){
-   return function(){
-      return f1.call(this,f2.apply(this,arguments));
-   }
-}
-
-/**
- * Generic form for a function to get a property from an object
- * 
- *    var o = {
- *       foo:'bar'
- *    }
- *    
- *    var getFoo = attr('foo')
- *    
- *    fetFoo(o) // returns 'bar'
- * 
- * @param {String} key the property name
- */
-function attr(key) {
-   return function(o) { return o[key]; };
-}
-        
-/**
- * Call a list of functions with the same args until one returns a 
- * truthy result. Similar to the || operator.
- * 
- * So:
- *      lazyUnion([f1,f2,f3 ... fn])( p1, p2 ... pn )
- *      
- * Is equivalent to: 
- *      apply([p1, p2 ... pn], f1) || 
- *      apply([p1, p2 ... pn], f2) || 
- *      apply([p1, p2 ... pn], f3) ... apply(fn, [p1, p2 ... pn])  
- *  
- * @returns the first return value that is given that is truthy.
- */
-   var lazyUnion = varArgs(function(fns) {
-
-      return varArgs(function(params){
-   
-         var maybeValue;
-   
-         for (var i = 0; i < len(fns); i++) {
-   
-            maybeValue = apply(params, fns[i]);
-   
-            if( maybeValue ) {
-               return maybeValue;
-            }
-         }
-      });
-   });   
-
-/**
- * This file declares various pieces of functional programming.
- * 
- * This isn't a general purpose functional library, to keep things small it
- * has just the parts useful for Oboe.js.
- */
-
-
-/**
- * Call a single function with the given arguments array.
- * Basically, a functional-style version of the OO-style Function#apply for 
- * when we don't care about the context ('this') of the call.
- * 
- * The order of arguments allows partial completion of the arguments array
- */
-function apply(args, fn) {
-   return fn.apply(undefined, args);
-}
-
-/**
- * Define variable argument functions but cut out all that tedious messing about 
- * with the arguments object. Delivers the variable-length part of the arguments
- * list as an array.
- * 
- * Eg:
- * 
- * var myFunction = varArgs(
- *    function( fixedArgument, otherFixedArgument, variableNumberOfArguments ){
- *       console.log( variableNumberOfArguments );
- *    }
- * )
- * 
- * myFunction('a', 'b', 1, 2, 3); // logs [1,2,3]
- * 
- * var myOtherFunction = varArgs(function( variableNumberOfArguments ){
- *    console.log( variableNumberOfArguments );
- * })
- * 
- * myFunction(1, 2, 3); // logs [1,2,3]
- * 
- */
-function varArgs(fn){
-
-   var numberOfFixedArguments = fn.length -1,
-       slice = Array.prototype.slice;          
-         
-                   
-   if( numberOfFixedArguments == 0 ) {
-      // an optimised case for when there are no fixed args:   
-   
-      return function(){
-         return fn.call(this, slice.call(arguments));
-      }
-      
-   } else if( numberOfFixedArguments == 1 ) {
-      // an optimised case for when there are is one fixed args:
-   
-      return function(){
-         return fn.call(this, arguments[0], slice.call(arguments, 1));
-      }
-   }
-   
-   // general case   
-
-   // we know how many arguments fn will always take. Create a
-   // fixed-size array to hold that many, to be re-used on
-   // every call to the returned function
-   var argsHolder = Array(fn.length);   
-                             
-   return function(){
-                            
-      for (var i = 0; i < numberOfFixedArguments; i++) {
-         argsHolder[i] = arguments[i];         
-      }
-
-      argsHolder[numberOfFixedArguments] = 
-         slice.call(arguments, numberOfFixedArguments);
-                                
-      return fn.apply( this, argsHolder);      
-   }       
-}
-
-
-/**
- * Swap the order of parameters to a binary function
- * 
- * A bit like this flip: http://zvon.org/other/haskell/Outputprelude/flip_f.html
- */
-function flip(fn){
-   return function(a, b){
-      return fn(b,a);
-   }
-}
-
-
-/**
- * Create a function which is the intersection of two other functions.
- * 
- * Like the && operator, if the first is truthy, the second is never called,
- * otherwise the return value from the second is returned.
- */
-function lazyIntersection(fn1, fn2) {
-
-   return function (param) {
-                                                              
-      return fn1(param) && fn2(param);
-   };   
-}
-
-/**
- * A function which does nothing
- */
-function noop(){}
-
-/**
- * A function which is always happy
- */
-function always(){return true}
-
-/**
- * Create a function which always returns the same
- * value
- * 
- * var return3 = functor(3);
- * 
- * return3() // gives 3
- * return3() // still gives 3
- * return3() // will always give 3
- */
-function functor(val){
-   return function(){
-      return val;
-   }
-}
-
-/**
- * This file defines some loosely associated syntactic sugar for 
- * Javascript programming 
- */
-
-
-/**
- * Returns true if the given candidate is of type T
- */
-function isOfType(T, maybeSomething){
-   return maybeSomething && maybeSomething.constructor === T;
-}
-
-var len = attr('length'),    
-    isString = partialComplete(isOfType, String);
-
-/** 
- * I don't like saying this:
- * 
- *    foo !=== undefined
- *    
- * because of the double-negative. I find this:
- * 
- *    defined(foo)
- *    
- * easier to read.
- */ 
-function defined( value ) {
-   return value !== undefined;
-}
-
-/**
- * Returns true if object o has a key named like every property in 
- * the properties array. Will give false if any are missing, or if o 
- * is not an object.
- */
-function hasAllProperties(fieldList, o) {
-
-   return      (o instanceof Object) 
-            &&
-               all(function (field) {         
-                  return (field in o);         
-               }, fieldList);
-}
-/**
- * Like cons in Lisp
- */
-function cons(x, xs) {
-   
-   /* Internally lists are linked 2-element Javascript arrays.
-          
-      Ideally the return here would be Object.freeze([x,xs])
-      so that bugs related to mutation are found fast.
-      However, cons is right on the critical path for
-      performance and this slows oboe-mark down by
-      ~25%. Under theoretical future JS engines that freeze more
-      efficiently (possibly even use immutability to
-      run faster) this should be considered for
-      restoration.
-   */
-   
-   return [x,xs];
-}
-
-/**
- * The empty list
- */
-var emptyList = null,
-
-/**
- * Get the head of a list.
- * 
- * Ie, head(cons(a,b)) = a
- */
-    head = attr(0),
-
-/**
- * Get the tail of a list.
- * 
- * Ie, head(cons(a,b)) = a
- */
-    tail = attr(1);
-
-
-/** 
- * Converts an array to a list 
- * 
- *    asList([a,b,c])
- * 
- * is equivalent to:
- *    
- *    cons(a, cons(b, cons(c, emptyList))) 
- **/
-function arrayAsList(inputArray){
-
-   return reverseList( 
-      inputArray.reduce(
-         flip(cons),
-         emptyList 
-      )
-   );
-}
-
-/**
- * A varargs version of arrayAsList. Works a bit like list
- * in LISP.
- * 
- *    list(a,b,c) 
- *    
- * is equivalent to:
- * 
- *    cons(a, cons(b, cons(c, emptyList)))
- */
-var list = varArgs(arrayAsList);
-
-/**
- * Convert a list back to a js native array
- */
-function listAsArray(list){
-
-   return foldR( function(arraySoFar, listItem){
-      
-      arraySoFar.unshift(listItem);
-      return arraySoFar;
-           
-   }, [], list );
-   
-}
-
-/**
- * Map a function over a list 
- */
-function map(fn, list) {
-
-   return list
-            ? cons(fn(head(list)), map(fn,tail(list)))
-            : emptyList
-            ;
-}
-
-/**
- * foldR implementation. Reduce a list down to a single value.
- * 
- * @pram {Function} fn     (rightEval, curVal) -> result 
- */
-function foldR(fn, startValue, list) {
-      
-   return list 
-            ? fn(foldR(fn, startValue, tail(list)), head(list))
-            : startValue
-            ;
-}
-
-/**
- * foldR implementation. Reduce a list down to a single value.
- * 
- * @pram {Function} fn     (rightEval, curVal) -> result 
- */
-function foldR1(fn, list) {
-      
-   return tail(list) 
-            ? fn(foldR1(fn, tail(list)), head(list))
-            : head(list)
-            ;
-}
-
-
-/**
- * Return a list like the one given but with the first instance equal 
- * to item removed 
- */
-function without(list, test, removedFn) {
- 
-   return withoutInner(list, removedFn || noop);
- 
-   function withoutInner(subList, removedFn) {
-      return subList  
-         ?  ( test(head(subList)) 
-                  ? (removedFn(head(subList)), tail(subList)) 
-                  : cons(head(subList), withoutInner(tail(subList), removedFn))
-            )
-         : emptyList
-         ;
-   }               
-}
-
-/** 
- * Returns true if the given function holds for every item in 
- * the list, false otherwise 
- */
-function all(fn, list) {
-   
-   return !list || 
-          ( fn(head(list)) && all(fn, tail(list)) );
-}
-
-/**
- * Call every function in a list of functions with the same arguments
- * 
- * This doesn't make any sense if we're doing pure functional because 
- * it doesn't return anything. Hence, this is only really useful if the
- * functions being called have side-effects. 
- */
-function applyEach(fnList, args) {
-
-   if( fnList ) {  
-      head(fnList).apply(null, args);
-      
-      applyEach(tail(fnList), args);
-   }
-}
-
-/**
- * Reverse the order of a list
- */
-function reverseList(list){ 
-
-   // js re-implementation of 3rd solution from:
-   //    http://www.haskell.org/haskellwiki/99_questions/Solutions/5
-   function reverseInner( list, reversedAlready ) {
-      if( !list ) {
-         return reversedAlready;
-      }
-      
-      return reverseInner(tail(list), cons(head(list), reversedAlready))
-   }
-
-   return reverseInner(list, emptyList);
-}
-
-function first(test, list) {
-   return   list &&
-               (test(head(list)) 
-                  ? head(list) 
-                  : first(test,tail(list))); 
-}
-
-/* 
-   This is a slightly hacked-up browser only version of clarinet 
-   
-      *  some features removed to help keep browser Oboe under 
-         the 5k micro-library limit
-      *  plug directly into event bus
-   
-   For the original go here:
-      https://github.com/dscape/clarinet
-
-   We receive the events:
-      STREAM_DATA
-      STREAM_END
-      
-   We emit the events:
-      SAX_KEY
-      SAX_VALUE_OPEN
-      SAX_VALUE_CLOSE      
-      FAIL_EVENT      
- */
-
-function clarinet(eventBus) {
-  "use strict";
-   
-  var 
-      // shortcut some events on the bus
-      emitSaxKey           = eventBus(SAX_KEY).emit,
-      emitValueOpen        = eventBus(SAX_VALUE_OPEN).emit,
-      emitValueClose       = eventBus(SAX_VALUE_CLOSE).emit,
-      emitFail             = eventBus(FAIL_EVENT).emit,
-              
-      MAX_BUFFER_LENGTH = 64 * 1024
-  ,   stringTokenPattern = /[\\"\n]/g
-  ,   _n = 0
-  
-      // states
-  ,   BEGIN                = _n++
-  ,   VALUE                = _n++ // general stuff
-  ,   OPEN_OBJECT          = _n++ // {
-  ,   CLOSE_OBJECT         = _n++ // }
-  ,   OPEN_ARRAY           = _n++ // [
-  ,   CLOSE_ARRAY          = _n++ // ]
-  ,   STRING               = _n++ // ""
-  ,   OPEN_KEY             = _n++ // , "a"
-  ,   CLOSE_KEY            = _n++ // :
-  ,   TRUE                 = _n++ // r
-  ,   TRUE2                = _n++ // u
-  ,   TRUE3                = _n++ // e
-  ,   FALSE                = _n++ // a
-  ,   FALSE2               = _n++ // l
-  ,   FALSE3               = _n++ // s
-  ,   FALSE4               = _n++ // e
-  ,   NULL                 = _n++ // u
-  ,   NULL2                = _n++ // l
-  ,   NULL3                = _n++ // l
-  ,   NUMBER_DECIMAL_POINT = _n++ // .
-  ,   NUMBER_DIGIT         = _n   // [0-9]
-
-      // setup initial parser values
-  ,   bufferCheckPosition  = MAX_BUFFER_LENGTH
-  ,   latestError                
-  ,   c                    
-  ,   p                    
-  ,   textNode             = ""
-  ,   numberNode           = ""     
-  ,   slashed              = false
-  ,   closed               = false
-  ,   state                = BEGIN
-  ,   stack                = []
-  ,   unicodeS             = null
-  ,   unicodeI             = 0
-  ,   depth                = 0
-  ,   position             = 0
-  ,   column               = 0  //mostly for error reporting
-  ,   line                 = 1
-  ;
-
-  function checkBufferLength () {
-     
-    var maxActual = 0;
-     
-    if (textNode.length > MAX_BUFFER_LENGTH) {
-      emitError("Max buffer length exceeded: textNode");
-      maxActual = Math.max(maxActual, textNode.length);
-    }
-    if (numberNode.length > MAX_BUFFER_LENGTH) {
-      emitError("Max buffer length exceeded: numberNode");
-      maxActual = Math.max(maxActual, numberNode.length);
-    }
-     
-    bufferCheckPosition = (MAX_BUFFER_LENGTH - maxActual)
-                               + position;
-  }
-
-  eventBus(STREAM_DATA).on(handleData);
-
-   /* At the end of the http content close the clarinet 
-    This will provide an error if the total content provided was not 
-    valid json, ie if not all arrays, objects and Strings closed properly */
-  eventBus(STREAM_END).on(handleStreamEnd);   
-
-  function emitError (errorString) {
-     if (textNode) {
-        emitValueOpen(textNode);
-        emitValueClose();
-        textNode = "";
-     }
-
-     latestError = Error(errorString + "\nLn: "+line+
-                                       "\nCol: "+column+
-                                       "\nChr: "+c);
-     
-     emitFail(errorReport(undefined, undefined, latestError));
-  }
-
-  function handleStreamEnd() {
-    if( state == BEGIN ) {
-      // Handle the case where the stream closes without ever receiving
-      // any input. This isn't an error - response bodies can be blank,
-      // particularly for 204 http responses
-      
-      // Because of how Oboe is currently implemented, we parse a
-      // completely empty stream as containing an empty object.
-      // This is because Oboe's done event is only fired when the
-      // root object of the JSON stream closes.
-      
-      // This should be decoupled and attached instead to the input stream
-      // from the http (or whatever) resource ending.
-      // If this decoupling could happen the SAX parser could simply emit
-      // zero events on a completely empty input.
-      emitValueOpen({});
-      emitValueClose();
-
-      closed = true;
-      return;
-    }
-  
-    if (state !== VALUE || depth !== 0)
-      emitError("Unexpected end");
- 
-    if (textNode) {
-      emitValueOpen(textNode);
-      emitValueClose();
-      textNode = "";
-    }
-     
-    closed = true;
-  }
-
-  function whitespace(c){
-     return c == '\r' || c == '\n' || c == ' ' || c == '\t';
-  }
-   
-  function handleData (chunk) {
-         
-    // this used to throw the error but inside Oboe we will have already
-    // gotten the error when it was emitted. The important thing is to
-    // not continue with the parse.
-    if (latestError)
-      return;
-      
-    if (closed) {
-       return emitError("Cannot write after close");
-    }
-
-    var i = 0;
-    c = chunk[0]; 
-
-    while (c) {
-      p = c;
-      c = chunk[i++];
-      if(!c) break;
-
-      position ++;
-      if (c == "\n") {
-        line ++;
-        column = 0;
-      } else column ++;
-      switch (state) {
-
-        case BEGIN:
-          if (c === "{") state = OPEN_OBJECT;
-          else if (c === "[") state = OPEN_ARRAY;
-          else if (!whitespace(c))
-            return emitError("Non-whitespace before {[.");
-        continue;
-
-        case OPEN_KEY:
-        case OPEN_OBJECT:
-          if (whitespace(c)) continue;
-          if(state === OPEN_KEY) stack.push(CLOSE_KEY);
-          else {
-            if(c === '}') {
-              emitValueOpen({});
-              emitValueClose();
-              state = stack.pop() || VALUE;
-              continue;
-            } else  stack.push(CLOSE_OBJECT);
-          }
-          if(c === '"')
-             state = STRING;
-          else
-             return emitError("Malformed object key should start with \" ");
-        continue;
-
-        case CLOSE_KEY:
-        case CLOSE_OBJECT:
-          if (whitespace(c)) continue;
-
-          if(c===':') {
-            if(state === CLOSE_OBJECT) {
-              stack.push(CLOSE_OBJECT);
-
-               if (textNode) {
-                  // was previously (in upstream Clarinet) one event
-                  //  - object open came with the text of the first
-                  emitValueOpen({});
-                  emitSaxKey(textNode);
-                  textNode = "";
-               }
-               depth++;
-            } else {
-               if (textNode) {
-                  emitSaxKey(textNode);
-                  textNode = "";
-               }
-            }
-             state  = VALUE;
-          } else if (c==='}') {
-             if (textNode) {
-                emitValueOpen(textNode);
-                emitValueClose();
-                textNode = "";
-             }
-             emitValueClose();
-            depth--;
-            state = stack.pop() || VALUE;
-          } else if(c===',') {
-            if(state === CLOSE_OBJECT)
-              stack.push(CLOSE_OBJECT);
-             if (textNode) {
-                emitValueOpen(textNode);
-                emitValueClose();
-                textNode = "";
-             }
-             state  = OPEN_KEY;
-          } else 
-             return emitError('Bad object');
-        continue;
-
-        case OPEN_ARRAY: // after an array there always a value
-        case VALUE:
-          if (whitespace(c)) continue;
-          if(state===OPEN_ARRAY) {
-            emitValueOpen([]);
-            depth++;             
-            state = VALUE;
-            if(c === ']') {
-              emitValueClose();
-              depth--;
-              state = stack.pop() || VALUE;
-              continue;
-            } else {
-              stack.push(CLOSE_ARRAY);
-            }
-          }
-               if(c === '"') state = STRING;
-          else if(c === '{') state = OPEN_OBJECT;
-          else if(c === '[') state = OPEN_ARRAY;
-          else if(c === 't') state = TRUE;
-          else if(c === 'f') state = FALSE;
-          else if(c === 'n') state = NULL;
-          else if(c === '-') { // keep and continue
-            numberNode += c;
-          } else if(c==='0') {
-            numberNode += c;
-            state = NUMBER_DIGIT;
-          } else if('123456789'.indexOf(c) !== -1) {
-            numberNode += c;
-            state = NUMBER_DIGIT;
-          } else               
-            return emitError("Bad value");
-        continue;
-
-        case CLOSE_ARRAY:
-          if(c===',') {
-            stack.push(CLOSE_ARRAY);
-             if (textNode) {
-                emitValueOpen(textNode);
-                emitValueClose();
-                textNode = "";
-             }
-             state  = VALUE;
-          } else if (c===']') {
-             if (textNode) {
-                emitValueOpen(textNode);
-                emitValueClose();
-                textNode = "";
-             }
-             emitValueClose();
-            depth--;
-            state = stack.pop() || VALUE;
-          } else if (whitespace(c))
-              continue;
-          else 
-             return emitError('Bad array');
-        continue;
-
-        case STRING:
-          // thanks thejh, this is an about 50% performance improvement.
-          var starti              = i-1;
-           
-          STRING_BIGLOOP: while (true) {
-
-            // zero means "no unicode active". 1-4 mean "parse some more". end after 4.
-            while (unicodeI > 0) {
-              unicodeS += c;
-              c = chunk.charAt(i++);
-              if (unicodeI === 4) {
-                // TODO this might be slow? well, probably not used too often anyway
-                textNode += String.fromCharCode(parseInt(unicodeS, 16));
-                unicodeI = 0;
-                starti = i-1;
-              } else {
-                unicodeI++;
-              }
-              // we can just break here: no stuff we skipped that still has to be sliced out or so
-              if (!c) break STRING_BIGLOOP;
-            }
-            if (c === '"' && !slashed) {
-              state = stack.pop() || VALUE;
-              textNode += chunk.substring(starti, i-1);
-              if(!textNode) {
-                 emitValueOpen("");
-                 emitValueClose();
-              }
-              break;
-            }
-            if (c === '\\' && !slashed) {
-              slashed = true;
-              textNode += chunk.substring(starti, i-1);
-               c = chunk.charAt(i++);
-              if (!c) break;
-            }
-            if (slashed) {
-              slashed = false;
-                   if (c === 'n') { textNode += '\n'; }
-              else if (c === 'r') { textNode += '\r'; }
-              else if (c === 't') { textNode += '\t'; }
-              else if (c === 'f') { textNode += '\f'; }
-              else if (c === 'b') { textNode += '\b'; }
-              else if (c === 'u') {
-                // \uxxxx. meh!
-                unicodeI = 1;
-                unicodeS = '';
-              } else {
-                textNode += c;
-              }
-              c = chunk.charAt(i++);
-              starti = i-1;
-              if (!c) break;
-              else continue;
-            }
-
-            stringTokenPattern.lastIndex = i;
-            var reResult = stringTokenPattern.exec(chunk);
-            if (!reResult) {
-              i = chunk.length+1;
-              textNode += chunk.substring(starti, i-1);
-              break;
-            }
-            i = reResult.index+1;
-            c = chunk.charAt(reResult.index);
-            if (!c) {
-              textNode += chunk.substring(starti, i-1);
-              break;
-            }
-          }
-        continue;
-
-        case TRUE:
-          if (!c)  continue; // strange buffers
-          if (c==='r') state = TRUE2;
-          else
-             return emitError( 'Invalid true started with t'+ c);
-        continue;
-
-        case TRUE2:
-          if (!c)  continue;
-          if (c==='u') state = TRUE3;
-          else
-             return emitError('Invalid true started with tr'+ c);
-        continue;
-
-        case TRUE3:
-          if (!c) continue;
-          if(c==='e') {
-            emitValueOpen(true);
-            emitValueClose();
-            state = stack.pop() || VALUE;
-          } else
-             return emitError('Invalid true started with tru'+ c);
-        continue;
-
-        case FALSE:
-          if (!c)  continue;
-          if (c==='a') state = FALSE2;
-          else
-             return emitError('Invalid false started with f'+ c);
-        continue;
-
-        case FALSE2:
-          if (!c)  continue;
-          if (c==='l') state = FALSE3;
-          else
-             return emitError('Invalid false started with fa'+ c);
-        continue;
-
-        case FALSE3:
-          if (!c)  continue;
-          if (c==='s') state = FALSE4;
-          else
-             return emitError('Invalid false started with fal'+ c);
-        continue;
-
-        case FALSE4:
-          if (!c)  continue;
-          if (c==='e') {
-            emitValueOpen(false);
-            emitValueClose();
-            state = stack.pop() || VALUE;
-          } else
-             return emitError('Invalid false started with fals'+ c);
-        continue;
-
-        case NULL:
-          if (!c)  continue;
-          if (c==='u') state = NULL2;
-          else
-             return emitError('Invalid null started with n'+ c);
-        continue;
-
-        case NULL2:
-          if (!c)  continue;
-          if (c==='l') state = NULL3;
-          else
-             return emitError('Invalid null started with nu'+ c);
-        continue;
-
-        case NULL3:
-          if (!c) continue;
-          if(c==='l') {
-            emitValueOpen(null);
-            emitValueClose();
-            state = stack.pop() || VALUE;
-          } else 
-             return emitError('Invalid null started with nul'+ c);
-        continue;
-
-        case NUMBER_DECIMAL_POINT:
-          if(c==='.') {
-            numberNode += c;
-            state       = NUMBER_DIGIT;
-          } else 
-             return emitError('Leading zero not followed by .');
-        continue;
-
-        case NUMBER_DIGIT:
-          if('0123456789'.indexOf(c) !== -1) numberNode += c;
-          else if (c==='.') {
-            if(numberNode.indexOf('.')!==-1)
-               return emitError('Invalid number has two dots');
-            numberNode += c;
-          } else if (c==='e' || c==='E') {
-            if(numberNode.indexOf('e')!==-1 ||
-               numberNode.indexOf('E')!==-1 )
-               return emitError('Invalid number has two exponential');
-            numberNode += c;
-          } else if (c==="+" || c==="-") {
-            if(!(p==='e' || p==='E'))
-               return emitError('Invalid symbol in number');
-            numberNode += c;
-          } else {
-            if (numberNode) {
-              emitValueOpen(parseFloat(numberNode));
-              emitValueClose();
-              numberNode = "";
-            }
-            i--; // go back one
-            state = stack.pop() || VALUE;
-          }
-        continue;
-
-        default:
-          return emitError("Unknown state: " + state);
-      }
-    }
-    if (position >= bufferCheckPosition)
-      checkBufferLength();
-  }
-}
-
-
-/** 
- * A bridge used to assign stateless functions to listen to clarinet.
- * 
- * As well as the parameter from clarinet, each callback will also be passed
- * the result of the last callback.
- * 
- * This may also be used to clear all listeners by assigning zero handlers:
- * 
- *    ascentManager( clarinet, {} )
- */
-function ascentManager(oboeBus, handlers){
-   "use strict";
-   
-   var listenerId = {},
-       ascent;
-
-   function stateAfter(handler) {
-      return function(param){
-         ascent = handler( ascent, param);
-      }
-   }
-   
-   for( var eventName in handlers ) {
-
-      oboeBus(eventName).on(stateAfter(handlers[eventName]), listenerId);
-   }
-   
-   oboeBus(NODE_SWAP).on(function(newNode) {
-      
-      var oldHead = head(ascent),
-          key = keyOf(oldHead),
-          ancestors = tail(ascent),
-          parentNode;
-
-      if( ancestors ) {
-         parentNode = nodeOf(head(ancestors));
-         parentNode[key] = newNode;
-      }
-   });
-
-   oboeBus(NODE_DROP).on(function() {
-
-      var oldHead = head(ascent),
-          key = keyOf(oldHead),
-          ancestors = tail(ascent),
-          parentNode;
-
-      if( ancestors ) {
-         parentNode = nodeOf(head(ancestors));
- 
-         delete parentNode[key];
-      }
-   });
-
-   oboeBus(ABORTING).on(function(){
-      
-      for( var eventName in handlers ) {
-         oboeBus(eventName).un(listenerId);
-      }
-   });   
-}
-
-// based on gist https://gist.github.com/monsur/706839
-
-/**
- * XmlHttpRequest's getAllResponseHeaders() method returns a string of response
- * headers according to the format described here:
- * http://www.w3.org/TR/XMLHttpRequest/#the-getallresponseheaders-method
- * This method parses that string into a user-friendly key/value pair object.
- */
-function parseResponseHeaders(headerStr) {
-   var headers = {};
-   
-   headerStr && headerStr.split('\u000d\u000a')
-      .forEach(function(headerPair){
-   
-         // Can't use split() here because it does the wrong thing
-         // if the header value has the string ": " in it.
-         var index = headerPair.indexOf('\u003a\u0020');
-         
-         headers[headerPair.substring(0, index)] 
-                     = headerPair.substring(index + 2);
-      });
-   
-   return headers;
-}
-
-/**
- * Detect if a given URL is cross-origin in the scope of the
- * current page.
- * 
- * Browser only (since cross-origin has no meaning in Node.js)
- *
- * @param {Object} pageLocation - as in window.location
- * @param {Object} ajaxHost - an object like window.location describing the 
- *    origin of the url that we want to ajax in
- */
-function isCrossOrigin(pageLocation, ajaxHost) {
-
-   /*
-    * NB: defaultPort only knows http and https.
-    * Returns undefined otherwise.
-    */
-   function defaultPort(protocol) {
-      return {'http:':80, 'https:':443}[protocol];
-   }
-   
-   function portOf(location) {
-      // pageLocation should always have a protocol. ajaxHost if no port or
-      // protocol is specified, should use the port of the containing page
-      
-      return location.port || defaultPort(location.protocol||pageLocation.protocol);
-   }
-
-   // if ajaxHost doesn't give a domain, port is the same as pageLocation
-   // it can't give a protocol but not a domain
-   // it can't give a port but not a domain
-   
-   return !!(  (ajaxHost.protocol  && (ajaxHost.protocol  != pageLocation.protocol)) ||
-               (ajaxHost.host      && (ajaxHost.host      != pageLocation.host))     ||
-               (ajaxHost.host      && (portOf(ajaxHost) != portOf(pageLocation)))
-          );
-}
-
-/* turn any url into an object like window.location */
-function parseUrlOrigin(url) {
-   // url could be domain-relative
-   // url could give a domain
-
-   // cross origin means:
-   //    same domain
-   //    same port
-   //    some protocol
-   // so, same everything up to the first (single) slash 
-   // if such is given
-   //
-   // can ignore everything after that   
-   
-   var URL_HOST_PATTERN = /(\w+:)?(?:\/\/)([\w.-]+)?(?::(\d+))?\/?/,
-
-         // if no match, use an empty array so that
-         // subexpressions 1,2,3 are all undefined
-         // and will ultimately return all empty
-         // strings as the parse result:
-       urlHostMatch = URL_HOST_PATTERN.exec(url) || [];
-   
-   return {
-      protocol:   urlHostMatch[1] || '',
-      host:       urlHostMatch[2] || '',
-      port:       urlHostMatch[3] || ''
-   };
-}
-
-function httpTransport(){
-   return new XMLHttpRequest();
-}
-
-/**
- * A wrapper around the browser XmlHttpRequest object that raises an 
- * event whenever a new part of the response is available.
- * 
- * In older browsers progressive reading is impossible so all the 
- * content is given in a single call. For newer ones several events
- * should be raised, allowing progressive interpretation of the response.
- *      
- * @param {Function} oboeBus an event bus local to this Oboe instance
- * @param {XMLHttpRequest} xhr the xhr to use as the transport. Under normal
- *          operation, will have been created using httpTransport() above
- *          but for tests a stub can be provided instead.
- * @param {String} method one of 'GET' 'POST' 'PUT' 'PATCH' 'DELETE'
- * @param {String} url the url to make a request to
- * @param {String|Null} data some content to be sent with the request.
- *                      Only valid if method is POST or PUT.
- * @param {Object} [headers] the http request headers to send
- * @param {boolean} withCredentials the XHR withCredentials property will be
- *    set to this value
- */  
-function streamingHttp(oboeBus, xhr, method, url, data, headers, withCredentials) {
-           
-   "use strict";
-   
-   var emitStreamData = oboeBus(STREAM_DATA).emit,
-       emitFail       = oboeBus(FAIL_EVENT).emit,
-       numberOfCharsAlreadyGivenToCallback = 0,
-       stillToSendStartEvent = true;
-
-   // When an ABORTING message is put on the event bus abort 
-   // the ajax request         
-   oboeBus( ABORTING ).on( function(){
-  
-      // if we keep the onreadystatechange while aborting the XHR gives 
-      // a callback like a successful call so first remove this listener
-      // by assigning null:
-      xhr.onreadystatechange = null;
-            
-      xhr.abort();
-   });
-
-   /** 
-    * Handle input from the underlying xhr: either a state change,
-    * the progress event or the request being complete.
-    */
-   function handleProgress() {
-                        
-      var textSoFar = xhr.responseText,
-          newText = textSoFar.substr(numberOfCharsAlreadyGivenToCallback);
-      
-      
-      /* Raise the event for new text.
-      
-         On older browsers, the new text is the whole response. 
-         On newer/better ones, the fragment part that we got since 
-         last progress. */
-         
-      if( newText ) {
-         emitStreamData( newText );
-      } 
-
-      numberOfCharsAlreadyGivenToCallback = len(textSoFar);
-   }
-   
-   
-   if('onprogress' in xhr){  // detect browser support for progressive delivery
-      xhr.onprogress = handleProgress;
-   }
-      
-   xhr.onreadystatechange = function() {
-
-      function sendStartIfNotAlready() {
-         // Internet Explorer is very unreliable as to when xhr.status etc can
-         // be read so has to be protected with try/catch and tried again on 
-         // the next readyState if it fails
-         try{
-            stillToSendStartEvent && oboeBus( HTTP_START ).emit(
-               xhr.status,
-               parseResponseHeaders(xhr.getAllResponseHeaders()) );
-            stillToSendStartEvent = false;
-         } catch(e){/* do nothing, will try again on next readyState*/}
-      }
-      
-      switch( xhr.readyState ) {
-               
-         case 2: // HEADERS_RECEIVED
-         case 3: // LOADING
-            return sendStartIfNotAlready();
-            
-         case 4: // DONE
-            sendStartIfNotAlready(); // if xhr.status hasn't been available yet, it must be NOW, huh IE?
-            
-            // is this a 2xx http code?
-            var successful = String(xhr.status)[0] == 2;
-            
-            if( successful ) {
-               // In Chrome 29 (not 28) no onprogress is emitted when a response
-               // is complete before the onload. We need to always do handleInput
-               // in case we get the load but have not had a final progress event.
-               // This looks like a bug and may change in future but let's take
-               // the safest approach and assume we might not have received a 
-               // progress event for each part of the response
-               handleProgress();
-               
-               oboeBus(STREAM_END).emit();
-            } else {
-
-               emitFail( errorReport(
-                  xhr.status, 
-                  xhr.responseText
-               ));
-            }
-      }
-   };
-   
-   try{
-   
-      xhr.open(method, url, true);
-   
-      for( var headerName in headers ){
-         xhr.setRequestHeader(headerName, headers[headerName]);
-      }
-      
-      if( !isCrossOrigin(window.location, parseUrlOrigin(url)) ) {
-         xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
-      }
-
-      xhr.withCredentials = withCredentials;
-      
-      xhr.send(data);
-      
-   } catch( e ) {
-      
-      // To keep a consistent interface with Node, we can't emit an event here.
-      // Node's streaming http adaptor receives the error as an asynchronous
-      // event rather than as an exception. If we emitted now, the Oboe user
-      // has had no chance to add a .fail listener so there is no way
-      // the event could be useful. For both these reasons defer the
-      // firing to the next JS frame.  
-      window.setTimeout(
-         partialComplete(emitFail, errorReport(undefined, undefined, e))
-      ,  0
-      );
-   }            
-}
-
-var jsonPathSyntax = (function() {
- 
-   var
-   
-   /** 
-    * Export a regular expression as a simple function by exposing just 
-    * the Regex#exec. This allows regex tests to be used under the same 
-    * interface as differently implemented tests, or for a user of the
-    * tests to not concern themselves with their implementation as regular
-    * expressions.
-    * 
-    * This could also be expressed point-free as:
-    *   Function.prototype.bind.bind(RegExp.prototype.exec),
-    *   
-    * But that's far too confusing! (and not even smaller once minified 
-    * and gzipped)
-    */
-       regexDescriptor = function regexDescriptor(regex) {
-            return regex.exec.bind(regex);
-       }
-       
-   /**
-    * Join several regular expressions and express as a function.
-    * This allows the token patterns to reuse component regular expressions
-    * instead of being expressed in full using huge and confusing regular
-    * expressions.
-    */       
-   ,   jsonPathClause = varArgs(function( componentRegexes ) {
-
-            // The regular expressions all start with ^ because we 
-            // only want to find matches at the start of the 
-            // JSONPath fragment we are inspecting           
-            componentRegexes.unshift(/^/);
-            
-            return   regexDescriptor(
-                        RegExp(
-                           componentRegexes.map(attr('source')).join('')
-                        )
-                     );
-       })
-       
-   ,   possiblyCapturing =           /(\$?)/
-   ,   namedNode =                   /([\w-_]+|\*)/
-   ,   namePlaceholder =             /()/
-   ,   nodeInArrayNotation =         /\["([^"]+)"\]/
-   ,   numberedNodeInArrayNotation = /\[(\d+|\*)\]/
-   ,   fieldList =                      /{([\w ]*?)}/
-   ,   optionalFieldList =           /(?:{([\w ]*?)})?/
-    
-
-       //   foo or *                  
-   ,   jsonPathNamedNodeInObjectNotation   = jsonPathClause( 
-                                                possiblyCapturing, 
-                                                namedNode, 
-                                                optionalFieldList
-                                             )
-                                             
-       //   ["foo"]   
-   ,   jsonPathNamedNodeInArrayNotation    = jsonPathClause( 
-                                                possiblyCapturing, 
-                                                nodeInArrayNotation, 
-                                                optionalFieldList
-                                             )  
-
-       //   [2] or [*]       
-   ,   jsonPathNumberedNodeInArrayNotation = jsonPathClause( 
-                                                possiblyCapturing, 
-                                                numberedNodeInArrayNotation, 
-                                                optionalFieldList
-                                             )
-
-       //   {a b c}      
-   ,   jsonPathPureDuckTyping              = jsonPathClause( 
-                                                possiblyCapturing, 
-                                                namePlaceholder, 
-                                                fieldList
-                                             )
-   
-       //   ..
-   ,   jsonPathDoubleDot                   = jsonPathClause(/\.\./)                  
-   
-       //   .
-   ,   jsonPathDot                         = jsonPathClause(/\./)                    
-   
-       //   !
-   ,   jsonPathBang                        = jsonPathClause(
-                                                possiblyCapturing, 
-                                                /!/
-                                             )  
-   
-       //   nada!
-   ,   emptyString                         = jsonPathClause(/$/)                     
-   
-   ;
-   
-  
-   /* We export only a single function. When called, this function injects 
-      into another function the descriptors from above.             
-    */
-   return function (fn){      
-      return fn(      
-         lazyUnion(
-            jsonPathNamedNodeInObjectNotation
-         ,  jsonPathNamedNodeInArrayNotation
-         ,  jsonPathNumberedNodeInArrayNotation
-         ,  jsonPathPureDuckTyping 
-         )
-      ,  jsonPathDoubleDot
-      ,  jsonPathDot
-      ,  jsonPathBang
-      ,  emptyString 
-      );
-   }; 
-
-}());
-/**
- * Get a new key->node mapping
- * 
- * @param {String|Number} key
- * @param {Object|Array|String|Number|null} node a value found in the json
- */
-function namedNode(key, node) {
-   return {key:key, node:node};
-}
-
-/** get the key of a namedNode */
-var keyOf = attr('key');
-
-/** get the node from a namedNode */
-var nodeOf = attr('node');
-/** 
- * This file provides various listeners which can be used to build up
- * a changing ascent based on the callbacks provided by Clarinet. It listens
- * to the low-level events from Clarinet and emits higher-level ones.
- *  
- * The building up is stateless so to track a JSON file
- * ascentManager.js is required to store the ascent state
- * between calls.
- */
-
-
-
-/** 
- * A special value to use in the path list to represent the path 'to' a root 
- * object (which doesn't really have any path). This prevents the need for 
- * special-casing detection of the root object and allows it to be treated 
- * like any other object. We might think of this as being similar to the 
- * 'unnamed root' domain ".", eg if I go to 
- * http://en.wikipedia.org./wiki/En/Main_page the dot after 'org' deliminates 
- * the unnamed root of the DNS.
- * 
- * This is kept as an object to take advantage that in Javascript's OO objects 
- * are guaranteed to be distinct, therefore no other object can possibly clash 
- * with this one. Strings, numbers etc provide no such guarantee. 
- **/
-var ROOT_PATH = {};
-
-
-/**
- * Create a new set of handlers for clarinet's events, bound to the emit 
- * function given.  
- */ 
-function incrementalContentBuilder( oboeBus ) {
-
-   var emitNodeOpened = oboeBus(NODE_OPENED).emit,
-       emitNodeClosed = oboeBus(NODE_CLOSED).emit,
-       emitRootOpened = oboeBus(ROOT_PATH_FOUND).emit,
-       emitRootClosed = oboeBus(ROOT_NODE_FOUND).emit;
-
-   function arrayIndicesAreKeys( possiblyInconsistentAscent, newDeepestNode) {
-   
-      /* for values in arrays we aren't pre-warned of the coming paths 
-         (Clarinet gives no call to onkey like it does for values in objects) 
-         so if we are in an array we need to create this path ourselves. The 
-         key will be len(parentNode) because array keys are always sequential 
-         numbers. */
-
-      var parentNode = nodeOf( head( possiblyInconsistentAscent));
-      
-      return      isOfType( Array, parentNode)
-               ?
-                  keyFound(  possiblyInconsistentAscent, 
-                              len(parentNode), 
-                              newDeepestNode
-                  )
-               :  
-                  // nothing needed, return unchanged
-                  possiblyInconsistentAscent 
-               ;
-   }
-                 
-   function nodeOpened( ascent, newDeepestNode ) {
-      
-      if( !ascent ) {
-         // we discovered the root node,         
-         emitRootOpened( newDeepestNode);
-                    
-         return keyFound( ascent, ROOT_PATH, newDeepestNode);         
-      }
-
-      // we discovered a non-root node
-                 
-      var arrayConsistentAscent  = arrayIndicesAreKeys( ascent, newDeepestNode),      
-          ancestorBranches       = tail( arrayConsistentAscent),
-          previouslyUnmappedName = keyOf( head( arrayConsistentAscent));
-          
-      appendBuiltContent( 
-         ancestorBranches, 
-         previouslyUnmappedName, 
-         newDeepestNode 
-      );
-                                                                                                         
-      return cons( 
-               namedNode( previouslyUnmappedName, newDeepestNode ), 
-               ancestorBranches
-      );                                                                          
-   }
-
-
-   /**
-    * Add a new value to the object we are building up to represent the
-    * parsed JSON
-    */
-   function appendBuiltContent( ancestorBranches, key, node ){
-     
-      nodeOf( head( ancestorBranches))[key] = node;
-   }
-
-     
-   /**
-    * For when we find a new key in the json.
-    * 
-    * @param {String|Number|Object} newDeepestName the key. If we are in an 
-    *    array will be a number, otherwise a string. May take the special 
-    *    value ROOT_PATH if the root node has just been found
-    *    
-    * @param {String|Number|Object|Array|Null|undefined} [maybeNewDeepestNode] 
-    *    usually this won't be known so can be undefined. Can't use null 
-    *    to represent unknown because null is a valid value in JSON
-    **/  
-   function keyFound(ascent, newDeepestName, maybeNewDeepestNode) {
-
-      if( ascent ) { // if not root
-      
-         // If we have the key but (unless adding to an array) no known value
-         // yet. Put that key in the output but against no defined value:      
-         appendBuiltContent( ascent, newDeepestName, maybeNewDeepestNode );
-      }
-   
-      var ascentWithNewPath = cons( 
-                                 namedNode( newDeepestName, 
-                                            maybeNewDeepestNode), 
-                                 ascent
-                              );
-
-      emitNodeOpened( ascentWithNewPath);
- 
-      return ascentWithNewPath;
-   }
-
-
-   /**
-    * For when the current node ends.
-    */
-   function nodeClosed( ascent ) {
-
-      emitNodeClosed( ascent);
-       
-      return tail( ascent) ||
-             // If there are no nodes left in the ascent the root node
-             // just closed. Emit a special event for this: 
-             emitRootClosed(nodeOf(head(ascent)));
-   }      
-
-   var contentBuilderHandlers = {};
-   contentBuilderHandlers[SAX_VALUE_OPEN] = nodeOpened;
-   contentBuilderHandlers[SAX_VALUE_CLOSE] = nodeClosed;
-   contentBuilderHandlers[SAX_KEY] = keyFound;
-   return contentBuilderHandlers;
-}
-
-/**
- * The jsonPath evaluator compiler used for Oboe.js. 
- * 
- * One function is exposed. This function takes a String JSONPath spec and 
- * returns a function to test candidate ascents for matches.
- * 
- *  String jsonPath -> (List ascent) -> Boolean|Object
- *
- * This file is coded in a pure functional style. That is, no function has 
- * side effects, every function evaluates to the same value for the same 
- * arguments and no variables are reassigned.
- */  
-// the call to jsonPathSyntax injects the token syntaxes that are needed 
-// inside the compiler
-var jsonPathCompiler = jsonPathSyntax(function (pathNodeSyntax, 
-                                                doubleDotSyntax, 
-                                                dotSyntax,
-                                                bangSyntax,
-                                                emptySyntax ) {
-
-   var CAPTURING_INDEX = 1;
-   var NAME_INDEX = 2;
-   var FIELD_LIST_INDEX = 3;
-
-   var headKey  = compose2(keyOf, head),
-       headNode = compose2(nodeOf, head);
-                   
-   /**
-    * Create an evaluator function for a named path node, expressed in the
-    * JSONPath like:
-    *    foo
-    *    ["bar"]
-    *    [2]   
-    */
-   function nameClause(previousExpr, detection ) {
-     
-      var name = detection[NAME_INDEX],
-            
-          matchesName = ( !name || name == '*' ) 
-                           ?  always
-                           :  function(ascent){return headKey(ascent) == name};
-     
-
-      return lazyIntersection(matchesName, previousExpr);
-   }
-
-   /**
-    * Create an evaluator function for a a duck-typed node, expressed like:
-    * 
-    *    {spin, taste, colour}
-    *    .particle{spin, taste, colour}
-    *    *{spin, taste, colour}
-    */
-   function duckTypeClause(previousExpr, detection) {
-
-      var fieldListStr = detection[FIELD_LIST_INDEX];
-
-      if (!fieldListStr) 
-         return previousExpr; // don't wrap at all, return given expr as-is      
-
-      var hasAllrequiredFields = partialComplete(
-                                    hasAllProperties, 
-                                    arrayAsList(fieldListStr.split(/\W+/))
-                                 ),
-                                 
-          isMatch =  compose2( 
-                        hasAllrequiredFields, 
-                        headNode
-                     );
-
-      return lazyIntersection(isMatch, previousExpr);
-   }
-
-   /**
-    * Expression for $, returns the evaluator function
-    */
-   function capture( previousExpr, detection ) {
-
-      // extract meaning from the detection      
-      var capturing = !!detection[CAPTURING_INDEX];
-
-      if (!capturing)          
-         return previousExpr; // don't wrap at all, return given expr as-is      
-      
-      return lazyIntersection(previousExpr, head);
-            
-   }            
-      
-   /**
-    * Create an evaluator function that moves onto the next item on the 
-    * lists. This function is the place where the logic to move up a 
-    * level in the ascent exists. 
-    * 
-    * Eg, for JSONPath ".foo" we need skip1(nameClause(always, [,'foo']))
-    */
-   function skip1(previousExpr) {
-   
-   
-      if( previousExpr == always ) {
-         /* If there is no previous expression this consume command 
-            is at the start of the jsonPath.
-            Since JSONPath specifies what we'd like to find but not 
-            necessarily everything leading down to it, when running
-            out of JSONPath to check against we default to true */
-         return always;
-      }
-
-      /** return true if the ascent we have contains only the JSON root,
-       *  false otherwise
-       */
-      function notAtRoot(ascent){
-         return headKey(ascent) != ROOT_PATH;
-      }
-      
-      return lazyIntersection(
-               /* If we're already at the root but there are more 
-                  expressions to satisfy, can't consume any more. No match.
-
-                  This check is why none of the other exprs have to be able 
-                  to handle empty lists; skip1 is the only evaluator that 
-                  moves onto the next token and it refuses to do so once it 
-                  reaches the last item in the list. */
-               notAtRoot,
-               
-               /* We are not at the root of the ascent yet.
-                  Move to the next level of the ascent by handing only 
-                  the tail to the previous expression */ 
-               compose2(previousExpr, tail) 
-      );
-                                                                                                               
-   }   
-   
-   /**
-    * Create an evaluator function for the .. (double dot) token. Consumes
-    * zero or more levels of the ascent, the fewest that are required to find
-    * a match when given to previousExpr.
-    */   
-   function skipMany(previousExpr) {
-
-      if( previousExpr == always ) {
-         /* If there is no previous expression this consume command 
-            is at the start of the jsonPath.
-            Since JSONPath specifies what we'd like to find but not 
-            necessarily everything leading down to it, when running
-            out of JSONPath to check against we default to true */            
-         return always;
-      }
-          
-      var 
-          // In JSONPath .. is equivalent to !.. so if .. reaches the root
-          // the match has succeeded. Ie, we might write ..foo or !..foo
-          // and both should match identically.
-          terminalCaseWhenArrivingAtRoot = rootExpr(),
-          terminalCaseWhenPreviousExpressionIsSatisfied = previousExpr,
-          recursiveCase = skip1(function(ascent) {
-             return cases(ascent);
-          }),
-
-          cases = lazyUnion(
-                     terminalCaseWhenArrivingAtRoot
-                  ,  terminalCaseWhenPreviousExpressionIsSatisfied
-                  ,  recursiveCase  
-                  );
-      
-      return cases;
-   }      
-   
-   /**
-    * Generate an evaluator for ! - matches only the root element of the json
-    * and ignores any previous expressions since nothing may precede !. 
-    */   
-   function rootExpr() {
-      
-      return function(ascent){
-         return headKey(ascent) == ROOT_PATH;
-      };
-   }   
-         
-   /**
-    * Generate a statement wrapper to sit around the outermost 
-    * clause evaluator.
-    * 
-    * Handles the case where the capturing is implicit because the JSONPath
-    * did not contain a '$' by returning the last node.
-    */   
-   function statementExpr(lastClause) {
-      
-      return function(ascent) {
-   
-         // kick off the evaluation by passing through to the last clause
-         var exprMatch = lastClause(ascent);
-                                                     
-         return exprMatch === true ? head(ascent) : exprMatch;
-      };
-   }      
-                          
-   /**
-    * For when a token has been found in the JSONPath input.
-    * Compiles the parser for that token and returns in combination with the
-    * parser already generated.
-    * 
-    * @param {Function} exprs  a list of the clause evaluator generators for
-    *                          the token that was found
-    * @param {Function} parserGeneratedSoFar the parser already found
-    * @param {Array} detection the match given by the regex engine when 
-    *                          the feature was found
-    */
-   function expressionsReader( exprs, parserGeneratedSoFar, detection ) {
-                     
-      // if exprs is zero-length foldR will pass back the 
-      // parserGeneratedSoFar as-is so we don't need to treat 
-      // this as a special case
-      
-      return   foldR( 
-                  function( parserGeneratedSoFar, expr ){
-         
-                     return expr(parserGeneratedSoFar, detection);
-                  }, 
-                  parserGeneratedSoFar, 
-                  exprs
-               );                     
-
-   }
-
-   /** 
-    *  If jsonPath matches the given detector function, creates a function which
-    *  evaluates against every clause in the clauseEvaluatorGenerators. The
-    *  created function is propagated to the onSuccess function, along with
-    *  the remaining unparsed JSONPath substring.
-    *  
-    *  The intended use is to create a clauseMatcher by filling in
-    *  the first two arguments, thus providing a function that knows
-    *  some syntax to match and what kind of generator to create if it
-    *  finds it. The parameter list once completed is:
-    *  
-    *    (jsonPath, parserGeneratedSoFar, onSuccess)
-    *  
-    *  onSuccess may be compileJsonPathToFunction, to recursively continue 
-    *  parsing after finding a match or returnFoundParser to stop here.
-    */
-   function generateClauseReaderIfTokenFound (
-     
-                        tokenDetector, clauseEvaluatorGenerators,
-                         
-                        jsonPath, parserGeneratedSoFar, onSuccess) {
-                        
-      var detected = tokenDetector(jsonPath);
-
-      if(detected) {
-         var compiledParser = expressionsReader(
-                                 clauseEvaluatorGenerators, 
-                                 parserGeneratedSoFar, 
-                                 detected
-                              ),
-         
-             remainingUnparsedJsonPath = jsonPath.substr(len(detected[0]));                
-                               
-         return onSuccess(remainingUnparsedJsonPath, compiledParser);
-      }         
-   }
-                 
-   /**
-    * Partially completes generateClauseReaderIfTokenFound above. 
-    */
-   function clauseMatcher(tokenDetector, exprs) {
-        
-      return   partialComplete( 
-                  generateClauseReaderIfTokenFound, 
-                  tokenDetector, 
-                  exprs 
-               );
-   }
-
-   /**
-    * clauseForJsonPath is a function which attempts to match against 
-    * several clause matchers in order until one matches. If non match the
-    * jsonPath expression is invalid and an error is thrown.
-    * 
-    * The parameter list is the same as a single clauseMatcher:
-    * 
-    *    (jsonPath, parserGeneratedSoFar, onSuccess)
-    */     
-   var clauseForJsonPath = lazyUnion(
-
-      clauseMatcher(pathNodeSyntax   , list( capture, 
-                                             duckTypeClause, 
-                                             nameClause, 
-                                             skip1 ))
-                                                     
-   ,  clauseMatcher(doubleDotSyntax  , list( skipMany))
-       
-       // dot is a separator only (like whitespace in other languages) but 
-       // rather than make it a special case, use an empty list of 
-       // expressions when this token is found
-   ,  clauseMatcher(dotSyntax        , list() )  
-                                                                                      
-   ,  clauseMatcher(bangSyntax       , list( capture,
-                                             rootExpr))
-                                                          
-   ,  clauseMatcher(emptySyntax      , list( statementExpr))
-   
-   ,  function (jsonPath) {
-         throw Error('"' + jsonPath + '" could not be tokenised')      
-      }
-   );
-
-
-   /**
-    * One of two possible values for the onSuccess argument of 
-    * generateClauseReaderIfTokenFound.
-    * 
-    * When this function is used, generateClauseReaderIfTokenFound simply 
-    * returns the compiledParser that it made, regardless of if there is 
-    * any remaining jsonPath to be compiled.
-    */
-   function returnFoundParser(_remainingJsonPath, compiledParser){ 
-      return compiledParser 
-   }     
-              
-   /**
-    * Recursively compile a JSONPath expression.
-    * 
-    * This function serves as one of two possible values for the onSuccess 
-    * argument of generateClauseReaderIfTokenFound, meaning continue to
-    * recursively compile. Otherwise, returnFoundParser is given and
-    * compilation terminates.
-    */
-   function compileJsonPathToFunction( uncompiledJsonPath, 
-                                       parserGeneratedSoFar ) {
-
-      /**
-       * On finding a match, if there is remaining text to be compiled
-       * we want to either continue parsing using a recursive call to 
-       * compileJsonPathToFunction. Otherwise, we want to stop and return 
-       * the parser that we have found so far.
-       */
-      var onFind =      uncompiledJsonPath
-                     ?  compileJsonPathToFunction 
-                     :  returnFoundParser;
-                   
-      return   clauseForJsonPath( 
-                  uncompiledJsonPath, 
-                  parserGeneratedSoFar, 
-                  onFind
-               );                              
-   }
-
-   /**
-    * This is the function that we expose to the rest of the library.
-    */
-   return function(jsonPath){
-        
-      try {
-         // Kick off the recursive parsing of the jsonPath 
-         return compileJsonPathToFunction(jsonPath, always);
-         
-      } catch( e ) {
-         throw Error( 'Could not compile "' + jsonPath + 
-                      '" because ' + e.message
-         );
-      }
-   }
-
-});
-
-/** 
- * A pub/sub which is responsible for a single event type. A 
- * multi-event type event bus is created by pubSub by collecting
- * several of these.
- * 
- * @param {String} eventType                   
- *    the name of the events managed by this singleEventPubSub
- * @param {singleEventPubSub} [newListener]    
- *    place to notify of new listeners
- * @param {singleEventPubSub} [removeListener] 
- *    place to notify of when listeners are removed
- */
-function singleEventPubSub(eventType, newListener, removeListener){
-
-   /** we are optimised for emitting events over firing them.
-    *  As well as the tuple list which stores event ids and
-    *  listeners there is a list with just the listeners which 
-    *  can be iterated more quickly when we are emitting
-    */
-   var listenerTupleList,
-       listenerList;
-
-   function hasId(id){
-      return function(tuple) {
-         return tuple.id == id;      
-      };  
-   }
-              
-   return {
-
-      /**
-       * @param {Function} listener
-       * @param {*} listenerId 
-       *    an id that this listener can later by removed by. 
-       *    Can be of any type, to be compared to other ids using ==
-       */
-      on:function( listener, listenerId ) {
-         
-         var tuple = {
-            listener: listener
-         ,  id:       listenerId || listener // when no id is given use the
-                                             // listener function as the id
-         };
-
-         if( newListener ) {
-            newListener.emit(eventType, listener, tuple.id);
-         }
-         
-         listenerTupleList = cons( tuple,    listenerTupleList );
-         listenerList      = cons( listener, listenerList      );
-
-         return this; // chaining
-      },
-     
-      emit:function () {                                                                                           
-         applyEach( listenerList, arguments );
-      },
-      
-      un: function( listenerId ) {
-             
-         var removed;             
-              
-         listenerTupleList = without(
-            listenerTupleList,
-            hasId(listenerId),
-            function(tuple){
-               removed = tuple;
-            }
-         );    
-         
-         if( removed ) {
-            listenerList = without( listenerList, function(listener){
-               return listener == removed.listener;
-            });
-         
-            if( removeListener ) {
-               removeListener.emit(eventType, removed.listener, removed.id);
-            }
-         }
-      },
-      
-      listeners: function(){
-         // differs from Node EventEmitter: returns list, not array
-         return listenerList;
-      },
-      
-      hasListener: function(listenerId){
-         var test = listenerId? hasId(listenerId) : always;
-      
-         return defined(first( test, listenerTupleList));
-      }
-   };
-}
-/**
- * pubSub is a curried interface for listening to and emitting
- * events.
- * 
- * If we get a bus:
- *    
- *    var bus = pubSub();
- * 
- * We can listen to event 'foo' like:
- * 
- *    bus('foo').on(myCallback)
- *    
- * And emit event foo like:
- * 
- *    bus('foo').emit()
- *    
- * or, with a parameter:
- * 
- *    bus('foo').emit('bar')
- *     
- * All functions can be cached and don't need to be 
- * bound. Ie:
- * 
- *    var fooEmitter = bus('foo').emit
- *    fooEmitter('bar');  // emit an event
- *    fooEmitter('baz');  // emit another
- *    
- * There's also an uncurried[1] shortcut for .emit and .on:
- * 
- *    bus.on('foo', callback)
- *    bus.emit('foo', 'bar')
- * 
- * [1]: http://zvon.org/other/haskell/Outputprelude/uncurry_f.html
- */
-function pubSub(){
-
-   var singles = {},
-       newListener = newSingle('newListener'),
-       removeListener = newSingle('removeListener'); 
-      
-   function newSingle(eventName) {
-      return singles[eventName] = singleEventPubSub(
-         eventName, 
-         newListener, 
-         removeListener
-      );   
-   }      
-
-   /** pubSub instances are functions */
-   function pubSubInstance( eventName ){   
-      
-      return singles[eventName] || newSingle( eventName );   
-   }
-
-   // add convenience EventEmitter-style uncurried form of 'emit' and 'on'
-   ['emit', 'on', 'un'].forEach(function(methodName){
-   
-      pubSubInstance[methodName] = varArgs(function(eventName, parameters){
-         apply( parameters, pubSubInstance( eventName )[methodName]);
-      });   
-   });
-         
-   return pubSubInstance;
-}
-
-/**
- * This file declares some constants to use as names for event types.
- */
-
-var // the events which are never exported are kept as 
-    // the smallest possible representation, in numbers:
-    _S = 1,
-
-    // fired whenever a new node starts in the JSON stream:
-    NODE_OPENED     = _S++,
-
-    // fired whenever a node closes in the JSON stream:
-    NODE_CLOSED     = _S++,
-
-    // called if a .node callback returns a value - 
-    NODE_SWAP       = _S++,
-    NODE_DROP       = _S++,
-
-    FAIL_EVENT      = 'fail',
-   
-    ROOT_NODE_FOUND = _S++,
-    ROOT_PATH_FOUND = _S++,
-   
-    HTTP_START      = 'start',
-    STREAM_DATA     = 'data',
-    STREAM_END      = 'end',
-    ABORTING        = _S++,
-
-    // SAX events butchered from Clarinet
-    SAX_KEY          = _S++,
-    SAX_VALUE_OPEN   = _S++,
-    SAX_VALUE_CLOSE  = _S++;
-    
-function errorReport(statusCode, body, error) {
-   try{
-      var jsonBody = JSON.parse(body);
-   }catch(e){}
-
-   return {
-      statusCode:statusCode,
-      body:body,
-      jsonBody:jsonBody,
-      thrown:error
-   };
-}    
-
-/** 
- *  The pattern adaptor listens for newListener and removeListener
- *  events. When patterns are added or removed it compiles the JSONPath
- *  and wires them up.
- *  
- *  When nodes and paths are found it emits the fully-qualified match 
- *  events with parameters ready to ship to the outside world
- */
-
-function patternAdapter(oboeBus, jsonPathCompiler) {
-
-   var predicateEventMap = {
-      node:oboeBus(NODE_CLOSED)
-   ,  path:oboeBus(NODE_OPENED)
-   };
-     
-   function emitMatchingNode(emitMatch, node, ascent) {
-         
-      /* 
-         We're now calling to the outside world where Lisp-style 
-         lists will not be familiar. Convert to standard arrays. 
-   
-         Also, reverse the order because it is more common to 
-         list paths "root to leaf" than "leaf to root"  */
-      var descent     = reverseList(ascent);
-                
-      emitMatch(
-         node,
-         
-         // To make a path, strip off the last item which is the special
-         // ROOT_PATH token for the 'path' to the root node          
-         listAsArray(tail(map(keyOf,descent))),  // path
-         listAsArray(map(nodeOf, descent))       // ancestors    
-      );         
-   }
-
-   /* 
-    * Set up the catching of events such as NODE_CLOSED and NODE_OPENED and, if 
-    * matching the specified pattern, propagate to pattern-match events such as 
-    * oboeBus('node:!')
-    * 
-    * 
-    * 
-    * @param {Function} predicateEvent 
-    *          either oboeBus(NODE_CLOSED) or oboeBus(NODE_OPENED).
-    * @param {Function} compiledJsonPath          
-    */
-   function addUnderlyingListener( fullEventName, predicateEvent, compiledJsonPath ){
-   
-      var emitMatch = oboeBus(fullEventName).emit;
-   
-      predicateEvent.on( function (ascent) {
-
-         var maybeMatchingMapping = compiledJsonPath(ascent);
-
-         /* Possible values for maybeMatchingMapping are now:
-
-          false: 
-          we did not match 
-
-          an object/array/string/number/null: 
-          we matched and have the node that matched.
-          Because nulls are valid json values this can be null.
-
-          undefined:
-          we matched but don't have the matching node yet.
-          ie, we know there is an upcoming node that matches but we 
-          can't say anything else about it. 
-          */
-         if (maybeMatchingMapping !== false) {
-
-            emitMatchingNode(
-               emitMatch, 
-               nodeOf(maybeMatchingMapping), 
-               ascent
-            );
-         }
-      }, fullEventName);
-     
-      oboeBus('removeListener').on( function(removedEventName){
-
-         // if the fully qualified match event listener is later removed, clean up 
-         // by removing the underlying listener if it was the last using that pattern:
-      
-         if( removedEventName == fullEventName ) {
-         
-            if( !oboeBus(removedEventName).listeners(  )) {
-               predicateEvent.un( fullEventName );
-            }
-         }
-      });   
-   }
-
-   oboeBus('newListener').on( function(fullEventName){
-
-      var match = /(node|path):(.*)/.exec(fullEventName);
-      
-      if( match ) {
-         var predicateEvent = predicateEventMap[match[1]];
-                    
-         if( !predicateEvent.hasListener( fullEventName) ) {  
-                  
-            addUnderlyingListener(
-               fullEventName,
-               predicateEvent, 
-               jsonPathCompiler( match[2] )
-            );
-         }
-      }    
-   })
-
-}
-
-/** 
- * The instance API is the thing that is returned when oboe() is called.
- * it allows:
- * 
- *    - listeners for various events to be added and removed
- *    - the http response header/headers to be read
- */
-function instanceApi(oboeBus, contentSource){
-
-   var oboeApi,
-       fullyQualifiedNamePattern = /^(node|path):./,
-       rootNodeFinishedEvent = oboeBus(ROOT_NODE_FOUND),
-       emitNodeDrop = oboeBus(NODE_DROP).emit,
-       emitNodeSwap = oboeBus(NODE_SWAP).emit,
-
-       /**
-        * Add any kind of listener that the instance api exposes 
-        */          
-       addListener = varArgs(function( eventId, parameters ){
-             
-            if( oboeApi[eventId] ) {
-       
-               // for events added as .on(event, callback), if there is a 
-               // .event() equivalent with special behaviour , pass through
-               // to that: 
-               apply(parameters, oboeApi[eventId]);                     
-            } else {
-       
-               // we have a standard Node.js EventEmitter 2-argument call.
-               // The first parameter is the listener.
-               var event = oboeBus(eventId),
-                   listener = parameters[0];
-       
-               if( fullyQualifiedNamePattern.test(eventId) ) {
-                
-                  // allow fully-qualified node/path listeners 
-                  // to be added                                             
-                  addForgettableCallback(event, listener);                  
-               } else  {
-       
-                  // the event has no special handling, pass through 
-                  // directly onto the event bus:          
-                  event.on( listener);
-               }
-            }
-                
-            return oboeApi; // chaining
-       }),
- 
-       /**
-        * Remove any kind of listener that the instance api exposes 
-        */ 
-       removeListener = function( eventId, p2, p3 ){
-             
-            if( eventId == 'done' ) {
-            
-               rootNodeFinishedEvent.un(p2);
-               
-            } else if( eventId == 'node' || eventId == 'path' ) {
-      
-               // allow removal of node and path 
-               oboeBus.un(eventId + ':' + p2, p3);          
-            } else {
-      
-               // we have a standard Node.js EventEmitter 2-argument call.
-               // The second parameter is the listener. This may be a call
-               // to remove a fully-qualified node/path listener but requires
-               // no special handling
-               var listener = p2;
-
-               oboeBus(eventId).un(listener);                  
-            }
-               
-            return oboeApi; // chaining      
-       };                               
-                        
-   /** 
-    * Add a callback, wrapped in a try/catch so as to not break the
-    * execution of Oboe if an exception is thrown (fail events are 
-    * fired instead)
-    * 
-    * The callback is used as the listener id so that it can later be
-    * removed using .un(callback)
-    */
-   function addProtectedCallback(eventName, callback) {
-      oboeBus(eventName).on(protectedCallback(callback), callback);
-      return oboeApi; // chaining            
-   }
-
-   /**
-    * Add a callback where, if .forget() is called during the callback's
-    * execution, the callback will be de-registered
-    */
-   function addForgettableCallback(event, callback, listenerId) {
-      
-      // listenerId is optional and if not given, the original
-      // callback will be used
-      listenerId = listenerId || callback;
-      
-      var safeCallback = protectedCallback(callback);
-   
-      event.on( function() {
-      
-         var discard = false;
-             
-         oboeApi.forget = function(){
-            discard = true;
-         };           
-         
-         apply( arguments, safeCallback );         
-               
-         delete oboeApi.forget;
-         
-         if( discard ) {
-            event.un(listenerId);
-         }
-      }, listenerId);
-      
-      return oboeApi; // chaining         
-   }
-      
-   /** 
-    *  wrap a callback so that if it throws, Oboe.js doesn't crash but instead
-    *  handles it like a normal error
-    */
-   function protectedCallback( callback ) {
-      return function() {
-         try{      
-            return callback.apply(oboeApi, arguments);   
-         }catch(e)  {
-         
-            // An error occured during the callback, publish it on the event bus 
-            oboeBus(FAIL_EVENT).emit( errorReport(undefined, undefined, e));
-         }      
-      }   
-   }
-
-   /**
-    * Return the fully qualified event for when a pattern matches
-    * either a node or a path
-    * 
-    * @param type {String} either 'node' or 'path'
-    */      
-   function fullyQualifiedPatternMatchEvent(type, pattern) {
-      return oboeBus(type + ':' + pattern);
-   }
-
-   function wrapCallbackToSwapNodeIfSomethingReturned( callback ) {
-      return function() {
-         var returnValueFromCallback = callback.apply(this, arguments);
-
-         if( defined(returnValueFromCallback) ) {
-            
-            if( returnValueFromCallback == oboe.drop ) {
-               emitNodeDrop();
-            } else {
-               emitNodeSwap(returnValueFromCallback);
-            }
-         }
-      }
-   }
-
-   function addSingleNodeOrPathListener(eventId, pattern, callback) {
-
-      var effectiveCallback;
-
-      if( eventId == 'node' ) {
-         effectiveCallback = wrapCallbackToSwapNodeIfSomethingReturned(callback);
-      } else {
-         effectiveCallback = callback;
-      }
-      
-      addForgettableCallback(
-         fullyQualifiedPatternMatchEvent(eventId, pattern),
-         effectiveCallback,
-         callback
-      );
-   }
-
-   /**
-    * Add several listeners at a time, from a map
-    */
-   function addMultipleNodeOrPathListeners(eventId, listenerMap) {
-   
-      for( var pattern in listenerMap ) {
-         addSingleNodeOrPathListener(eventId, pattern, listenerMap[pattern]);
-      }
-   }    
-         
-   /**
-    * implementation behind .onPath() and .onNode()
-    */       
-   function addNodeOrPathListenerApi( eventId, jsonPathOrListenerMap, callback ){
-         
-      if( isString(jsonPathOrListenerMap) ) {
-         addSingleNodeOrPathListener(eventId, jsonPathOrListenerMap, callback);
-
-      } else {
-         addMultipleNodeOrPathListeners(eventId, jsonPathOrListenerMap);
-      }
-      
-      return oboeApi; // chaining
-   }
-      
-   
-   // some interface methods are only filled in after we receive
-   // values and are noops before that:          
-   oboeBus(ROOT_PATH_FOUND).on( function(rootNode) {
-      oboeApi.root = functor(rootNode);   
-   });
-
-   /**
-    * When content starts make the headers readable through the
-    * instance API
-    */
-   oboeBus(HTTP_START).on( function(_statusCode, headers) {
-   
-      oboeApi.header =  function(name) {
-                           return name ? headers[name] 
-                                       : headers
-                                       ;
-                        }
-   });
-                                                               
-   /**
-    * Construct and return the public API of the Oboe instance to be 
-    * returned to the calling application
-    */       
-   return oboeApi = {
-      on             : addListener,
-      addListener    : addListener, 
-      removeListener : removeListener,
-      emit           : oboeBus.emit,                
-                
-      node           : partialComplete(addNodeOrPathListenerApi, 'node'),
-      path           : partialComplete(addNodeOrPathListenerApi, 'path'),
-      
-      done           : partialComplete(addForgettableCallback, rootNodeFinishedEvent),            
-      start          : partialComplete(addProtectedCallback, HTTP_START ),
-      
-      // fail doesn't use protectedCallback because 
-      // could lead to non-terminating loops
-      fail           : oboeBus(FAIL_EVENT).on,
-      
-      // public api calling abort fires the ABORTING event
-      abort          : oboeBus(ABORTING).emit,
-      
-      // initially return nothing for header and root
-      header         : noop,
-      root           : noop,
-      
-      source         : contentSource
-   };   
-}
-    
-
-/**
- * This file sits just behind the API which is used to attain a new
- * Oboe instance. It creates the new components that are required
- * and introduces them to each other.
- */
-
-function wire (httpMethodName, contentSource, body, headers, withCredentials){
-
-   var oboeBus = pubSub();
-   
-   // Wire the input stream in if we are given a content source.
-   // This will usually be the case. If not, the instance created
-   // will have to be passed content from an external source.
-  
-   if( contentSource ) {
-
-      streamingHttp( oboeBus,
-                     httpTransport(), 
-                     httpMethodName,
-                     contentSource,
-                     body,
-                     headers,
-                     withCredentials
-      );
-   }
-
-   clarinet(oboeBus);
-
-   ascentManager(oboeBus, incrementalContentBuilder(oboeBus));
-      
-   patternAdapter(oboeBus, jsonPathCompiler);      
-      
-   return instanceApi(oboeBus, contentSource);
-}
-
-function applyDefaults( passthrough, url, httpMethodName, body, headers, withCredentials, cached ){
-
-   headers = headers ?
-      // Shallow-clone the headers array. This allows it to be
-      // modified without side effects to the caller. We don't
-      // want to change objects that the user passes in.
-      JSON.parse(JSON.stringify(headers))
-      : {};
-
-   if( body ) {
-      if( !isString(body) ) {
-
-         // If the body is not a string, stringify it. This allows objects to
-         // be given which will be sent as JSON.
-         body = JSON.stringify(body);
-
-         // Default Content-Type to JSON unless given otherwise.
-         headers['Content-Type'] = headers['Content-Type'] || 'application/json';
-      }
-   } else {
-      body = null;
-   }
-
-   // support cache busting like jQuery.ajax({cache:false})
-   function modifiedUrl(baseUrl, cached) {
-
-      if( cached === false ) {
-
-         if( baseUrl.indexOf('?') == -1 ) {
-            baseUrl += '?';
-         } else {
-            baseUrl += '&';
-         }
-
-         baseUrl += '_=' + new Date().getTime();
-      }
-      return baseUrl;
-   }
-
-   return passthrough( httpMethodName || 'GET', modifiedUrl(url, cached), body, headers, withCredentials || false );
-}
-
-// export public API
-function oboe(arg1) {
-
-   // We use duck-typing to detect if the parameter given is a stream, with the
-   // below list of parameters.
-   // Unpipe and unshift would normally be present on a stream but this breaks
-   // compatibility with Request streams.
-   // See https://github.com/jimhigson/oboe.js/issues/65
-   
-   var nodeStreamMethodNames = list('resume', 'pause', 'pipe'),
-       isStream = partialComplete(
-                     hasAllProperties
-                  ,  nodeStreamMethodNames
-                  );
-   
-   if( arg1 ) {
-      if (isStream(arg1) || isString(arg1)) {
-
-         //  simple version for GETs. Signature is:
-         //    oboe( url )
-         //  or, under node:
-         //    oboe( readableStream )
-         return applyDefaults(
-            wire,
-            arg1 // url
-         );
-
-      } else {
-
-         // method signature is:
-         //    oboe({method:m, url:u, body:b, headers:{...}})
-
-         return applyDefaults(
-            wire,
-            arg1.url,
-            arg1.method,
-            arg1.body,
-            arg1.headers,
-            arg1.withCredentials,
-            arg1.cached
-         );
-         
-      }
-   } else {
-      // wire up a no-AJAX, no-stream Oboe. Will have to have content 
-      // fed in externally and using .emit.
-      return wire();
-   }
-}
-
-/* oboe.drop is a special value. If a node callback returns this value the
-   parsed node is deleted from the JSON
- */
-oboe.drop = function() {
-   return oboe.drop;
-};
-
-
-   if ( typeof define === "function" && define.amd ) {
-      define( "oboe", [], function () { return oboe; } );
-   } else if (typeof exports === 'object') {
-      module.exports = oboe;
-   } else {
-      window.oboe = oboe;
-   }
-})((function(){
-   // Access to the window object throws an exception in HTML5 web workers so
-   // point it to "self" if it runs in a web worker
-      try {
-         return window;
-      } catch (e) {
-         return self;
-      }
-   }()), Object, Array, Error, JSON);
diff --git a/lib/oboe-browser.min.js b/lib/oboe-browser.min.js
deleted file mode 100644
index 6d8dd72fc..000000000
--- a/lib/oboe-browser.min.js
+++ /dev/null
@@ -1 +0,0 @@
-!function(a,b,c,d,e,f){function g(a,b){return function(){return a.call(this,b.apply(this,arguments))}}function h(a){return function(b){return b[a]}}function i(a,b){return b.apply(f,a)}function j(a){var b=a.length-1,d=c.prototype.slice;if(0==b)return function(){return a.call(this,d.call(arguments))};if(1==b)return function(){return a.call(this,arguments[0],d.call(arguments,1))};var e=c(a.length);return function(){for(var c=0;b>c;c++)e[c]=arguments[c];return e[b]=d.call(arguments,b),a.apply(this,e)}}function k(a){return function(b,c){return a(c,b)}}function l(a,b){return function(c){return a(c)&&b(c)}}function m(){}function n(){return!0}function o(a){return function(){return a}}function p(a,b){return b&&b.constructor===a}function q(a){return a!==f}function r(a,c){return c instanceof b&&y(function(a){return a in c},a)}function s(a,b){return[a,b]}function t(a){return A(a.reduce(k(s),X))}function u(a){return w(function(a,b){return a.unshift(b),a},[],a)}function v(a,b){return b?s(a(Y(b)),v(a,Z(b))):X}function w(a,b,c){return c?a(w(a,b,Z(c)),Y(c)):b}function x(a,b,c){function d(a,c){return a?b(Y(a))?(c(Y(a)),Z(a)):s(Y(a),d(Z(a),c)):X}return d(a,c||m)}function y(a,b){return!b||a(Y(b))&&y(a,Z(b))}function z(a,b){a&&(Y(a).apply(null,b),z(Z(a),b))}function A(a){function b(a,c){return a?b(Z(a),s(Y(a),c)):c}return b(a,X)}function B(a,b){return b&&(a(Y(b))?Y(b):B(a,Z(b)))}function C(a){"use strict";function b(){var a=0;P.length>p&&(c("Max buffer length exceeded: textNode"),a=Math.max(a,P.length)),Q.length>p&&(c("Max buffer length exceeded: numberNode"),a=Math.max(a,Q.length)),O=p-a+Y}function c(a){P&&(m(P),n(),P=""),i=d(a+"\nLn: "+$+"\nCol: "+Z+"\nChr: "+j),o(N(f,f,i))}function e(){return T==s?(m({}),n(),void(S=!0)):((T!==t||0!==X)&&c("Unexpected end"),P&&(m(P),n(),P=""),void(S=!0))}function g(a){return"\r"==a||"\n"==a||" "==a||"	"==a}function h(a){if(!i){if(S)return c("Cannot write after close");var d=0;for(j=a[0];j&&(k=j,j=a[d++]);)switch(Y++,"\n"==j?($++,Z=0):Z++,T){case s:if("{"===j)T=u;else if("["===j)T=w;else if(!g(j))return c("Non-whitespace before {[.");continue;case z:case u:if(g(j))continue;if(T===z)U.push(A);else{if("}"===j){m({}),n(),T=U.pop()||t;continue}U.push(v)}if('"'!==j)return c('Malformed object key should start with " ');T=y;continue;case A:case v:if(g(j))continue;if(":"===j)T===v?(U.push(v),P&&(m({}),l(P),P=""),X++):P&&(l(P),P=""),T=t;else if("}"===j)P&&(m(P),n(),P=""),n(),X--,T=U.pop()||t;else{if(","!==j)return c("Bad object");T===v&&U.push(v),P&&(m(P),n(),P=""),T=z}continue;case w:case t:if(g(j))continue;if(T===w){if(m([]),X++,T=t,"]"===j){n(),X--,T=U.pop()||t;continue}U.push(x)}if('"'===j)T=y;else if("{"===j)T=u;else if("["===j)T=w;else if("t"===j)T=B;else if("f"===j)T=E;else if("n"===j)T=I;else if("-"===j)Q+=j;else if("0"===j)Q+=j,T=M;else{if(-1==="123456789".indexOf(j))return c("Bad value");Q+=j,T=M}continue;case x:if(","===j)U.push(x),P&&(m(P),n(),P=""),T=t;else{if("]"!==j){if(g(j))continue;return c("Bad array")}P&&(m(P),n(),P=""),n(),X--,T=U.pop()||t}continue;case y:var e=d-1;a:for(;;){for(;W>0;)if(V+=j,j=a.charAt(d++),4===W?(P+=String.fromCharCode(parseInt(V,16)),W=0,e=d-1):W++,!j)break a;if('"'===j&&!R){T=U.pop()||t,P+=a.substring(e,d-1),P||(m(""),n());break}if("\\"===j&&!R&&(R=!0,P+=a.substring(e,d-1),j=a.charAt(d++),!j))break;if(R){if(R=!1,"n"===j?P+="\n":"r"===j?P+="\r":"t"===j?P+="	":"f"===j?P+="\f":"b"===j?P+="\b":"u"===j?(W=1,V=""):P+=j,j=a.charAt(d++),e=d-1,j)continue;break}q.lastIndex=d;var f=q.exec(a);if(!f){d=a.length+1,P+=a.substring(e,d-1);break}if(d=f.index+1,j=a.charAt(f.index),!j){P+=a.substring(e,d-1);break}}continue;case B:if(!j)continue;if("r"!==j)return c("Invalid true started with t"+j);T=C;continue;case C:if(!j)continue;if("u"!==j)return c("Invalid true started with tr"+j);T=D;continue;case D:if(!j)continue;if("e"!==j)return c("Invalid true started with tru"+j);m(!0),n(),T=U.pop()||t;continue;case E:if(!j)continue;if("a"!==j)return c("Invalid false started with f"+j);T=F;continue;case F:if(!j)continue;if("l"!==j)return c("Invalid false started with fa"+j);T=G;continue;case G:if(!j)continue;if("s"!==j)return c("Invalid false started with fal"+j);T=H;continue;case H:if(!j)continue;if("e"!==j)return c("Invalid false started with fals"+j);m(!1),n(),T=U.pop()||t;continue;case I:if(!j)continue;if("u"!==j)return c("Invalid null started with n"+j);T=J;continue;case J:if(!j)continue;if("l"!==j)return c("Invalid null started with nu"+j);T=K;continue;case K:if(!j)continue;if("l"!==j)return c("Invalid null started with nul"+j);m(null),n(),T=U.pop()||t;continue;case L:if("."!==j)return c("Leading zero not followed by .");Q+=j,T=M;continue;case M:if(-1!=="0123456789".indexOf(j))Q+=j;else if("."===j){if(-1!==Q.indexOf("."))return c("Invalid number has two dots");Q+=j}else if("e"===j||"E"===j){if(-1!==Q.indexOf("e")||-1!==Q.indexOf("E"))return c("Invalid number has two exponential");Q+=j}else if("+"===j||"-"===j){if("e"!==k&&"E"!==k)return c("Invalid symbol in number");Q+=j}else Q&&(m(parseFloat(Q)),n(),Q=""),d--,T=U.pop()||t;continue;default:return c("Unknown state: "+T)}Y>=O&&b()}}var i,j,k,l=a(qb).emit,m=a(rb).emit,n=a(sb).emit,o=a(jb).emit,p=65536,q=/[\\"\n]/g,r=0,s=r++,t=r++,u=r++,v=r++,w=r++,x=r++,y=r++,z=r++,A=r++,B=r++,C=r++,D=r++,E=r++,F=r++,G=r++,H=r++,I=r++,J=r++,K=r++,L=r++,M=r,O=p,P="",Q="",R=!1,S=!1,T=s,U=[],V=null,W=0,X=0,Y=0,Z=0,$=1;a(nb).on(h),a(ob).on(e)}function D(a,b){"use strict";function c(a){return function(b){d=a(d,b)}}var d,e={};for(var f in b)a(f).on(c(b[f]),e);a(hb).on(function(a){var b,c=Y(d),e=ab(c),f=Z(d);f&&(b=bb(Y(f)),b[e]=a)}),a(ib).on(function(){var a,b=Y(d),c=ab(b),e=Z(d);e&&(a=bb(Y(e)),delete a[c])}),a(pb).on(function(){for(var c in b)a(c).un(e)})}function E(a){var b={};return a&&a.split("\r\n").forEach(function(a){var c=a.indexOf(": ");b[a.substring(0,c)]=a.substring(c+2)}),b}function F(a,b){function c(a){return{"http:":80,"https:":443}[a]}function d(b){return b.port||c(b.protocol||a.protocol)}return!!(b.protocol&&b.protocol!=a.protocol||b.host&&b.host!=a.host||b.host&&d(b)!=d(a))}function G(a){var b=/(\w+:)?(?:\/\/)([\w.-]+)?(?::(\d+))?\/?/,c=b.exec(a)||[];return{protocol:c[1]||"",host:c[2]||"",port:c[3]||""}}function H(){return new XMLHttpRequest}function I(b,c,d,e,g,h,i){"use strict";function j(){var a=c.responseText,b=a.substr(m);b&&k(b),m=V(a)}var k=b(nb).emit,l=b(jb).emit,m=0,n=!0;b(pb).on(function(){c.onreadystatechange=null,c.abort()}),"onprogress"in c&&(c.onprogress=j),c.onreadystatechange=function(){function a(){try{n&&b(mb).emit(c.status,E(c.getAllResponseHeaders())),n=!1}catch(a){}}switch(c.readyState){case 2:case 3:return a();case 4:a();var d=2==String(c.status)[0];d?(j(),b(ob).emit()):l(N(c.status,c.responseText))}};try{c.open(d,e,!0);for(var o in h)c.setRequestHeader(o,h[o]);F(a.location,G(e))||c.setRequestHeader("X-Requested-With","XMLHttpRequest"),c.withCredentials=i,c.send(g)}catch(p){a.setTimeout(T(l,N(f,f,p)),0)}}function J(a,b){return{key:a,node:b}}function K(a){function b(a,b){var d=bb(Y(a));return p(c,d)?f(a,V(d),b):a}function d(a,c){if(!a)return j(c),f(a,cb,c);var d=b(a,c),g=Z(d),h=ab(Y(d));return e(g,h,c),s(J(h,c),g)}function e(a,b,c){bb(Y(a))[b]=c}function f(a,b,c){a&&e(a,b,c);var d=s(J(b,c),a);return h(d),d}function g(a){return i(a),Z(a)||k(bb(Y(a)))}var h=a(fb).emit,i=a(gb).emit,j=a(lb).emit,k=a(kb).emit,l={};return l[rb]=d,l[sb]=g,l[qb]=f,l}function L(a,b,c){function d(a){return function(b){return b.id==a}}var e,f;return{on:function(c,d){var g={listener:c,id:d||c};return b&&b.emit(a,c,g.id),e=s(g,e),f=s(c,f),this},emit:function(){z(f,arguments)},un:function(b){var g;e=x(e,d(b),function(a){g=a}),g&&(f=x(f,function(a){return a==g.listener}),c&&c.emit(a,g.listener,g.id))},listeners:function(){return f},hasListener:function(a){var b=a?d(a):n;return q(B(b,e))}}}function M(){function a(a){return c[a]=L(a,d,e)}function b(b){return c[b]||a(b)}var c={},d=a("newListener"),e=a("removeListener");return["emit","on","un"].forEach(function(a){b[a]=j(function(c,d){i(d,b(c)[a])})}),b}function N(a,b,c){try{var d=e.parse(b)}catch(f){}return{statusCode:a,body:b,jsonBody:d,thrown:c}}function O(a,b){function c(a,b,c){var d=A(c);a(b,u(Z(v(ab,d))),u(v(bb,d)))}function d(b,d,e){var f=a(b).emit;d.on(function(a){var b=e(a);b!==!1&&c(f,bb(b),a)},b),a("removeListener").on(function(c){c==b&&(a(c).listeners()||d.un(b))})}var e={node:a(gb),path:a(fb)};a("newListener").on(function(a){var c=/(node|path):(.*)/.exec(a);if(c){var f=e[c[1]];f.hasListener(a)||d(a,f,b(c[2]))}})}function P(a,b){function c(b,c){return a(b).on(e(c),c),p}function d(a,b,c){c=c||b;var d=e(b);return a.on(function(){var b=!1;p.forget=function(){b=!0},i(arguments,d),delete p.forget,b&&a.un(c)},c),p}function e(b){return function(){try{return b.apply(p,arguments)}catch(c){a(jb).emit(N(f,f,c))}}}function g(b,c){return a(b+":"+c)}function h(a){return function(){var b=a.apply(this,arguments);q(b)&&(b==S.drop?t():u(b))}}function k(a,b,c){var e;e="node"==a?h(c):c,d(g(a,b),e,c)}function l(a,b){for(var c in b)k(a,c,b[c])}function n(a,b,c){return W(b)?k(a,b,c):l(a,b),p}var p,r=/^(node|path):./,s=a(kb),t=a(ib).emit,u=a(hb).emit,v=j(function(b,c){if(p[b])i(c,p[b]);else{var e=a(b),f=c[0];r.test(b)?d(e,f):e.on(f)}return p}),w=function(b,c,d){if("done"==b)s.un(c);else if("node"==b||"path"==b)a.un(b+":"+c,d);else{var e=c;a(b).un(e)}return p};return a(lb).on(function(a){p.root=o(a)}),a(mb).on(function(a,b){p.header=function(a){return a?b[a]:b}}),p={on:v,addListener:v,removeListener:w,emit:a.emit,node:T(n,"node"),path:T(n,"path"),done:T(d,s),start:T(c,mb),fail:a(jb).on,abort:a(pb).emit,header:m,root:m,source:b}}function Q(a,b,c,d,e){var f=M();return b&&I(f,H(),a,b,c,d,e),C(f),D(f,K(f)),O(f,db),P(f,b)}function R(a,b,c,d,f,g,h){function i(a,b){return b===!1&&(a+=-1==a.indexOf("?")?"?":"&",a+="_="+(new Date).getTime()),a}return f=f?e.parse(e.stringify(f)):{},d?W(d)||(d=e.stringify(d),f["Content-Type"]=f["Content-Type"]||"application/json"):d=null,a(c||"GET",i(b,h),d,f,g||!1)}function S(a){var b=$("resume","pause","pipe"),c=T(r,b);return a?c(a)||W(a)?R(Q,a):R(Q,a.url,a.method,a.body,a.headers,a.withCredentials,a.cached):Q()}var T=j(function(a,b){var c=b.length;return j(function(d){for(var e=0;e<d.length;e++)b[c+e]=d[e];return b.length=c+d.length,a.apply(this,b)})}),U=(j(function(a){function b(a,b){return[i(a,b)]}var c=t(a);return j(function(a){return w(b,a,c)[0]})}),j(function(a){return j(function(b){for(var c,d=0;d<V(a);d++)if(c=i(b,a[d]))return c})})),V=h("length"),W=T(p,String),X=null,Y=h(0),Z=h(1),$=j(t),_=function(){var a=function(a){return a.exec.bind(a)},b=j(function(b){return b.unshift(/^/),a(RegExp(b.map(h("source")).join("")))}),c=/(\$?)/,d=/([\w-_]+|\*)/,e=/()/,f=/\["([^"]+)"\]/,g=/\[(\d+|\*)\]/,i=/{([\w ]*?)}/,k=/(?:{([\w ]*?)})?/,l=b(c,d,k),m=b(c,f,k),n=b(c,g,k),o=b(c,e,i),p=b(/\.\./),q=b(/\./),r=b(c,/!/),s=b(/$/);return function(a){return a(U(l,m,n,o),p,q,r,s)}}(),ab=h("key"),bb=h("node"),cb={},db=_(function(a,b,c,e,f){function h(a,b){var c=b[z],d=c&&"*"!=c?function(a){return B(a)==c}:n;return l(d,a)}function i(a,b){var c=b[A];if(!c)return a;var d=T(r,t(c.split(/\W+/))),e=g(d,C);return l(e,a)}function j(a,b){var c=!!b[y];return c?l(a,Y):a}function k(a){function b(a){return B(a)!=cb}return a==n?n:l(b,g(a,Z))}function m(a){if(a==n)return n;var b=o(),c=a,d=k(function(a){return e(a)}),e=U(b,c,d);return e}function o(){return function(a){return B(a)==cb}}function p(a){return function(b){var c=a(b);return c===!0?Y(b):c}}function q(a,b,c){return w(function(a,b){return b(a,c)},b,a)}function s(a,b,c,d,e){var f=a(c);if(f){var g=q(b,d,f),h=c.substr(V(f[0]));return e(h,g)}}function u(a,b){return T(s,a,b)}function v(a,b){return b}function x(a,b){var c=a?x:v;return D(a,b,c)}var y=1,z=2,A=3,B=g(ab,Y),C=g(bb,Y),D=U(u(a,$(j,i,h,k)),u(b,$(m)),u(c,$()),u(e,$(j,o)),u(f,$(p)),function(a){throw d('"'+a+'" could not be tokenised')});return function(a){try{return x(a,n)}catch(b){throw d('Could not compile "'+a+'" because '+b.message)}}}),eb=1,fb=eb++,gb=eb++,hb=eb++,ib=eb++,jb="fail",kb=eb++,lb=eb++,mb="start",nb="data",ob="end",pb=eb++,qb=eb++,rb=eb++,sb=eb++;S.drop=function(){return S.drop},"function"==typeof define&&define.amd?define("oboe",[],function(){return S}):"object"==typeof exports?module.exports=S:a.oboe=S}(function(){try{return window}catch(a){return self}}(),Object,Array,Error,JSON);
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 000000000..3602f9178
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,230 @@
+{
+  "name": "rn-fetch-blob",
+  "version": "0.13.0-beta.1",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "rn-fetch-blob",
+      "version": "0.13.0-beta.1",
+      "license": "MIT",
+      "dependencies": {
+        "base-64": "0.1.0",
+        "glob": "7.0.6",
+        "lodash": "^4.17.21",
+        "oboe": "^2.1.5"
+      }
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+    },
+    "node_modules/base-64": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz",
+      "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA=="
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
+    },
+    "node_modules/fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
+    },
+    "node_modules/glob": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz",
+      "integrity": "sha512-f8c0rE8JiCxpa52kWPAOa3ZaYEnzofDzCQLCn3Vdk0Z5OVLq3BsRFJI4S4ykpeVW6QMGBUkMeUpoEgWnMTnw5Q==",
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.2",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/http-https": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz",
+      "integrity": "sha512-o0PWwVCSp3O0wS6FvNr6xfBCHgt0m1tvPLFOCc2iFDKTRAXhB7m8klDf7ErowFH8POa6dVdGatKU5I1YYwzUyg=="
+    },
+    "node_modules/inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+      "dependencies": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    },
+    "node_modules/lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+    },
+    "node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/oboe": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.5.tgz",
+      "integrity": "sha512-zRFWiF+FoicxEs3jNI/WYUrVEgA7DeET/InK0XQuudGHRg8iIob3cNPrJTKaz4004uaA9Pbe+Dwa8iluhjLZWA==",
+      "dependencies": {
+        "http-https": "^1.0.0"
+      }
+    },
+    "node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+    }
+  },
+  "dependencies": {
+    "balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+    },
+    "base-64": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz",
+      "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA=="
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "requires": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
+    },
+    "glob": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz",
+      "integrity": "sha512-f8c0rE8JiCxpa52kWPAOa3ZaYEnzofDzCQLCn3Vdk0Z5OVLq3BsRFJI4S4ykpeVW6QMGBUkMeUpoEgWnMTnw5Q==",
+      "requires": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.2",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      }
+    },
+    "http-https": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz",
+      "integrity": "sha512-o0PWwVCSp3O0wS6FvNr6xfBCHgt0m1tvPLFOCc2iFDKTRAXhB7m8klDf7ErowFH8POa6dVdGatKU5I1YYwzUyg=="
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+      "requires": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    },
+    "lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+    },
+    "minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "requires": {
+        "brace-expansion": "^1.1.7"
+      }
+    },
+    "oboe": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.5.tgz",
+      "integrity": "sha512-zRFWiF+FoicxEs3jNI/WYUrVEgA7DeET/InK0XQuudGHRg8iIob3cNPrJTKaz4004uaA9Pbe+Dwa8iluhjLZWA==",
+      "requires": {
+        "http-https": "^1.0.0"
+      }
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+    }
+  }
+}
diff --git a/package.json b/package.json
index 7de11230e..b7a14ca03 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,8 @@
   "dependencies": {
     "base-64": "0.1.0",
     "glob": "7.0.6",
-    "lodash": "4.17.15"
+    "lodash": "^4.17.21",
+    "oboe": "^2.1.5"
   },
   "keywords": [
     "react-native",