UNPKG

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