Skip to content

Commit

Permalink
v0.5.4 Released
Browse files Browse the repository at this point in the history
Added:

- Generator core functions as stable API
  - `V7Generator#generateOrResetCore()`
  - `V7Generator#generateOrAbortCore()`

Changed:

- Changed edge case behavior of generator functions' rollback allowance handling

Maintenance:

- Refactored default random number generator to defer detection Web Crypto API
  until creation of `V7Generator`
- Expanded test cases for `V7Generator` and `UUID.parse()`
- Updated reference to draft RFC: rfc4122bis/07 -> rfc4122bis/08
  • Loading branch information
LiosK committed Aug 1, 2023
2 parents 88f78f7 + 966b7c6 commit 490d9e0
Show file tree
Hide file tree
Showing 19 changed files with 285 additions and 124 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Command-line interface:
npx uuidv7
```

See [draft-ietf-uuidrev-rfc4122bis-07](https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-07.html).
See [draft-ietf-uuidrev-rfc4122bis-08](https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-08.html).

## Field and bit layout

Expand Down Expand Up @@ -66,7 +66,7 @@ UUIDv7, by design, heavily relies on the system's wall clock to guarantee the
monotonically increasing order of generated IDs. A generator may not be able to
produce a monotonic sequence if the system clock goes backwards. This library
ignores a clock rollback and freezes the previously used `unix_ts_ms` unless the
clock rollback is considered significant (by ten seconds or more). If such a
clock rollback is considered significant (by more than ten seconds). If such a
significant rollback takes place, this library resets the generator and thus
breaks the monotonic order of generated IDs.

Expand Down
6 changes: 2 additions & 4 deletions dist/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export declare class V7Generator {
* generator upon significant timestamp rollback.
*
* This method returns monotonically increasing UUIDs unless the up-to-date
* timestamp is significantly (by ten seconds or more) smaller than the one
* timestamp is significantly (by more than ten seconds) smaller than the one
* embedded in the immediately preceding UUID. If such a significant clock
* rollback is detected, this method resets the generator and returns a new
* UUID based on the current timestamp.
Expand All @@ -108,7 +108,7 @@ export declare class V7Generator {
* `undefined` upon significant timestamp rollback.
*
* This method returns monotonically increasing UUIDs unless the up-to-date
* timestamp is significantly (by ten seconds or more) smaller than the one
* timestamp is significantly (by more than ten seconds) smaller than the one
* embedded in the immediately preceding UUID. If such a significant clock
* rollback is detected, this method aborts and returns `undefined`.
*/
Expand All @@ -123,7 +123,6 @@ export declare class V7Generator {
* @param rollbackAllowance - The amount of `unixTsMs` rollback that is
* considered significant. A suggested value is `10_000` (milliseconds).
* @throws RangeError if `unixTsMs` is not a 48-bit positive integer.
* @experimental
*/
generateOrResetCore(unixTsMs: number, rollbackAllowance: number): UUID;
/**
Expand All @@ -136,7 +135,6 @@ export declare class V7Generator {
* @param rollbackAllowance - The amount of `unixTsMs` rollback that is
* considered significant. A suggested value is `10_000` (milliseconds).
* @throws RangeError if `unixTsMs` is not a 48-bit positive integer.
* @experimental
*/
generateOrAbortCore(unixTsMs: number, rollbackAllowance: number): UUID | undefined;
/** Initializes the counter at a 42-bit random integer. */
Expand Down
69 changes: 37 additions & 32 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,14 +201,14 @@ export class V7Generator {
* generator.
*/
static create() {
return new V7Generator(new DefaultRandom());
return new V7Generator(getDefaultRandom());
}
/**
* Generates a new UUIDv7 object from the current timestamp, or resets the
* generator upon significant timestamp rollback.
*
* This method returns monotonically increasing UUIDs unless the up-to-date
* timestamp is significantly (by ten seconds or more) smaller than the one
* timestamp is significantly (by more than ten seconds) smaller than the one
* embedded in the immediately preceding UUID. If such a significant clock
* rollback is detected, this method resets the generator and returns a new
* UUID based on the current timestamp.
Expand All @@ -221,7 +221,7 @@ export class V7Generator {
* `undefined` upon significant timestamp rollback.
*
* This method returns monotonically increasing UUIDs unless the up-to-date
* timestamp is significantly (by ten seconds or more) smaller than the one
* timestamp is significantly (by more than ten seconds) smaller than the one
* embedded in the immediately preceding UUID. If such a significant clock
* rollback is detected, this method aborts and returns `undefined`.
*/
Expand All @@ -238,7 +238,6 @@ export class V7Generator {
* @param rollbackAllowance - The amount of `unixTsMs` rollback that is
* considered significant. A suggested value is `10_000` (milliseconds).
* @throws RangeError if `unixTsMs` is not a 48-bit positive integer.
* @experimental
*/
generateOrResetCore(unixTsMs, rollbackAllowance) {
let value = this.generateOrAbortCore(unixTsMs, rollbackAllowance);
Expand All @@ -259,7 +258,6 @@ export class V7Generator {
* @param rollbackAllowance - The amount of `unixTsMs` rollback that is
* considered significant. A suggested value is `10_000` (milliseconds).
* @throws RangeError if `unixTsMs` is not a 48-bit positive integer.
* @experimental
*/
generateOrAbortCore(unixTsMs, rollbackAllowance) {
const MAX_COUNTER = 4398046511103;
Expand All @@ -275,7 +273,7 @@ export class V7Generator {
this.timestamp = unixTsMs;
this.resetCounter();
}
else if (unixTsMs + rollbackAllowance > this.timestamp) {
else if (unixTsMs + rollbackAllowance >= this.timestamp) {
// go on with previous timestamp if new one is not much smaller
this.counter++;
if (this.counter > MAX_COUNTER) {
Expand All @@ -295,37 +293,49 @@ export class V7Generator {
this.counter =
this.random.nextUint32() * 0x400 + (this.random.nextUint32() & 0x3ff);
}
/**
* Generates a new UUIDv4 object utilizing the random number generator inside.
*
* @internal
*/
generateV4() {
const bytes = new Uint8Array(Uint32Array.of(this.random.nextUint32(), this.random.nextUint32(), this.random.nextUint32(), this.random.nextUint32()).buffer);
bytes[6] = 0x40 | (bytes[6] >>> 4);
bytes[8] = 0x80 | (bytes[8] >>> 2);
return UUID.ofInner(bytes);
}
}
/** Stores `crypto.getRandomValues()` available in the environment. */
let getRandomValues = (buffer) => {
// fall back on Math.random() unless the flag is set to true
if (typeof UUIDV7_DENY_WEAK_RNG !== "undefined" && UUIDV7_DENY_WEAK_RNG) {
throw new Error("no cryptographically strong RNG available");
/** Returns the default random number generator available in the environment. */
const getDefaultRandom = () => {
// detect Web Crypto API
if (typeof crypto !== "undefined" &&
typeof crypto.getRandomValues !== "undefined") {
return new BufferedCryptoRandom();
}
for (let i = 0; i < buffer.length; i++) {
buffer[i] =
Math.trunc(Math.random() * 65536) * 65536 +
Math.trunc(Math.random() * 65536);
else {
// fall back on Math.random() unless the flag is set to true
if (typeof UUIDV7_DENY_WEAK_RNG !== "undefined" && UUIDV7_DENY_WEAK_RNG) {
throw new Error("no cryptographically strong RNG available");
}
return {
nextUint32: () => Math.trunc(Math.random() * 65536) * 65536 +
Math.trunc(Math.random() * 65536),
};
}
return buffer;
};
// detect Web Crypto API
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
getRandomValues = (buffer) => crypto.getRandomValues(buffer);
}
/**
* Wraps `crypto.getRandomValues()` and compatibles to enable buffering; this
* uses a small buffer by default to avoid unbearable throughput decline in some
* environments as well as the waste of time and space for unused values.
* Wraps `crypto.getRandomValues()` to enable buffering; this uses a small
* buffer by default to avoid both unbearable throughput decline in some
* environments and the waste of time and space for unused values.
*/
class DefaultRandom {
class BufferedCryptoRandom {
constructor() {
this.buffer = new Uint32Array(8);
this.cursor = 99;
this.cursor = 0xffff;
}
nextUint32() {
if (this.cursor >= this.buffer.length) {
getRandomValues(this.buffer);
crypto.getRandomValues(this.buffer);
this.cursor = 0;
}
return this.buffer[this.cursor++];
Expand All @@ -349,9 +359,4 @@ export const uuidv7obj = () => (defaultGenerator || (defaultGenerator = V7Genera
*/
export const uuidv4 = () => uuidv4obj().toString();
/** Generates a UUIDv4 object. */
export const uuidv4obj = () => {
const bytes = getRandomValues(new Uint8Array(16));
bytes[6] = 0x40 | (bytes[6] >>> 4);
bytes[8] = 0x80 | (bytes[8] >>> 2);
return UUID.ofInner(bytes);
};
export const uuidv4obj = () => (defaultGenerator || (defaultGenerator = V7Generator.create())).generateV4();
26 changes: 13 additions & 13 deletions docs/classes/UUID.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ <h4>Hierarchy</h4>
<ul class="tsd-hierarchy">
<li><span class="target">UUID</span></li></ul></section><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.3/src/index.ts#L12">index.ts:12</a></li></ul></aside>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.4/src/index.ts#L12">index.ts:12</a></li></ul></aside>
<section class="tsd-panel-group tsd-index-group">
<section class="tsd-panel tsd-index-panel">
<details class="tsd-index-content tsd-index-accordion" open><summary class="tsd-accordion-summary tsd-index-summary">
Expand Down Expand Up @@ -69,7 +69,7 @@ <h5><span class="tsd-kind-parameter">bytes</span>: <span class="tsd-signature-ty
<h4 class="tsd-returns-title">Returns <a href="UUID.html" class="tsd-signature-type tsd-kind-class">UUID</a></h4>
<div class="tsd-comment tsd-typography"></div><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.3/src/index.ts#L14">index.ts:14</a></li></ul></aside></li></ul></section></section>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.4/src/index.ts#L14">index.ts:14</a></li></ul></aside></li></ul></section></section>
<section class="tsd-panel-group tsd-member-group">
<h2>Properties</h2>
<section class="tsd-panel tsd-member"><a id="bytes" class="tsd-anchor"></a>
Expand All @@ -79,7 +79,7 @@ <h3 class="tsd-anchor-link"><code class="tsd-tag ts-flagReadonly">Readonly</code
</div>
<div class="tsd-comment tsd-typography"></div><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.3/src/index.ts#L14">index.ts:14</a></li></ul></aside></section></section>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.4/src/index.ts#L14">index.ts:14</a></li></ul></aside></section></section>
<section class="tsd-panel-group tsd-member-group">
<h2>Methods</h2>
<section class="tsd-panel tsd-member"><a id="clone" class="tsd-anchor"></a>
Expand All @@ -92,7 +92,7 @@ <h3 class="tsd-anchor-link"><span>clone</span><a href="#clone" aria-label="Perma
<h4 class="tsd-returns-title">Returns <a href="UUID.html" class="tsd-signature-type tsd-kind-class">UUID</a></h4>
<div class="tsd-comment tsd-typography"></div><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.3/src/index.ts#L179">index.ts:179</a></li></ul></aside></li></ul></section>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.4/src/index.ts#L179">index.ts:179</a></li></ul></aside></li></ul></section>
<section class="tsd-panel tsd-member"><a id="compareTo" class="tsd-anchor"></a>
<h3 class="tsd-anchor-link"><span>compare<wbr/>To</span><a href="#compareTo" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></h3>
<ul class="tsd-signatures">
Expand All @@ -109,7 +109,7 @@ <h5><span class="tsd-kind-parameter">other</span>: <a href="UUID.html" class="ts
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">number</span></h4>
<div class="tsd-comment tsd-typography"></div><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.3/src/index.ts#L192">index.ts:192</a></li></ul></aside></li></ul></section>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.4/src/index.ts#L192">index.ts:192</a></li></ul></aside></li></ul></section>
<section class="tsd-panel tsd-member"><a id="equals" class="tsd-anchor"></a>
<h3 class="tsd-anchor-link"><span>equals</span><a href="#equals" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></h3>
<ul class="tsd-signatures">
Expand All @@ -125,7 +125,7 @@ <h5><span class="tsd-kind-parameter">other</span>: <a href="UUID.html" class="ts
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">boolean</span></h4>
<div class="tsd-comment tsd-typography"></div><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.3/src/index.ts#L184">index.ts:184</a></li></ul></aside></li></ul></section>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.4/src/index.ts#L184">index.ts:184</a></li></ul></aside></li></ul></section>
<section class="tsd-panel tsd-member"><a id="getVariant" class="tsd-anchor"></a>
<h3 class="tsd-anchor-link"><span>get<wbr/>Variant</span><a href="#getVariant" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></h3>
<ul class="tsd-signatures">
Expand All @@ -140,7 +140,7 @@ <h3 class="tsd-anchor-link"><span>get<wbr/>Variant</span><a href="#getVariant" a
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">&quot;VAR_0&quot;</span><span class="tsd-signature-symbol"> | </span><span class="tsd-signature-type">&quot;VAR_10&quot;</span><span class="tsd-signature-symbol"> | </span><span class="tsd-signature-type">&quot;VAR_110&quot;</span><span class="tsd-signature-symbol"> | </span><span class="tsd-signature-type">&quot;VAR_RESERVED&quot;</span><span class="tsd-signature-symbol"> | </span><span class="tsd-signature-type">&quot;NIL&quot;</span><span class="tsd-signature-symbol"> | </span><span class="tsd-signature-type">&quot;MAX&quot;</span></h4>
<div class="tsd-comment tsd-typography"></div><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.3/src/index.ts#L147">index.ts:147</a></li></ul></aside></li></ul></section>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.4/src/index.ts#L147">index.ts:147</a></li></ul></aside></li></ul></section>
<section class="tsd-panel tsd-member"><a id="getVersion" class="tsd-anchor"></a>
<h3 class="tsd-anchor-link"><span>get<wbr/>Version</span><a href="#getVersion" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></h3>
<ul class="tsd-signatures">
Expand All @@ -152,7 +152,7 @@ <h3 class="tsd-anchor-link"><span>get<wbr/>Version</span><a href="#getVersion" a
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">undefined</span><span class="tsd-signature-symbol"> | </span><span class="tsd-signature-type">number</span></h4>
<div class="tsd-comment tsd-typography"></div><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.3/src/index.ts#L174">index.ts:174</a></li></ul></aside></li></ul></section>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.4/src/index.ts#L174">index.ts:174</a></li></ul></aside></li></ul></section>
<section class="tsd-panel tsd-member"><a id="toJSON" class="tsd-anchor"></a>
<h3 class="tsd-anchor-link"><span>toJSON</span><a href="#toJSON" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></h3>
<ul class="tsd-signatures">
Expand All @@ -162,7 +162,7 @@ <h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">string</s

<div class="tsd-comment tsd-typography"></div><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.3/src/index.ts#L125">index.ts:125</a></li></ul></aside></li></ul></section>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.4/src/index.ts#L125">index.ts:125</a></li></ul></aside></li></ul></section>
<section class="tsd-panel tsd-member"><a id="toString" class="tsd-anchor"></a>
<h3 class="tsd-anchor-link"><span>to<wbr/>String</span><a href="#toString" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></h3>
<ul class="tsd-signatures">
Expand All @@ -172,7 +172,7 @@ <h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">string</s

<div class="tsd-comment tsd-typography"></div><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.3/src/index.ts#L112">index.ts:112</a></li></ul></aside></li></ul></section>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.4/src/index.ts#L112">index.ts:112</a></li></ul></aside></li></ul></section>
<section class="tsd-panel tsd-member"><a id="fromFieldsV7" class="tsd-anchor"></a>
<h3 class="tsd-anchor-link"><code class="tsd-tag ts-flagStatic">Static</code> <span>from<wbr/>Fields<wbr/>V7</span><a href="#fromFieldsV7" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></h3>
<ul class="tsd-signatures">
Expand Down Expand Up @@ -208,7 +208,7 @@ <h4 class="tsd-returns-title">Returns <a href="UUID.html" class="tsd-signature-t
<h4>Throws</h4><p>RangeError if any field value is out of the specified range.</p>
</div><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.3/src/index.ts#L42">index.ts:42</a></li></ul></aside></li></ul></section>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.4/src/index.ts#L42">index.ts:42</a></li></ul></aside></li></ul></section>
<section class="tsd-panel tsd-member"><a id="ofInner" class="tsd-anchor"></a>
<h3 class="tsd-anchor-link"><code class="tsd-tag ts-flagStatic">Static</code> <span>of<wbr/>Inner</span><a href="#ofInner" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></h3>
<ul class="tsd-signatures">
Expand All @@ -229,7 +229,7 @@ <h4 class="tsd-returns-title">Returns <a href="UUID.html" class="tsd-signature-t
<h4>Throws</h4><p>TypeError if the length of the argument is not 16.</p>
</div><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.3/src/index.ts#L25">index.ts:25</a></li></ul></aside></li></ul></section>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.4/src/index.ts#L25">index.ts:25</a></li></ul></aside></li></ul></section>
<section class="tsd-panel tsd-member"><a id="parse" class="tsd-anchor"></a>
<h3 class="tsd-anchor-link"><code class="tsd-tag ts-flagStatic">Static</code> <span>parse</span><a href="#parse" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></h3>
<ul class="tsd-signatures">
Expand All @@ -248,7 +248,7 @@ <h4 class="tsd-returns-title">Returns <a href="UUID.html" class="tsd-signature-t
<h4>Throws</h4><p>SyntaxError if the argument could not parse as a valid UUID string.</p>
</div><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.3/src/index.ts#L92">index.ts:92</a></li></ul></aside></li></ul></section></section></div>
<li>Defined in <a href="https://github.com/LiosK/uuidv7/blob/v0.5.4/src/index.ts#L92">index.ts:92</a></li></ul></aside></li></ul></section></section></div>
<div class="col-sidebar">
<div class="page-menu">
<div class="tsd-navigation settings">
Expand Down
Loading

0 comments on commit 490d9e0

Please sign in to comment.