Comparing version 0.4.0 to 0.5.0
@@ -24,36 +24,7 @@ /** | ||
/** | ||
* Represents a SCRU128 ID generator that encapsulates the monotonic counter and | ||
* other internal states. | ||
* Represents a SCRU128 ID and provides various converters and comparison | ||
* operators. | ||
* | ||
* @example | ||
* ```javascript | ||
* import { Scru128Generator } from "scru128"; | ||
* | ||
* const g = new Scru128Generator(); | ||
* const x = g.generate(); | ||
* console.log(String(x)); | ||
* console.log(BigInt(x.toHex())); | ||
* ``` | ||
*/ | ||
export declare class Scru128Generator { | ||
/** Timestamp at last generation. */ | ||
private tsLastGen; | ||
/** Counter at last generation. */ | ||
private counter; | ||
/** Timestamp at last renewal of perSecRandom. */ | ||
private tsLastSec; | ||
/** Per-second random value at last generation. */ | ||
private perSecRandom; | ||
/** Maximum number of checking the system clock until it goes forward. */ | ||
private nClockCheckMax; | ||
/** Returns a 32-bit (cryptographically strong) random unsigned integer. */ | ||
private getRandomUint32; | ||
/** Generates a new SCRU128 ID object. */ | ||
generate(): Scru128Id; | ||
} | ||
/** | ||
* Represents a SCRU128 ID and provides converters to/from string and numbers. | ||
* | ||
* @example | ||
* ```javascript | ||
* import { Scru128Id } from "scru128"; | ||
@@ -127,2 +98,43 @@ * | ||
} | ||
/** | ||
* Specifies the logger object used in the package. | ||
* | ||
* Logging is disabled by default. Set a logger object to enable logging. The | ||
* interface is compatible with the console object. | ||
*/ | ||
export declare const setLogger: (newLogger: { | ||
error: (message: string) => void; | ||
warn: (message: string) => void; | ||
info: (message: string) => void; | ||
}) => void; | ||
/** | ||
* Represents a SCRU128 ID generator that encapsulates the monotonic counter and | ||
* other internal states. | ||
* | ||
* @example | ||
* ```javascript | ||
* import { Scru128Generator } from "scru128"; | ||
* | ||
* const g = new Scru128Generator(); | ||
* const x = g.generate(); | ||
* console.log(String(x)); | ||
* console.log(BigInt(x.toHex())); | ||
* ``` | ||
*/ | ||
export declare class Scru128Generator { | ||
/** Timestamp at last generation. */ | ||
private tsLastGen; | ||
/** Counter at last generation. */ | ||
private counter; | ||
/** Timestamp at last renewal of perSecRandom. */ | ||
private tsLastSec; | ||
/** Per-second random value at last generation. */ | ||
private perSecRandom; | ||
/** Maximum number of checking the system clock until it goes forward. */ | ||
private nClockCheckMax; | ||
/** Returns a 32-bit (cryptographically strong) random unsigned integer. */ | ||
private getRandomUint32; | ||
/** Generates a new SCRU128 ID object. */ | ||
generate(): Scru128Id; | ||
} | ||
/** Generates a new SCRU128 ID object. */ | ||
@@ -129,0 +141,0 @@ export declare const scru128: () => Scru128Id; |
@@ -23,3 +23,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.scru128String = exports.scru128 = exports.Scru128Id = exports.Scru128Generator = exports.TIMESTAMP_BIAS = void 0; | ||
exports.scru128String = exports.scru128 = exports.Scru128Generator = exports.setLogger = exports.Scru128Id = exports.TIMESTAMP_BIAS = void 0; | ||
const crypto_1 = require("crypto"); | ||
@@ -30,84 +30,10 @@ /** Unix time in milliseconds at 2020-01-01 00:00:00+00:00. */ | ||
const MAX_COUNTER = 268435455; | ||
/** Digit characters used in the base 32 notation. */ | ||
const DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUV"; | ||
/** Returns a random number generator based on available cryptographic RNG. */ | ||
const detectRng = () => { | ||
if (typeof window !== "undefined" && window.crypto) { | ||
// Web Crypto API on browsers | ||
return () => window.crypto.getRandomValues(new Uint32Array(1))[0]; | ||
} | ||
else if (crypto_1.randomFillSync) { | ||
// Node.js Crypto | ||
return () => (0, crypto_1.randomFillSync)(new Uint32Array(1))[0]; | ||
} | ||
else { | ||
console.warn("scru128: fell back on Math.random() as no cryptographic RNG was detected"); | ||
return () => Math.trunc(Math.random() * 65536) * 65536 + | ||
Math.trunc(Math.random() * 65536); | ||
} | ||
}; | ||
/** | ||
* Represents a SCRU128 ID generator that encapsulates the monotonic counter and | ||
* other internal states. | ||
* Represents a SCRU128 ID and provides various converters and comparison | ||
* operators. | ||
* | ||
* @example | ||
* ```javascript | ||
* import { Scru128Generator } from "scru128"; | ||
* | ||
* const g = new Scru128Generator(); | ||
* const x = g.generate(); | ||
* console.log(String(x)); | ||
* console.log(BigInt(x.toHex())); | ||
* ``` | ||
*/ | ||
class Scru128Generator { | ||
constructor() { | ||
/** Timestamp at last generation. */ | ||
this.tsLastGen = 0; | ||
/** Counter at last generation. */ | ||
this.counter = 0; | ||
/** Timestamp at last renewal of perSecRandom. */ | ||
this.tsLastSec = 0; | ||
/** Per-second random value at last generation. */ | ||
this.perSecRandom = 0; | ||
/** Maximum number of checking the system clock until it goes forward. */ | ||
this.nClockCheckMax = 1000000; | ||
/** Returns a 32-bit (cryptographically strong) random unsigned integer. */ | ||
this.getRandomUint32 = detectRng(); | ||
} | ||
/** Generates a new SCRU128 ID object. */ | ||
generate() { | ||
// update timestamp and counter | ||
let tsNow = Date.now(); | ||
if (tsNow > this.tsLastGen) { | ||
this.tsLastGen = tsNow; | ||
this.counter = this.getRandomUint32() >>> 4; | ||
} | ||
else if (++this.counter > MAX_COUNTER) { | ||
console.info("scru128: counter limit reached; will wait until clock goes forward"); | ||
let nClockCheck = 0; | ||
while (tsNow <= this.tsLastGen) { | ||
tsNow = Date.now(); | ||
if (++nClockCheck > this.nClockCheckMax) { | ||
console.warn("scru128: reset state as clock did not go forward"); | ||
this.tsLastSec = 0; | ||
break; | ||
} | ||
} | ||
this.tsLastGen = tsNow; | ||
this.counter = this.getRandomUint32() >>> 4; | ||
} | ||
// update perSecRandom | ||
if (this.tsLastGen - this.tsLastSec > 1000) { | ||
this.tsLastSec = this.tsLastGen; | ||
this.perSecRandom = this.getRandomUint32() >>> 8; | ||
} | ||
return Scru128Id.fromFields(this.tsLastGen - exports.TIMESTAMP_BIAS, this.counter, this.perSecRandom, this.getRandomUint32()); | ||
} | ||
} | ||
exports.Scru128Generator = Scru128Generator; | ||
/** | ||
* Represents a SCRU128 ID and provides converters to/from string and numbers. | ||
* | ||
* @example | ||
* ```javascript | ||
* import { Scru128Id } from "scru128"; | ||
@@ -250,5 +176,93 @@ * | ||
exports.Scru128Id = Scru128Id; | ||
const defaultGenerator = new Scru128Generator(); | ||
/** Logger object used in the package. */ | ||
let logger = undefined; | ||
/** | ||
* Specifies the logger object used in the package. | ||
* | ||
* Logging is disabled by default. Set a logger object to enable logging. The | ||
* interface is compatible with the console object. | ||
*/ | ||
const setLogger = (newLogger) => { | ||
logger = newLogger; | ||
}; | ||
exports.setLogger = setLogger; | ||
/** Returns a random number generator based on available cryptographic RNG. */ | ||
const detectRng = () => { | ||
if (typeof window !== "undefined" && window.crypto) { | ||
// Web Crypto API on browsers | ||
return () => window.crypto.getRandomValues(new Uint32Array(1))[0]; | ||
} | ||
else if (crypto_1.randomFillSync) { | ||
// Node.js Crypto | ||
return () => (0, crypto_1.randomFillSync)(new Uint32Array(1))[0]; | ||
} | ||
else { | ||
logger === null || logger === void 0 ? void 0 : logger.warn("scru128: fell back on Math.random() as no cryptographic RNG was detected"); | ||
return () => Math.trunc(Math.random() * 65536) * 65536 + | ||
Math.trunc(Math.random() * 65536); | ||
} | ||
}; | ||
/** | ||
* Represents a SCRU128 ID generator that encapsulates the monotonic counter and | ||
* other internal states. | ||
* | ||
* @example | ||
* ```javascript | ||
* import { Scru128Generator } from "scru128"; | ||
* | ||
* const g = new Scru128Generator(); | ||
* const x = g.generate(); | ||
* console.log(String(x)); | ||
* console.log(BigInt(x.toHex())); | ||
* ``` | ||
*/ | ||
class Scru128Generator { | ||
constructor() { | ||
/** Timestamp at last generation. */ | ||
this.tsLastGen = 0; | ||
/** Counter at last generation. */ | ||
this.counter = 0; | ||
/** Timestamp at last renewal of perSecRandom. */ | ||
this.tsLastSec = 0; | ||
/** Per-second random value at last generation. */ | ||
this.perSecRandom = 0; | ||
/** Maximum number of checking the system clock until it goes forward. */ | ||
this.nClockCheckMax = 1000000; | ||
/** Returns a 32-bit (cryptographically strong) random unsigned integer. */ | ||
this.getRandomUint32 = detectRng(); | ||
} | ||
/** Generates a new SCRU128 ID object. */ | ||
generate() { | ||
// update timestamp and counter | ||
let tsNow = Date.now(); | ||
if (tsNow > this.tsLastGen) { | ||
this.tsLastGen = tsNow; | ||
this.counter = this.getRandomUint32() >>> 4; | ||
} | ||
else if (++this.counter > MAX_COUNTER) { | ||
logger === null || logger === void 0 ? void 0 : logger.info("scru128: counter limit reached; will wait until clock goes forward"); | ||
let nClockCheck = 0; | ||
while (tsNow <= this.tsLastGen) { | ||
tsNow = Date.now(); | ||
if (++nClockCheck > this.nClockCheckMax) { | ||
logger === null || logger === void 0 ? void 0 : logger.warn("scru128: reset state as clock did not go forward"); | ||
this.tsLastSec = 0; | ||
break; | ||
} | ||
} | ||
this.tsLastGen = tsNow; | ||
this.counter = this.getRandomUint32() >>> 4; | ||
} | ||
// update perSecRandom | ||
if (this.tsLastGen - this.tsLastSec > 1000) { | ||
this.tsLastSec = this.tsLastGen; | ||
this.perSecRandom = this.getRandomUint32() >>> 8; | ||
} | ||
return Scru128Id.fromFields(this.tsLastGen - exports.TIMESTAMP_BIAS, this.counter, this.perSecRandom, this.getRandomUint32()); | ||
} | ||
} | ||
exports.Scru128Generator = Scru128Generator; | ||
let defaultGenerator; | ||
/** Generates a new SCRU128 ID object. */ | ||
const scru128 = () => defaultGenerator.generate(); | ||
const scru128 = () => (defaultGenerator || (defaultGenerator = new Scru128Generator())).generate(); | ||
exports.scru128 = scru128; | ||
@@ -255,0 +269,0 @@ /** |
{ | ||
"name": "scru128", | ||
"version": "0.4.0", | ||
"version": "0.5.0", | ||
"description": "SCRU128: Sortable, Clock and Random number-based Unique identifier", | ||
@@ -47,6 +47,6 @@ "main": "./dist/index.js", | ||
"typedoc": "^0.22.10", | ||
"typescript": "^4.5.2", | ||
"webpack": "^5.64.4", | ||
"typescript": "^4.5.3", | ||
"webpack": "^5.65.0", | ||
"webpack-cli": "^4.9.1" | ||
} | ||
} |
# SCRU128: Sortable, Clock and Random number-based Unique identifier | ||
[![npm](https://img.shields.io/npm/v/scru128)](https://www.npmjs.com/package/scru128) | ||
[![License](https://img.shields.io/npm/l/scru128)](https://github.com/scru128/javascript/blob/main/LICENSE) | ||
SCRU128 ID is yet another attempt to supersede [UUID] in the use cases that need | ||
@@ -35,19 +38,7 @@ decentralized, globally unique time-ordered identifiers. SCRU128 is inspired by | ||
Copyright 2021 LiosK | ||
Licensed under the Apache License, Version 2.0. | ||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use | ||
this file except in compliance with the License. You may obtain a copy of the | ||
License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software distributed | ||
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
specific language governing permissions and limitations under the License. | ||
## See also | ||
- [scru128 - npm](https://www.npmjs.com/package/scru128) | ||
- [API Documentation](https://scru128.github.io/javascript/docs/) | ||
- [Run tests on your browser](https://scru128.github.io/javascript/test/) |
30136
441
44