Socket
Socket
Sign inDemoInstall

scru64

Package Overview
Dependencies
0
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.1.0 to 0.2.0

148

dist/index.d.ts

@@ -9,9 +9,21 @@ /**

/**
* 8-byte byte array containing the 64-bit unsigned integer representation in
* the big-endian (network) byte order.
* An 8-byte byte array containing the 64-bit unsigned integer representation
* in the big-endian (network) byte order.
*/
readonly bytes: Readonly<Uint8Array>;
/** Creates an object from a 8-byte byte array. */
/** Creates an object from an 8-byte byte array. */
private constructor();
/**
* Creates an object from the internal representation, an 8-byte byte array
* containing the 64-bit unsigned integer representation in the big-endian
* (network) byte order.
*
* This method does NOT shallow-copy the argument, and thus the created object
* holds the reference to the underlying buffer.
*
* @throws RangeError if the length of the argument is not 8 or the argument
* contains an integer out of the valid value range.
*/
static ofInner(bytes: Uint8Array): Scru64Id;
/**
* Returns the 12-digit canonical string representation.

@@ -66,3 +78,3 @@ *

* Note that this class is designed to be immutable, and thus `clone()` is not
* necessary unless properties marked as private are modified directly.
* necessary unless properties marked as read-only are modified.
*/

@@ -85,19 +97,19 @@ clone(): Scru64Id;

*
* | Flavor | Timestamp | On big clock rewind |
* | ---------------------------- | --------- | -------------------- |
* | {@link generate} | Now | Rewinds state |
* | {@link generateNoRewind} | Now | Returns `undefined` |
* | {@link generateOrWait} | Now | Waits (blocking) |
* | {@link generateOrWaitAsync} | Now | Waits (non-blocking) |
* | {@link generateCore} | Argument | Rewinds state |
* | {@link generateCoreNoRewind} | Argument | Returns `undefined` |
* | Flavor | Timestamp | On big clock rewind |
* | --------------------------- | --------- | ------------------- |
* | {@link generate} | Now | Returns `undefined` |
* | {@link generateOrReset} | Now | Resets generator |
* | {@link generateOrSleep} | Now | Sleeps (blocking) |
* | {@link generateOrAwait} | Now | Sleeps (async) |
* | {@link generateOrAbortCore} | Argument | Returns `undefined` |
* | {@link generateOrResetCore} | Argument | Resets generator |
*
* Each method returns monotonically increasing IDs unless a timestamp provided
* is significantly (by ~10 seconds or more) smaller than the one embedded in
* the immediately preceding ID. If such a significant clock rollback is
* detected, (i) the standard `generate` rewinds the generator state and returns
* a new ID based on the current timestamp; (ii) `NoRewind` variants keep the
* state untouched and return `undefined`; and, (iii) `OrWait` functions sleep
* and wait for the next timestamp tick. `core` functions offer low-level
* primitives.
* All of these methods return monotonically increasing IDs unless a timestamp
* provided is significantly (by default, approx. 10 seconds or more) smaller
* than the one embedded in the immediately preceding ID. If such a significant
* clock rollback is detected, (1) the `generate` (OrAbort) method aborts and
* returns `undefined`; (2) the `OrReset` variants reset the generator and
* return a new ID based on the given timestamp; and, (3) the `OrSleep` and
* `OrAwait` methods sleep and wait for the next timestamp tick. The `Core`
* functions offer low-level primitives.
*/

@@ -139,18 +151,18 @@ export declare class Scru64Generator {

/**
* Generates a new SCRU64 ID object from the current `timestamp`.
* Generates a new SCRU64 ID object from the current `timestamp`, or returns
* `undefined` upon significant timestamp rollback.
*
* See the {@link Scru64Generator} class documentation for the description.
*/
generate(): Scru64Id;
generate(): Scru64Id | undefined;
/**
* Generates a new SCRU64 ID object from the current `timestamp`, guaranteeing
* the monotonic order of generated IDs despite a significant timestamp
* rollback.
* Generates a new SCRU64 ID object from the current `timestamp`, or resets
* the generator upon significant timestamp rollback.
*
* See the {@link Scru64Generator} class documentation for the description.
*/
generateNoRewind(): Scru64Id | undefined;
generateOrReset(): Scru64Id;
/**
* Returns a new SCRU64 ID object, or waits for one if not immediately
* available.
* Returns a new SCRU64 ID object, or synchronously sleeps and waits for one
* if not immediately available.
*

@@ -160,32 +172,36 @@ * See the {@link Scru64Generator} class documentation for the description.

* This method uses a blocking busy loop to wait for the next `timestamp`
* tick. Use {@link generateOrWaitAsync} where possible.
* tick. Use {@link generateOrAwait} where possible.
*/
generateOrWait(): Scru64Id;
generateOrSleep(): Scru64Id;
/**
* Returns a new SCRU64 ID object, or waits for one if not immediately
* available.
* Returns a new SCRU64 ID object, or asynchronously sleeps and waits for one
* if not immediately available.
*
* See the {@link Scru64Generator} class documentation for the description.
*/
generateOrWaitAsync(): Promise<Scru64Id>;
generateOrAwait(): Promise<Scru64Id>;
/**
* Generates a new SCRU64 ID object from a Unix timestamp in milliseconds.
* Generates a new SCRU64 ID object from a Unix timestamp in milliseconds, or
* resets the generator upon significant timestamp rollback.
*
* See the {@link Scru64Generator} class documentation for the description.
*
* @throws RangeError if the argument is not a positive integer within the
* valid range.
* @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 positive integer within the valid
* range.
*/
generateCore(unixTsMs: number): Scru64Id;
generateOrResetCore(unixTsMs: number, rollbackAllowance: number): Scru64Id;
/**
* Generates a new SCRU64 ID object from a Unix timestamp in milliseconds,
* guaranteeing the monotonic order of generated IDs despite a significant
* timestamp rollback.
* Generates a new SCRU64 ID object from a Unix timestamp in milliseconds, or
* returns `undefined` upon significant timestamp rollback.
*
* See the {@link Scru64Generator} class documentation for the description.
*
* @throws RangeError if the argument is not a positive integer within the
* valid range.
* @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 positive integer within the valid
* range.
*/
generateCoreNoRewind(unixTsMs: number): Scru64Id | undefined;
generateOrAbortCore(unixTsMs: number, rollbackAllowance: number): Scru64Id | undefined;
}

@@ -195,6 +211,14 @@ /**

*
* The global generator reads the node configuration from the `SCRU64_NODE_SPEC`
* global variable. A node spec string consists of `nodeId` and `nodeIdSize`
* separated by a slash (e.g., `"42/8"`, `"12345/16"`).
*
* This function usually returns a value immediately, but if not possible, it
* sleeps and waits for the next timestamp tick. It employs a blocking busy loop
* to wait; use the non-blocking {@link scru64} where possible.
*
* @throws Error if the global generator is not properly configured through the
* `SCRU64_NODE_SPEC` global variable.
* global variable.
*/
export declare const scru64: () => Scru64Id;
export declare const scru64Sync: () => Scru64Id;
/**

@@ -204,13 +228,28 @@ * Generates a new SCRU64 ID encoded in the 12-digit canonical string

*
* The global generator reads the node configuration from the `SCRU64_NODE_SPEC`
* global variable. A node spec string consists of `nodeId` and `nodeIdSize`
* separated by a slash (e.g., `"42/8"`, `"12345/16"`).
*
* This function usually returns a value immediately, but if not possible, it
* sleeps and waits for the next timestamp tick. It employs a blocking busy loop
* to wait; use the non-blocking {@link scru64String} where possible.
*
* @throws Error if the global generator is not properly configured through the
* `SCRU64_NODE_SPEC` global variable.
* global variable.
*/
export declare const scru64String: () => string;
export declare const scru64StringSync: () => string;
/**
* Generates a new SCRU64 ID object using the global generator.
*
* The global generator reads the node configuration from the `SCRU64_NODE_SPEC`
* global variable. A node spec string consists of `nodeId` and `nodeIdSize`
* separated by a slash (e.g., `"42/8"`, `"12345/16"`).
*
* This function usually returns a value immediately, but if not possible, it
* sleeps and waits for the next timestamp tick.
*
* @throws Error if the global generator is not properly configured through the
* `SCRU64_NODE_SPEC` global variable.
* global variable.
*/
export declare const scru64Async: () => Promise<Scru64Id>;
export declare const scru64: () => Promise<Scru64Id>;
/**

@@ -220,5 +259,12 @@ * Generates a new SCRU64 ID encoded in the 12-digit canonical string

*
* The global generator reads the node configuration from the `SCRU64_NODE_SPEC`
* global variable. A node spec string consists of `nodeId` and `nodeIdSize`
* separated by a slash (e.g., `"42/8"`, `"12345/16"`).
*
* This function usually returns a value immediately, but if not possible, it
* sleeps and waits for the next timestamp tick.
*
* @throws Error if the global generator is not properly configured through the
* `SCRU64_NODE_SPEC` global variable.
* global variable.
*/
export declare const scru64StringAsync: () => Promise<string>;
export declare const scru64String: () => Promise<string>;

@@ -6,11 +6,13 @@ /**

*/
/** Total size in bits of the `nodeId` and `counter` fields. */
/** The maximum valid value (i.e., `zzzzzzzzzzzz`). */
const MAX_SCRU64_BYTES = Uint8Array.of(65, 194, 28, 184, 224, 255, 255, 255);
/** The total size in bits of the `nodeId` and `counter` fields. */
const NODE_CTR_SIZE = 24;
/// Maximum valid value of the `timestamp` field.
const TIMESTAMP_MAX = 282429536480; // (36n ** 12n - 1n) >> 24n
/// Maximum valid value of the combined `nodeCtr` field.
const NODE_CTR_MAX = (1 << NODE_CTR_SIZE) - 1;
/// The maximum valid value of the `timestamp` field.
const MAX_TIMESTAMP = 282429536480; // (36n ** 12n - 1n) >> 24n
/// The maximum valid value of the combined `nodeCtr` field.
const MAX_NODE_CTR = (1 << NODE_CTR_SIZE) - 1;
/** 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 = [

@@ -30,3 +32,3 @@ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,

export class Scru64Id {
/** Creates an object from a 8-byte byte array. */
/** Creates an object from an 8-byte byte array. */
constructor(bytes) {

@@ -36,2 +38,27 @@ this.bytes = bytes;

/**
* Creates an object from the internal representation, an 8-byte byte array
* containing the 64-bit unsigned integer representation in the big-endian
* (network) byte order.
*
* This method does NOT shallow-copy the argument, and thus the created object
* holds the reference to the underlying buffer.
*
* @throws RangeError if the length of the argument is not 8 or the argument
* contains an integer out of the valid value range.
*/
static ofInner(bytes) {
if (bytes.length !== 8) {
throw new RangeError("invalid length: " + bytes.length);
}
for (let i = 0; i < 8; i++) {
if (bytes[i] > MAX_SCRU64_BYTES[i]) {
throw new RangeError("integer out of valid value range");
}
else if (bytes[i] < MAX_SCRU64_BYTES[i]) {
break;
}
}
return new Scru64Id(bytes);
}
/**
* Returns the 12-digit canonical string representation.

@@ -140,3 +167,3 @@ *

if (timestamp < 0 ||
timestamp > TIMESTAMP_MAX ||
timestamp > MAX_TIMESTAMP ||
!Number.isInteger(timestamp)) {

@@ -146,3 +173,3 @@ throw new RangeError("`timestamp` out of range");

else if (nodeCtr < 0 ||
nodeCtr > NODE_CTR_MAX ||
nodeCtr > MAX_NODE_CTR ||
!Number.isInteger(nodeCtr)) {

@@ -185,3 +212,3 @@ throw new RangeError("`nodeCtr` out of range");

* Note that this class is designed to be immutable, and thus `clone()` is not
* necessary unless properties marked as private are modified directly.
* necessary unless properties marked as read-only are modified.
*/

@@ -222,19 +249,19 @@ clone() {

*
* | Flavor | Timestamp | On big clock rewind |
* | ---------------------------- | --------- | -------------------- |
* | {@link generate} | Now | Rewinds state |
* | {@link generateNoRewind} | Now | Returns `undefined` |
* | {@link generateOrWait} | Now | Waits (blocking) |
* | {@link generateOrWaitAsync} | Now | Waits (non-blocking) |
* | {@link generateCore} | Argument | Rewinds state |
* | {@link generateCoreNoRewind} | Argument | Returns `undefined` |
* | Flavor | Timestamp | On big clock rewind |
* | --------------------------- | --------- | ------------------- |
* | {@link generate} | Now | Returns `undefined` |
* | {@link generateOrReset} | Now | Resets generator |
* | {@link generateOrSleep} | Now | Sleeps (blocking) |
* | {@link generateOrAwait} | Now | Sleeps (async) |
* | {@link generateOrAbortCore} | Argument | Returns `undefined` |
* | {@link generateOrResetCore} | Argument | Resets generator |
*
* Each method returns monotonically increasing IDs unless a timestamp provided
* is significantly (by ~10 seconds or more) smaller than the one embedded in
* the immediately preceding ID. If such a significant clock rollback is
* detected, (i) the standard `generate` rewinds the generator state and returns
* a new ID based on the current timestamp; (ii) `NoRewind` variants keep the
* state untouched and return `undefined`; and, (iii) `OrWait` functions sleep
* and wait for the next timestamp tick. `core` functions offer low-level
* primitives.
* All of these methods return monotonically increasing IDs unless a timestamp
* provided is significantly (by default, approx. 10 seconds or more) smaller
* than the one embedded in the immediately preceding ID. If such a significant
* clock rollback is detected, (1) the `generate` (OrAbort) method aborts and
* returns `undefined`; (2) the `OrReset` variants reset the generator and
* return a new ID based on the given timestamp; and, (3) the `OrSleep` and
* `OrAwait` methods sleep and wait for the next timestamp tick. The `Core`
* functions offer low-level primitives.
*/

@@ -303,3 +330,4 @@ export class Scru64Generator {

/**
* Generates a new SCRU64 ID object from the current `timestamp`.
* Generates a new SCRU64 ID object from the current `timestamp`, or returns
* `undefined` upon significant timestamp rollback.
*

@@ -309,17 +337,16 @@ * See the {@link Scru64Generator} class documentation for the description.

generate() {
return this.generateCore(Date.now());
return this.generateOrAbortCore(Date.now(), 10000);
}
/**
* Generates a new SCRU64 ID object from the current `timestamp`, guaranteeing
* the monotonic order of generated IDs despite a significant timestamp
* rollback.
* Generates a new SCRU64 ID object from the current `timestamp`, or resets
* the generator upon significant timestamp rollback.
*
* See the {@link Scru64Generator} class documentation for the description.
*/
generateNoRewind() {
return this.generateCoreNoRewind(Date.now());
generateOrReset() {
return this.generateOrResetCore(Date.now(), 10000);
}
/**
* Returns a new SCRU64 ID object, or waits for one if not immediately
* available.
* Returns a new SCRU64 ID object, or synchronously sleeps and waits for one
* if not immediately available.
*

@@ -329,7 +356,7 @@ * See the {@link Scru64Generator} class documentation for the description.

* This method uses a blocking busy loop to wait for the next `timestamp`
* tick. Use {@link generateOrWaitAsync} where possible.
* tick. Use {@link generateOrAwait} where possible.
*/
generateOrWait() {
generateOrSleep() {
while (true) {
const value = this.generateNoRewind();
const value = this.generate();
if (value !== undefined) {

@@ -344,11 +371,11 @@ return value;

/**
* Returns a new SCRU64 ID object, or waits for one if not immediately
* available.
* Returns a new SCRU64 ID object, or asynchronously sleeps and waits for one
* if not immediately available.
*
* See the {@link Scru64Generator} class documentation for the description.
*/
async generateOrWaitAsync() {
async generateOrAwait() {
const DELAY = 64;
while (true) {
const value = this.generateNoRewind();
const value = this.generate();
if (value !== undefined) {

@@ -363,11 +390,14 @@ return value;

/**
* Generates a new SCRU64 ID object from a Unix timestamp in milliseconds.
* Generates a new SCRU64 ID object from a Unix timestamp in milliseconds, or
* resets the generator upon significant timestamp rollback.
*
* See the {@link Scru64Generator} class documentation for the description.
*
* @throws RangeError if the argument is not a positive integer within the
* valid range.
* @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 positive integer within the valid
* range.
*/
generateCore(unixTsMs) {
const value = this.generateCoreNoRewind(unixTsMs);
generateOrResetCore(unixTsMs, rollbackAllowance) {
const value = this.generateOrAbortCore(unixTsMs, rollbackAllowance);
if (value !== undefined) {

@@ -384,17 +414,21 @@ return value;

/**
* Generates a new SCRU64 ID object from a Unix timestamp in milliseconds,
* guaranteeing the monotonic order of generated IDs despite a significant
* timestamp rollback.
* Generates a new SCRU64 ID object from a Unix timestamp in milliseconds, or
* returns `undefined` upon significant timestamp rollback.
*
* See the {@link Scru64Generator} class documentation for the description.
*
* @throws RangeError if the argument is not a positive integer within the
* valid range.
* @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 positive integer within the valid
* range.
*/
generateCoreNoRewind(unixTsMs) {
const ROLLBACK_ALLOWANCE = 40; // x256 milliseconds = ~10 seconds
generateOrAbortCore(unixTsMs, rollbackAllowance) {
const timestamp = Math.trunc(unixTsMs / 0x100);
const allowance = Math.trunc(rollbackAllowance / 0x100);
if (timestamp <= 0) {
throw new RangeError("`timestamp` out of range");
}
else if (allowance < 0 || allowance > 1099511627775) {
throw new RangeError("`rollbackAllowance` out of reasonable range");
}
if (timestamp > this.prevTimestamp) {

@@ -404,3 +438,3 @@ this.prevTimestamp = timestamp;

}
else if (timestamp + ROLLBACK_ALLOWANCE > this.prevTimestamp) {
else if (timestamp + allowance > this.prevTimestamp) {
// go on with previous timestamp if new one is not much smaller

@@ -418,3 +452,3 @@ const counterMask = (1 << this.counterSize) - 1;

else {
// abort if clock moves back to unbearable extent
// abort if clock went backwards to unbearable extent
return undefined;

@@ -438,6 +472,14 @@ }

*
* The global generator reads the node configuration from the `SCRU64_NODE_SPEC`
* global variable. A node spec string consists of `nodeId` and `nodeIdSize`
* separated by a slash (e.g., `"42/8"`, `"12345/16"`).
*
* This function usually returns a value immediately, but if not possible, it
* sleeps and waits for the next timestamp tick. It employs a blocking busy loop
* to wait; use the non-blocking {@link scru64} where possible.
*
* @throws Error if the global generator is not properly configured through the
* `SCRU64_NODE_SPEC` global variable.
* global variable.
*/
export const scru64 = () => getGlobalGenerator().generateOrWait();
export const scru64Sync = () => getGlobalGenerator().generateOrSleep();
/**

@@ -447,13 +489,28 @@ * Generates a new SCRU64 ID encoded in the 12-digit canonical string

*
* The global generator reads the node configuration from the `SCRU64_NODE_SPEC`
* global variable. A node spec string consists of `nodeId` and `nodeIdSize`
* separated by a slash (e.g., `"42/8"`, `"12345/16"`).
*
* This function usually returns a value immediately, but if not possible, it
* sleeps and waits for the next timestamp tick. It employs a blocking busy loop
* to wait; use the non-blocking {@link scru64String} where possible.
*
* @throws Error if the global generator is not properly configured through the
* `SCRU64_NODE_SPEC` global variable.
* global variable.
*/
export const scru64String = () => scru64().toString();
export const scru64StringSync = () => scru64Sync().toString();
/**
* Generates a new SCRU64 ID object using the global generator.
*
* The global generator reads the node configuration from the `SCRU64_NODE_SPEC`
* global variable. A node spec string consists of `nodeId` and `nodeIdSize`
* separated by a slash (e.g., `"42/8"`, `"12345/16"`).
*
* This function usually returns a value immediately, but if not possible, it
* sleeps and waits for the next timestamp tick.
*
* @throws Error if the global generator is not properly configured through the
* `SCRU64_NODE_SPEC` global variable.
* global variable.
*/
export const scru64Async = async () => getGlobalGenerator().generateOrWaitAsync();
export const scru64 = async () => getGlobalGenerator().generateOrAwait();
/**

@@ -463,5 +520,12 @@ * Generates a new SCRU64 ID encoded in the 12-digit canonical string

*
* The global generator reads the node configuration from the `SCRU64_NODE_SPEC`
* global variable. A node spec string consists of `nodeId` and `nodeIdSize`
* separated by a slash (e.g., `"42/8"`, `"12345/16"`).
*
* This function usually returns a value immediately, but if not possible, it
* sleeps and waits for the next timestamp tick.
*
* @throws Error if the global generator is not properly configured through the
* `SCRU64_NODE_SPEC` global variable.
* global variable.
*/
export const scru64StringAsync = async () => (await scru64Async()).toString();
export const scru64String = async () => (await scru64()).toString();
{
"name": "scru64",
"version": "0.1.0",
"version": "0.2.0",
"description": "SCRU64: Sortable, Clock-based, Realm-specifically Unique identifier",

@@ -18,3 +18,3 @@ "type": "module",

"predoc": "rm -rf ./docs",
"prepare": "npm run build && npm run doc",
"prepare": "npm run build && npm run doc && npm test",
"test": "mocha"

@@ -34,5 +34,5 @@ },

"mocha": "^10.2.0",
"typedoc": "^0.23.26",
"typescript": "^4.9.5"
"typedoc": "^0.23.28",
"typescript": "^5.0.2"
}
}

@@ -19,8 +19,8 @@ # SCRU64: Sortable, Clock-based, Realm-specifically Unique identifier

import { scru64, scru64String } from "scru64";
import { scru64Sync, scru64StringSync } from "scru64";
// or on browsers:
// import { scru64, scru64String } from "https://unpkg.com/scru64@^0.1";
// import { scru64Sync, scru64StringSync } from "https://unpkg.com/scru64@^0.2";
// generate a new identifier object
const x = scru64();
const x = scru64Sync();
console.log(String(x)); // e.g. "0u2r85hm2pt3"

@@ -30,3 +30,3 @@ console.log(BigInt(x.toHex())); // as a 64-bit unsigned integer

// generate a textual representation directly
console.log(scru64String()); // e.g. "0u2r85hm2pt4"
console.log(scru64StringSync()); // e.g. "0u2r85hm2pt4"
```

@@ -50,1 +50,2 @@

- [API Documentation](https://scru64.github.io/javascript/docs/)
- [Run tests on your browser](https://scru64.github.io/javascript/test/)
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc