ui7
Version:
Generate sortable, timestamped UUID's, based on the new-uuid-format-04 RFC draft
148 lines (147 loc) • 4.03 kB
JavaScript
var crypto = globalThis.crypto ?? (await import("node:crypto")).webcrypto ?? function(crypto2) {
return {
getRandomValues(array) {
return crypto2.randomFillSync(array);
}
};
}(await import("node:crypto"));
const v7 = (opt) => {
const time = getTime(opt);
let dashes = true;
let upper = false;
let version = 7 << 4;
let rand = random;
if (typeof opt === "object" && opt !== null && !(opt instanceof Date)) {
if (opt.dashes != null)
dashes = opt.dashes;
if (opt.version != null)
version = (opt.version & 15) << 4;
if (opt.upper != null)
upper = opt.upper;
if (opt.entropy != null)
rand = opt.entropy === 0 || opt.entropy === 255 ? constantEntropy(opt.entropy) : opt.entropy;
}
let timestamp2 = hex(time, 12);
if (dashes)
timestamp2 = timestamp2.slice(0, 8) + "-" + timestamp2.slice(8);
const suffixBytes = Array(10);
rand(10, time).forEach((b, i) => {
if (i === 0) {
b = version | b & 15;
} else if (i === 2) {
b = variant | b & 63;
}
suffixBytes[i] = hex(b);
});
const suffix = suffixBytes.join("");
const id = dashes ? `${timestamp2}-${suffix.slice(0, 4)}-${suffix.slice(4, 8)}-${suffix.slice(8)}` : timestamp2 + suffix;
return upper ? id.toUpperCase() : id;
function constantEntropy(k) {
return (n) => new Uint8Array(n).map(() => k);
}
};
var mod_default = v7;
const generator = (options) => {
options = {
entropy: monotonic(),
...options
};
return (opt) => {
if (opt == null)
return v7(options);
if (opt instanceof Date || typeof opt === "number" || typeof opt === "function")
return v7({ ...options, time: opt });
return v7({ ...options, ...opt });
};
};
const timestamp = (uuid) => {
const match = pattern.exec(uuid);
if (match == null)
throw new ParseError("Invalid v7 UUID; cannot determine timestamp");
const ts = match[1].replace("-", "");
return parseInt(ts, 16);
};
const pattern = /^([0-9a-f]{8}-?[0-9a-f]{4})-?7([0-9a-f]{3}-?[0-9a-f]{4}-?[0-9a-f]{12})$/i;
const bufferedRandom = (blockSize = 200) => {
let bytes;
let buffer;
let pos;
return (size) => {
if (size > blockSize) {
bytes = void 0;
blockSize = size;
}
if (!bytes) {
bytes = new Uint8Array(blockSize);
buffer = bytes.buffer;
crypto.getRandomValues(bytes);
pos = 0;
}
let next = pos + size;
if (next >= blockSize) {
const leftover = blockSize - pos;
bytes.copyWithin(0, pos);
crypto.getRandomValues(new Uint8Array(buffer, leftover));
pos = 0;
next = size;
}
const result = new Uint8Array(buffer, pos, size);
pos = next;
return result;
};
};
const random = bufferedRandom();
const monotonic = (entropy = random) => {
let bytes = new Uint8Array(10);
let randomBytes = new Uint8Array(bytes.buffer, 2);
let lastTimestamp = 0;
let seq;
return (size, timestamp2) => {
if (bytes.byteLength !== size) {
bytes = new Uint8Array(size);
randomBytes = new Uint8Array(bytes.buffer, 2);
}
if (timestamp2 > lastTimestamp) {
bytes.set(entropy(10, timestamp2));
bytes[0] &= 7;
lastTimestamp = timestamp2;
seq = void 0;
} else {
if (seq === void 0)
seq = (bytes[0] & 15) << 8 | bytes[1];
seq++;
randomBytes.set(entropy(8, timestamp2));
bytes[0] = seq >> 8 & 255;
bytes[1] = seq & 255;
}
return bytes;
};
};
class ParseError extends Error {
name = "ParseError";
}
const getTime = (time) => {
if (time == null)
return Date.now();
if (typeof time === "number")
return time;
if (time instanceof Date)
return +time;
if (typeof time === "function")
return time();
return getTime(time.time);
};
const hex = (n, width = 2) => n.toString(16).padStart(width, "0");
const variant = 2 << 6;
export {
ParseError,
bufferedRandom,
mod_default as default,
generator,
monotonic,
pattern,
random,
timestamp,
v7
};
//# sourceMappingURL=mod.js.map