Skip to content

Latest commit

 

History

History
436 lines (304 loc) · 9.7 KB

README.md

File metadata and controls

436 lines (304 loc) · 9.7 KB

AnyID - ID generator for node.js

npm version travis ci Coverage Status Try on Runkit

Verified on node.js v6, v8, v10, v11 and v12

Table of Contents

Introduction

AnyID is a simple and flexible API to generate various kinds of string ID / code.

The generated ID is compounded by sections with optional delimiter. Each section is encoded into specificed chars and may have fix length.

sdk3ksxjcs-JEDke3x8F-sldle34CZs
^^^^^^^^^^          ^
│                   │
└ section           └ delimiter

A section may contain one or more values. Multiple values are concatenated into bit stream before encoded.

┌───────────────────────┬───────────────┬──────────────────┐
│  timestamp (41 bits)  │ seq (12 bits) │ worker (10 bits) │
└───────────────────────┴───────────────┴──────────────────┘

Use it in your code:

import {anyid} from 'anyid';
const ids = anyid().encode('Aa0').length(21).random();
console.log(ids.id());

If you aren't using ES6, this is the way to require it:

var anyid = require('anyid').anyid;

>>> Try it in your browser. <<<

Encode

Encode with given charset:

encode(charset: string)

Charset is specified by simple letter:

  • A - Alphabet in upper case
  • a - Alphabet in lower case
  • 0 - Numeric

Individual letters can be exclude by followed after -, be appended by followed after +.

Example:

  • Aa0 = A-Z a-z 0-9
  • 0A-IO = 0-9 A-Z, excludes I and O
  • 0+ABCDEF = 0-9 A-F
  • A+012-IO = A-Z 0 1 2, excludes I and O

Section and Delimiter

A section accepts an AnyId object as parameter. For ID containing single section, section function is not used.

section( anyid: AnyId )

Section length can be fixed or variant. When length is specified, section will be trimmed or padded at beginning side.

length(n: number)

For some kinds of value, e.g. random, length must be given.

Hint What length will be if not specified?

b: value bytes
a: charset size
length ≧ logₐ256ᵇ = log₂ 256ᵇ / log₂ a = 8b / log₂ a

For example:

Value is 4 bytes UInt32, charset is A-Za-z0-9 which has 62 characters. 8 * 4 / log₂ 62 = 5.37. Maximum length will be 6.

Delimiter can be put between sections. It's output as is and never be encoded.

delimiter( s: string )

Value

AnyID supports several kinds of value:

  • random
  • timestamp
  • sequence
  • fix value
  • function result
  • variable

Value is either non-negative integer (UInt32) or Buffer (byte array).

A section may have more than one values. Values will be concatenated as bit stream before encoded.

You can use bits to specify the bit width of a value. Higher bits will be discard if value has more bits than desired.

bits( n: number )

Random

Generate value by random. Length or bits must be specified.

random()

Internally it uses crypto.randomBytes() to generate random.

Hint The probability of duplicates in n random IDs is:

p(n) ≈ n²/(2*C^L)

Where L is the length of random id, C is the size of encode charset.

For example: using anyid().encode('Aa0').length(21).random() to generate randome ID, L is 21 and C is 62. Then p(n) ≈ n²/(2*62^21). It has lower probability of duplicates then type 4 (random) UUID.

Timestamp

Use current timestamp as value.

time( unit: string = 'ms' )

Unit can be ms, s, m, h, d.

By default, timestamp value is since UNIX epoch time. You can overwrite it to a recent date to save some bits.

since( t: Date )

Sequence

Sequence increases everytime an ID is generated.

seq()

By default, sequence starts from 0. You can set it to any non-negative integer.

startWith( n: number )

Sequence will be reset after it reaches max value. It can not exceed 2^32 (max value represented by UInt32).

max( n: number )

Or, let it reset when timestamp changes:

resetByTime()

To use resetByTime, there must be a timestamp value in the ID.

Fixed value

fixed( n: number | Buffer )

Value is either non-negative integer (UInt32) or Buffer (byte array).

Function result

Similar to fix value, but the value is returned by a function which is called an ID is to be generated.

of( f: () => number | Buffer )

Variable

Similar to fix value, but the value is given in id function call.

variable( name?: string )

When there is only one variable used in ID generator, the name can be omitted in variable() and the value is directly passing in id function call:

id( v: number | Buffer )

When there are multiple variables used in ID generator, the values need to be passing in id in an object:

id( v: {[name: string]: number | Buffer} )

Read example below to check how it's used.

Examples

Single section, random value

This id has essence the same low probability of a clash as type 4 (random) UUID:

const ids = anyid().encode('Aa0').length(21).random()
const id  = ids.id();
1LrKcmd0uk1Ma8szUxtda

Multiple sections, fix prefix and timestamp

const ids = anyid()
  .encode('0A-IO')
  .section( anyid().fixed(process.pid) )
  .delimiter('-')
  .section( anyid().time() );

It uses human friendly charset: I and O are excluded because of similarity to 1 and 0.

008CL-00TYMZS0P3

Sequence and bit stream concatenation

It's Twitter Snowflake style ID with timestamp, sequence and worker.

const ids = anyid()
  .encode('0')
  .bits(41).time().since(new Date('2016-7-1'))
  .bits(12).seq().resetByTime();
  .bits(10).fix(workerId);

Timestamp is since 2016-7-1. Sequence is reset every millisecond.

071243223959339218

Function value

ID contains second and nanosecond. Nanosecond is retrieved by a function.

const nanotime = () => process.hrtime()[1];

const ids = anyid()
  .encode('Aa0')
  .section( anyid().time('s') )
  .delimiter('+')
  .section( anyid().of(nanotime) );
BlX6bX+j3Uz0

Use different charset in sections

The ID has default charset A-IO. The second section uses charset 0.

const ids = anyid()
  .encode('A-IO')
  .section( anyid().length(3).random() )
  .delimiter(' ')
  .section( anyid().encode('0').length(3).random() )
  .delimiter(' ')
  .section( anyid().length(3).random() );
HQX 552 ATC

Single variable

const ids = anyid()
  .encode('Aa0')
  .section( anyid().variable() )
  .delimiter('-')
  .section( anyid().time() );

const id = ids(Buffer.from('user-xxx'));
KFLnaOrolmA-AAZ28TmMo

Multiple variables

const ids = anyid()
  .encode('Aa0')
  .section( anyid().variable('countryId') )
  .delimiter('-')
  .section( anyid().variable('userId') )
  .delimiter('-')
  .section( anyid().length(5).random() );

const id = ids.id({ countryId: 86, userId: 635023 });
AAABY-ACpMT-EBwQJ