@gandlaf21/bc-ur
Version:
A JS implementation of the Uniform Resources (UR) specification from Blockchain Commons
133 lines (132 loc) • 6.49 kB
JavaScript
;
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
Object.defineProperty(exports, "__esModule", { value: true });
var buffer_1 = require("buffer");
var utils_1 = require("./utils");
var bytewords = 'ableacidalsoapexaquaarchatomauntawayaxisbackbaldbarnbeltbetabiasbluebodybragbrewbulbbuzzcalmcashcatschefcityclawcodecolacookcostcruxcurlcuspcyandarkdatadaysdelidicedietdoordowndrawdropdrumdulldutyeacheasyechoedgeepicevenexamexiteyesfactfairfernfigsfilmfishfizzflapflewfluxfoxyfreefrogfuelfundgalagamegeargemsgiftgirlglowgoodgraygrimgurugushgyrohalfhanghardhawkheathelphighhillholyhopehornhutsicedideaidleinchinkyintoirisironitemjadejazzjoinjoltjowljudojugsjumpjunkjurykeepkenokeptkeyskickkilnkingkitekiwiknoblamblavalazyleaflegsliarlimplionlistlogoloudloveluaulucklungmainmanymathmazememomenumeowmildmintmissmonknailnavyneednewsnextnoonnotenumbobeyoboeomitonyxopenovalowlspaidpartpeckplaypluspoempoolposepuffpumapurrquadquizraceramprealredorichroadrockroofrubyruinrunsrustsafesagascarsetssilkskewslotsoapsolosongstubsurfswantacotasktaxitenttiedtimetinytoiltombtoystriptunatwinuglyundouniturgeuservastveryvetovialvibeviewvisavoidvowswallwandwarmwaspwavewaxywebswhatwhenwhizwolfworkyankyawnyellyogayurtzapszerozestzinczonezoom';
var bytewordsLookUpTable = [];
var BYTEWORDS_NUM = 256;
var BYTEWORD_LENGTH = 4;
var MINIMAL_BYTEWORD_LENGTH = 2;
var STYLES;
(function (STYLES) {
STYLES["STANDARD"] = "standard";
STYLES["URI"] = "uri";
STYLES["MINIMAL"] = "minimal";
})(STYLES || (STYLES = {}));
var getWord = function (index) {
return bytewords.slice(index * BYTEWORD_LENGTH, (index * BYTEWORD_LENGTH) + BYTEWORD_LENGTH);
};
var getMinimalWord = function (index) {
var byteword = getWord(index);
return "" + byteword[0] + byteword[BYTEWORD_LENGTH - 1];
};
var addCRC = function (string) {
var crc = utils_1.getCRCHex(buffer_1.Buffer.from(string, 'hex'));
return "" + string + crc;
};
var encodeWithSeparator = function (word, separator) {
var crcAppendedWord = addCRC(word);
var crcWordBuff = buffer_1.Buffer.from(crcAppendedWord, 'hex');
var result = crcWordBuff.reduce(function (result, w) { return (__spreadArrays(result, [getWord(w)])); }, []);
return result.join(separator);
};
var encodeMinimal = function (word) {
var crcAppendedWord = addCRC(word);
var crcWordBuff = buffer_1.Buffer.from(crcAppendedWord, 'hex');
var result = crcWordBuff.reduce(function (result, w) { return result + getMinimalWord(w); }, '');
return result;
};
var decodeWord = function (word, wordLength) {
if (word.length !== wordLength) {
throw new Error("'Invalid Bytewords: word.length does not match wordLength provided'");
}
var dim = 26;
// Since the first and last letters of each Byteword are unique,
// we can use them as indexes into a two-dimensional lookup table.
// This table is generated lazily.
if (bytewordsLookUpTable.length === 0) {
var array_len = dim * dim;
bytewordsLookUpTable = __spreadArrays(new Array(array_len)).map(function () { return -1; });
for (var i = 0; i < BYTEWORDS_NUM; i++) {
var byteword = getWord(i);
var x_1 = byteword[0].charCodeAt(0) - 'a'.charCodeAt(0);
var y_1 = byteword[3].charCodeAt(0) - 'a'.charCodeAt(0);
var offset_1 = y_1 * dim + x_1;
bytewordsLookUpTable[offset_1] = i;
}
}
// If the coordinates generated by the first and last letters are out of bounds,
// or the lookup table contains -1 at the coordinates, then the word is not valid.
var x = (word[0]).toLowerCase().charCodeAt(0) - 'a'.charCodeAt(0);
var y = (word[wordLength == 4 ? 3 : 1]).toLowerCase().charCodeAt(0) - 'a'.charCodeAt(0);
if (!(0 <= x && x < dim && 0 <= y && y < dim)) {
throw new Error("Invalid Bytewords: invalid word");
}
var offset = y * dim + x;
var value = bytewordsLookUpTable[offset];
if (value === -1) {
throw new Error("Invalid Bytewords: value not in lookup table");
}
// If we're decoding a full four-letter word, verify that the two middle letters are correct.
if (wordLength == BYTEWORD_LENGTH) {
var byteword = getWord(value);
var c1 = word[1].toLowerCase();
var c2 = word[2].toLowerCase();
if (!(c1 === byteword[1] && c2 === byteword[2])) {
throw new Error("Invalid Bytewords: invalid middle letters of word");
}
}
// Successful decode.
return buffer_1.Buffer.from([value]).toString('hex');
};
var _decode = function (string, separator, wordLength) {
var words = wordLength == BYTEWORD_LENGTH ? string.split(separator) : utils_1.partition(string, 2);
var decodedString = words.map(function (word) { return decodeWord(word, wordLength); }).join('');
if (decodedString.length < 5) {
throw new Error("Invalid Bytewords: invalid decoded string length");
}
var _a = utils_1.split(buffer_1.Buffer.from(decodedString, 'hex'), 4), body = _a[0], bodyChecksum = _a[1];
var checksum = utils_1.getCRCHex(body); // convert to hex
if (checksum !== bodyChecksum.toString('hex')) {
throw new Error("Invalid Checksum");
}
return body.toString('hex');
};
var decode = function (string, style) {
if (style === void 0) { style = STYLES.MINIMAL; }
switch (style) {
case STYLES.STANDARD:
return _decode(string, ' ', BYTEWORD_LENGTH);
case STYLES.URI:
return _decode(string, '-', BYTEWORD_LENGTH);
case STYLES.MINIMAL:
return _decode(string, '', MINIMAL_BYTEWORD_LENGTH);
default:
throw new Error("Invalid style " + style);
}
};
var encode = function (string, style) {
if (style === void 0) { style = STYLES.MINIMAL; }
switch (style) {
case STYLES.STANDARD:
return encodeWithSeparator(string, ' ');
case STYLES.URI:
return encodeWithSeparator(string, '-');
case STYLES.MINIMAL:
return encodeMinimal(string);
default:
throw new Error("Invalid style " + style);
}
};
exports.default = {
decode: decode,
encode: encode,
STYLES: STYLES
};