UNPKG

ksuid-edge

Version:

Edge and browser-compatible KSUID generator

143 lines 5.61 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; const base62_1 = require("./base62"); const crypto_1 = __importDefault(require("./crypto")); const EPOCH_IN_MS = 14e11; const MAX_TIME_IN_MS = 1e3 * (Math.pow(2, 32) - 1) + EPOCH_IN_MS; // Timestamp is a uint32 const TIMESTAMP_BYTE_LENGTH = 4; // Payload is 16-bytes const PAYLOAD_BYTE_LENGTH = 16; // KSUIDs are 20 bytes when binary encoded const BYTE_LENGTH = TIMESTAMP_BYTE_LENGTH + PAYLOAD_BYTE_LENGTH; // The length of a KSUID when string (base62) encoded const STRING_ENCODED_LENGTH = 27; const TIME_IN_MS_ASSERTION = `Valid KSUID timestamps must be in milliseconds since ${new Date(0).toISOString()}, no earlier than ${new Date(EPOCH_IN_MS).toISOString()} and no later than ${new Date(MAX_TIME_IN_MS).toISOString()} ` .trim() .replace(/(\n|\s)+/g, ' ') .replace(/\.000Z/g, 'Z'); const VALID_ENCODING_ASSERTION = `Valid encoded KSUIDs are ${STRING_ENCODED_LENGTH} characters`; const VALID_BUFFER_ASSERTION = `Valid KSUID buffers are ${BYTE_LENGTH} bytes`; const VALID_PAYLOAD_ASSERTION = `Valid KSUID payloads are ${PAYLOAD_BYTE_LENGTH} bytes`; function randomBytes() { return crypto_1.default.getRandomValues(new Uint8Array(16)); } function fromParts(timestamp, payload) { const buffer = new ArrayBuffer(BYTE_LENGTH); const view = new DataView(buffer); const timestampEpoch = Math.floor((timestamp - EPOCH_IN_MS) / 1e3); view.setUint32(0, timestampEpoch, false); let offset = TIMESTAMP_BYTE_LENGTH; for (const byte of payload) { view.setUint8(offset, byte); offset++; } return view.buffer; } class KSUID { constructor(buffer) { if (!KSUID.isValid(buffer)) { throw new TypeError(VALID_BUFFER_ASSERTION); } this.view = new DataView(buffer); } get raw() { return this.view.buffer.slice(0); } get date() { return new Date(1e3 * this.timestamp + EPOCH_IN_MS); } get timestamp() { return this.view.getUint32(0, false); } get payload() { return this.view.buffer.slice(TIMESTAMP_BYTE_LENGTH, BYTE_LENGTH); } get string() { return (0, base62_1.base62)(this.view, STRING_ENCODED_LENGTH).padStart(STRING_ENCODED_LENGTH, '0'); } get [Symbol.toStringTag]() { return 'KSUID'; } compare(other) { if (this === other) return 0; const a = new Uint8Array(this.view.buffer); const b = new Uint8Array(other.view.buffer); for (let offset = 0; offset < a.length; offset++) { if (a[offset] < b[offset]) return -1; if (a[offset] > b[offset]) return 1; } return 0; } equals(other) { return this.compare(other) === 0; } toJSON() { return this.string; } toString() { return `${this[Symbol.toStringTag]} { ${this.string} }`; } static random(timestamp = Date.now()) { return __awaiter(this, void 0, void 0, function* () { return KSUID.randomSync(timestamp); }); } static randomSync(timestamp = Date.now()) { return new KSUID(fromParts(timestamp, randomBytes())); } static fromParts(timestamp, payload) { if (!Number.isInteger(timestamp) || timestamp < EPOCH_IN_MS || timestamp > MAX_TIME_IN_MS) { throw new TypeError(TIME_IN_MS_ASSERTION); } if (!Buffer.isBuffer(payload) || payload.byteLength !== PAYLOAD_BYTE_LENGTH) { throw new TypeError(VALID_PAYLOAD_ASSERTION); } return new KSUID(fromParts(timestamp, payload)); } static isValid(buffer) { return buffer.byteLength === BYTE_LENGTH; } static parse(data) { if (data.length !== STRING_ENCODED_LENGTH) { throw new TypeError(VALID_ENCODING_ASSERTION); } const decoded = (0, base62_1.debase62)(data, BYTE_LENGTH); if (decoded.byteLength === BYTE_LENGTH) { return new KSUID(decoded); } const buffer = new ArrayBuffer(BYTE_LENGTH); const dstView = new DataView(buffer); const srcView = new DataView(decoded); const padEnd = BYTE_LENGTH - decoded.byteLength; for (let offset = 0; offset < padEnd; offset++) { dstView.setUint8(offset, 0); } for (let offset = padEnd; offset < BYTE_LENGTH; offset++) { dstView.setUint8(offset, srcView.getUint8(offset - padEnd)); } return new KSUID(buffer); } } /** A string-encoded maximum value for a KSUID */ KSUID.MAX_STRING_ENCODED = 'aWgEPTl1tmebfsQzFP4bxwgy80V'; /** A string-encoded minimum value for a KSUID */ KSUID.MIN_STRING_ENCODED = '000000000000000000000000000'; module.exports = KSUID; //# sourceMappingURL=index.js.map