UNPKG

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