-
-
Notifications
You must be signed in to change notification settings - Fork 38
/
hyperid.js
104 lines (89 loc) · 2.5 KB
/
hyperid.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
'use strict'
const uuidv4 = require('./uuid-node')
const parser = require('uuid-parse')
const Buffer = loadBuffer()
function loadBuffer () {
const b = require('buffer')
// use third party module if no buffer module
return b && b.Buffer
? b.Buffer
: require('buffer/').Buffer
}
const base64Padding = Buffer.from('==', 'base64')
function hyperid (opts) {
let fixedLength = false
let urlSafe = false
// gaurd if instantiated using boolean for fixedLength or with no args
let maxInt = Math.pow(2, 31) - 1
if (typeof opts === 'boolean') {
fixedLength = opts
} else {
opts = opts || {}
maxInt = opts.maxInt || Math.pow(2, 31) - 1
urlSafe = !!opts.urlSafe
fixedLength = !!opts.fixedLength
}
generate.uuid = uuidv4()
generate.decode = decode
let id = baseId(generate.uuid, urlSafe)
let count = Math.floor(opts.startFrom || 0)
if (isNaN(maxInt)) throw new Error(`maxInt must be a number. recieved ${opts.maxInt}`)
if (isNaN(count) || !(maxInt > count && count >= 0)) {
throw new Error([
`when passed, opts.startFrom must be a number between 0 and ${maxInt}.`,
'Only the integer part matters.',
`- got: ${opts.startFrom}`
].join('\n'))
}
return generate
function generate () {
let result
if (count === maxInt) {
generate.uuid = uuidv4()
id = baseId(generate.uuid, urlSafe) // rebase
count = 0
}
if (fixedLength) {
result = id + `0000000000${count}`.slice(-10)
} else {
result = id + count
}
count = (count + 1) | 0
return result
}
}
function baseId (id, urlSafe) {
const base64Id = Buffer.concat([Buffer.from(parser.parse(id)), base64Padding]).toString('base64')
if (urlSafe) {
return base64Id.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '-')
}
return base64Id.replace(/=+$/, '/')
}
function decode (id, opts) {
opts = opts || {}
const urlSafe = !!opts.urlSafe
if (urlSafe) {
id = id.replace(/-([^-]*)$/, '/' + '$1')
.replace(/-/g, '+')
.replace(/_/g, '/')
}
if (id.length < 22) {
return null
}
const lastSlashIndex = id.lastIndexOf('/')
if (lastSlashIndex === -1) {
return null
}
const uuidPart = id.substring(0, lastSlashIndex)
const countPart = Number(id.substring(lastSlashIndex + 1))
if (!uuidPart || isNaN(countPart)) {
return null
}
const result = {
uuid: parser.unparse(Buffer.from(uuidPart + '==', 'base64')),
count: countPart
}
return result
}
module.exports = hyperid
module.exports.decode = decode