src/com/cognitect/transit.js
// Copyright 2014 Cognitect. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
"use strict";
goog.provide("com.cognitect.transit");
goog.require("com.cognitect.transit.impl.reader");
goog.require("com.cognitect.transit.impl.writer");
goog.require("com.cognitect.transit.types");
goog.require("com.cognitect.transit.eq");
goog.require("com.cognitect.transit.impl.decoder");
goog.require("com.cognitect.transit.caching");
/** @define {boolean} */
var TRANSIT_DEV = true;
/** @define {boolean} */
var TRANSIT_NODE_TARGET = false;
/** @define {boolean} */
var TRANSIT_BROWSER_TARGET = false;
/** @define {boolean} */
var TRANSIT_BROWSER_AMD_TARGET = false;
goog.scope(function() {
/**
* @class transit
*/
var transit = com.cognitect.transit;
var reader = com.cognitect.transit.impl.reader,
writer = com.cognitect.transit.impl.writer,
decoder = com.cognitect.transit.impl.decoder,
types = com.cognitect.transit.types,
eq = com.cognitect.transit.eq,
caching = com.cognitect.transit.caching;
/**
* @typedef {Map|com.cognitect.transit.types.TransitArrayMap|com.cognitect.transit.types.TransitMap}
*/
transit.MapLike;
/**
* @typedef {Set|com.cognitect.transit.types.TransitSet}
*/
transit.SetLike;
/**
* Create a transit reader instance.
* @method transit.reader
* @param {string=} type type of reader to construct.
* Default to "json". For verbose mode supply "json-verbose".
* @param {Object=} opts reader options. A JavaScript object to
* customize the writer Valid entries include "defaultHandler",
* and "handler". "defaultHandler" should be JavaScript function
* taking two arguments, the first is the tag, the second the
* value. "handlers" should be an object of tags to handle. The
* values are functions that will receive the value of matched
* tag. "preferBuffers" may be supplied to customize binary
* decoding. If available binary data will read as Node.js Buffers,
* If Buffer is not available or "prefersBuffers" is set to false
* data will be read as Uint8Array. If neither Buffer nor Uint8Array is
* available - defaults to tagged value that simply wraps the
* base64 encoded string.
* @return {com.cognitect.transit.impl.reader.Reader} A transit reader.
* @example
* var r = transit.reader("json", {
* handlers: {
* "point": function(v) { return new Point(v[0], v[1]); }
* }
* });
*/
transit.reader = function(type, opts) {
if(type === "json" || type === "json-verbose" || type == null) {
type = "json";
var unmarshaller = new reader.JSONUnmarshaller(opts);
return new reader.Reader(unmarshaller, opts);
} else {
throw new Error("Cannot create reader of type " + type);
}
};
/**
* Create a transit writer instance.
* @method transit.writer
* @param {string=} type type of writer to construct.
* Defaults to "json". For verbose mode supply "json-verbose".
* @param {Object=} opts writer options. A JavaScript object to
* customize the writer. "handlers" options, a transit.map of
* JavaScript constructor and transit writer handler instance
* entries. "handlerForForeign" option, for dealing with values
* from other JavaScript contexts. This function will be passed
* the unmatchable value and the installed handlers. The function
* should return the correct handler. Note if this function is
* provided, special handling for Objects will also be
* auto-installed to catch plain Objects from the foreign context.
* @return {com.cognitect.transit.impl.writer.Writer} A transit writer.
* @example
* var r = transit.writer("json", {
* handlers: transit.map([
* Point, PointHandler
* ])
* });
*/
transit.writer = function(type, opts) {
if(type === "json" || type === "json-verbose" || type == null) {
if(type === "json-verbose") {
if(opts == null) opts = {};
opts["verbose"] = true;
}
type = "json";
var marshaller = new writer.JSONMarshaller(opts);
return new writer.Writer(marshaller, opts);
} else {
var err = new Error("Type must be \"json\"");
err.data = {type: type};
throw err;
}
};
/**
* Create a transit writer handler.
* @method transit.makeWriteHandler
* @param {Object} obj An object containing 3 functions, tag, rep and stringRep.
* "tag" should return a string representing the tag to be written on the wire.
* "rep" should return the representation on the wire. "stringRep" is should return
* the string representation of the value. Optional "getVerboseHandler" should return a
* handler for writing verbose output.
* @return {Object} A transit write handler.
* @example
* var PointHandler = transit.makeWriteHandler({
* tag: function(p) { return "point"; },
* rep: function(p) { return [p.x, p.y]; },
* stringRep: function(p) { return null; }
* });
*/
transit.makeWriteHandler = function(obj) {
var Handler = function(){};
Handler.prototype.tag = obj["tag"];
Handler.prototype.rep = obj["rep"];
Handler.prototype.stringRep = obj["stringRep"];
Handler.prototype.getVerboseHandler = obj["getVerboseHandler"];
return new Handler();
};
transit.makeBuilder = function(obj) {
var Builder = function(){};
Builder.prototype.init = obj["init"];
Builder.prototype.add = obj["add"];
Builder.prototype.finalize = obj["finalize"];
Builder.prototype.fromArray = obj["fromArray"];
return new Builder();
};
/**
* Create a transit date.
* @method transit.date
* @param {number|string} A number or string representing milliseconds since epoch.
* @return {Date} A JavaScript Date.
*/
transit.date = types.date;
/**
* Create an integer. If given a transit integer or a JavaScript
* number will simply return that value. Given a string will
* return a JavaScript number if the string represents an integer
* value in the 53bit range and a transit integer otherwise.
* @method transit.integer
* @param {number|string} s A value representing an integer.
* @return {number|goog.math.Long} A JavaScript number or transit integer.
*/
transit.integer = types.intValue;
/**
* Test if an object is a transit integer. Will return true if argument
* is a 64 bit integer or a JavaScript number that has an interpretation as
* an integer value, i.e. parseFloat(n) === parseInt(n)
* @method transit.isInteger
* @param {Object} x Any JavaScript value.
* @return {boolean} true if the value is a transit integer, false otherwise.
*/
transit.isInteger = types.isInteger;
/**
* Create transit UUID from a string
* @method transit.uuid
* @param {string}
* @return {com.cognitect.transit.types.UUID} A transit UUID.
*/
transit.uuid = types.uuid;
/**
* Test if an object is a transit UUID.
* @method transit.isUUID
* @param {Object} x Any JavaScript value.
* @return {boolean} true if the vlaue is a transit UUID instance, false otherwise.
*/
transit.isUUID = types.isUUID;
/**
* Create a transit big integer.
* @method transit.bigInt
* @param {string} s A string representing an arbitrary size integer value.
* @return {com.cognitect.transit.types.TaggedValue} A transit big integer.
*/
transit.bigInt = types.bigInteger;
/**
* Test if an object is a transit big integer.
* @method transit.isBigInt
* @param {Object} x Any JavaScript value.
* @return {boolean} true if x is a transit big integer, false otherwise.
*/
transit.isBigInt = types.isBigInteger;
/**
* Create a transit big decimal.
* @method transit.bigDec
* @param {string} s A string representing an arbitrary precisions decimal value.
* @return {com.cognitect.transit.types.TaggedValue} A transit big decimal.
*/
transit.bigDec = types.bigDecimalValue;
/**
* Test if an object is a transit big decimal.
* @method transit.isBigDec
* @param {Object} x Any JavaScript value.
* @return {boolean} true if x is a transit big decimal, false otherwise.
*/
transit.isBigDec = types.isBigDecimal;
/**
* Create transit keyword.
* @method transit.keyword
* @param {string} name A string.
* @return {com.cognitect.transit.types.Keyword} A transit keyword.
* @example
* transit.keyword("foo");
*/
transit.keyword = types.keyword;
/**
* Test if an object is a transit keyword.
* @method transit.isKeyword
* @param {Object} x Any JavaScript value.
* @return {boolean} true if x is a transit keyword, false otherwise.
*/
transit.isKeyword = types.isKeyword;
/**
* Create a transit symbol.
* @method transit.symbol
* @param {string} name A string.
* @return {com.cognitect.transit.types.Symbol} A transit symbol instance.
* @example
* transit.symbol("foo");
*/
transit.symbol = types.symbol;
/**
* Test if an object is a transit symbol
* @method transit.isSymbol
* @param {Object} x Any JavaScript value.
* @return {boolean} true if x is a transit symbol, false otherwise.
*/
transit.isSymbol = types.isSymbol;
/**
* Create transit binary blob.
* @method transit.binary
* @param {string} s A base64 encoded string.
* @return {com.cognitect.transit.types.TaggedValue|Uint8Array} A transit binary blob instance.
*/
transit.binary = types.binary;
/**
* Test if an object is a transit binary blob.
* @method transit.isBinary
* @param {Object} x Any JavaScript value.
*/
transit.isBinary = types.isBinary;
/**
* Create a transit URI.
* @method transit.uri
* @param {string} A string representing a valid URI.
* @return {com.cognitect.transit.types.TaggedValue} A transit URI.
*/
transit.uri = types.uri;
/**
* Test if an object is a transit URI.
* @method transit.isURI
* @param {Object} Any JavaScript value.
* @return {Boolean} true if x is a transit symbol, false otherwise.
*/
transit.isURI = types.isURI;
/**
* Create a transit hash map. Transit maps satisfy the current version
* of the ECMAScript 6 Map specification.
* @method transit.map
* @param {array} xs A JavaScript array of alternating key value pairs.
* @return {com.cognitect.transit.types.TransitArrayMap|com.cognitect.transit.types.TransitMap} A transit map.
* @example
* transit.map([new Date(), "foo", [1,2], 3]);
*/
transit.map = types.map;
/**
* Test if an object is a transit map.
* @method transit.isMap
* @param {Object} x Any JavaScript value.
* @return {boolean} true if x is a transit map, false otherwise.
*/
transit.isMap = types.isMap;
/**
* Create a transit set. Transit sets satisfy the current version of the
* of the ECMAScript 6 Set specification.
* @method transit.set
* @param {array} xs A JavaScript array of values.
* @return {com.cognitect.transit.types.TransitSet} A transit set.
* @example
* transit.set(["foo", [1,2], 3, {bar: "baz"}]);
*/
transit.set = types.set;
/**
* Test if an object is a transit set.
* @method transit.isSet
* @param {Object} x Any JavaScript value.
* @return {boolean} true if x is a transit set, false otherwise.
*/
transit.isSet = types.isSet;
/**
* Create a transit list.
* @method transit.list
* @param {Array} A JavaScript array.
* @return {com.cognitect.transit.types.TaggedValue} A transit list.
*/
transit.list = types.list;
/**
* Test if an object is a transit list.
* @method transit.isList
* @param {Object} x Any JavaScript value.
* @return {boolean} true if x is a transit list, false otherwise.
*/
transit.isList = types.isList;
/**
* Create a transit quoted value.
* @method transit.quoted
* @param {Object} x Any JavaScript value.
* @return {com.cognitect.transit.types.TaggedValue} A transit quoted value.
*/
transit.quoted = types.quoted;
/**
* Test if an object is a transit quoted value.
* @method transit.isQuoted
* @param {Object} x Any JavaScript value.
* @return {boolean} true if x is a transit value, false otherwise.
*/
transit.isQuoted = types.isQuoted;
/**
* Create a transit tagged value.
* @method transit.tagged
* @param {String} tag A tag.
* @param {Object} value A JavaScrpt array, object, or string.
* @return {com.cognitect.transit.types.TaggedValue} A transit tagged value.
* @example
* transit.tagged("point", new Point(1,2));
*/
transit.tagged = types.taggedValue;
/**
* Test if an object is a transit tagged value.
* @method transit.isTaggedValue
* @param {Object} x Any JavaScript value.
* @return {boolean} true if x is a transit value, false otherwise.
*/
transit.isTaggedValue = types.isTaggedValue;
/**
* Create a transit link.
* @method transit.link
* @param {com.cognitect.transit.types.TransitArrayMap|com.cognitect.transit.types.TransitMap} A transit map which must contain at a
* minimum the following keys: href, rel. It may optionally include
* name, render, and prompt. href must be a transit.uri, all other
* values are strings, and render must be either "image" or "link".
* @return {Object} A transit link.
*/
transit.link = types.link;
/**
* Test if an object is a transit link.
* @method transit.isLink
* @param {Object} x Any JavaScript object.
* @return {boolean} true if x is a transit link, false otherwise.
*/
transit.isLink = types.isLink;
/**
* Compute the hashCode for any JavaScript object that has been
* extend to transit's equality and hashing protocol. JavaScript
* primitives and transit value are already extended to this protocol.
* Custom types may be extended to the protocol via transit.extenToEQ.
* @method transit.hash
* @param {Object} x Any JavaScript object that has been extended to
* transit's equality and hashing protocol.
* @return {number} Returns JavaScript number - semantically a 32bit integer.
*/
transit.hash = eq.hashCode;
/**
* Compute the hashCode for JavaScript map-like types - either a JavaScript
* object or a JavaScript object that implements ES6 Map forEach.
* @method transit.hashMapLike
* @param {Object} x A plain JavaScript Object or Object that implements
* ES6 Map forEach.
* @return {number} Returns JavaScript number - semantically a 32bit integer.
*/
transit.hashMapLike = eq.hashMapLike;
/**
* Compute the hashCode for JavaScript array-like types - either a JavaScript
* array or a JavaScript object that implements Array forEach.
* @method transit.hashArrayLike
* @param {Object} x A JavaScript Array or Object that implements
* Array forEach.
* @return {number} Returns JavaScript number - semantically a 32bit integer.
*/
transit.hashMapLike = eq.hashArrayLike;
/**
* Test whether two JavaScript objects represent equal values. The
* objects to be tested should be extended to transit's equality
* and hasing protocol. JavaScript natives and transit value have
* already been extended to the protocol, including objects and
* arrays. Also transit maps and JavaScript objects may be
* compared for equality. Custom types may be extended via
* transit.extendToEQ.
* @param {Object} x A JavaScript object
* @param {Object} y A JavaScript object
* @return {Boolean} true if the x and y are equal, false otherwise.
*/
transit.equals = eq.equals;
/**
* Extend an object to hashing and equality required by
* transit maps and sets. Only required for custom
* types, JavaScript primitive types and transit
* types are handled.
* @method transit.extendToEQ
* @param {Object} x A JavaScript object, will be mutated.
* @return {Object} x
* @example
* transit.extendToEq(Point.protototype, {
* hashCode: function() {
* var bits = (this.x | 0) ^ ((this.y | 0) * 31);
* return bits ^ (bits >>> 32);
* },
* equals: function(other) {
* return this.x == other.x && this.y == other.y;
* }
* });
*/
transit.extendToEQ = eq.extendToEQ;
/**
* Convert a transit map instance into a JavaScript Object.
* Throws if the map has keys which have no string representation.
* @method transit.mapToObject
* @param {com.cognitect.transit.types.TransitArrayMap|com.cognitect.transit.types.TransitMap} m a transit map
* @return {Object} a JavaScript Object
*/
transit.mapToObject = function(m) {
var ret = {};
m.forEach(function(v, k) {
if(typeof k !== "string") {
throw Error("Cannot convert map with non-string keys");
} else {
ret[k] = v;
}
})
return ret;
};
/**
* Convert a POJO into a transit map.
* @method transit.objectToMap
* @param {Object} a JavaScript Object
* @return {com.cognitect.transit.types.TransitArrayMap|com.cognitect.transit.types.TransitMap} a transit map
*/
transit.objectToMap = function(obj) {
var ret = transit.map();
for(var p in obj) {
if(obj.hasOwnProperty(p)) {
ret.set(p, obj[p]);
}
}
return ret;
};
/**
* Construct a Transit JSON decoder.
* @method transit.decoder
* @param {Object} options to the decoder. Can include map of
* handlers.
* @return {com.cognitect.transit.impl.decoder.Decoder} a Transit JSON decoder
* @example
* var decoder = transit.decoder();
* var x = decoder.decode(json, transit.readCache());
*/
transit.decoder = decoder.decoder;
/**
* Construct a Transit read cache
* @method transit.readCache
* @return {com.cognitect.transit.caching.ReadCache} a Transit read cache
*/
transit.readCache = caching.readCache;
/**
* Construct a Transit write cache
* @method transit.writeCache
* @return {com.cognitect.transit.caching.WriteCache} a Transit write cache
*/
transit.writeCache = caching.writeCache;
transit.UUIDfromString = types.UUIDfromString;
transit.randomUUID = types.randomUUID;
transit.stringableKeys = writer.stringableKeys;
if(TRANSIT_BROWSER_TARGET) {
goog.exportSymbol("transit.reader", transit.reader);
goog.exportSymbol("transit.writer", transit.writer);
goog.exportSymbol("transit.makeBuilder", transit.makeBuilder);
goog.exportSymbol("transit.makeWriteHandler", transit.makeWriteHandler);
goog.exportSymbol("transit.date", types.date);
goog.exportSymbol("transit.integer", types.intValue);
goog.exportSymbol("transit.isInteger", types.isInteger);
goog.exportSymbol("transit.uuid", types.uuid);
goog.exportSymbol("transit.isUUID", types.isUUID);
goog.exportSymbol("transit.bigInt", types.bigInteger);
goog.exportSymbol("transit.isBigInt", types.isBigInteger);
goog.exportSymbol("transit.bigDec", types.bigDecimalValue);
goog.exportSymbol("transit.isBigDec", types.isBigDecimal);
goog.exportSymbol("transit.keyword", types.keyword);
goog.exportSymbol("transit.isKeyword", types.isKeyword);
goog.exportSymbol("transit.symbol", types.symbol);
goog.exportSymbol("transit.isSymbol", types.isSymbol);
goog.exportSymbol("transit.binary", types.binary);
goog.exportSymbol("transit.isBinary", types.isBinary);
goog.exportSymbol("transit.uri", types.uri);
goog.exportSymbol("transit.isURI", types.isURI);
goog.exportSymbol("transit.map", types.map);
goog.exportSymbol("transit.isMap", types.isMap);
goog.exportSymbol("transit.set", types.set);
goog.exportSymbol("transit.isSet", types.isSet);
goog.exportSymbol("transit.list", types.list);
goog.exportSymbol("transit.isList", types.isList);
goog.exportSymbol("transit.quoted", types.quoted);
goog.exportSymbol("transit.isQuoted", types.isQuoted);
goog.exportSymbol("transit.tagged", types.taggedValue);
goog.exportSymbol("transit.isTaggedValue", types.isTaggedValue);
goog.exportSymbol("transit.link", types.link);
goog.exportSymbol("transit.isLink", types.isLink);
goog.exportSymbol("transit.hash", eq.hashCode);
goog.exportSymbol("transit.hashMapLike", eq.hashMapLike);
goog.exportSymbol("transit.hashArrayLike", eq.hashArrayLike);
goog.exportSymbol("transit.equals", eq.equals);
goog.exportSymbol("transit.extendToEQ", eq.extendToEQ);
goog.exportSymbol("transit.mapToObject", transit.mapToObject);
goog.exportSymbol("transit.objectToMap", transit.objectToMap);
goog.exportSymbol("transit.decoder", decoder.decoder);
goog.exportSymbol("transit.UUIDfromString", types.UUIDfromString);
goog.exportSymbol("transit.randomUUID", types.randomUUID);
goog.exportSymbol("transit.stringableKeys", writer.stringableKeys);
goog.exportSymbol("transit.readCache", caching.readCache);
goog.exportSymbol("transit.writeCache", caching.writeCache);
}
if(TRANSIT_NODE_TARGET) {
module.exports = {
reader: transit.reader,
writer: transit.writer,
makeBuilder: transit.makeBuilder,
makeWriteHandler: transit.makeWriteHandler,
date: types.date,
integer: types.intValue,
isInteger: types.isInteger,
uuid: types.uuid,
isUUID: types.isUUID,
bigInt: types.bigInteger,
isBigInt: types.isBigInteger,
bigDec: types.bigDecimalValue,
isBigDec: types.isBigDecimal,
keyword: types.keyword,
isKeyword: types.isKeyword,
symbol: types.symbol,
isSymbol: types.isSymbol,
binary: types.binary,
isBinary: types.isBinary,
uri: types.uri,
isURI: types.isURI,
map: types.map,
isMap: types.isMap,
set: types.set,
isSet: types.isSet,
list: types.list,
isList: types.isList,
quoted: types.quoted,
isQuoted: types.isQuoted,
tagged: types.taggedValue,
isTaggedValue: types.isTaggedValue,
link: types.link,
isLink: types.isLink,
hash: eq.hashCode,
hashArrayLike: eq.hashArrayLike,
hashMapLike: eq.hashMapLike,
equals: eq.equals,
extendToEQ: eq.extendToEQ,
mapToObject: transit.mapToObject,
objectToMap: transit.objectToMap,
decoder: decoder.decoder,
UUIDfromString: types.UUIDfromString,
randomUUID: types.randomUUID,
stringableKeys: writer.stringableKeys,
readCache: caching.readCache,
writeCache: caching.writeCache
};
}
});