initial commit
[urlittler] / src / slug.js
1 'use strict';
2
3 const common = require('./common');
4 const { createHash } = require('crypto');
5
6 /**
7 * TODO: base62 would be slightly prettier
8 * TODO: filter potential horrors
9 * @param {string} seed
10 * @param {number} maxLength
11 */
12 async function newSlug(seed, maxLength = 86) {
13 let slug;
14 if (maxLength > 86) {
15 maxLength = 86;
16 }
17 if (typeof seed !== 'undefined') {
18 const hash = createHash('sha512');
19 hash.update(seed);
20 slug = hash.digest('base64');
21 } else {
22 const slugRaw = await common.randomBytesAsync(Math.round(maxLength * 3 / 4));
23 slug = slugRaw.toString('base64');
24 }
25 return slug.slice(0, maxLength).replace('/', '-').replace('+', '.');
26 }
27
28
29 async function* makeSlugGenerator(seed, initialLength = 5, maxLength = 22) {
30 let length = initialLength;
31 let slug = await newSlug(seed, maxLength);
32 while (true) {
33 yield slug.slice(0, length);
34 length++;
35 if (length > maxLength) {
36 length = initialLength;
37 slug = await newSlug(undefined, maxLength); // Hash not suitable, try randomness.
38 }
39 }
40 }
41
42 module.exports = {
43 makeSlugGenerator,
44 newSlug,
45 };