├── .vscode └── settings.json ├── README.md ├── async.ts ├── benchmark.ts ├── cli.ts ├── customAlphabet.ts ├── customRandom.ts ├── examples ├── example.1.ts └── example.2.ts ├── mod.ts ├── nanoid.ts ├── random.ts ├── tsconfig.json └── urlAlphabet.ts /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true, 3 | "deno.lint": true, 4 | "deno.unstable": true, 5 | "deno.import_intellisense_origins": { 6 | "https://deno.land": true 7 | } 8 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nano ID 2 | A port of [nanoid] to Deno 3 | 4 | A tiny, secure, URL-friendly, unique string ID generator for JavaScript. 5 | 6 | ```js 7 | import { nanoid } from "https://deno.land/x/nanoid/mod.ts" 8 | nanoid() //=> "lQLTBJKVRCuc" 9 | ``` 10 | 11 | ## Table of Contents 12 | 13 | 1. [Comparison with UUID](#comparison-with-uuid) 14 | 4. [Tools](#tools) 15 | 3. [Security](#security) 16 | 6. Usage 17 | 1. [JS](#js) 18 | 2. [Other Programming Languages](#other-programming-languages) 19 | 5. CLI 20 | 1. [Installation](#installation) 21 | 2. [Usage](#usage-1) 22 | 7. API 23 | 1. [Async](#async) 24 | 3. [Custom Alphabet or Length](#custom-alphabet-or-length) 25 | 4. [Custom Random Bytes Generator](#custom-random-bytes-generator) 26 | 27 | 28 | ## Comparison with UUID 29 | 30 | Nano ID is quite comparable to UUID v4 (random-based). 31 | It has a similar number of random bits in the ID 32 | (126 in Nano ID and 122 in UUID), so it has a similar collision probability: 33 | 34 | > For there to be a one in a billion chance of duplication, 35 | > 103 trillion version 4 IDs must be generated. 36 | 37 | There are three main differences between Nano ID and UUID v4: 38 | 39 | 1. Nano ID uses a bigger alphabet, so a similar number of random bits 40 | are packed in just 21 symbols instead of 36. 41 | 2. Nano ID code is 4 times less than `uuid/v4` package: 42 | 127 bytes instead of 435. 43 | 3. Because of memory allocation tricks, Nano ID is 16% faster than UUID. 44 | 45 | ## Tools 46 | 47 | * [ID size calculator] to choice smaller ID size depends on your case. 48 | * [`nanoid-dictionary`] with popular alphabets to use with `nanoid/generate`. 49 | 50 | [`nanoid-dictionary`]: https://github.com/CyberAP/nanoid-dictionary 51 | [ID size calculator]: https://zelark.github.io/nano-id-cc/ 52 | 53 | 54 | ## Security 55 | 56 | *See a good article about random generators theory: 57 | [Secure random values (in Node.js)]* 58 | 59 | 60 | ### Unpredictability 61 | 62 | Instead of using the unsafe `Math.random()`, Nano ID uses the Web Crypto API. These modules use unpredictable 63 | hardware random generator. 64 | 65 | ## Usage 66 | 67 | ### JS 68 | 69 | The main module uses URL-friendly symbols (`A-Za-z0-9_-`) and returns an ID 70 | with 21 characters (to have a collision probability similar to UUID v4). 71 | 72 | ```js 73 | import { nanoid } from "https://deno.land/x/nanoid/mod.ts" 74 | nanoid() //=> "lQLTBJKVRCuc" 75 | ``` 76 | 77 | If you want to reduce ID length (and increase collisions probability), 78 | you can pass the length as an argument. 79 | 80 | ```js 81 | nanoid(10) //=> "IRFa-VaY2b" 82 | ``` 83 | 84 | Don’t forget to check the safety of your ID length 85 | in our [ID collision probability] calculator. 86 | 87 | [ID collision probability]: https://zelark.github.io/nano-id-cc/ 88 | [nanoid]: https://github.com/ai/nanoid 89 | 90 | 91 | ### Other Programming Languages 92 | 93 | Nano ID was ported to many languages. You can use these ports to have the same 94 | ID generators on client and server side. 95 | 96 | * [Node.js](https://github.com/ai/nanoid) 97 | * [C#](https://github.com/codeyu/nanoid-net) 98 | * [Clojure and ClojureScript](https://github.com/zelark/nano-id) 99 | * [Crystal](https://github.com/mamantoha/nanoid.cr) 100 | * [Dart](https://github.com/pd4d10/nanoid-dart) 101 | * [Go](https://github.com/matoous/go-nanoid) 102 | * [Elixir](https://github.com/railsmechanic/nanoid) 103 | * [Haskell](https://github.com/4e6/nanoid-hs) 104 | * [Java](https://github.com/aventrix/jnanoid) 105 | * [Nim](https://github.com/icyphox/nanoid.nim) 106 | * [PHP](https://github.com/hidehalo/nanoid-php) 107 | * [Python](https://github.com/puyuan/py-nanoid) with [dictionaries](https://pypi.org/project/nanoid-dictionary) 108 | * [Ruby](https://github.com/radeno/nanoid.rb) 109 | * [Rust](https://github.com/nikolay-govorov/nanoid) 110 | * [Swift](https://github.com/antiflasher/NanoID) 111 | 112 | Also, CLI tool is available to generate IDs from a command line. 113 | 114 | ```sh 115 | $ deno run https://deno.land/x/nanoid/cli.ts 116 | ``` 117 | 118 | ## CLI 119 | 120 | ### Installation 121 | 122 | ```sh 123 | $ deno install --name nanoid https://deno.land/x/nanoid/cli.ts 124 | ``` 125 | 126 | ### Usage 127 | 128 | ``` 129 | Usage: nanoid 130 | Version: v2.0.0 131 | 132 | Description: 133 | 134 | A CLI for generating cryptographically-secure random IDs. 135 | 136 | Options: 137 | 138 | -h, --help - Show this help. 139 | -V, --version - Show the version number for this program. 140 | -s, --size - The desired length of IDs to be generated. 141 | -a, --alphabet - The alphabet that IDs should be generated with. 142 | -n, --number - The number of IDs to generate, if you would like more than one (Default: 1) 143 | 144 | Commands: 145 | 146 | completions - Generate shell completions. 147 | ``` 148 | 149 | ## API 150 | 151 | ### Async 152 | 153 | To generate hardware random bytes, CPU will collect electromagnetic noise. 154 | During the collection, CPU doesn’t work. 155 | 156 | If we will use asynchronous API for random generator, 157 | another code could be executed during the entropy collection. 158 | 159 | ```js 160 | import { nanoid } from "https://deno.land/x/nanoid/async.ts"; 161 | 162 | async function createUser () { 163 | user.id = await nanoid(); 164 | } 165 | ``` 166 | 167 | 168 | ### Custom Alphabet or Length 169 | 170 | If you want to change the ID's alphabet or length 171 | you can use the low-level `generate` module. 172 | 173 | ```js 174 | import { customAlphabet } from "https://deno.land/x/nanoid/customAlphabet.ts"; 175 | const nanoid = customAlphabet('1234567890abcdef', 10) 176 | nanoid() // => "4f90d13a42" 177 | ``` 178 | 179 | Alphabet must contain 256 symbols or less. 180 | Otherwise, the generator will not be secure. 181 | 182 | Asynchronous API is also available: 183 | 184 | ```js 185 | import { customAlphabet } from "https://deno.land/x/nanoid/async.ts"; 186 | const nanoid = await customAlphabet('1234567890abcdef', 10); 187 | async function createUser() { 188 | let id = nanoid(); 189 | } 190 | ``` 191 | 192 | ### Custom Random Bytes Generator 193 | 194 | You can replace the default safe random generator using the `format` module. 195 | For instance, to use a seed-based generator. 196 | 197 | ```js 198 | import { customRandom } from "https://deno.land/x/nanoid/customRandom.ts"; 199 | 200 | function random (size) { 201 | const result = [] 202 | for (let i = 0; i < size; i++) { 203 | result.push(randomByte()) 204 | } 205 | return result; 206 | } 207 | 208 | const nanoid = customRandom(random, "abcdef", 10); 209 | nanoid(); // => "fbaefaadeb" 210 | ``` 211 | 212 | `random` callback must accept the array size and return an array 213 | with random numbers. 214 | 215 | If you want to use the same URL-friendly symbols with `format`, 216 | you can get the default alphabet from the `url` file. 217 | 218 | ```js 219 | import { customRandom } from "https://deno.land/x/nanoid/customRandom.ts"; 220 | import { urlAlphabet } from "https://deno.land/x/nanoid/urlAlphabet.ts"; 221 | 222 | const nanoid = customRandom(random, chars, 10); 223 | nanoid(); // => "93ce_Ltuub" 224 | ``` 225 | 226 | Asynchronous API is also available: 227 | 228 | ```js 229 | import { customRandom } from 'https://deno.land/x/nanoid/async.ts'; 230 | import { urlAlphabet } from "https://deno.land/x/nanoid/urlAlphabet.ts"; 231 | 232 | function random (size) { 233 | return Promise.resolve(/*…*/) 234 | } 235 | const nanoid = await customRandom(random, url, 10); 236 | async function createUser () { 237 | let id = nanoid(); // => "93ce_Ltuub" 238 | } 239 | ``` 240 | -------------------------------------------------------------------------------- /async.ts: -------------------------------------------------------------------------------- 1 | import { 2 | customAlphabet as __customAlphabet, 3 | customRandom as __customRandom, 4 | CustomRandomGenerator, 5 | nanoid as __nanoid, 6 | } from "./mod.ts"; 7 | 8 | export const nanoid = async (size: number) => __nanoid(size); 9 | export const customAlphabet = async (alphabet: string, size: number) => __customAlphabet(alphabet, size); 10 | export const customRandom = async ( 11 | random: CustomRandomGenerator, 12 | alphabet: string, 13 | size: number, 14 | ) => __customRandom(random, alphabet, size); 15 | -------------------------------------------------------------------------------- /benchmark.ts: -------------------------------------------------------------------------------- 1 | import { nanoid } from "./mod.ts"; 2 | 3 | console.time("nanoid (x100)"); 4 | for (let i = 0; i < 100; i++) { 5 | nanoid(); 6 | } 7 | console.timeEnd("nanoid (x100)"); -------------------------------------------------------------------------------- /cli.ts: -------------------------------------------------------------------------------- 1 | // deno-lint-ignore-file 2 | /** 3 | * @module nanoid_cli 4 | * @author Ian Fabs 5 | */ 6 | 7 | import { nanoid } from "./nanoid.ts"; 8 | import { customAlphabet } from "./customAlphabet.ts" 9 | import { Command, CompletionsCommand } from "https://deno.land/x/cliffy@v0.15.0/command/mod.ts"; 10 | 11 | `original by Andrey Sitnik 12 | port for deno by Ian Fabs 13 | 14 | Usage 15 | $ nanoid 16 | Options 17 | --alphabet, -a Use a different alphabet to generate the id 18 | --size, -s Generate an id of a different size 19 | --help, -h Display this help page 20 | Examples 21 | $ nanoid 22 | eJgswWA5uW8I 23 | $ nanoid --size 32 24 | xgX77wBFcY1lso9R12Y2lHrluUbLjAPV 25 | $ nanoid -s16 26 | YZ-MJ4oXIGUK2edY 27 | $ nanoid --alphabet "_~0123456789abcdefghijklmnopqrstuvwxyz" 28 | pejh~ujt2lln 29 | ` 30 | 31 | interface Options { 32 | size?: number; 33 | alphabet?: string; 34 | number: number; 35 | format?: string; 36 | } 37 | 38 | function* iterator(n: number, fn: (...args: any[]) => any, ...args: any[]): Iterable { 39 | if(n==1) yield fn(...args); 40 | else for(let i=0;i() 45 | .name("nanoid") 46 | .version("2.0.0") 47 | .description("A CLI for generating cryptographically-secure random IDs.") 48 | .option("-s, --size ", "The desired length of IDs to be generated.") 49 | .option("-a, --alphabet ", "The alphabet that IDs should be generated with.") 50 | .option("-n, --number ", "The number of IDs to generate, if you would like more than one", {default: 1}) 51 | .option("-f, --format ", "An output format can be specified if more than one ID is generated with -n", { 52 | depends: ["number"], 53 | hidden: true, 54 | action: () => { 55 | console.warn("The --format functionality is unimplemented, and cannot be used :-("); 56 | Deno.exit(1); 57 | } 58 | }); 59 | 60 | cmd.command("completions", new CompletionsCommand()); 61 | 62 | const {options: {alphabet, number, size}} = await cmd.parse(Deno.args); 63 | 64 | const fn = alphabet ? customAlphabet(alphabet, size ?? 21) : nanoid; 65 | 66 | console.log( [...iterator(number,fn,...[size])].join("\n") ); 67 | -------------------------------------------------------------------------------- /customAlphabet.ts: -------------------------------------------------------------------------------- 1 | import { random } from "./random.ts"; 2 | import { customRandom } from "./customRandom.ts"; 3 | /** 4 | * Low-level function to change alphabet and ID size. 5 | * 6 | * Alphabet must contain 256 symbols or less. Otherwise, the generator 7 | * will not be secure. 8 | * 9 | * @param {string} alphabet The alphabet that will be used to generate IDs. 10 | * @param {number} size The size(length) of the IDs that will be genereated. 11 | * 12 | * @returns A unique ID based on the alphabet provided. 13 | * 14 | * @example 15 | * import { customAlphabet } from "https://deno.land/x/nanoid/customAlphabet.ts"; 16 | * 17 | * const alphabet = '0123456789абвгдеё'; 18 | * const nanoid = customAlphabet(alphabet, 5); 19 | * 20 | * console.log(nanoid()); // => "8ё56а" 21 | * 22 | */ 23 | export const customAlphabet = (alphabet: string, size: number) => customRandom(random, alphabet, size); 24 | -------------------------------------------------------------------------------- /customRandom.ts: -------------------------------------------------------------------------------- 1 | export type CustomRandomGenerator = (size: number) => Uint8Array | Uint16Array | Uint32Array; 2 | 3 | export const customRandom = (random: CustomRandomGenerator, alphabet: string, size: number) => { 4 | const mask = (2 << (Math.log(alphabet.length - 1) / Math.LN2)) - 1; 5 | const step = -~(1.6 * mask * size / alphabet.length); 6 | 7 | return (): string => { 8 | let id = ""; 9 | while (true) { 10 | const bytes = random(step); 11 | let i = step; 12 | while (i--) { 13 | // Adding `|| ''` refuses a random byte that exceeds the alphabet size. 14 | id += alphabet[bytes[i] & mask] || ''; 15 | if (id.length === +size) return id; 16 | } 17 | } 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /examples/example.1.ts: -------------------------------------------------------------------------------- 1 | import nanoid from "../mod.ts"; 2 | 3 | const array = [0, 1, 2, 3, 4, 5, 6]; 4 | const ids = array.map(element => nanoid()); 5 | console.log(ids); 6 | -------------------------------------------------------------------------------- /examples/example.2.ts: -------------------------------------------------------------------------------- 1 | import { customAlphabet } from "../mod.ts"; 2 | 3 | const alphabet = '1234567890abcdef-'; 4 | const idLength = 18; 5 | 6 | function User(name: string, email?: string) { 7 | this.id = customAlphabet(alphabet, idLength); 8 | this.name = name; 9 | this.email = email; 10 | } 11 | 12 | const me = new User("Jane Doe"); 13 | console.log(me.id); 14 | -------------------------------------------------------------------------------- /mod.ts: -------------------------------------------------------------------------------- 1 | export * from "./nanoid.ts"; 2 | export * from "./random.ts"; 3 | export * from "./urlAlphabet.ts"; 4 | export * from "./customAlphabet.ts"; 5 | export * from "./customRandom.ts"; 6 | -------------------------------------------------------------------------------- /nanoid.ts: -------------------------------------------------------------------------------- 1 | import { random } from "./random.ts"; 2 | import { urlAlphabet } from "./urlAlphabet.ts"; 3 | 4 | export const nanoid = (size: number = 21): string => { 5 | let id: string = ""; 6 | const bytes: Uint8Array = random(size); 7 | // Compact alternative for `for (var i = 0; i < size; i++)` 8 | // We can’t use bytes bigger than the alphabet. 63 is 00111111 bitmask. 9 | // This mask reduces random byte 0-255 to 0-63 values. 10 | // There is no need in `|| ''` and `* 1.6` hacks in here, 11 | // because bitmask trim bytes exact to alphabet size. 12 | while (size--) id += urlAlphabet[bytes[size] & 63]; 13 | return id; 14 | }; -------------------------------------------------------------------------------- /random.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @param bytes The desired length of the Uint8Array to be created. 3 | */ 4 | export type RandomValueFunction = (bytes: number) => Uint8Array; 5 | 6 | export const random: RandomValueFunction = bytes => crypto.getRandomValues(new Uint8Array(bytes)); 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "lib": [ 5 | "DOM", 6 | "ES2020" 7 | ] 8 | } 9 | } -------------------------------------------------------------------------------- /urlAlphabet.ts: -------------------------------------------------------------------------------- 1 | export const urlAlphabet = 'ModuleSymbhasOwnPr-0123456789ABCDEFGHNRVfgctiUvz_KqYTJkLxpZXIjQW'; --------------------------------------------------------------------------------