@tangential/core
Version:
Core types and support code for Tangential
60 lines • 8.44 kB
JavaScript
/**
* From https://gist.github.com/mikelehen/3596a30bd69384624c11, via Firebase blog at
* https://firebase.googleblog.com/2015/02/the-2120-ways-to-ensure-unique_68.html
* With minor tweaks for use with TypeScript
**/
/**
* Fancy ID generator that creates 20-character string identifiers with the following properties:
*
* 1. They're based on timestamp so that they sort *after* any existing ids.
* 2. They contain 72-bits of random data after the timestamp so that IDs won't collide with other clients' IDs.
* 3. They sort *lexicographically* (so the timestamp is converted to characters that will sort properly).
* 4. They're monotonically increasing. Even if you generate more than one in the same timestamp, the
* latter ones will sort after the former ones. We do this by using the previous random bits
* but "incrementing" them by 1 (only in the case of a timestamp collision).
*/
// Modeled after base64 web-safe chars, but ordered by ASCII.
const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
// Timestamp of last push, used to prevent local collisions if you push twice in one ms.
let lastPushTime = 0;
// We generate 72-bits of randomness which get turned into 12 characters and appended to the
// timestamp to prevent collisions with other clients. We store the last characters we
// generated because in the event of a collision, we'll use those same characters except
// "incremented" by one.
const lastRandChars = [];
export const generatePushID = function () {
let i;
let now = Date.now();
const duplicateTime = (now === lastPushTime);
lastPushTime = now;
const timeStampChars = new Array(8);
for (i = 7; i >= 0; i--) {
timeStampChars[i] = PUSH_CHARS.charAt(now % 64);
// NOTE: Can't use << here because javascript will convert to int and lose the upper bits.
now = Math.floor(now / 64);
}
if (now !== 0) {
throw new Error('We should have converted the entire timestamp.');
}
let id = timeStampChars.join('');
if (!duplicateTime) {
for (i = 0; i < 12; i++) {
lastRandChars[i] = Math.floor(Math.random() * 64);
}
}
else {
// If the timestamp hasn't changed since last push, use the same random number, except incremented by 1.
for (i = 11; i >= 0 && lastRandChars[i] === 63; i--) {
lastRandChars[i] = 0;
}
lastRandChars[i]++;
}
for (i = 0; i < 12; i++) {
id += PUSH_CHARS.charAt(lastRandChars[i]);
}
if (id.length !== 20) {
throw new Error('Length should be 20.');
}
return id;
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2VuZXJhdGUtcHVzaC1pZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3RhbmdlbnRpYWwvY29yZS9zcmMvbGliL3V0aWwvZ2VuZXJhdGUtcHVzaC1pZC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7OztJQUlJO0FBR0o7Ozs7Ozs7OztHQVNHO0FBRUgsNkRBQTZEO0FBQzdELE1BQU0sVUFBVSxHQUFHLGtFQUFrRSxDQUFDO0FBQ3RGLHdGQUF3RjtBQUN4RixJQUFJLFlBQVksR0FBVyxDQUFDLENBQUM7QUFFN0IsNEZBQTRGO0FBQzVGLHVGQUF1RjtBQUN2Rix3RkFBd0Y7QUFDeEYsd0JBQXdCO0FBQ3hCLE1BQU0sYUFBYSxHQUFhLEVBQUUsQ0FBQztBQUVuQyxNQUFNLENBQUMsTUFBTSxjQUFjLEdBQWlCO0lBQzFDLElBQUksQ0FBUyxDQUFDO0lBQ2QsSUFBSSxHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO0lBQ3JCLE1BQU0sYUFBYSxHQUFHLENBQUMsR0FBRyxLQUFLLFlBQVksQ0FBQyxDQUFDO0lBQzdDLFlBQVksR0FBRyxHQUFHLENBQUM7SUFFbkIsTUFBTSxjQUFjLEdBQUcsSUFBSSxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDcEMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUU7UUFDdkIsY0FBYyxDQUFDLENBQUMsQ0FBQyxHQUFHLFVBQVUsQ0FBQyxNQUFNLENBQUMsR0FBRyxHQUFHLEVBQUUsQ0FBQyxDQUFDO1FBQ2hELDBGQUEwRjtRQUMxRixHQUFHLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLEdBQUcsRUFBRSxDQUFDLENBQUM7S0FDNUI7SUFDRCxJQUFJLEdBQUcsS0FBSyxDQUFDLEVBQUU7UUFDYixNQUFNLElBQUksS0FBSyxDQUFDLGdEQUFnRCxDQUFDLENBQUM7S0FDbkU7SUFHRCxJQUFJLEVBQUUsR0FBRyxjQUFjLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBRWpDLElBQUksQ0FBQyxhQUFhLEVBQUU7UUFDbEIsS0FBSyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxFQUFFLEVBQUU7WUFDdkIsYUFBYSxDQUFDLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDO1NBQ25EO0tBQ0Y7U0FBTTtRQUNMLHdHQUF3RztRQUN4RyxLQUFLLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsSUFBSSxhQUFhLENBQUMsQ0FBQyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsRUFBRSxFQUFFO1lBQ25ELGFBQWEsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUM7U0FDdEI7UUFDRCxhQUFhLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztLQUNwQjtJQUNELEtBQUssQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsRUFBRSxFQUFFO1FBQ3ZCLEVBQUUsSUFBSSxVQUFVLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0tBQzNDO0lBQ0QsSUFBSSxFQUFFLENBQUMsTUFBTSxLQUFLLEVBQUUsRUFBRTtRQUNwQixNQUFNLElBQUksS0FBSyxDQUFDLHNCQUFzQixDQUFDLENBQUM7S0FDekM7SUFFRCxPQUFPLEVBQUUsQ0FBQztBQUNaLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogRnJvbSBodHRwczovL2dpc3QuZ2l0aHViLmNvbS9taWtlbGVoZW4vMzU5NmEzMGJkNjkzODQ2MjRjMTEsIHZpYSBGaXJlYmFzZSBibG9nIGF0XG4gKiBodHRwczovL2ZpcmViYXNlLmdvb2dsZWJsb2cuY29tLzIwMTUvMDIvdGhlLTIxMjAtd2F5cy10by1lbnN1cmUtdW5pcXVlXzY4Lmh0bWxcbiAqIFdpdGggbWlub3IgdHdlYWtzIGZvciB1c2Ugd2l0aCBUeXBlU2NyaXB0XG4gKiovXG5cblxuLyoqXG4gKiBGYW5jeSBJRCBnZW5lcmF0b3IgdGhhdCBjcmVhdGVzIDIwLWNoYXJhY3RlciBzdHJpbmcgaWRlbnRpZmllcnMgd2l0aCB0aGUgZm9sbG93aW5nIHByb3BlcnRpZXM6XG4gKlxuICogMS4gVGhleSdyZSBiYXNlZCBvbiB0aW1lc3RhbXAgc28gdGhhdCB0aGV5IHNvcnQgKmFmdGVyKiBhbnkgZXhpc3RpbmcgaWRzLlxuICogMi4gVGhleSBjb250YWluIDcyLWJpdHMgb2YgcmFuZG9tIGRhdGEgYWZ0ZXIgdGhlIHRpbWVzdGFtcCBzbyB0aGF0IElEcyB3b24ndCBjb2xsaWRlIHdpdGggb3RoZXIgY2xpZW50cycgSURzLlxuICogMy4gVGhleSBzb3J0ICpsZXhpY29ncmFwaGljYWxseSogKHNvIHRoZSB0aW1lc3RhbXAgaXMgY29udmVydGVkIHRvIGNoYXJhY3RlcnMgdGhhdCB3aWxsIHNvcnQgcHJvcGVybHkpLlxuICogNC4gVGhleSdyZSBtb25vdG9uaWNhbGx5IGluY3JlYXNpbmcuICBFdmVuIGlmIHlvdSBnZW5lcmF0ZSBtb3JlIHRoYW4gb25lIGluIHRoZSBzYW1lIHRpbWVzdGFtcCwgdGhlXG4gKiAgICBsYXR0ZXIgb25lcyB3aWxsIHNvcnQgYWZ0ZXIgdGhlIGZvcm1lciBvbmVzLiAgV2UgZG8gdGhpcyBieSB1c2luZyB0aGUgcHJldmlvdXMgcmFuZG9tIGJpdHNcbiAqICAgIGJ1dCBcImluY3JlbWVudGluZ1wiIHRoZW0gYnkgMSAob25seSBpbiB0aGUgY2FzZSBvZiBhIHRpbWVzdGFtcCBjb2xsaXNpb24pLlxuICovXG5cbi8vIE1vZGVsZWQgYWZ0ZXIgYmFzZTY0IHdlYi1zYWZlIGNoYXJzLCBidXQgb3JkZXJlZCBieSBBU0NJSS5cbmNvbnN0IFBVU0hfQ0hBUlMgPSAnLTAxMjM0NTY3ODlBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWl9hYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5eic7XG4vLyBUaW1lc3RhbXAgb2YgbGFzdCBwdXNoLCB1c2VkIHRvIHByZXZlbnQgbG9jYWwgY29sbGlzaW9ucyBpZiB5b3UgcHVzaCB0d2ljZSBpbiBvbmUgbXMuXG5sZXQgbGFzdFB1c2hUaW1lOiBudW1iZXIgPSAwO1xuXG4vLyBXZSBnZW5lcmF0ZSA3Mi1iaXRzIG9mIHJhbmRvbW5lc3Mgd2hpY2ggZ2V0IHR1cm5lZCBpbnRvIDEyIGNoYXJhY3RlcnMgYW5kIGFwcGVuZGVkIHRvIHRoZVxuLy8gdGltZXN0YW1wIHRvIHByZXZlbnQgY29sbGlzaW9ucyB3aXRoIG90aGVyIGNsaWVudHMuICBXZSBzdG9yZSB0aGUgbGFzdCBjaGFyYWN0ZXJzIHdlXG4vLyBnZW5lcmF0ZWQgYmVjYXVzZSBpbiB0aGUgZXZlbnQgb2YgYSBjb2xsaXNpb24sIHdlJ2xsIHVzZSB0aG9zZSBzYW1lIGNoYXJhY3RlcnMgZXhjZXB0XG4vLyBcImluY3JlbWVudGVkXCIgYnkgb25lLlxuY29uc3QgbGFzdFJhbmRDaGFyczogbnVtYmVyW10gPSBbXTtcblxuZXhwb3J0IGNvbnN0IGdlbmVyYXRlUHVzaElEOiAoKSA9PiBzdHJpbmcgPSBmdW5jdGlvbiAoKSB7XG4gIGxldCBpOiBudW1iZXI7XG4gIGxldCBub3cgPSBEYXRlLm5vdygpO1xuICBjb25zdCBkdXBsaWNhdGVUaW1lID0gKG5vdyA9PT0gbGFzdFB1c2hUaW1lKTtcbiAgbGFzdFB1c2hUaW1lID0gbm93O1xuXG4gIGNvbnN0IHRpbWVTdGFtcENoYXJzID0gbmV3IEFycmF5KDgpO1xuICBmb3IgKGkgPSA3OyBpID49IDA7IGktLSkge1xuICAgIHRpbWVTdGFtcENoYXJzW2ldID0gUFVTSF9DSEFSUy5jaGFyQXQobm93ICUgNjQpO1xuICAgIC8vIE5PVEU6IENhbid0IHVzZSA8PCBoZXJlIGJlY2F1c2UgamF2YXNjcmlwdCB3aWxsIGNvbnZlcnQgdG8gaW50IGFuZCBsb3NlIHRoZSB1cHBlciBiaXRzLlxuICAgIG5vdyA9IE1hdGguZmxvb3Iobm93IC8gNjQpO1xuICB9XG4gIGlmIChub3cgIT09IDApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ1dlIHNob3VsZCBoYXZlIGNvbnZlcnRlZCB0aGUgZW50aXJlIHRpbWVzdGFtcC4nKTtcbiAgfVxuXG5cbiAgbGV0IGlkID0gdGltZVN0YW1wQ2hhcnMuam9pbignJyk7XG5cbiAgaWYgKCFkdXBsaWNhdGVUaW1lKSB7XG4gICAgZm9yIChpID0gMDsgaSA8IDEyOyBpKyspIHtcbiAgICAgIGxhc3RSYW5kQ2hhcnNbaV0gPSBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiA2NCk7XG4gICAgfVxuICB9IGVsc2Uge1xuICAgIC8vIElmIHRoZSB0aW1lc3RhbXAgaGFzbid0IGNoYW5nZWQgc2luY2UgbGFzdCBwdXNoLCB1c2UgdGhlIHNhbWUgcmFuZG9tIG51bWJlciwgZXhjZXB0IGluY3JlbWVudGVkIGJ5IDEuXG4gICAgZm9yIChpID0gMTE7IGkgPj0gMCAmJiBsYXN0UmFuZENoYXJzW2ldID09PSA2MzsgaS0tKSB7XG4gICAgICBsYXN0UmFuZENoYXJzW2ldID0gMDtcbiAgICB9XG4gICAgbGFzdFJhbmRDaGFyc1tpXSsrO1xuICB9XG4gIGZvciAoaSA9IDA7IGkgPCAxMjsgaSsrKSB7XG4gICAgaWQgKz0gUFVTSF9DSEFSUy5jaGFyQXQobGFzdFJhbmRDaGFyc1tpXSk7XG4gIH1cbiAgaWYgKGlkLmxlbmd0aCAhPT0gMjApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0xlbmd0aCBzaG91bGQgYmUgMjAuJyk7XG4gIH1cblxuICByZXR1cm4gaWQ7XG59O1xuIl19