position-strings
Version:
Lexicographically-ordered position strings for collaborative lists and text
107 lines • 4.51 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.IDs = void 0;
const util_1 = require("./util");
/**
* Utitilies for generating `PositionSource` IDs
* (the `options.ID` constructor argument).
*/
class IDs {
constructor() {
// Not instantiable.
}
/**
* Returns a cryptographically random ID made of alphanumeric characters.
*
* @param options.length The length of the ID, in characters.
* Default: `IDs.DEFAULT_LENGTH`.
* @param options.chars The characters to draw from. Default: `IDs.DEFAULT_CHARS`.
*
* If specified, only the first 256 elements are used, and you achieve
* about `log_2(chars.length)` bits of entropy per `length`.
*/
static random(options) {
const length = options?.length ?? this.DEFAULT_LENGTH;
const chars = options?.chars ?? this.DEFAULT_CHARS;
const arr = new Array(length);
let randomValues = new Uint8Array(length);
if (typeof window === "undefined") {
// Use Node crypto library.
// We use eval("require") to prevent Webpack from attempting
// to bundle the crypto module and complaining.
// In theory we should also be able to do this by
// adding "browser": {"crypto": false} to package.json,
// but that is not working, and besides, every user
// of this package would have to remember to do so.
// See https://github.com/webpack/webpack/issues/8826
const cryptoReal = (eval("require")("crypto"));
const randomBuffer = cryptoReal.randomBytes(length);
randomValues = new Uint8Array(randomBuffer);
}
else {
// Use browser crypto library.
window.crypto.getRandomValues(randomValues);
}
for (let i = 0; i < length; i++) {
// This will be biased if chars.length does not divide 256,
// but it will still give at least floor(log_2(chars.length))
// bits of entropy.
arr[i] = chars[randomValues[i] % chars.length];
}
return arr.join("");
}
/**
* Returns a psuedorandom ID made of alphanumeric characters,
* generated using `rng` from package [seedrandom](https://www.npmjs.com/package/seedrandom).
*
* Pseudorandom IDs with a fixed seed are recommended for
* tests and benchmarks, to make them deterministic.
*
* @param options.length The length of the ID, in characters.
* Default: `IDs.DEFAULT_LENGTH`.
* @param options.chars The characters to draw from. Default: `IDs.DEFAULT_CHARS`.
*
* If specified, only the first 256 elements are used, and you achieve
* about `log_2(chars.length)` bits of entropy per `length`.
*/
static pseudoRandom(rng, options) {
const length = options?.length ?? this.DEFAULT_LENGTH;
const chars = options?.chars ?? this.DEFAULT_CHARS;
const arr = new Array(length);
for (let i = 0; i < arr.length; i++) {
// Although we could pick chars without bias, we instead use the
// same bias as `random`, for consistency.
arr[i] = chars[Math.floor(rng() * 256) % chars.length];
}
return arr.join("");
}
/**
* Throws an error if `ID` does not satisfy the
* following requirements from `PositionSource`'s constructor:
* - It does not contain `','` or `'.'`.
* - The first character is lexicographically less than `'~'` (code point 126).
*/
static validate(ID) {
(0, util_1.precond)(ID < util_1.LastInternal, "ID must be less than", util_1.LastInternal, ":", ID);
(0, util_1.precond)(!ID.includes(","), "ID must not contain ',':", ID);
(0, util_1.precond)(!ID.includes("."), "ID must not contain '.':", ID);
}
}
exports.IDs = IDs;
/**
* Default characters used in IDs: alphanumeric chars.
*/
IDs.DEFAULT_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
// Rationale for value 8:
// Each character of the ID gives us ~6 bits of entropy,
// for a total of ~48 bits. This gives a < 1%
// probability that two connected `PositionSource`s
// will ever choose the same IDs, even if we
// consider the total probability across 100,000,000
// documents with 1,000 IDs each
// (= 10 users x 100 days x 1 ID/user/day).
/**
* The default length of an ID, in characters.
*/
IDs.DEFAULT_LENGTH = 8;
//# sourceMappingURL=ids.js.map