# Rando

Rando is a library for generating identifiers. IDs are random and universally unique by default. Options can be modified to fit a wide variety of requirements. In a node environment, randomness is provided by the `node:crypto` module. In a browser environment, randomness is provided by the Web Crypto API.

### Install

```
npm i @balancer-team/rando
```

### Usage

Import the rando class and create an instance. The instance generates IDs with the `generate()` method.

```js
import { Rando } from '@balancer-team/rando'

const rando = new Rando()
rando.generate() // => "Jb46bM8xM3mhDLnpQrwNvN"
```

### Customizing the Length

If you want a longer random string, for example if you want extra security for an API key, it's easy to modify the length. The example below generates a 46-character ID with over 256 bits of entropy:

```js
const rando = new Rando({ length: 46 })
rando.generate() //=> "X5533mwXPH6V2nMN5548t1vkFVN4RTJqM89JNQcbVjmt1G"
```

### Sortable IDs

Rando can generate sortable IDs where the beginning of the ID is an encoded timestamp using the given `alphabet`. Rando will evaluate the `alphabet` length and automatically determine how many characters are required to encode a timestamp at millisecond precision. The `length` must be sufficient for millisecond precision. Refer to the table below for guidance on the length needed to support a given year with a given alphabet base.

```js
const rando = new Rando({ sortable: true })
rando.generate()

// Output:
//
// "13DNYFr2DBJvhWfWCs58Nb"
//  |------||------------|
//  Sortable    Random
//  Segment     Segment
```

Sortable IDs can be decoded to return a date. Note that the instance doing the decoding must have the same options as the instance that generated it.

```js
rando.getDate('13DNYFr2DBJvhWfWCs58Nb').toISOString() //=> "2025-02-25T13:11:45.061Z"
```

### All Options

Rando instances can be extensively customized to generate the perfect identifier for your requirements. Here's the type definition of all available options, with details below:

```ts
type RandoOptions = {
  alphabet?: string
  length?: number
  sortable?: boolean
  supportDate?: Date
}
```

| Property      | Default   | Description                                                                                                                                               |
| ------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `alphabet`    | `BASE_50` | A string of characters to use to generate your IDs. Each character must be unique. The default base 50 alphabet excludes vowels and ambiguous characters. |
| `length`      | `22`      | The length of the ID. By default, the `length` is `22` which provides 124 bits of entropy, slightly more than a UUIDv4.                                   |
| `sortable`    | `false`   | Makes the ID sortable when set to `true`. The leading characters encode a timestamp at millisecond precision.                                             |
| `supportDate` | `'4000'`  | Allows you to specify a target date for the sortable segment. See below for additional details.                                                           |

### Special Considerations for Sortable IDs

The `length` and base of the `alphabet` together determine how long the sortable segment must be for millisecond precision. If the `length` is too short, Rando will throw an error when instantiated. The `supportDate` property allows you to specify a target date for the sortable segment to support. The sortable segment will be left-padded to support the target date with a consistent overall length.

Note that making an ID sortable will reduce the number of random characters in the ID. If you `console.log` the instance, you will see several helpful properties such as `randomBits` that can help you determine whether or not the ID meets your requirements.

```js
const rando = new Rando({ sortable: true })

// Output:
//
// Rando {
//   alphabet: '123456789BCDFGHJKLMNPQRSTVWXYZbcdfghjkmnpqrstvwxyz',
//   length: 22,
//   randomLength: 13,
//   base: 50,
//   randomBits: 73.37013046707142,
//   randomLimit: 1.2207031249999986e+22,
//   sortable: true,
//   supportDate: 4000-01-01T00:00:00.000Z,
//   sortableLength: 9,
//   sortableLimit: +063862-01-25T10:13:20.000Z
// }
```

### Presets

Rando comes with a few presets to make it easy to generate IDs for common use cases.

```js
import { rando, sorto, locker, sesame, pinto } from '@balancer-team/rando/presets'

rando.generate() //=> "7kFD8XHYp1JdXXzYsxRvXT"
sorto.generate() //=> "13DNYDsRBvc6TQ2HwHh1GW"
locker.generate() //=> "5Myvnv4BQm7rDhz3zntYGMXf9Srr71z7wFwF1SSYMQVvQQ"
tapper.generate() //=> "C5W3C4FP14KSDXBJ"
sesame.generate() //=> "E@MvHJG4JbA$o{8ll0AK"
pinto.generate() //=> "368230"
```

- `rando` Default, like a compact UUIDv4.
- `sorto` Sortable string followed by a random string, like a compact UUIDv7.
- `locker` Long string with over 256 bits of entropy, suitable for API keys.
- `tapper` Case-insensitive codes or serial numbers that are easy to type.
- `sesame` Secure password with over 128 bits of entropy.
- `pinto` Numerical 6-digit pin for verification codes.

### Guidance for Sortable IDs

The following table is a guide for the length needed to support at least the year 3000 with a given alphabet base.

| Base | Length | Max Year |
| ---- | ------ | -------- |
| 2    | 46     | 4199     |
| 3    | 29     | 4144     |
| 4    | 23     | 4199     |
| 5    | 20     | 4992     |
| 6    | 18     | 5188     |
| 7    | 17     | 9341     |
| 8    | 16     | 10889    |
| 9    | 15     | 8494     |
| 10   | 14     | 5138     |
| 11   | 14     | 14003    |
| 12   | 13     | 5360     |
| 13   | 13     | 11567    |
| 14   | 13     | 27121    |
| 15   | 12     | 6081     |
| 16   | 12     | 10889    |
| 17   | 12     | 20432    |
| 18   | 11     | 4006     |
| 19   | 11     | 5661     |
| 20   | 11     | 8459     |
| 21   | 11     | 13069    |
| 22   | 11     | 20486    |
| 23   | 11     | 32163    |
| 24   | 11     | 50190    |
| 25   | 10     | 4992     |
| 26   | 10     | 6443     |
| 27   | 10     | 8494     |
| 28   | 10     | 11356    |
| 29   | 10     | 15301    |
| 30   | 10     | 20681    |
| 31   | 10     | 27942    |
| 32   | 10     | 37648    |
| 33   | 10     | 50503    |
| 34   | 10     | 67387    |
| 35   | 9      | 4467     |
| 36   | 9      | 5188     |
| 37   | 9      | 6088     |
| 38   | 9      | 7205     |
| 39   | 9      | 8584     |
| 40   | 9      | 10277    |
| 41   | 9      | 12344    |
| 42   | 9      | 14856    |
| 43   | 9      | 17896    |
| 44   | 9      | 21557    |
| 45   | 9      | 25948    |
| 46   | 9      | 31193    |
| 47   | 9      | 37433    |
| 48   | 9      | 44832    |
| 49   | 9      | 53572    |
| 50   | 9      | 63862    |
| 51   | 9      | 75936    |
| 52   | 9      | 90061    |
| 53   | 9      | 106535   |
| 54   | 8      | 4261     |
| 55   | 8      | 4623     |
| 56   | 8      | 5034     |
| 57   | 8      | 5501     |
| 58   | 8      | 6028     |
| 59   | 8      | 6622     |
| 60   | 8      | 7292     |
| 61   | 8      | 8044     |
| 62   | 8      | 8888     |
| 63   | 8      | 9833     |
| 64   | 8      | 10889    |
