|
@@ -0,0 +1,439 @@
|
|
|
|
+(function (global, factory) {
|
|
|
|
+ if (typeof define === "function" && define.amd) {
|
|
|
|
+ define("Hashids", ["exports"], factory);
|
|
|
|
+ } else if (typeof exports !== "undefined") {
|
|
|
|
+ factory(exports);
|
|
|
|
+ } else {
|
|
|
|
+ var mod = {
|
|
|
|
+ exports: {}
|
|
|
|
+ };
|
|
|
|
+ factory(mod.exports);
|
|
|
|
+ global.Hashids = mod.exports;
|
|
|
|
+ }
|
|
|
|
+})(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports) {
|
|
|
|
+ "use strict";
|
|
|
|
+
|
|
|
|
+ _exports.__esModule = true;
|
|
|
|
+ _exports.onlyChars = _exports.withoutChars = _exports.keepUnique = _exports.default = void 0;
|
|
|
|
+
|
|
|
|
+ function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }
|
|
|
|
+
|
|
|
|
+ function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }
|
|
|
|
+
|
|
|
|
+ function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }
|
|
|
|
+
|
|
|
|
+ function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }
|
|
|
|
+
|
|
|
|
+ var Hashids =
|
|
|
|
+ /*#__PURE__*/
|
|
|
|
+ function () {
|
|
|
|
+ function Hashids(salt, minLength, alphabet, seps) {
|
|
|
|
+ if (salt === void 0) {
|
|
|
|
+ salt = '';
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (minLength === void 0) {
|
|
|
|
+ minLength = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (alphabet === void 0) {
|
|
|
|
+ alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (seps === void 0) {
|
|
|
|
+ seps = 'cfhistuCFHISTU';
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.minLength = minLength;
|
|
|
|
+
|
|
|
|
+ if (typeof minLength !== 'number') {
|
|
|
|
+ throw new TypeError("Hashids: Provided 'minLength' has to be a number (is " + typeof minLength + ")");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (typeof salt !== 'string') {
|
|
|
|
+ throw new TypeError("Hashids: Provided 'salt' has to be a string (is " + typeof salt + ")");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (typeof alphabet !== 'string') {
|
|
|
|
+ throw new TypeError("Hashids: Provided alphabet has to be a string (is " + typeof alphabet + ")");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var saltChars = Array.from(salt);
|
|
|
|
+ var alphabetChars = Array.from(alphabet);
|
|
|
|
+ var sepsChars = Array.from(seps);
|
|
|
|
+ this.salt = saltChars;
|
|
|
|
+ var uniqueAlphabet = keepUnique(alphabetChars);
|
|
|
|
+
|
|
|
|
+ if (uniqueAlphabet.length < minAlphabetLength) {
|
|
|
|
+ throw new Error("Hashids: alphabet must contain at least " + minAlphabetLength + " unique characters, provided: " + uniqueAlphabet);
|
|
|
|
+ }
|
|
|
|
+ /** `alphabet` should not contains `seps` */
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ this.alphabet = withoutChars(uniqueAlphabet, sepsChars);
|
|
|
|
+ /** `seps` should contain only characters present in `alphabet` */
|
|
|
|
+
|
|
|
|
+ var filteredSeps = onlyChars(sepsChars, uniqueAlphabet);
|
|
|
|
+ this.seps = shuffle(filteredSeps, saltChars);
|
|
|
|
+ var sepsLength;
|
|
|
|
+ var diff;
|
|
|
|
+
|
|
|
|
+ if (this.seps.length === 0 || this.alphabet.length / this.seps.length > sepDiv) {
|
|
|
|
+ sepsLength = Math.ceil(this.alphabet.length / sepDiv);
|
|
|
|
+
|
|
|
|
+ if (sepsLength > this.seps.length) {
|
|
|
|
+ var _this$seps;
|
|
|
|
+
|
|
|
|
+ diff = sepsLength - this.seps.length;
|
|
|
|
+
|
|
|
|
+ (_this$seps = this.seps).push.apply(_this$seps, _toConsumableArray(this.alphabet.slice(0, diff)));
|
|
|
|
+
|
|
|
|
+ this.alphabet = this.alphabet.slice(diff);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.alphabet = shuffle(this.alphabet, saltChars);
|
|
|
|
+ var guardCount = Math.ceil(this.alphabet.length / guardDiv);
|
|
|
|
+
|
|
|
|
+ if (this.alphabet.length < 3) {
|
|
|
|
+ this.guards = this.seps.slice(0, guardCount);
|
|
|
|
+ this.seps = this.seps.slice(guardCount);
|
|
|
|
+ } else {
|
|
|
|
+ this.guards = this.alphabet.slice(0, guardCount);
|
|
|
|
+ this.alphabet = this.alphabet.slice(guardCount);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.guardsRegExp = makeAnyOfCharsRegExp(this.guards);
|
|
|
|
+ this.sepsRegExp = makeAnyOfCharsRegExp(this.seps);
|
|
|
|
+ this.allowedCharsRegExp = makeAtLeastSomeCharRegExp([].concat(_toConsumableArray(this.alphabet), _toConsumableArray(this.guards), _toConsumableArray(this.seps)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var _proto = Hashids.prototype;
|
|
|
|
+
|
|
|
|
+ _proto.encode = function encode(first) {
|
|
|
|
+ for (var _len = arguments.length, numbers = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
|
|
+ numbers[_key - 1] = arguments[_key];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var ret = '';
|
|
|
|
+
|
|
|
|
+ if (Array.isArray(first)) {
|
|
|
|
+ numbers = first;
|
|
|
|
+ } else {
|
|
|
|
+ // eslint-disable-next-line eqeqeq
|
|
|
|
+ numbers = [].concat(_toConsumableArray(first != null ? [first] : []), _toConsumableArray(numbers));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!numbers.length) {
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!numbers.every(isIntegerNumber)) {
|
|
|
|
+ numbers = numbers.map(function (n) {
|
|
|
|
+ return typeof n === 'bigint' || typeof n === 'number' ? n : safeParseInt10(String(n));
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!numbers.every(isPositiveAndFinite)) {
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return this._encode(numbers).join('');
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ _proto.decode = function decode(id) {
|
|
|
|
+ if (!id || typeof id !== 'string' || id.length === 0) return [];
|
|
|
|
+ return this._decode(id);
|
|
|
|
+ }
|
|
|
|
+ /**
|
|
|
|
+ * @description Splits a hex string into groups of 12-digit hexadecimal numbers,
|
|
|
|
+ * then prefixes each with '1' and encodes the resulting array of numbers
|
|
|
|
+ *
|
|
|
|
+ * Encoding '00000000000f00000000000f000f' would be the equivalent of:
|
|
|
|
+ * Hashids.encode([0x100000000000f, 0x100000000000f, 0x1000f])
|
|
|
|
+ *
|
|
|
|
+ * This means that if your environment supports BigInts,
|
|
|
|
+ * you will get different (shorter) results if you provide
|
|
|
|
+ * a BigInt representation of your hex and use `encode` directly, e.g.:
|
|
|
|
+ * Hashids.encode(BigInt(`0x${hex}`))
|
|
|
|
+ *
|
|
|
|
+ * To decode such a representation back to a hex string, use the following snippet:
|
|
|
|
+ * Hashids.decode(id)[0].toString(16)
|
|
|
|
+ */
|
|
|
|
+ ;
|
|
|
|
+
|
|
|
|
+ _proto.encodeHex = function encodeHex(hex) {
|
|
|
|
+ switch (typeof hex) {
|
|
|
|
+ case 'bigint':
|
|
|
|
+ hex = hex.toString(16);
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case 'string':
|
|
|
|
+ if (!/^[0-9a-fA-F]+$/.test(hex)) return '';
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ default:
|
|
|
|
+ throw new Error("Hashids: The provided value is neither a string, nor a BigInt (got: " + typeof hex + ")");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var numbers = splitAtIntervalAndMap(hex, 12, function (part) {
|
|
|
|
+ return parseInt("1" + part, 16);
|
|
|
|
+ });
|
|
|
|
+ return this.encode(numbers);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ _proto.decodeHex = function decodeHex(id) {
|
|
|
|
+ return this.decode(id).map(function (number) {
|
|
|
|
+ return number.toString(16).slice(1);
|
|
|
|
+ }).join('');
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ _proto._encode = function _encode(numbers) {
|
|
|
|
+ var _this = this;
|
|
|
|
+
|
|
|
|
+ var alphabet = this.alphabet;
|
|
|
|
+ var numbersIdInt = numbers.reduce(function (last, number, i) {
|
|
|
|
+ return last + (typeof number === 'bigint' ? Number(number % BigInt(i + 100)) : number % (i + 100));
|
|
|
|
+ }, 0);
|
|
|
|
+ var ret = [alphabet[numbersIdInt % alphabet.length]];
|
|
|
|
+ var lottery = ret.slice();
|
|
|
|
+ var seps = this.seps;
|
|
|
|
+ var guards = this.guards;
|
|
|
|
+ numbers.forEach(function (number, i) {
|
|
|
|
+ var _ret;
|
|
|
|
+
|
|
|
|
+ var buffer = lottery.concat(_this.salt, alphabet);
|
|
|
|
+ alphabet = shuffle(alphabet, buffer);
|
|
|
|
+ var last = toAlphabet(number, alphabet);
|
|
|
|
+
|
|
|
|
+ (_ret = ret).push.apply(_ret, _toConsumableArray(last));
|
|
|
|
+
|
|
|
|
+ if (i + 1 < numbers.length) {
|
|
|
|
+ var charCode = last[0].codePointAt(0) + i;
|
|
|
|
+ var extraNumber = typeof number === 'bigint' ? Number(number % BigInt(charCode)) : number % charCode;
|
|
|
|
+ ret.push(seps[extraNumber % seps.length]);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ if (ret.length < this.minLength) {
|
|
|
|
+ var prefixGuardIndex = (numbersIdInt + ret[0].codePointAt(0)) % guards.length;
|
|
|
|
+ ret.unshift(guards[prefixGuardIndex]);
|
|
|
|
+
|
|
|
|
+ if (ret.length < this.minLength) {
|
|
|
|
+ var suffixGuardIndex = (numbersIdInt + ret[2].codePointAt(0)) % guards.length;
|
|
|
|
+ ret.push(guards[suffixGuardIndex]);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var halfLength = Math.floor(alphabet.length / 2);
|
|
|
|
+
|
|
|
|
+ while (ret.length < this.minLength) {
|
|
|
|
+ var _ret2, _ret3;
|
|
|
|
+
|
|
|
|
+ alphabet = shuffle(alphabet, alphabet);
|
|
|
|
+
|
|
|
|
+ (_ret2 = ret).unshift.apply(_ret2, _toConsumableArray(alphabet.slice(halfLength)));
|
|
|
|
+
|
|
|
|
+ (_ret3 = ret).push.apply(_ret3, _toConsumableArray(alphabet.slice(0, halfLength)));
|
|
|
|
+
|
|
|
|
+ var excess = ret.length - this.minLength;
|
|
|
|
+
|
|
|
|
+ if (excess > 0) {
|
|
|
|
+ var halfOfExcess = excess / 2;
|
|
|
|
+ ret = ret.slice(halfOfExcess, halfOfExcess + this.minLength);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ _proto.isValidId = function isValidId(id) {
|
|
|
|
+ return this.allowedCharsRegExp.test(id);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ _proto._decode = function _decode(id) {
|
|
|
|
+ if (!this.isValidId(id)) {
|
|
|
|
+ throw new Error("The provided ID (" + id + ") is invalid, as it contains characters that do not exist in the alphabet (" + this.guards.join('') + this.seps.join('') + this.alphabet.join('') + ")");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var idGuardsArray = id.split(this.guardsRegExp);
|
|
|
|
+ var splitIndex = idGuardsArray.length === 3 || idGuardsArray.length === 2 ? 1 : 0;
|
|
|
|
+ var idBreakdown = idGuardsArray[splitIndex];
|
|
|
|
+ if (idBreakdown.length === 0) return [];
|
|
|
|
+ var lotteryChar = idBreakdown[Symbol.iterator]().next().value;
|
|
|
|
+ var idArray = idBreakdown.slice(lotteryChar.length).split(this.sepsRegExp);
|
|
|
|
+ var lastAlphabet = this.alphabet;
|
|
|
|
+ var result = [];
|
|
|
|
+
|
|
|
|
+ for (var _iterator = idArray, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
|
|
|
|
+ var _ref;
|
|
|
|
+
|
|
|
|
+ if (_isArray) {
|
|
|
|
+ if (_i >= _iterator.length) break;
|
|
|
|
+ _ref = _iterator[_i++];
|
|
|
|
+ } else {
|
|
|
|
+ _i = _iterator.next();
|
|
|
|
+ if (_i.done) break;
|
|
|
|
+ _ref = _i.value;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var subId = _ref;
|
|
|
|
+ var buffer = [lotteryChar].concat(_toConsumableArray(this.salt), _toConsumableArray(lastAlphabet));
|
|
|
|
+ var nextAlphabet = shuffle(lastAlphabet, buffer.slice(0, lastAlphabet.length));
|
|
|
|
+ result.push(fromAlphabet(Array.from(subId), nextAlphabet));
|
|
|
|
+ lastAlphabet = nextAlphabet;
|
|
|
|
+ } // if the result is different from what we'd expect, we return an empty result (malformed input):
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ if (this._encode(result).join('') !== id) return [];
|
|
|
|
+ return result;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ return Hashids;
|
|
|
|
+ }();
|
|
|
|
+
|
|
|
|
+ _exports.default = Hashids;
|
|
|
|
+ var minAlphabetLength = 16;
|
|
|
|
+ var sepDiv = 3.5;
|
|
|
|
+ var guardDiv = 12;
|
|
|
|
+
|
|
|
|
+ var keepUnique = function keepUnique(content) {
|
|
|
|
+ return Array.from(new Set(content));
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ _exports.keepUnique = keepUnique;
|
|
|
|
+
|
|
|
|
+ var withoutChars = function withoutChars(chars, _withoutChars) {
|
|
|
|
+ return chars.filter(function (char) {
|
|
|
|
+ return !_withoutChars.includes(char);
|
|
|
|
+ });
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ _exports.withoutChars = withoutChars;
|
|
|
|
+
|
|
|
|
+ var onlyChars = function onlyChars(chars, keepChars) {
|
|
|
|
+ return chars.filter(function (char) {
|
|
|
|
+ return keepChars.includes(char);
|
|
|
|
+ });
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ _exports.onlyChars = onlyChars;
|
|
|
|
+
|
|
|
|
+ var isIntegerNumber = function isIntegerNumber(n) {
|
|
|
|
+ return typeof n === 'bigint' || !Number.isNaN(Number(n)) && Math.floor(Number(n)) === n;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ var isPositiveAndFinite = function isPositiveAndFinite(n) {
|
|
|
|
+ return typeof n === 'bigint' || n >= 0 && Number.isSafeInteger(n);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ function shuffle(alphabetChars, saltChars) {
|
|
|
|
+ if (saltChars.length === 0) {
|
|
|
|
+ return alphabetChars;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var integer;
|
|
|
|
+ var transformed = alphabetChars.slice();
|
|
|
|
+
|
|
|
|
+ for (var i = transformed.length - 1, v = 0, p = 0; i > 0; i--, v++) {
|
|
|
|
+ v %= saltChars.length;
|
|
|
|
+ p += integer = saltChars[v].codePointAt(0);
|
|
|
|
+ var j = (integer + v + p) % i; // swap characters at positions i and j
|
|
|
|
+
|
|
|
|
+ var a = transformed[i];
|
|
|
|
+ var b = transformed[j];
|
|
|
|
+ transformed[j] = a;
|
|
|
|
+ transformed[i] = b;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return transformed;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var toAlphabet = function toAlphabet(input, alphabetChars) {
|
|
|
|
+ var id = [];
|
|
|
|
+
|
|
|
|
+ if (typeof input === 'bigint') {
|
|
|
|
+ var alphabetLength = BigInt(alphabetChars.length);
|
|
|
|
+
|
|
|
|
+ do {
|
|
|
|
+ id.unshift(alphabetChars[Number(input % alphabetLength)]);
|
|
|
|
+ input = input / alphabetLength;
|
|
|
|
+ } while (input > BigInt(0));
|
|
|
|
+ } else {
|
|
|
|
+ do {
|
|
|
|
+ id.unshift(alphabetChars[input % alphabetChars.length]);
|
|
|
|
+ input = Math.floor(input / alphabetChars.length);
|
|
|
|
+ } while (input > 0);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return id;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ var fromAlphabet = function fromAlphabet(inputChars, alphabetChars) {
|
|
|
|
+ return inputChars.reduce(function (carry, item) {
|
|
|
|
+ var index = alphabetChars.indexOf(item);
|
|
|
|
+
|
|
|
|
+ if (index === -1) {
|
|
|
|
+ throw new Error("The provided ID (" + inputChars.join('') + ") is invalid, as it contains characters that do not exist in the alphabet (" + alphabetChars.join('') + ")");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (typeof carry === 'bigint') {
|
|
|
|
+ return carry * BigInt(alphabetChars.length) + BigInt(index);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var value = carry * alphabetChars.length + index;
|
|
|
|
+ var isSafeValue = Number.isSafeInteger(value);
|
|
|
|
+
|
|
|
|
+ if (isSafeValue) {
|
|
|
|
+ return value;
|
|
|
|
+ } else {
|
|
|
|
+ if (typeof BigInt === 'function') {
|
|
|
|
+ return BigInt(carry) * BigInt(alphabetChars.length) + BigInt(index);
|
|
|
|
+ } else {
|
|
|
|
+ // we do not have support for BigInt:
|
|
|
|
+ throw new Error("Unable to decode the provided string, due to lack of support for BigInt numbers in the current environment");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }, 0);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ var safeToParseNumberRegExp = /^\+?[0-9]+$/;
|
|
|
|
+
|
|
|
|
+ var safeParseInt10 = function safeParseInt10(str) {
|
|
|
|
+ return safeToParseNumberRegExp.test(str) ? parseInt(str, 10) : NaN;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ var splitAtIntervalAndMap = function splitAtIntervalAndMap(str, nth, map) {
|
|
|
|
+ return Array.from({
|
|
|
|
+ length: Math.ceil(str.length / nth)
|
|
|
|
+ }, function (_, index) {
|
|
|
|
+ return map(str.slice(index * nth, (index + 1) * nth));
|
|
|
|
+ });
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ var makeAnyOfCharsRegExp = function makeAnyOfCharsRegExp(chars) {
|
|
|
|
+ return new RegExp(chars.map(function (char) {
|
|
|
|
+ return escapeRegExp(char);
|
|
|
|
+ }) // we need to sort these from longest to shortest,
|
|
|
|
+ // as they may contain multibyte unicode characters (these should come first)
|
|
|
|
+ .sort(function (a, b) {
|
|
|
|
+ return b.length - a.length;
|
|
|
|
+ }).join('|'));
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ var makeAtLeastSomeCharRegExp = function makeAtLeastSomeCharRegExp(chars) {
|
|
|
|
+ return new RegExp("^[" + chars.map(function (char) {
|
|
|
|
+ return escapeRegExp(char);
|
|
|
|
+ }) // we need to sort these from longest to shortest,
|
|
|
|
+ // as they may contain multibyte unicode characters (these should come first)
|
|
|
|
+ .sort(function (a, b) {
|
|
|
|
+ return b.length - a.length;
|
|
|
|
+ }).join('') + "]+$");
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ var escapeRegExp = function escapeRegExp(text) {
|
|
|
|
+ return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
|
|
|
+ };
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+//# sourceMappingURL=hashids.js.map
|