UNPKG

13.4 kBJavaScriptView Raw
1function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }
2
3function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }
4
5function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }
6
7function _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; } }
8
9var Hashids =
10/*#__PURE__*/
11function () {
12 function Hashids(salt, minLength, alphabet, seps) {
13 if (salt === void 0) {
14 salt = '';
15 }
16
17 if (minLength === void 0) {
18 minLength = 0;
19 }
20
21 if (alphabet === void 0) {
22 alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
23 }
24
25 if (seps === void 0) {
26 seps = 'cfhistuCFHISTU';
27 }
28
29 this.minLength = minLength;
30
31 if (typeof minLength !== 'number') {
32 throw new TypeError("Hashids: Provided 'minLength' has to be a number (is " + typeof minLength + ")");
33 }
34
35 if (typeof salt !== 'string') {
36 throw new TypeError("Hashids: Provided 'salt' has to be a string (is " + typeof salt + ")");
37 }
38
39 if (typeof alphabet !== 'string') {
40 throw new TypeError("Hashids: Provided alphabet has to be a string (is " + typeof alphabet + ")");
41 }
42
43 var saltChars = Array.from(salt);
44 var alphabetChars = Array.from(alphabet);
45 var sepsChars = Array.from(seps);
46 this.salt = saltChars;
47 var uniqueAlphabet = keepUnique(alphabetChars);
48
49 if (uniqueAlphabet.length < minAlphabetLength) {
50 throw new Error("Hashids: alphabet must contain at least " + minAlphabetLength + " unique characters, provided: " + uniqueAlphabet);
51 }
52 /** `alphabet` should not contains `seps` */
53
54
55 this.alphabet = withoutChars(uniqueAlphabet, sepsChars);
56 /** `seps` should contain only characters present in `alphabet` */
57
58 var filteredSeps = onlyChars(sepsChars, uniqueAlphabet);
59 this.seps = shuffle(filteredSeps, saltChars);
60 var sepsLength;
61 var diff;
62
63 if (this.seps.length === 0 || this.alphabet.length / this.seps.length > sepDiv) {
64 sepsLength = Math.ceil(this.alphabet.length / sepDiv);
65
66 if (sepsLength > this.seps.length) {
67 var _this$seps;
68
69 diff = sepsLength - this.seps.length;
70
71 (_this$seps = this.seps).push.apply(_this$seps, _toConsumableArray(this.alphabet.slice(0, diff)));
72
73 this.alphabet = this.alphabet.slice(diff);
74 }
75 }
76
77 this.alphabet = shuffle(this.alphabet, saltChars);
78 var guardCount = Math.ceil(this.alphabet.length / guardDiv);
79
80 if (this.alphabet.length < 3) {
81 this.guards = this.seps.slice(0, guardCount);
82 this.seps = this.seps.slice(guardCount);
83 } else {
84 this.guards = this.alphabet.slice(0, guardCount);
85 this.alphabet = this.alphabet.slice(guardCount);
86 }
87
88 this.guardsRegExp = makeAnyOfCharsRegExp(this.guards);
89 this.sepsRegExp = makeAnyOfCharsRegExp(this.seps);
90 this.allowedCharsRegExp = makeAtLeastSomeCharRegExp([].concat(_toConsumableArray(this.alphabet), _toConsumableArray(this.guards), _toConsumableArray(this.seps)));
91 }
92
93 var _proto = Hashids.prototype;
94
95 _proto.encode = function encode(first) {
96 for (var _len = arguments.length, numbers = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
97 numbers[_key - 1] = arguments[_key];
98 }
99
100 var ret = '';
101
102 if (Array.isArray(first)) {
103 numbers = first;
104 } else {
105 // eslint-disable-next-line eqeqeq
106 numbers = [].concat(_toConsumableArray(first != null ? [first] : []), _toConsumableArray(numbers));
107 }
108
109 if (!numbers.length) {
110 return ret;
111 }
112
113 if (!numbers.every(isIntegerNumber)) {
114 numbers = numbers.map(function (n) {
115 return typeof n === 'bigint' || typeof n === 'number' ? n : safeParseInt10(String(n));
116 });
117 }
118
119 if (!numbers.every(isPositiveAndFinite)) {
120 return ret;
121 }
122
123 return this._encode(numbers).join('');
124 };
125
126 _proto.decode = function decode(id) {
127 if (!id || typeof id !== 'string' || id.length === 0) return [];
128 return this._decode(id);
129 }
130 /**
131 * @description Splits a hex string into groups of 12-digit hexadecimal numbers,
132 * then prefixes each with '1' and encodes the resulting array of numbers
133 *
134 * Encoding '00000000000f00000000000f000f' would be the equivalent of:
135 * Hashids.encode([0x100000000000f, 0x100000000000f, 0x1000f])
136 *
137 * This means that if your environment supports BigInts,
138 * you will get different (shorter) results if you provide
139 * a BigInt representation of your hex and use `encode` directly, e.g.:
140 * Hashids.encode(BigInt(`0x${hex}`))
141 *
142 * To decode such a representation back to a hex string, use the following snippet:
143 * Hashids.decode(id)[0].toString(16)
144 */
145 ;
146
147 _proto.encodeHex = function encodeHex(hex) {
148 switch (typeof hex) {
149 case 'bigint':
150 hex = hex.toString(16);
151 break;
152
153 case 'string':
154 if (!/^[0-9a-fA-F]+$/.test(hex)) return '';
155 break;
156
157 default:
158 throw new Error("Hashids: The provided value is neither a string, nor a BigInt (got: " + typeof hex + ")");
159 }
160
161 var numbers = splitAtIntervalAndMap(hex, 12, function (part) {
162 return parseInt("1" + part, 16);
163 });
164 return this.encode(numbers);
165 };
166
167 _proto.decodeHex = function decodeHex(id) {
168 return this.decode(id).map(function (number) {
169 return number.toString(16).slice(1);
170 }).join('');
171 };
172
173 _proto._encode = function _encode(numbers) {
174 var _this = this;
175
176 var alphabet = this.alphabet;
177 var numbersIdInt = numbers.reduce(function (last, number, i) {
178 return last + (typeof number === 'bigint' ? Number(number % BigInt(i + 100)) : number % (i + 100));
179 }, 0);
180 var ret = [alphabet[numbersIdInt % alphabet.length]];
181 var lottery = ret.slice();
182 var seps = this.seps;
183 var guards = this.guards;
184 numbers.forEach(function (number, i) {
185 var _ret;
186
187 var buffer = lottery.concat(_this.salt, alphabet);
188 alphabet = shuffle(alphabet, buffer);
189 var last = toAlphabet(number, alphabet);
190
191 (_ret = ret).push.apply(_ret, _toConsumableArray(last));
192
193 if (i + 1 < numbers.length) {
194 var charCode = last[0].codePointAt(0) + i;
195 var extraNumber = typeof number === 'bigint' ? Number(number % BigInt(charCode)) : number % charCode;
196 ret.push(seps[extraNumber % seps.length]);
197 }
198 });
199
200 if (ret.length < this.minLength) {
201 var prefixGuardIndex = (numbersIdInt + ret[0].codePointAt(0)) % guards.length;
202 ret.unshift(guards[prefixGuardIndex]);
203
204 if (ret.length < this.minLength) {
205 var suffixGuardIndex = (numbersIdInt + ret[2].codePointAt(0)) % guards.length;
206 ret.push(guards[suffixGuardIndex]);
207 }
208 }
209
210 var halfLength = Math.floor(alphabet.length / 2);
211
212 while (ret.length < this.minLength) {
213 var _ret2, _ret3;
214
215 alphabet = shuffle(alphabet, alphabet);
216
217 (_ret2 = ret).unshift.apply(_ret2, _toConsumableArray(alphabet.slice(halfLength)));
218
219 (_ret3 = ret).push.apply(_ret3, _toConsumableArray(alphabet.slice(0, halfLength)));
220
221 var excess = ret.length - this.minLength;
222
223 if (excess > 0) {
224 var halfOfExcess = excess / 2;
225 ret = ret.slice(halfOfExcess, halfOfExcess + this.minLength);
226 }
227 }
228
229 return ret;
230 };
231
232 _proto.isValidId = function isValidId(id) {
233 return this.allowedCharsRegExp.test(id);
234 };
235
236 _proto._decode = function _decode(id) {
237 if (!this.isValidId(id)) {
238 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('') + ")");
239 }
240
241 var idGuardsArray = id.split(this.guardsRegExp);
242 var splitIndex = idGuardsArray.length === 3 || idGuardsArray.length === 2 ? 1 : 0;
243 var idBreakdown = idGuardsArray[splitIndex];
244 if (idBreakdown.length === 0) return [];
245 var lotteryChar = idBreakdown[Symbol.iterator]().next().value;
246 var idArray = idBreakdown.slice(lotteryChar.length).split(this.sepsRegExp);
247 var lastAlphabet = this.alphabet;
248 var result = [];
249
250 for (var _iterator = idArray, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
251 var _ref;
252
253 if (_isArray) {
254 if (_i >= _iterator.length) break;
255 _ref = _iterator[_i++];
256 } else {
257 _i = _iterator.next();
258 if (_i.done) break;
259 _ref = _i.value;
260 }
261
262 var subId = _ref;
263 var buffer = [lotteryChar].concat(_toConsumableArray(this.salt), _toConsumableArray(lastAlphabet));
264 var nextAlphabet = shuffle(lastAlphabet, buffer.slice(0, lastAlphabet.length));
265 result.push(fromAlphabet(Array.from(subId), nextAlphabet));
266 lastAlphabet = nextAlphabet;
267 } // if the result is different from what we'd expect, we return an empty result (malformed input):
268
269
270 if (this._encode(result).join('') !== id) return [];
271 return result;
272 };
273
274 return Hashids;
275}();
276
277export { Hashids as default };
278var minAlphabetLength = 16;
279var sepDiv = 3.5;
280var guardDiv = 12;
281export var keepUnique = function keepUnique(content) {
282 return Array.from(new Set(content));
283};
284export var withoutChars = function withoutChars(chars, _withoutChars) {
285 return chars.filter(function (char) {
286 return !_withoutChars.includes(char);
287 });
288};
289export var onlyChars = function onlyChars(chars, keepChars) {
290 return chars.filter(function (char) {
291 return keepChars.includes(char);
292 });
293};
294
295var isIntegerNumber = function isIntegerNumber(n) {
296 return typeof n === 'bigint' || !Number.isNaN(Number(n)) && Math.floor(Number(n)) === n;
297};
298
299var isPositiveAndFinite = function isPositiveAndFinite(n) {
300 return typeof n === 'bigint' || n >= 0 && Number.isSafeInteger(n);
301};
302
303function shuffle(alphabetChars, saltChars) {
304 if (saltChars.length === 0) {
305 return alphabetChars;
306 }
307
308 var integer;
309 var transformed = alphabetChars.slice();
310
311 for (var i = transformed.length - 1, v = 0, p = 0; i > 0; i--, v++) {
312 v %= saltChars.length;
313 p += integer = saltChars[v].codePointAt(0);
314 var j = (integer + v + p) % i; // swap characters at positions i and j
315
316 var a = transformed[i];
317 var b = transformed[j];
318 transformed[j] = a;
319 transformed[i] = b;
320 }
321
322 return transformed;
323}
324
325var toAlphabet = function toAlphabet(input, alphabetChars) {
326 var id = [];
327
328 if (typeof input === 'bigint') {
329 var alphabetLength = BigInt(alphabetChars.length);
330
331 do {
332 id.unshift(alphabetChars[Number(input % alphabetLength)]);
333 input = input / alphabetLength;
334 } while (input > BigInt(0));
335 } else {
336 do {
337 id.unshift(alphabetChars[input % alphabetChars.length]);
338 input = Math.floor(input / alphabetChars.length);
339 } while (input > 0);
340 }
341
342 return id;
343};
344
345var fromAlphabet = function fromAlphabet(inputChars, alphabetChars) {
346 return inputChars.reduce(function (carry, item) {
347 var index = alphabetChars.indexOf(item);
348
349 if (index === -1) {
350 throw new Error("The provided ID (" + inputChars.join('') + ") is invalid, as it contains characters that do not exist in the alphabet (" + alphabetChars.join('') + ")");
351 }
352
353 if (typeof carry === 'bigint') {
354 return carry * BigInt(alphabetChars.length) + BigInt(index);
355 }
356
357 var value = carry * alphabetChars.length + index;
358 var isSafeValue = Number.isSafeInteger(value);
359
360 if (isSafeValue) {
361 return value;
362 } else {
363 if (typeof BigInt === 'function') {
364 return BigInt(carry) * BigInt(alphabetChars.length) + BigInt(index);
365 } else {
366 // we do not have support for BigInt:
367 throw new Error("Unable to decode the provided string, due to lack of support for BigInt numbers in the current environment");
368 }
369 }
370 }, 0);
371};
372
373var safeToParseNumberRegExp = /^\+?[0-9]+$/;
374
375var safeParseInt10 = function safeParseInt10(str) {
376 return safeToParseNumberRegExp.test(str) ? parseInt(str, 10) : NaN;
377};
378
379var splitAtIntervalAndMap = function splitAtIntervalAndMap(str, nth, map) {
380 return Array.from({
381 length: Math.ceil(str.length / nth)
382 }, function (_, index) {
383 return map(str.slice(index * nth, (index + 1) * nth));
384 });
385};
386
387var makeAnyOfCharsRegExp = function makeAnyOfCharsRegExp(chars) {
388 return new RegExp(chars.map(function (char) {
389 return escapeRegExp(char);
390 }) // we need to sort these from longest to shortest,
391 // as they may contain multibyte unicode characters (these should come first)
392 .sort(function (a, b) {
393 return b.length - a.length;
394 }).join('|'));
395};
396
397var makeAtLeastSomeCharRegExp = function makeAtLeastSomeCharRegExp(chars) {
398 return new RegExp("^[" + chars.map(function (char) {
399 return escapeRegExp(char);
400 }) // we need to sort these from longest to shortest,
401 // as they may contain multibyte unicode characters (these should come first)
402 .sort(function (a, b) {
403 return b.length - a.length;
404 }).join('') + "]+$");
405};
406
407var escapeRegExp = function escapeRegExp(text) {
408 return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
409};
410
411//# sourceMappingURL=index.js.map
\No newline at end of file