Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@thi.ng/ksuid

Package Overview
Dependencies
Maintainers
1
Versions
147
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@thi.ng/ksuid - npm Package Compare versions

Comparing version 0.2.6 to 0.3.0

aksuid.d.ts

25

CHANGELOG.md

@@ -6,2 +6,27 @@ # Change Log

# [0.3.0](https://github.com/thi-ng/umbrella/compare/@thi.ng/ksuid@0.2.6...@thi.ng/ksuid@0.3.0) (2021-08-07)
### Code Refactoring
* **ksuid:** extract IKSUID, update impls, docs ([1276c94](https://github.com/thi-ng/umbrella/commit/1276c940d6e7b584d90eb871261ff6a28352de4f))
### Features
* **ksuid:** pkg restructure, add 64bit version ([9c40b20](https://github.com/thi-ng/umbrella/commit/9c40b2053afb9067723bfb0377e5e3ea2a38c52a))
### BREAKING CHANGES
* **ksuid:** Rename KSUID => KSUID32 / defKSUID32()
- update readme
- update tests
- update pkg meta
## [0.2.6](https://github.com/thi-ng/umbrella/compare/@thi.ng/ksuid@0.2.5...@thi.ng/ksuid@0.2.6) (2021-08-04)

@@ -8,0 +33,0 @@

99

index.d.ts

@@ -1,96 +0,5 @@

import { BaseN } from "@thi.ng/base-n";
import { IRandom } from "@thi.ng/random";
export interface KSUIDOpts {
/**
* {@link @this.ng/base-n#BaseN} instance for string encoding the generated
* binary IDs.
*
* @defaultValue BASE62
*/
base: BaseN;
/**
* Optional PRNG instance for sourcing random values (for development/debug
* purposes only).
*
* @defaultValue window.crypto
*/
rnd: IRandom;
/**
* Number of bytes for random payload.
*
* @defaultValue 16
*/
bytes: number;
/**
* Time offset in seconds, relative to standard Unix epoch. This is used to
* extend the time headroom of IDs into the future.
*
* @remarks
* The default value is approx. 2020-09-13, meaning this is the T0 epoch for
* all IDs (providing an additional ~50 year lifespan compared to the
* standard 1970-01-01 epoch)
*
* @defaultValue 1_600_000_000
*/
epoch: number;
}
export declare class KSUID {
/**
* Returns the byte size of a single ID, based on the KSUID's configuration.
* The default config (payload 16 bytes) will result in 20-byte IDs (27
* chars base62 encoded).
*/
readonly size: number;
protected base: BaseN;
protected rnd?: IRandom;
protected epoch: number;
protected pad: (x: any) => string;
constructor(opts?: Partial<KSUIDOpts>);
/**
* Returns a new baseN encoded ID string.
*/
next(): string;
/**
* Returns a new ID as byte array.
*/
nextBinary(): Uint8Array;
/**
* Returns a new baseN encoded ID string for given `epoch` (default: current
* time) and with all random payload bytes set to 0.
*
* @param epoch
*/
timeOnly(epoch?: number): string;
/**
* Binary version of {@link KSUI.timeOnly}, but returns byte array. The
* first 4 bytes will contain the timestamp.
*
* @param epoch
*/
timeOnlyBinary(epoch?: number): Uint8Array;
/**
* Returns baseN encoded version of given binary ID (generated via
* `.nextBinary()`).
*/
format(buf: Uint8Array): string;
/**
* Takes a KSUID string (assumed to be generated with the same config as
* this instance) and parses it into an object of: `{ epoch, id }`, where
* `epoch` is the Unix epoch of the ID and `id` the random bytes.
*
* @remarks
* This operation requires `bigint` support by the host environment.
*
* @param id
*/
parse(id: string): {
epoch: number;
id: Uint8Array;
};
}
/**
*
* @param opts
*/
export declare const defKSUID: (opts?: Partial<KSUIDOpts> | undefined) => KSUID;
export * from "./api";
export * from "./aksuid";
export * from "./ksuid32";
export * from "./ksuid64";
//# sourceMappingURL=index.d.ts.map

@@ -1,84 +0,4 @@

import { assert } from "@thi.ng/api";
import { BASE62 } from "@thi.ng/base-n";
import { randomBytes, randomBytesFrom } from "@thi.ng/random";
import { padLeft } from "@thi.ng/strings";
export class KSUID {
constructor(opts) {
opts = Object.assign({ base: BASE62, epoch: 1600000000, bytes: 16 }, opts);
this.base = opts.base;
this.rnd = opts.rnd;
this.epoch = opts.epoch;
this.size = 4 + opts.bytes;
this.pad = padLeft(this.base.size(2 ** (this.size * 8) - 1), this.base.base[0]);
}
/**
* Returns a new baseN encoded ID string.
*/
next() {
return this.format(this.nextBinary());
}
/**
* Returns a new ID as byte array.
*/
nextBinary() {
const buf = this.timeOnlyBinary();
return this.rnd
? randomBytesFrom(this.rnd, buf, 4)
: randomBytes(buf, 4);
}
/**
* Returns a new baseN encoded ID string for given `epoch` (default: current
* time) and with all random payload bytes set to 0.
*
* @param epoch
*/
timeOnly(epoch) {
return this.format(this.timeOnlyBinary(epoch));
}
/**
* Binary version of {@link KSUI.timeOnly}, but returns byte array. The
* first 4 bytes will contain the timestamp.
*
* @param epoch
*/
timeOnlyBinary(epoch = Date.now()) {
const buf = new Uint8Array(this.size);
let t = epoch / 1000 - this.epoch;
assert(t >= 0, "configured base epoch must be in the past");
buf.set([t >>> 24, (t >>> 16) & 0xff, (t >>> 8) & 0xff, t & 0xff]);
return buf;
}
/**
* Returns baseN encoded version of given binary ID (generated via
* `.nextBinary()`).
*/
format(buf) {
assert(buf.length == this.size, `illegal KSUID size, expected ${this.size} bytes`);
return this.pad(this.base.encodeBytes(buf));
}
/**
* Takes a KSUID string (assumed to be generated with the same config as
* this instance) and parses it into an object of: `{ epoch, id }`, where
* `epoch` is the Unix epoch of the ID and `id` the random bytes.
*
* @remarks
* This operation requires `bigint` support by the host environment.
*
* @param id
*/
parse(id) {
const buf = new Uint8Array(this.size);
this.base.decodeBytes(id, buf);
return {
epoch: (((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]) +
this.epoch) *
1000,
id: buf.slice(4),
};
}
}
/**
*
* @param opts
*/
export const defKSUID = (opts) => new KSUID(opts);
export * from "./api";
export * from "./aksuid";
export * from "./ksuid32";
export * from "./ksuid64";

@@ -10,9 +10,9 @@ 'use strict';

class KSUID {
constructor(opts) {
opts = Object.assign({ base: baseN.BASE62, epoch: 1600000000, bytes: 16 }, opts);
this.base = opts.base;
class AKSUID {
constructor(epochSize, opts) {
this.epochSize = epochSize;
this.base = opts.base || baseN.BASE62;
this.rnd = opts.rnd;
this.epoch = opts.epoch;
this.size = 4 + opts.bytes;
this.size = this.epochSize + opts.bytes;
this.pad = strings.padLeft(this.base.size(2 ** (this.size * 8) - 1), this.base.base[0]);

@@ -26,4 +26,4 @@ }

return this.rnd
? random.randomBytesFrom(this.rnd, buf, 4)
: random.randomBytes(buf, 4);
? random.randomBytesFrom(this.rnd, buf, this.epochSize)
: random.randomBytes(buf, this.epochSize);
}

@@ -33,13 +33,33 @@ timeOnly(epoch) {

}
format(buf) {
this.ensureSize(buf);
return this.pad(this.base.encodeBytes(buf));
}
ensureSize(buf) {
api.assert(buf.length == this.size, `illegal KSUID size, expected ${this.size} bytes`);
return buf;
}
ensureTime(t) {
api.assert(t >= 0, "configured base epoch must be in the past");
return t;
}
u32(buf, i = 0) {
return (((buf[i] << 24) |
(buf[i + 1] << 16) |
(buf[i + 2] << 8) |
buf[i + 3]) >>>
0);
}
}
class KSUID32 extends AKSUID {
constructor(opts) {
super(4, Object.assign({ epoch: 1600000000, bytes: 16 }, opts));
}
timeOnlyBinary(epoch = Date.now()) {
const buf = new Uint8Array(this.size);
let t = epoch / 1000 - this.epoch;
api.assert(t >= 0, "configured base epoch must be in the past");
buf.set([t >>> 24, (t >>> 16) & 0xff, (t >>> 8) & 0xff, t & 0xff]);
const t = this.ensureTime((epoch / 1000 - this.epoch) | 0);
buf.set([t >>> 24, (t >> 16) & 0xff, (t >> 8) & 0xff, t & 0xff]);
return buf;
}
format(buf) {
api.assert(buf.length == this.size, `illegal KSUID size, expected ${this.size} bytes`);
return this.pad(this.base.encodeBytes(buf));
}
parse(id) {

@@ -49,5 +69,3 @@ const buf = new Uint8Array(this.size);

return {
epoch: (((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]) +
this.epoch) *
1000,
epoch: (this.u32(buf) + this.epoch) * 1000,
id: buf.slice(4),

@@ -57,5 +75,40 @@ };

}
const defKSUID = (opts) => new KSUID(opts);
const defKSUID32 = (opts) => new KSUID32(opts);
exports.KSUID = KSUID;
exports.defKSUID = defKSUID;
class KSUID64 extends AKSUID {
constructor(opts) {
super(8, Object.assign({ epoch: 1600000000000, bytes: 12 }, opts));
}
timeOnlyBinary(epoch = Date.now()) {
const buf = new Uint8Array(this.size);
const t = this.ensureTime(epoch - this.epoch);
const h = (t / 4294967296) >>> 0;
const l = (t & 4294967295) >>> 0;
buf.set([
h >>> 24,
(h >> 16) & 0xff,
(h >> 8) & 0xff,
h & 0xff,
l >>> 24,
(l >> 16) & 0xff,
(l >> 8) & 0xff,
l & 0xff,
]);
return buf;
}
parse(id) {
const buf = new Uint8Array(this.size);
this.base.decodeBytes(id, buf);
return {
epoch: this.u32(buf) * 4294967296 + this.u32(buf, 4) + this.epoch,
id: buf.slice(8),
};
}
}
const defKSUID64 = (opts) => new KSUID64(opts);
exports.AKSUID = AKSUID;
exports.KSUID32 = KSUID32;
exports.KSUID64 = KSUID64;
exports.defKSUID32 = defKSUID32;
exports.defKSUID64 = defKSUID64;

@@ -1,1 +0,1 @@

!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@thi.ng/api"),require("@thi.ng/base-n"),require("@thi.ng/random"),require("@thi.ng/strings")):"function"==typeof define&&define.amd?define(["exports","@thi.ng/api","@thi.ng/base-n","@thi.ng/random","@thi.ng/strings"],t):t(((e="undefined"!=typeof globalThis?globalThis:e||self).thi=e.thi||{},e.thi.ng=e.thi.ng||{},e.thi.ng.ksuid={}),e.thi.ng.api,e.thi.ng.baseN,e.thi.ng.random,e.thi.ng.strings)}(this,(function(e,t,i,s,n){"use strict";class r{constructor(e){e=Object.assign({base:i.BASE62,epoch:16e8,bytes:16},e),this.base=e.base,this.rnd=e.rnd,this.epoch=e.epoch,this.size=4+e.bytes,this.pad=n.padLeft(this.base.size(2**(8*this.size)-1),this.base.base[0])}next(){return this.format(this.nextBinary())}nextBinary(){const e=this.timeOnlyBinary();return this.rnd?s.randomBytesFrom(this.rnd,e,4):s.randomBytes(e,4)}timeOnly(e){return this.format(this.timeOnlyBinary(e))}timeOnlyBinary(e=Date.now()){const i=new Uint8Array(this.size);let s=e/1e3-this.epoch;return t.assert(s>=0,"configured base epoch must be in the past"),i.set([s>>>24,s>>>16&255,s>>>8&255,255&s]),i}format(e){return t.assert(e.length==this.size,`illegal KSUID size, expected ${this.size} bytes`),this.pad(this.base.encodeBytes(e))}parse(e){const t=new Uint8Array(this.size);return this.base.decodeBytes(e,t),{epoch:1e3*((t[0]<<24|t[1]<<16|t[2]<<8|t[3])+this.epoch),id:t.slice(4)}}}e.KSUID=r,e.defKSUID=e=>new r(e),Object.defineProperty(e,"__esModule",{value:!0})}));
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@thi.ng/api"),require("@thi.ng/base-n"),require("@thi.ng/random"),require("@thi.ng/strings")):"function"==typeof define&&define.amd?define(["exports","@thi.ng/api","@thi.ng/base-n","@thi.ng/random","@thi.ng/strings"],t):t(((e="undefined"!=typeof globalThis?globalThis:e||self).thi=e.thi||{},e.thi.ng=e.thi.ng||{},e.thi.ng.ksuid={}),e.thi.ng.api,e.thi.ng.baseN,e.thi.ng.random,e.thi.ng.strings)}(this,(function(e,t,s,i,n){"use strict";class r{constructor(e,t){this.epochSize=e,this.base=t.base||s.BASE62,this.rnd=t.rnd,this.epoch=t.epoch,this.size=this.epochSize+t.bytes,this.pad=n.padLeft(this.base.size(2**(8*this.size)-1),this.base.base[0])}next(){return this.format(this.nextBinary())}nextBinary(){const e=this.timeOnlyBinary();return this.rnd?i.randomBytesFrom(this.rnd,e,this.epochSize):i.randomBytes(e,this.epochSize)}timeOnly(e){return this.format(this.timeOnlyBinary(e))}format(e){return this.ensureSize(e),this.pad(this.base.encodeBytes(e))}ensureSize(e){return t.assert(e.length==this.size,`illegal KSUID size, expected ${this.size} bytes`),e}ensureTime(e){return t.assert(e>=0,"configured base epoch must be in the past"),e}u32(e,t=0){return(e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3])>>>0}}class h extends r{constructor(e){super(4,Object.assign({epoch:16e8,bytes:16},e))}timeOnlyBinary(e=Date.now()){const t=new Uint8Array(this.size),s=this.ensureTime(e/1e3-this.epoch|0);return t.set([s>>>24,s>>16&255,s>>8&255,255&s]),t}parse(e){const t=new Uint8Array(this.size);return this.base.decodeBytes(e,t),{epoch:1e3*(this.u32(t)+this.epoch),id:t.slice(4)}}}class o extends r{constructor(e){super(8,Object.assign({epoch:16e11,bytes:12},e))}timeOnlyBinary(e=Date.now()){const t=new Uint8Array(this.size),s=this.ensureTime(e-this.epoch),i=s/4294967296>>>0,n=(4294967295&s)>>>0;return t.set([i>>>24,i>>16&255,i>>8&255,255&i,n>>>24,n>>16&255,n>>8&255,255&n]),t}parse(e){const t=new Uint8Array(this.size);return this.base.decodeBytes(e,t),{epoch:4294967296*this.u32(t)+this.u32(t,4)+this.epoch,id:t.slice(8)}}}e.AKSUID=r,e.KSUID32=h,e.KSUID64=o,e.defKSUID32=e=>new h(e),e.defKSUID64=e=>new o(e),Object.defineProperty(e,"__esModule",{value:!0})}));
{
"name": "@thi.ng/ksuid",
"version": "0.2.6",
"description": "Configurable K-sortable unique identifiers, binary & base-N encoded",
"version": "0.3.0",
"description": "Configurable sortable unique IDs, binary & base-N encoded, 32/64bit time resolution",
"module": "./index.js",

@@ -53,2 +53,4 @@ "main": "./lib/index.js",

"keywords": [
"32bit",
"64bit",
"base62",

@@ -65,2 +67,3 @@ "bigint",

"id",
"millisecond",
"random",

@@ -81,6 +84,5 @@ "sort",

],
"status": "alpha",
"year": 2020
},
"gitHead": "5a289330f80e3c253c3b434655825c5dcfaebfd2"
"gitHead": "0c3f02d2670775eefda522bcee8789f1a686b1fc"
}

@@ -24,3 +24,3 @@ <!-- This file is generated - DO NOT EDIT! -->

Configurable K-sortable unique identifiers, binary & base-N encoded.
Configurable sortable unique IDs, binary & base-N encoded, 32/64bit time resolution.

@@ -30,15 +30,17 @@ Idea based on [segmentio/ksuid](https://github.com/segmentio/ksuid), though with

- Configurable bit size (default: 128bits)
- Configurable bit size (default: 160 bits)
- Base-N encoding scheme (default: base62, see
[@thi.ng/base-n](https://github.com/thi-ng/umbrella/tree/develop/packages/base-n)
for alternatives)
- Timestamp resolution (seconds [32 bits], milliseconds [64 bits])
- Epoch start time offset
- Time-only base ID generation (optional)
- KSUID parsing / decomposition
- Configurable RNG source (default: `window.crypto` or `Math.random`)
- Configurable RNG source (default: `window.crypto`, `Math.random` fallback)
KSUIDs generated w/ this package consist of the lower 32bits of an Unix epoch
(potentially time shifted to free up bits for future timestamps) and N bits of a
random payload (from a configurable source). IDs can be generated as byte arrays
or base-N encoded strings. For the latter, the JS runtime MUST support
KSUIDs generated w/ this package are composed from a 32 bit or 64 bit Unix epoch
(by default time shifted to free up bits for future timestamps) and N additional
bits of a random payload (from a configurable source). IDs can be generated as
byte arrays or base-N encoded strings. For the latter, the JS runtime MUST
support
[`BigInt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt).

@@ -50,3 +52,3 @@

**ALPHA** - bleeding edge / work-in-progress
**STABLE** - used in production

@@ -74,3 +76,3 @@ [Search or submit any issues for this package](https://github.com/thi-ng/umbrella/issues?q=%5Bksuid%5D+in%3Atitle)

Package sizes (gzipped, pre-treeshake): ESM: 563 bytes / CJS: 620 bytes / UMD: 743 bytes
Package sizes (gzipped, pre-treeshake): ESM: 730 bytes / CJS: 798 bytes / UMD: 907 bytes

@@ -89,6 +91,8 @@ ## Dependencies

```ts
import { defKSUID } from "@thi.ng/ksuid";
import { defKSUID32, defKSUID64 } from "@thi.ng/ksuid";
// init w/ defaults
const id = defKSUID();
// init 32bit epoch (resolution: seconds) w/ defaults
const id = defKSUID32();
// init 64bit epoch (resolution: milliseconds), same API
const id = defKSUID64();

@@ -130,3 +134,3 @@ id.next();

// no time shift, 64bit random
const id36 = defKSUID({ base: BASE36, epoch: 0, bytes: 8 });
const id36 = defKSUID32({ base: BASE36, epoch: 0, bytes: 8 });
// '2VOUKH4K59AG0RXR4XH'

@@ -133,0 +137,0 @@ ```

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc