Comparing version 2.3.1 to 2.3.2
# Changelog | ||
## v2.3.2 - 2023-03-19 | ||
### Added | ||
- `generateNoRewind()` and `generateCoreNoRewind()` to `Scru128Generator` | ||
(experimental) | ||
### Changed | ||
- Precedence of PRNG selection in Node.js: Web Crypto first if available | ||
- node:crypto > Web Crypto > Math.random -> Web > node > Math | ||
### Maintenance | ||
- Improved documentation about generator method flavors | ||
- Updated dev dependencies | ||
## v2.3.1 - 2023-02-18 | ||
@@ -4,0 +21,0 @@ |
@@ -37,3 +37,3 @@ /** | ||
/** | ||
* 16-byte byte array containing the 128-bit unsigned integer representation | ||
* A 16-byte byte array containing the 128-bit unsigned integer representation | ||
* in the big-endian (network) byte order. | ||
@@ -58,6 +58,6 @@ */ | ||
* | ||
* @param timestamp - 48-bit `timestamp` field value. | ||
* @param counterHi - 24-bit `counter_hi` field value. | ||
* @param counterLo - 24-bit `counter_lo` field value. | ||
* @param entropy - 32-bit `entropy` field value. | ||
* @param timestamp - A 48-bit `timestamp` field value. | ||
* @param counterHi - A 24-bit `counter_hi` field value. | ||
* @param counterLo - A 24-bit `counter_lo` field value. | ||
* @param entropy - A 32-bit `entropy` field value. | ||
* @throws RangeError if any argument is out of the value range of the field. | ||
@@ -118,4 +118,4 @@ * @category Conversion | ||
* | ||
* @param value - 16-byte buffer that represents a 128-bit unsigned integer in | ||
* the big-endian (network) byte order. | ||
* @param value - A 16-byte buffer that represents a 128-bit unsigned integer | ||
* in the big-endian (network) byte order. | ||
* @throws TypeError if the byte length of the argument is not 16. | ||
@@ -180,2 +180,20 @@ * @category Conversion | ||
* ``` | ||
* | ||
* @remarks | ||
* The generator offers four different methods to generate a SCRU128 ID: | ||
* | ||
* | Flavor | Timestamp | On big clock rewind | | ||
* | ---------------------------- | --------- | ------------------- | | ||
* | {@link generate} | Now | Rewinds state | | ||
* | {@link generateNoRewind} | Now | Returns `undefined` | | ||
* | {@link generateCore} | Argument | Rewinds state | | ||
* | {@link generateCoreNoRewind} | Argument | Returns `undefined` | | ||
* | ||
* Each method returns monotonically increasing IDs unless a `timestamp` | ||
* provided is significantly (by ten seconds or more by default) smaller than | ||
* the one embedded in the immediately preceding ID. If such a significant clock | ||
* rollback is detected, the `generate` method rewinds the generator state and | ||
* returns a new ID based on the current `timestamp`, whereas the experimental | ||
* `NoRewind` variants keep the state untouched and return `undefined`. `Core` | ||
* functions offer low-level primitives. | ||
*/ | ||
@@ -186,7 +204,7 @@ export declare class Scru128Generator { | ||
private counterLo; | ||
/** Timestamp at the last renewal of `counter_hi` field. */ | ||
/** The timestamp at the last renewal of `counter_hi` field. */ | ||
private tsCounterHi; | ||
/** Status code reported at the last generation. */ | ||
/** The status code reported at the last generation. */ | ||
private lastStatus; | ||
/** Random number generator used by the generator. */ | ||
/** The random number generator used by the generator. */ | ||
private rng; | ||
@@ -202,11 +220,40 @@ /** | ||
}); | ||
/** Generates a new SCRU128 ID object. */ | ||
/** | ||
* Generates a new SCRU128 ID object from the current `timestamp`. | ||
* | ||
* See the {@link Scru128Generator} class documentation for the description. | ||
*/ | ||
generate(): Scru128Id; | ||
/** | ||
* Generates a new SCRU128 ID object with the `timestamp` passed. | ||
* Generates a new SCRU128 ID object from the current `timestamp`, | ||
* guaranteeing the monotonic order of generated IDs despite a significant | ||
* timestamp rollback. | ||
* | ||
* @throws RangeError if the argument is not a 48-bit positive integer. | ||
* See the {@link Scru128Generator} class documentation for the description. | ||
* | ||
* @experimental | ||
*/ | ||
generateNoRewind(): Scru128Id | undefined; | ||
/** | ||
* Generates a new SCRU128 ID object from the `timestamp` passed. | ||
* | ||
* See the {@link Scru128Generator} class documentation for the description. | ||
* | ||
* @throws RangeError if `timestamp` is not a 48-bit positive integer. | ||
*/ | ||
generateCore(timestamp: number): Scru128Id; | ||
/** | ||
* Generates a new SCRU128 ID object from the `timestamp` passed, guaranteeing | ||
* the monotonic order of generated IDs despite a significant timestamp | ||
* rollback. | ||
* | ||
* See the {@link Scru128Generator} class documentation for the description. | ||
* | ||
* @param rollbackAllowance - The amount of `timestamp` rollback that is | ||
* considered significant. A suggested value is `10_000` (milliseconds). | ||
* @throws RangeError if `timestamp` is not a 48-bit positive integer. | ||
* @experimental | ||
*/ | ||
generateCoreNoRewind(timestamp: number, rollbackAllowance: number): Scru128Id | undefined; | ||
/** | ||
* Returns a status code that indicates the internal state involved in the | ||
@@ -261,4 +308,4 @@ * last generation of ID. | ||
* | ||
* This method wraps the result of {@link generate | generate()} in an | ||
* [`IteratorResult`] object to use `this` as an infinite iterator. | ||
* This method wraps the result of {@link generate} in an [`IteratorResult`] | ||
* object to use `this` as an infinite iterator. | ||
* | ||
@@ -269,10 +316,10 @@ * [`IteratorResult`]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Iteration_protocols | ||
} | ||
/** Generates a new SCRU128 ID object. */ | ||
/** Generates a new SCRU128 ID object using the global generator. */ | ||
export declare const scru128: () => Scru128Id; | ||
/** | ||
* Generates a new SCRU128 ID encoded in a string. | ||
* Generates a new SCRU128 ID encoded in a string using the global generator. | ||
* | ||
* Use this function to quickly get a new SCRU128 ID as a string. | ||
* | ||
* @returns 25-digit canonical string representation. | ||
* @returns The 25-digit canonical string representation. | ||
* @example | ||
@@ -279,0 +326,0 @@ * ```javascript |
@@ -21,11 +21,11 @@ /** | ||
*/ | ||
/** Maximum value of 48-bit `timestamp` field. */ | ||
/** The maximum value of 48-bit `timestamp` field. */ | ||
const MAX_TIMESTAMP = 281474976710655; | ||
/** Maximum value of 24-bit `counter_hi` field. */ | ||
/** The maximum value of 24-bit `counter_hi` field. */ | ||
const MAX_COUNTER_HI = 16777215; | ||
/** Maximum value of 24-bit `counter_lo` field. */ | ||
/** The maximum value of 24-bit `counter_lo` field. */ | ||
const MAX_COUNTER_LO = 16777215; | ||
/** Digit characters used in the Base36 notation. */ | ||
const DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | ||
/** O(1) map from ASCII code points to Base36 digit values. */ | ||
/** An O(1) map from ASCII code points to Base36 digit values. */ | ||
const DECODE_MAP = [ | ||
@@ -43,2 +43,4 @@ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, | ||
]; | ||
/** The default timestamp rollback allowance. */ | ||
const DEFAULT_ROLLBACK_ALLOWANCE = 10000; // 10 seconds | ||
/** | ||
@@ -82,6 +84,6 @@ * Represents a SCRU128 ID and provides converters and comparison operators. | ||
* | ||
* @param timestamp - 48-bit `timestamp` field value. | ||
* @param counterHi - 24-bit `counter_hi` field value. | ||
* @param counterLo - 24-bit `counter_lo` field value. | ||
* @param entropy - 32-bit `entropy` field value. | ||
* @param timestamp - A 48-bit `timestamp` field value. | ||
* @param counterHi - A 24-bit `counter_hi` field value. | ||
* @param counterLo - A 24-bit `counter_lo` field value. | ||
* @param entropy - A 32-bit `entropy` field value. | ||
* @throws RangeError if any argument is out of the value range of the field. | ||
@@ -221,3 +223,3 @@ * @category Conversion | ||
let text = ""; | ||
for (let d of dst) { | ||
for (const d of dst) { | ||
text += DIGITS.charAt(d); | ||
@@ -261,4 +263,4 @@ } | ||
* | ||
* @param value - 16-byte buffer that represents a 128-bit unsigned integer in | ||
* the big-endian (network) byte order. | ||
* @param value - A 16-byte buffer that represents a 128-bit unsigned integer | ||
* in the big-endian (network) byte order. | ||
* @throws TypeError if the byte length of the argument is not 16. | ||
@@ -314,3 +316,3 @@ * @category Conversion | ||
let text = "0x"; | ||
for (let e of this.bytes) { | ||
for (const e of this.bytes) { | ||
text += digits.charAt(e >>> 4); | ||
@@ -373,2 +375,20 @@ text += digits.charAt(e & 0xf); | ||
* ``` | ||
* | ||
* @remarks | ||
* The generator offers four different methods to generate a SCRU128 ID: | ||
* | ||
* | Flavor | Timestamp | On big clock rewind | | ||
* | ---------------------------- | --------- | ------------------- | | ||
* | {@link generate} | Now | Rewinds state | | ||
* | {@link generateNoRewind} | Now | Returns `undefined` | | ||
* | {@link generateCore} | Argument | Rewinds state | | ||
* | {@link generateCoreNoRewind} | Argument | Returns `undefined` | | ||
* | ||
* Each method returns monotonically increasing IDs unless a `timestamp` | ||
* provided is significantly (by ten seconds or more by default) smaller than | ||
* the one embedded in the immediately preceding ID. If such a significant clock | ||
* rollback is detected, the `generate` method rewinds the generator state and | ||
* returns a new ID based on the current `timestamp`, whereas the experimental | ||
* `NoRewind` variants keep the state untouched and return `undefined`. `Core` | ||
* functions offer low-level primitives. | ||
*/ | ||
@@ -385,9 +405,13 @@ export class Scru128Generator { | ||
this.counterLo = 0; | ||
/** Timestamp at the last renewal of `counter_hi` field. */ | ||
/** The timestamp at the last renewal of `counter_hi` field. */ | ||
this.tsCounterHi = 0; | ||
/** Status code reported at the last generation. */ | ||
/** The status code reported at the last generation. */ | ||
this.lastStatus = "NOT_EXECUTED"; | ||
this.rng = randomNumberGenerator || new DefaultRandom(); | ||
} | ||
/** Generates a new SCRU128 ID object. */ | ||
/** | ||
* Generates a new SCRU128 ID object from the current `timestamp`. | ||
* | ||
* See the {@link Scru128Generator} class documentation for the description. | ||
*/ | ||
generate() { | ||
@@ -397,7 +421,45 @@ return this.generateCore(Date.now()); | ||
/** | ||
* Generates a new SCRU128 ID object with the `timestamp` passed. | ||
* Generates a new SCRU128 ID object from the current `timestamp`, | ||
* guaranteeing the monotonic order of generated IDs despite a significant | ||
* timestamp rollback. | ||
* | ||
* @throws RangeError if the argument is not a 48-bit positive integer. | ||
* See the {@link Scru128Generator} class documentation for the description. | ||
* | ||
* @experimental | ||
*/ | ||
generateNoRewind() { | ||
return this.generateCoreNoRewind(Date.now(), DEFAULT_ROLLBACK_ALLOWANCE); | ||
} | ||
/** | ||
* Generates a new SCRU128 ID object from the `timestamp` passed. | ||
* | ||
* See the {@link Scru128Generator} class documentation for the description. | ||
* | ||
* @throws RangeError if `timestamp` is not a 48-bit positive integer. | ||
*/ | ||
generateCore(timestamp) { | ||
const rollbackAllowance = DEFAULT_ROLLBACK_ALLOWANCE; | ||
let value = this.generateCoreNoRewind(timestamp, rollbackAllowance); | ||
if (value === undefined) { | ||
// reset state and resume | ||
this.timestamp = 0; | ||
this.tsCounterHi = 0; | ||
value = this.generateCoreNoRewind(timestamp, rollbackAllowance); | ||
this.lastStatus = "CLOCK_ROLLBACK"; | ||
} | ||
return value; | ||
} | ||
/** | ||
* Generates a new SCRU128 ID object from the `timestamp` passed, guaranteeing | ||
* the monotonic order of generated IDs despite a significant timestamp | ||
* rollback. | ||
* | ||
* See the {@link Scru128Generator} class documentation for the description. | ||
* | ||
* @param rollbackAllowance - The amount of `timestamp` rollback that is | ||
* considered significant. A suggested value is `10_000` (milliseconds). | ||
* @throws RangeError if `timestamp` is not a 48-bit positive integer. | ||
* @experimental | ||
*/ | ||
generateCoreNoRewind(timestamp, rollbackAllowance) { | ||
if (!Number.isInteger(timestamp) || | ||
@@ -408,8 +470,12 @@ timestamp < 1 || | ||
} | ||
this.lastStatus = "NEW_TIMESTAMP"; | ||
else if (rollbackAllowance < 0 || rollbackAllowance > MAX_TIMESTAMP) { | ||
throw new RangeError("`rollbackAllowance` out of reasonable range"); | ||
} | ||
if (timestamp > this.timestamp) { | ||
this.timestamp = timestamp; | ||
this.counterLo = this.rng.nextUint32() & MAX_COUNTER_LO; | ||
this.lastStatus = "NEW_TIMESTAMP"; | ||
} | ||
else if (timestamp + 10000 > this.timestamp) { | ||
else if (timestamp + rollbackAllowance > this.timestamp) { | ||
// go on with previous timestamp if new one is not much smaller | ||
this.counterLo++; | ||
@@ -431,7 +497,4 @@ this.lastStatus = "COUNTER_LO_INC"; | ||
else { | ||
// reset state if clock moves back by ten seconds or more | ||
this.tsCounterHi = 0; | ||
this.timestamp = timestamp; | ||
this.counterLo = this.rng.nextUint32() & MAX_COUNTER_LO; | ||
this.lastStatus = "CLOCK_ROLLBACK"; | ||
// abort if clock moves back to unbearable extent | ||
return undefined; | ||
} | ||
@@ -498,4 +561,4 @@ if (this.timestamp - this.tsCounterHi >= 1000 || this.tsCounterHi < 1) { | ||
* | ||
* This method wraps the result of {@link generate | generate()} in an | ||
* [`IteratorResult`] object to use `this` as an infinite iterator. | ||
* This method wraps the result of {@link generate} in an [`IteratorResult`] | ||
* object to use `this` as an infinite iterator. | ||
* | ||
@@ -547,11 +610,11 @@ * [`IteratorResult`]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Iteration_protocols | ||
} | ||
let defaultGenerator; | ||
/** Generates a new SCRU128 ID object. */ | ||
export const scru128 = () => (defaultGenerator || (defaultGenerator = new Scru128Generator())).generate(); | ||
let globalGenerator; | ||
/** Generates a new SCRU128 ID object using the global generator. */ | ||
export const scru128 = () => (globalGenerator || (globalGenerator = new Scru128Generator())).generate(); | ||
/** | ||
* Generates a new SCRU128 ID encoded in a string. | ||
* Generates a new SCRU128 ID encoded in a string using the global generator. | ||
* | ||
* Use this function to quickly get a new SCRU128 ID as a string. | ||
* | ||
* @returns 25-digit canonical string representation. | ||
* @returns The 25-digit canonical string representation. | ||
* @example | ||
@@ -558,0 +621,0 @@ * ```javascript |
export * from "./index.js"; | ||
import * as nodeCrypto from "crypto"; | ||
import { _setRandom } from "./index.js"; | ||
if (nodeCrypto && nodeCrypto.randomFillSync) { | ||
_setRandom(nodeCrypto.randomFillSync); | ||
if (typeof crypto === "undefined" || !crypto.getRandomValues) { | ||
if (nodeCrypto && nodeCrypto.randomFillSync) { | ||
_setRandom(nodeCrypto.randomFillSync); | ||
} | ||
} |
{ | ||
"name": "scru128", | ||
"version": "2.3.1", | ||
"version": "2.3.2", | ||
"description": "SCRU128: Sortable, Clock and Random number-based Unique identifier", | ||
@@ -55,7 +55,7 @@ "type": "module", | ||
"mocha": "^10.2.0", | ||
"typedoc": "^0.23.25", | ||
"typedoc": "^0.23.27", | ||
"typescript": "^4.9.5", | ||
"webpack": "^5.75.0", | ||
"webpack": "^5.76.2", | ||
"webpack-cli": "^5.0.1" | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
77944
1608