├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── feature_request.yml ├── PULL_REQUEST_TEMPLATE.md ├── SECURITY.md └── workflows │ ├── browser.yml │ ├── ci.yml │ ├── release-please.yml │ └── stale-issues.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .npmrc ├── .prettierignore ├── .vscode ├── extensions.json └── tasks.json ├── AUTHORS ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── README_js.md ├── bundlewatch.config.json ├── eslint.config.mjs ├── examples ├── benchmark │ ├── README.md │ ├── benchmark.html │ ├── benchmark.js │ ├── browser.js │ ├── node.js │ └── package.json ├── browser-esmodules │ ├── README.md │ ├── example.html │ ├── example.js │ └── package.json ├── browser-rollup │ ├── README.md │ ├── example-all.html │ ├── example-all.js │ ├── example-v1.html │ ├── example-v1.js │ ├── example-v4.html │ ├── example-v4.js │ ├── example-v7.html │ ├── example-v7.js │ ├── example.html │ ├── package.json │ ├── rollup.config.js │ ├── size-v1.js │ ├── size-v3.js │ ├── size-v4.js │ ├── size-v5.js │ ├── size-v6.js │ └── size-v7.js ├── browser-webpack │ ├── README.md │ ├── example-all-require.html │ ├── example-all-require.js │ ├── example-all.html │ ├── example-all.js │ ├── example-v1.html │ ├── example-v1.js │ ├── example-v4.html │ ├── example-v4.js │ ├── example-v7.html │ ├── example-v7.js │ ├── example.html │ ├── package.json │ ├── size-v1.js │ ├── size-v3.js │ ├── size-v4.js │ ├── size-v5.js │ ├── size-v6.js │ ├── size-v7.js │ └── webpack.config.js ├── node-commonjs │ ├── README.md │ ├── example.js │ └── package.json ├── node-esmodules │ ├── README.md │ ├── example.mjs │ ├── package.json │ └── package.mjs ├── node-jest │ ├── README.md │ ├── jsdom.test.js │ ├── node.test.js │ └── package.json ├── node-webpack │ ├── README.md │ ├── example-all.js │ ├── example-v1.js │ ├── example-v4.js │ ├── example-v7.js │ ├── package.json │ └── webpack.config.js ├── typescript │ ├── README.md │ ├── buffer.test.ts │ ├── index.ts │ ├── package.json │ └── tsconfig.json └── utils │ └── testpage.js ├── package-lock.json ├── package.json ├── prettier.config.js ├── scripts ├── build.sh └── iodd ├── src ├── bin │ └── uuid ├── index.ts ├── max.ts ├── md5-browser.ts ├── md5.ts ├── native-browser.ts ├── native.ts ├── nil.ts ├── parse.ts ├── regex.ts ├── rng-browser.ts ├── rng.ts ├── sha1-browser.ts ├── sha1.ts ├── stringify.ts ├── test │ ├── parse.test.ts │ ├── rng.test.ts │ ├── stringify.test.ts │ ├── test_constants.ts │ ├── v1.test.ts │ ├── v35.test.ts │ ├── v4.test.ts │ ├── v6.test.ts │ ├── v7.test.ts │ ├── validate.test.ts │ └── version.test.ts ├── types.ts ├── uuid-bin.ts ├── v1.ts ├── v1ToV6.ts ├── v3.ts ├── v35.ts ├── v4.ts ├── v5.ts ├── v6.ts ├── v6ToV1.ts ├── v7.ts ├── validate.ts └── version.ts ├── test └── browser │ └── browser.spec.js ├── tsconfig.base.json ├── tsconfig.cjs.json ├── tsconfig.esm.json ├── tsconfig.json └── wdio.conf.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [broofa, ctavan] 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug report 2 | description: File a bug against the `uuid` project 3 | labels: ['bug'] 4 | title: '[BUG] ' 5 | 6 | body: 7 | - type: checkboxes 8 | attributes: 9 | label: Before you begin... 10 | options: 11 | - label: I have searched the existing issues 12 | required: true 13 | - label: My issue title is descriptive and specific to the problem (i.e. search-engine friendly) 14 | required: true 15 | - label: I understand what an "MRE" is, and why providing one is important. (If not, [read this](https://stackoverflow.com/help/minimal-reproducible-example)) 16 | required: true 17 | 18 | - type: textarea 19 | attributes: 20 | label: MRE and Description 21 | description: '**IMPORTANT**: Failure to provide an MRE is likely to result in your issue being closed without comment.' 22 | render: bash 23 | validations: 24 | required: false 25 | 26 | - type: textarea 27 | attributes: 28 | label: Environment 29 | description: 'Output of `npx envinfo --system --browsers --npmPackages --binaries` goes here:' 30 | validations: 31 | required: false 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Suggest an idea for this project 3 | labels: ['feature'] 4 | 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: Feature description 9 | 10 | - type: textarea 11 | attributes: 12 | label: Additional information 13 | description: E.g. alternatives you've considered, examples, screenshots, or anything else that may be helpful 14 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | <!-- 2 | Thank you for your contribution! 3 | 4 | If your pull request contains considerable changes please run the benchmark before and after your 5 | changes and include the results in the pull request description. To run the benchmark execute: 6 | 7 | npm run test:benchmark 8 | 9 | from the root of this repository. 10 | --> 11 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | For issues related to `uuid` security please email the module maintainers. Their email addresses are available via `npm owner ls uuid`. 2 | -------------------------------------------------------------------------------- /.github/workflows/browser.yml: -------------------------------------------------------------------------------- 1 | name: Browser 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | pull_request_target: 7 | types: [labeled] 8 | 9 | permissions: 10 | contents: read 11 | 12 | env: 13 | HUSKY: 0 14 | 15 | jobs: 16 | browser: 17 | if: github.repository == 'uuidjs/uuid' && (contains(github.event.pull_request.labels.*.name, 'safe to test') || github.event_name == 'push') 18 | runs-on: ubuntu-latest 19 | timeout-minutes: 30 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | with: 24 | fetch-depth: 10 25 | - name: Use Node.js 20.x 26 | uses: actions/setup-node@v4 27 | with: 28 | node-version: 20.x 29 | - run: npm ci 30 | - name: Test Browser 31 | run: npm run test:browser 32 | env: 33 | CI: true 34 | BROWSERSTACK_USER: ${{ secrets.BROWSERSTACK_USER }} 35 | BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} 36 | - run: npx bundlewatch --config bundlewatch.config.json 37 | env: 38 | BUNDLEWATCH_GITHUB_TOKEN: ${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }} 39 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: 6 | contents: read 7 | 8 | env: 9 | HUSKY: 0 10 | 11 | jobs: 12 | lint: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Use Node.js 17 | uses: actions/setup-node@v4 18 | with: 19 | node-version: 22.x 20 | 21 | - run: npm ci 22 | - run: npm run lint 23 | - run: npm run docs:diff 24 | 25 | test: 26 | runs-on: ubuntu-latest 27 | strategy: 28 | fail-fast: false 29 | matrix: 30 | node-version: [16.x, 18.x, 20.x, 22.x] 31 | steps: 32 | - uses: actions/checkout@v4 33 | - name: Use Node.js ${{ matrix.node-version }} 34 | uses: actions/setup-node@v4 35 | with: 36 | node-version: ${{ matrix.node-version }} 37 | cache: npm 38 | 39 | - run: npm ci 40 | - run: npm run test 41 | - run: npm run test:node 42 | -------------------------------------------------------------------------------- /.github/workflows/release-please.yml: -------------------------------------------------------------------------------- 1 | # Relevent docs: 2 | # - https://github.com/googleapis/release-please 3 | # - https://github.com/googleapis/release-please-action 4 | 5 | on: 6 | workflow_dispatch: 7 | 8 | push: 9 | branches: 10 | - main 11 | 12 | permissions: 13 | contents: write 14 | pull-requests: write 15 | 16 | name: release-please 17 | 18 | jobs: 19 | release-please: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: googleapis/release-please-action@v4 23 | id: release 24 | with: 25 | token: ${{ secrets.UUID_RELEASE_PLEASE_TOKEN }} 26 | release-type: node 27 | 28 | - run: echo "Release created?... ${{ steps.release.outputs.release_created }}" 29 | 30 | # Steps below handle publication to NPM 31 | - uses: actions/checkout@v4 32 | if: ${{ steps.release.outputs.release_created }} 33 | - uses: actions/setup-node@v4 34 | with: 35 | node-version: 20 36 | registry-url: 'https://registry.npmjs.org' 37 | if: ${{ steps.release.outputs.release_created }} 38 | 39 | - run: npm ci 40 | if: ${{ steps.release.outputs.release_created }} 41 | 42 | - run: npm test 43 | if: ${{ steps.release.outputs.release_created }} 44 | 45 | - run: npm publish 46 | env: 47 | NODE_AUTH_TOKEN: ${{secrets.UUID_NPM_RELEASE_TOKEN}} 48 | if: ${{ steps.release.outputs.release_created }} 49 | -------------------------------------------------------------------------------- /.github/workflows/stale-issues.yml: -------------------------------------------------------------------------------- 1 | name: Close inactive issues 2 | on: 3 | schedule: 4 | - cron: '45 2 * * *' 5 | 6 | jobs: 7 | close-issues: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | issues: write 11 | pull-requests: write 12 | steps: 13 | - uses: actions/stale@v7 14 | with: 15 | close-issue-message: 'Closing issue due to 30 days since being marked as stale.' 16 | days-before-issue-close: 30 17 | days-before-issue-stale: 90 18 | days-before-pr-close: -1 19 | days-before-pr-stale: -1 20 | exempt-issue-labels: 'stale-exempt' 21 | exempt-pr-labels: 'stale-exempt' 22 | repo-token: ${{ secrets.GITHUB_TOKEN }} 23 | stale-issue-label: 'stale' 24 | stale-issue-message: 'Marking as stale due to 90 days with no activity.' 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.tgz 2 | browserstack.err 3 | dist/ 4 | .build/ 5 | local.log 6 | logs/ 7 | node_modules/ 8 | examples/**/package-lock.json -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | npx --no-install commitlint --edit $1 3 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npx --no-install lint-staged 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | tag-version-prefix="v" 2 | save-prefix="" 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | README.md 4 | CHANGELOG.md 5 | *.sh 6 | .gitignore 7 | .prettierignore 8 | .husky 9 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "ms-vscode.vscode-typescript-next", 5 | "esbenp.prettier-vscode" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Build watcher", 6 | "group": "build", 7 | "type": "shell", 8 | "command": "npm run build:watch", 9 | "runOptions": { 10 | "runOn": "folderOpen" 11 | } 12 | }, 13 | { 14 | "label": "Test watcher", 15 | "group": "build", 16 | "type": "shell", 17 | "command": "npm run test:watch", 18 | "windows": { 19 | "command": "npm run test:watch" 20 | }, 21 | "presentation": { 22 | "reveal": "always", 23 | "panel": "new" 24 | }, 25 | "runOptions": { 26 | "runOn": "folderOpen" 27 | } 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Robert Kieffer <robert@broofa.com> 2 | Christoph Tavan <dev@tavan.de> 3 | AJ ONeal <coolaj86@gmail.com> 4 | Vincent Voyer <vincent@zeroload.net> 5 | Roman Shtylman <shtylman@gmail.com> 6 | Patrick McCarren <patrick@cumulative.io> 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Development 4 | 5 | 1. Clone this repo 6 | 2. `npm install` to install dev dependencies 7 | 3. VSCode is the recommended dev environment 8 | 9 | - Includes "Build Watcher" and "Test Watcher" tasks (Terminal pane) 10 | - (optional) Install the recommended extensions (Extensions sidebar) 11 | 12 | 4. Before submitting your PR, make sure tests pass: 13 | 14 | - `npm test && npm run test:node` 15 | 16 | ## README updates 17 | 18 | **Do not edit README.md manually**. To make changes to the README: 19 | 20 | 1. Edit README_js.md 21 | 2. Run `npm run docs` to regenerate README.md 22 | 3. If using VSCode, it's 23 | 24 | ## Testing 25 | 26 | ```shell 27 | npm test 28 | ``` 29 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2010-2020 Robert Kieffer and other contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | <!-- 2 | -- This file is auto-generated from README_js.md. Changes should be made there. 3 | --> 4 | 5 | # uuid [![CI](https://github.com/uuidjs/uuid/workflows/CI/badge.svg)](https://github.com/uuidjs/uuid/actions?query=workflow%3ACI) [![Browser](https://github.com/uuidjs/uuid/workflows/Browser/badge.svg)](https://github.com/uuidjs/uuid/actions/workflows/browser.yml) 6 | 7 | For the creation of [RFC9562](https://www.rfc-editor.org/rfc/rfc9562.html) (formerly [RFC4122](https://www.rfc-editor.org/rfc/rfc4122.html)) UUIDs 8 | 9 | - **Complete** - Support for all RFC9562 UUID versions 10 | - **Cross-platform** - Support for... 11 | - ESM & Common JS 12 | - [Typescript](#support) 13 | - [Chrome, Safari, Firefox, and Edge](#support) 14 | - [NodeJS](#support) 15 | - [React Native / Expo](#react-native--expo) 16 | - **Secure** - Uses modern `crypto` API for random values 17 | - **Compact** - Zero-dependency, [tree-shakable](https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking) 18 | - **CLI** - [`uuid` command line](#command-line) utility 19 | 20 | <!-- prettier-ignore --> 21 | > [!NOTE] 22 | > `uuid@11` is now available: See the [CHANGELOG](./CHANGELOG.md) for details. TL;DR: 23 | > * TypeScript support is now included (remove `@types/uuid` from your dependencies) 24 | > * Subtle changes to how the `options` arg is interpreted for `v1()`, `v6()`, and `v7()`. [See details](#options-handling-for-timestamp-uuids) 25 | > * Binary UUIDs are now `Uint8Array`s. (May impact callers of `parse()`, `stringify()`, or that pass an `option#buf` argument to `v1()`-`v7()`.) 26 | 27 | ## Quickstart 28 | 29 | **1. Install** 30 | 31 | ```shell 32 | npm install uuid 33 | ``` 34 | 35 | **2. Create a UUID** 36 | 37 | ESM-syntax (must use named exports): 38 | 39 | ```javascript 40 | import { v4 as uuidv4 } from 'uuid'; 41 | uuidv4(); // ⇨ '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d' 42 | ``` 43 | 44 | ... CommonJS: 45 | 46 | ```javascript 47 | const { v4: uuidv4 } = require('uuid'); 48 | uuidv4(); // ⇨ '1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed' 49 | ``` 50 | 51 | For timestamp UUIDs, namespace UUIDs, and other options read on ... 52 | 53 | ## API Summary 54 | 55 | | | | | 56 | | --- | --- | --- | 57 | | [`uuid.NIL`](#uuidnil) | The nil UUID string (all zeros) | New in `uuid@8.3` | 58 | | [`uuid.MAX`](#uuidmax) | The max UUID string (all ones) | New in `uuid@9.1` | 59 | | [`uuid.parse()`](#uuidparsestr) | Convert UUID string to array of bytes | New in `uuid@8.3` | 60 | | [`uuid.stringify()`](#uuidstringifyarr-offset) | Convert array of bytes to UUID string | New in `uuid@8.3` | 61 | | [`uuid.v1()`](#uuidv1options-buffer-offset) | Create a version 1 (timestamp) UUID | | 62 | | [`uuid.v1ToV6()`](#uuidv1tov6uuid) | Create a version 6 UUID from a version 1 UUID | New in `uuid@10` | 63 | | [`uuid.v3()`](#uuidv3name-namespace-buffer-offset) | Create a version 3 (namespace w/ MD5) UUID | | 64 | | [`uuid.v4()`](#uuidv4options-buffer-offset) | Create a version 4 (random) UUID | | 65 | | [`uuid.v5()`](#uuidv5name-namespace-buffer-offset) | Create a version 5 (namespace w/ SHA-1) UUID | | 66 | | [`uuid.v6()`](#uuidv6options-buffer-offset) | Create a version 6 (timestamp, reordered) UUID | New in `uuid@10` | 67 | | [`uuid.v6ToV1()`](#uuidv6tov1uuid) | Create a version 1 UUID from a version 6 UUID | New in `uuid@10` | 68 | | [`uuid.v7()`](#uuidv7options-buffer-offset) | Create a version 7 (Unix Epoch time-based) UUID | New in `uuid@10` | 69 | | ~~[`uuid.v8()`](#uuidv8)~~ | "Intentionally left blank" | | 70 | | [`uuid.validate()`](#uuidvalidatestr) | Test a string to see if it is a valid UUID | New in `uuid@8.3` | 71 | | [`uuid.version()`](#uuidversionstr) | Detect RFC version of a UUID | New in `uuid@8.3` | 72 | 73 | ## API 74 | 75 | ### uuid.NIL 76 | 77 | The nil UUID string (all zeros). 78 | 79 | Example: 80 | 81 | ```javascript 82 | import { NIL as NIL_UUID } from 'uuid'; 83 | 84 | NIL_UUID; // ⇨ '00000000-0000-0000-0000-000000000000' 85 | ``` 86 | 87 | ### uuid.MAX 88 | 89 | The max UUID string (all ones). 90 | 91 | Example: 92 | 93 | ```javascript 94 | import { MAX as MAX_UUID } from 'uuid'; 95 | 96 | MAX_UUID; // ⇨ 'ffffffff-ffff-ffff-ffff-ffffffffffff' 97 | ``` 98 | 99 | ### uuid.parse(str) 100 | 101 | Convert UUID string to array of bytes 102 | 103 | | | | 104 | | --------- | ---------------------------------------- | 105 | | `str` | A valid UUID `String` | 106 | | _returns_ | `Uint8Array[16]` | 107 | | _throws_ | `TypeError` if `str` is not a valid UUID | 108 | 109 | <!-- prettier-ignore --> 110 | > [!NOTE] 111 | > Ordering of values in the byte arrays used by `parse()` and `stringify()` follows the left ↠ right order of hex-pairs in UUID strings. As shown in the example below. 112 | 113 | Example: 114 | 115 | ```javascript 116 | import { parse as uuidParse } from 'uuid'; 117 | 118 | // Parse a UUID 119 | uuidParse('6ec0bd7f-11c0-43da-975e-2a8ad9ebae0b'); // ⇨ 120 | // Uint8Array(16) [ 121 | // 110, 192, 189, 127, 17, 122 | // 192, 67, 218, 151, 94, 123 | // 42, 138, 217, 235, 174, 124 | // 11 125 | // ] 126 | ``` 127 | 128 | ### uuid.stringify(arr[, offset]) 129 | 130 | Convert array of bytes to UUID string 131 | 132 | | | | 133 | | -------------- | ---------------------------------------------------------------------------- | 134 | | `arr` | `Array`-like collection of 16 values (starting from `offset`) between 0-255. | 135 | | [`offset` = 0] | `Number` Starting index in the Array | 136 | | _returns_ | `String` | 137 | | _throws_ | `TypeError` if a valid UUID string cannot be generated | 138 | 139 | <!-- prettier-ignore --> 140 | > [!NOTE] 141 | > Ordering of values in the byte arrays used by `parse()` and `stringify()` follows the left ↠ right order of hex-pairs in UUID strings. As shown in the example below. 142 | 143 | Example: 144 | 145 | ```javascript 146 | import { stringify as uuidStringify } from 'uuid'; 147 | 148 | const uuidBytes = Uint8Array.of( 149 | 0x6e, 150 | 0xc0, 151 | 0xbd, 152 | 0x7f, 153 | 0x11, 154 | 0xc0, 155 | 0x43, 156 | 0xda, 157 | 0x97, 158 | 0x5e, 159 | 0x2a, 160 | 0x8a, 161 | 0xd9, 162 | 0xeb, 163 | 0xae, 164 | 0x0b 165 | ); 166 | 167 | uuidStringify(uuidBytes); // ⇨ '6ec0bd7f-11c0-43da-975e-2a8ad9ebae0b' 168 | ``` 169 | 170 | ### uuid.v1([options[, buffer[, offset]]]) 171 | 172 | Create an RFC version 1 (timestamp) UUID 173 | 174 | | | | 175 | | --- | --- | 176 | | [`options`] | `Object` with one or more of the following properties: | 177 | | [`options.node = (random)` ] | RFC "node" field as an `Array[6]` of byte values (per 4.1.6) | 178 | | [`options.clockseq = (random)`] | RFC "clock sequence" as a `Number` between 0 - 0x3fff | 179 | | [`options.msecs = (current time)`] | RFC "timestamp" field (`Number` of milliseconds, unix epoch) | 180 | | [`options.nsecs = 0`] | RFC "timestamp" field (`Number` of nanoseconds to add to `msecs`, should be 0-10,000) | 181 | | [`options.random = (random)`] | `Array` of 16 random bytes (0-255) used to generate other fields, above | 182 | | [`options.rng`] | Alternative to `options.random`, a `Function` that returns an `Array` of 16 random bytes (0-255) | 183 | | [`buffer`] | `Uint8Array` or `Uint8Array` subtype (e.g. Node.js `Buffer`). If provided, binary UUID is written into the array, starting at `offset` | 184 | | [`offset` = 0] | `Number` Index to start writing UUID bytes in `buffer` | 185 | | _returns_ | UUID `String` if no `buffer` is specified, otherwise returns `buffer` | 186 | | _throws_ | `Error` if more than 10M UUIDs/sec are requested | 187 | 188 | <!-- prettier-ignore --> 189 | > [!NOTE] 190 | > The default [node id](https://datatracker.ietf.org/doc/html/rfc9562#section-5.1) (the last 12 digits in the UUID) is generated once, randomly, on process startup, and then remains unchanged for the duration of the process. 191 | 192 | <!-- prettier-ignore --> 193 | > [!NOTE] 194 | > `options.random` and `options.rng` are only meaningful on the very first call to `v1()`, where they may be passed to initialize the internal `node` and `clockseq` fields. 195 | 196 | Example: 197 | 198 | ```javascript 199 | import { v1 as uuidv1 } from 'uuid'; 200 | 201 | uuidv1(); // ⇨ '2c5ea4c0-4067-11e9-9bdd-2b0d7b3dcb6d' 202 | ``` 203 | 204 | Example using `options`: 205 | 206 | ```javascript 207 | import { v1 as uuidv1 } from 'uuid'; 208 | 209 | const options = { 210 | node: Uint8Array.of(0x01, 0x23, 0x45, 0x67, 0x89, 0xab), 211 | clockseq: 0x1234, 212 | msecs: new Date('2011-11-01').getTime(), 213 | nsecs: 5678, 214 | }; 215 | uuidv1(options); // ⇨ '710b962e-041c-11e1-9234-0123456789ab' 216 | ``` 217 | 218 | ### uuid.v1ToV6(uuid) 219 | 220 | Convert a UUID from version 1 to version 6 221 | 222 | ```javascript 223 | import { v1ToV6 } from 'uuid'; 224 | 225 | v1ToV6('92f62d9e-22c4-11ef-97e9-325096b39f47'); // ⇨ '1ef22c49-2f62-6d9e-97e9-325096b39f47' 226 | ``` 227 | 228 | ### uuid.v3(name, namespace[, buffer[, offset]]) 229 | 230 | Create an RFC version 3 (namespace w/ MD5) UUID 231 | 232 | API is identical to `v5()`, but uses "v3" instead. 233 | 234 | <!-- prettier-ignore --> 235 | > [!IMPORTANT] 236 | > Per the RFC, "_If backward compatibility is not an issue, SHA-1 [Version 5] is preferred_." 237 | 238 | ### uuid.v4([options[, buffer[, offset]]]) 239 | 240 | Create an RFC version 4 (random) UUID 241 | 242 | | | | 243 | | --- | --- | 244 | | [`options`] | `Object` with one or more of the following properties: | 245 | | [`options.random`] | `Array` of 16 random bytes (0-255) | 246 | | [`options.rng`] | Alternative to `options.random`, a `Function` that returns an `Array` of 16 random bytes (0-255) | 247 | | [`buffer`] | `Uint8Array` or `Uint8Array` subtype (e.g. Node.js `Buffer`). If provided, binary UUID is written into the array, starting at `offset` | 248 | | [`offset` = 0] | `Number` Index to start writing UUID bytes in `buffer` | 249 | | _returns_ | UUID `String` if no `buffer` is specified, otherwise returns `buffer` | 250 | 251 | Example: 252 | 253 | ```javascript 254 | import { v4 as uuidv4 } from 'uuid'; 255 | 256 | uuidv4(); // ⇨ '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d' 257 | ``` 258 | 259 | Example using predefined `random` values: 260 | 261 | ```javascript 262 | import { v4 as uuidv4 } from 'uuid'; 263 | 264 | const v4options = { 265 | random: Uint8Array.of( 266 | 0x10, 267 | 0x91, 268 | 0x56, 269 | 0xbe, 270 | 0xc4, 271 | 0xfb, 272 | 0xc1, 273 | 0xea, 274 | 0x71, 275 | 0xb4, 276 | 0xef, 277 | 0xe1, 278 | 0x67, 279 | 0x1c, 280 | 0x58, 281 | 0x36 282 | ), 283 | }; 284 | uuidv4(v4options); // ⇨ '109156be-c4fb-41ea-b1b4-efe1671c5836' 285 | ``` 286 | 287 | ### uuid.v5(name, namespace[, buffer[, offset]]) 288 | 289 | Create an RFC version 5 (namespace w/ SHA-1) UUID 290 | 291 | | | | 292 | | --- | --- | 293 | | `name` | `String \| Array` | 294 | | `namespace` | `String \| Array[16]` Namespace UUID | 295 | | [`buffer`] | `Uint8Array` or `Uint8Array` subtype (e.g. Node.js `Buffer`). If provided, binary UUID is written into the array, starting at `offset` | 296 | | [`offset` = 0] | `Number` Index to start writing UUID bytes in `buffer` | 297 | | _returns_ | UUID `String` if no `buffer` is specified, otherwise returns `buffer` | 298 | 299 | <!-- prettier-ignore --> 300 | > [!NOTE] 301 | > The RFC `DNS` and `URL` namespaces are available as `v5.DNS` and `v5.URL`. 302 | 303 | Example with custom namespace: 304 | 305 | ```javascript 306 | import { v5 as uuidv5 } from 'uuid'; 307 | 308 | // Define a custom namespace. Readers, create your own using something like 309 | // https://www.uuidgenerator.net/ 310 | const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341'; 311 | 312 | uuidv5('Hello, World!', MY_NAMESPACE); // ⇨ '630eb68f-e0fa-5ecc-887a-7c7a62614681' 313 | ``` 314 | 315 | Example with RFC `URL` namespace: 316 | 317 | ```javascript 318 | import { v5 as uuidv5 } from 'uuid'; 319 | 320 | uuidv5('https://www.w3.org/', uuidv5.URL); // ⇨ 'c106a26a-21bb-5538-8bf2-57095d1976c1' 321 | ``` 322 | 323 | ### uuid.v6([options[, buffer[, offset]]]) 324 | 325 | Create an RFC version 6 (timestamp, reordered) UUID 326 | 327 | This method takes the same arguments as uuid.v1(). 328 | 329 | ```javascript 330 | import { v6 as uuidv6 } from 'uuid'; 331 | 332 | uuidv6(); // ⇨ '1e940672-c5ea-64c0-9b5d-ab8dfbbd4bed' 333 | ``` 334 | 335 | Example using `options`: 336 | 337 | ```javascript 338 | import { v6 as uuidv6 } from 'uuid'; 339 | 340 | const options = { 341 | node: [0x01, 0x23, 0x45, 0x67, 0x89, 0xab], 342 | clockseq: 0x1234, 343 | msecs: new Date('2011-11-01').getTime(), 344 | nsecs: 5678, 345 | }; 346 | uuidv6(options); // ⇨ '1e1041c7-10b9-662e-9234-0123456789ab' 347 | ``` 348 | 349 | ### uuid.v6ToV1(uuid) 350 | 351 | Convert a UUID from version 6 to version 1 352 | 353 | ```javascript 354 | import { v6ToV1 } from 'uuid'; 355 | 356 | v6ToV1('1ef22c49-2f62-6d9e-97e9-325096b39f47'); // ⇨ '92f62d9e-22c4-11ef-97e9-325096b39f47' 357 | ``` 358 | 359 | ### uuid.v7([options[, buffer[, offset]]]) 360 | 361 | Create an RFC version 7 (random) UUID 362 | 363 | | | | 364 | | --- | --- | 365 | | [`options`] | `Object` with one or more of the following properties: | 366 | | [`options.msecs = (current time)`] | RFC "timestamp" field (`Number` of milliseconds, unix epoch) | 367 | | [`options.random = (random)`] | `Array` of 16 random bytes (0-255) used to generate other fields, above | 368 | | [`options.rng`] | Alternative to `options.random`, a `Function` that returns an `Array` of 16 random bytes (0-255) | 369 | | [`options.seq = (random)`] | 32-bit sequence `Number` between 0 - 0xffffffff. This may be provided to help ensure uniqueness for UUIDs generated within the same millisecond time interval. Default = random value. | 370 | | [`buffer`] | `Uint8Array` or `Uint8Array` subtype (e.g. Node.js `Buffer`). If provided, binary UUID is written into the array, starting at `offset` | 371 | | [`offset` = 0] | `Number` Index to start writing UUID bytes in `buffer` | 372 | | _returns_ | UUID `String` if no `buffer` is specified, otherwise returns `buffer` | 373 | 374 | Example: 375 | 376 | ```javascript 377 | import { v7 as uuidv7 } from 'uuid'; 378 | 379 | uuidv7(); // ⇨ '01695553-c90c-705a-b56d-778dfbbd4bed' 380 | ``` 381 | 382 | ### ~~uuid.v8()~~ 383 | 384 | **_"Intentionally left blank"_** 385 | 386 | <!-- prettier-ignore --> 387 | > [!NOTE] 388 | > Version 8 (experimental) UUIDs are "[for experimental or vendor-specific use cases](https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-8)". The RFC does not define a creation algorithm for them, which is why this package does not offer a `v8()` method. The `validate()` and `version()` methods do work with such UUIDs, however. 389 | 390 | ### uuid.validate(str) 391 | 392 | Test a string to see if it is a valid UUID 393 | 394 | | | | 395 | | --------- | --------------------------------------------------- | 396 | | `str` | `String` to validate | 397 | | _returns_ | `true` if string is a valid UUID, `false` otherwise | 398 | 399 | Example: 400 | 401 | ```javascript 402 | import { validate as uuidValidate } from 'uuid'; 403 | 404 | uuidValidate('not a UUID'); // ⇨ false 405 | uuidValidate('6ec0bd7f-11c0-43da-975e-2a8ad9ebae0b'); // ⇨ true 406 | ``` 407 | 408 | Using `validate` and `version` together it is possible to do per-version validation, e.g. validate for only v4 UUIds. 409 | 410 | ```javascript 411 | import { version as uuidVersion } from 'uuid'; 412 | import { validate as uuidValidate } from 'uuid'; 413 | 414 | function uuidValidateV4(uuid) { 415 | return uuidValidate(uuid) && uuidVersion(uuid) === 4; 416 | } 417 | 418 | const v1Uuid = 'd9428888-122b-11e1-b85c-61cd3cbb3210'; 419 | const v4Uuid = '109156be-c4fb-41ea-b1b4-efe1671c5836'; 420 | 421 | uuidValidateV4(v4Uuid); // ⇨ true 422 | uuidValidateV4(v1Uuid); // ⇨ false 423 | ``` 424 | 425 | ### uuid.version(str) 426 | 427 | Detect RFC version of a UUID 428 | 429 | | | | 430 | | --------- | ---------------------------------------- | 431 | | `str` | A valid UUID `String` | 432 | | _returns_ | `Number` The RFC version of the UUID | 433 | | _throws_ | `TypeError` if `str` is not a valid UUID | 434 | 435 | Example: 436 | 437 | ```javascript 438 | import { version as uuidVersion } from 'uuid'; 439 | 440 | uuidVersion('45637ec4-c85f-11ea-87d0-0242ac130003'); // ⇨ 1 441 | uuidVersion('6ec0bd7f-11c0-43da-975e-2a8ad9ebae0b'); // ⇨ 4 442 | ``` 443 | 444 | <!-- prettier-ignore --> 445 | > [!NOTE] 446 | > This method returns `0` for the `NIL` UUID, and `15` for the `MAX` UUID. 447 | 448 | ## Command Line 449 | 450 | UUIDs can be generated from the command line using `uuid`. 451 | 452 | ```shell 453 | $ npx uuid 454 | ddeb27fb-d9a0-4624-be4d-4615062daed4 455 | ``` 456 | 457 | The default is to generate version 4 UUIDS, however the other versions are supported. Type `uuid --help` for details: 458 | 459 | ```shell 460 | $ npx uuid --help 461 | 462 | Usage: 463 | uuid 464 | uuid v1 465 | uuid v3 <name> <namespace uuid> 466 | uuid v4 467 | uuid v5 <name> <namespace uuid> 468 | uuid v7 469 | uuid --help 470 | 471 | Note: <namespace uuid> may be "URL" or "DNS" to use the corresponding UUIDs 472 | defined by RFC9562 473 | ``` 474 | 475 | ## `options` Handling for Timestamp UUIDs 476 | 477 | Prior to `uuid@11`, it was possible for `options` state to interfere with the internal state used to ensure uniqueness of timestamp-based UUIDs (the `v1()`, `v6()`, and `v7()` methods). Starting with `uuid@11`, this issue has been addressed by using the presence of the `options` argument as a flag to select between two possible behaviors: 478 | 479 | - Without `options`: Internal state is utilized to improve UUID uniqueness. 480 | - With `options`: Internal state is **NOT** used and, instead, appropriate defaults are applied as needed. 481 | 482 | ## Support 483 | 484 | **Browsers**: `uuid` [builds are tested](/uuidjs/uuid/blob/main/wdio.conf.js) against the latest version of desktop Chrome, Safari, Firefox, and Edge. Mobile versions of these same browsers are expected to work but aren't currently tested. 485 | 486 | **Node**: `uuid` [builds are tested](https://github.com/uuidjs/uuid/blob/main/.github/workflows/ci.yml#L26-L27) against node ([LTS releases](https://github.com/nodejs/Release)), plus one prior. E.g. `node@18` is in maintainence mode, and `node@22` is the current LTS release. So `uuid` supports `node@16`-`node@22`. 487 | 488 | **Typescript**: TS versions released within the past two years are supported. [source](https://github.com/microsoft/TypeScript/issues/49088#issuecomment-2468723715) 489 | 490 | ## Known issues 491 | 492 | <!-- This header is referenced as an anchor in src/rng-browser.ts --> 493 | 494 | ### "getRandomValues() not supported" 495 | 496 | This error occurs in environments where the standard [`crypto.getRandomValues()`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues) API is not supported. This issue can be resolved by adding an appropriate polyfill: 497 | 498 | #### React Native / Expo 499 | 500 | 1. Install [`react-native-get-random-values`](https://github.com/LinusU/react-native-get-random-values#readme) 501 | 1. Import it _before_ `uuid`. Since `uuid` might also appear as a transitive dependency of some other imports it's safest to just import `react-native-get-random-values` as the very first thing in your entry point: 502 | 503 | ```javascript 504 | import 'react-native-get-random-values'; 505 | import { v4 as uuidv4 } from 'uuid'; 506 | ``` 507 | 508 | --- 509 | 510 | Markdown generated from [README_js.md](README_js.md) by <a href="https://github.com/broofa/runmd"><image height="13" src="https://camo.githubusercontent.com/5c7c603cd1e6a43370b0a5063d457e0dabb74cf317adc7baba183acb686ee8d0/687474703a2f2f692e696d6775722e636f6d2f634a4b6f3662552e706e67" /></a> 511 | -------------------------------------------------------------------------------- /README_js.md: -------------------------------------------------------------------------------- 1 | ```javascript --hide 2 | runmd.onRequire = (path) => { 3 | if (path == 'rng') return fun; 4 | return path.replace(/^uuid/, './dist/cjs/'); 5 | }; 6 | 7 | // Shim Date and crypto so generated ids are consistent across doc revisions 8 | runmd.Date.now = () => 1551914748172; 9 | 10 | let seed = 0xdefaced; 11 | require('crypto').randomFillSync = function (a) { 12 | for (let i = 0; i < 16; i++) a[i] = (seed = (seed * 0x41a7) & 0x7fffffff) & 0xff; 13 | return a; 14 | }; 15 | 16 | // Prevent usage of native randomUUID implementation to ensure deterministic UUIDs 17 | require('crypto').randomUUID = undefined; 18 | ``` 19 | 20 | # uuid [![CI](https://github.com/uuidjs/uuid/workflows/CI/badge.svg)](https://github.com/uuidjs/uuid/actions?query=workflow%3ACI) [![Browser](https://github.com/uuidjs/uuid/workflows/Browser/badge.svg)](https://github.com/uuidjs/uuid/actions/workflows/browser.yml) 21 | 22 | For the creation of [RFC9562](https://www.rfc-editor.org/rfc/rfc9562.html) (formerly [RFC4122](https://www.rfc-editor.org/rfc/rfc4122.html)) UUIDs 23 | 24 | - **Complete** - Support for all RFC9562 UUID versions 25 | - **Cross-platform** - Support for... 26 | - ESM & Common JS 27 | - [Typescript](#support) 28 | - [Chrome, Safari, Firefox, and Edge](#support) 29 | - [NodeJS](#support) 30 | - [React Native / Expo](#react-native--expo) 31 | - **Secure** - Uses modern `crypto` API for random values 32 | - **Compact** - Zero-dependency, [tree-shakable](https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking) 33 | - **CLI** - [`uuid` command line](#command-line) utility 34 | 35 | <!-- prettier-ignore --> 36 | > [!NOTE] 37 | > `uuid@11` is now available: See the [CHANGELOG](./CHANGELOG.md) for details. TL;DR: 38 | > * TypeScript support is now included (remove `@types/uuid` from your dependencies) 39 | > * Subtle changes to how the `options` arg is interpreted for `v1()`, `v6()`, and `v7()`. [See details](#options-handling-for-timestamp-uuids) 40 | > * Binary UUIDs are now `Uint8Array`s. (May impact callers of `parse()`, `stringify()`, or that pass an `option#buf` argument to `v1()`-`v7()`.) 41 | 42 | ## Quickstart 43 | 44 | **1. Install** 45 | 46 | ```shell 47 | npm install uuid 48 | ``` 49 | 50 | **2. Create a UUID** 51 | 52 | ESM-syntax (must use named exports): 53 | 54 | ```javascript --run 55 | import { v4 as uuidv4 } from 'uuid'; 56 | uuidv4(); // RESULT 57 | ``` 58 | 59 | ... CommonJS: 60 | 61 | ```javascript --run 62 | const { v4: uuidv4 } = require('uuid'); 63 | uuidv4(); // RESULT 64 | ``` 65 | 66 | For timestamp UUIDs, namespace UUIDs, and other options read on ... 67 | 68 | ## API Summary 69 | 70 | | | | | 71 | | --- | --- | --- | 72 | | [`uuid.NIL`](#uuidnil) | The nil UUID string (all zeros) | New in `uuid@8.3` | 73 | | [`uuid.MAX`](#uuidmax) | The max UUID string (all ones) | New in `uuid@9.1` | 74 | | [`uuid.parse()`](#uuidparsestr) | Convert UUID string to array of bytes | New in `uuid@8.3` | 75 | | [`uuid.stringify()`](#uuidstringifyarr-offset) | Convert array of bytes to UUID string | New in `uuid@8.3` | 76 | | [`uuid.v1()`](#uuidv1options-buffer-offset) | Create a version 1 (timestamp) UUID | | 77 | | [`uuid.v1ToV6()`](#uuidv1tov6uuid) | Create a version 6 UUID from a version 1 UUID | New in `uuid@10` | 78 | | [`uuid.v3()`](#uuidv3name-namespace-buffer-offset) | Create a version 3 (namespace w/ MD5) UUID | | 79 | | [`uuid.v4()`](#uuidv4options-buffer-offset) | Create a version 4 (random) UUID | | 80 | | [`uuid.v5()`](#uuidv5name-namespace-buffer-offset) | Create a version 5 (namespace w/ SHA-1) UUID | | 81 | | [`uuid.v6()`](#uuidv6options-buffer-offset) | Create a version 6 (timestamp, reordered) UUID | New in `uuid@10` | 82 | | [`uuid.v6ToV1()`](#uuidv6tov1uuid) | Create a version 1 UUID from a version 6 UUID | New in `uuid@10` | 83 | | [`uuid.v7()`](#uuidv7options-buffer-offset) | Create a version 7 (Unix Epoch time-based) UUID | New in `uuid@10` | 84 | | ~~[`uuid.v8()`](#uuidv8)~~ | "Intentionally left blank" | | 85 | | [`uuid.validate()`](#uuidvalidatestr) | Test a string to see if it is a valid UUID | New in `uuid@8.3` | 86 | | [`uuid.version()`](#uuidversionstr) | Detect RFC version of a UUID | New in `uuid@8.3` | 87 | 88 | ## API 89 | 90 | ### uuid.NIL 91 | 92 | The nil UUID string (all zeros). 93 | 94 | Example: 95 | 96 | ```javascript --run 97 | import { NIL as NIL_UUID } from 'uuid'; 98 | 99 | NIL_UUID; // RESULT 100 | ``` 101 | 102 | ### uuid.MAX 103 | 104 | The max UUID string (all ones). 105 | 106 | Example: 107 | 108 | ```javascript --run 109 | import { MAX as MAX_UUID } from 'uuid'; 110 | 111 | MAX_UUID; // RESULT 112 | ``` 113 | 114 | ### uuid.parse(str) 115 | 116 | Convert UUID string to array of bytes 117 | 118 | | | | 119 | | --------- | ---------------------------------------- | 120 | | `str` | A valid UUID `String` | 121 | | _returns_ | `Uint8Array[16]` | 122 | | _throws_ | `TypeError` if `str` is not a valid UUID | 123 | 124 | <!-- prettier-ignore --> 125 | > [!NOTE] 126 | > Ordering of values in the byte arrays used by `parse()` and `stringify()` follows the left ↠ right order of hex-pairs in UUID strings. As shown in the example below. 127 | 128 | Example: 129 | 130 | ```javascript --run 131 | import { parse as uuidParse } from 'uuid'; 132 | 133 | // Parse a UUID 134 | uuidParse('6ec0bd7f-11c0-43da-975e-2a8ad9ebae0b'); // RESULT 135 | ``` 136 | 137 | ### uuid.stringify(arr[, offset]) 138 | 139 | Convert array of bytes to UUID string 140 | 141 | | | | 142 | | -------------- | ---------------------------------------------------------------------------- | 143 | | `arr` | `Array`-like collection of 16 values (starting from `offset`) between 0-255. | 144 | | [`offset` = 0] | `Number` Starting index in the Array | 145 | | _returns_ | `String` | 146 | | _throws_ | `TypeError` if a valid UUID string cannot be generated | 147 | 148 | <!-- prettier-ignore --> 149 | > [!NOTE] 150 | > Ordering of values in the byte arrays used by `parse()` and `stringify()` follows the left ↠ right order of hex-pairs in UUID strings. As shown in the example below. 151 | 152 | Example: 153 | 154 | ```javascript --run 155 | import { stringify as uuidStringify } from 'uuid'; 156 | 157 | const uuidBytes = Uint8Array.of( 158 | 0x6e, 159 | 0xc0, 160 | 0xbd, 161 | 0x7f, 162 | 0x11, 163 | 0xc0, 164 | 0x43, 165 | 0xda, 166 | 0x97, 167 | 0x5e, 168 | 0x2a, 169 | 0x8a, 170 | 0xd9, 171 | 0xeb, 172 | 0xae, 173 | 0x0b 174 | ); 175 | 176 | uuidStringify(uuidBytes); // RESULT 177 | ``` 178 | 179 | ### uuid.v1([options[, buffer[, offset]]]) 180 | 181 | Create an RFC version 1 (timestamp) UUID 182 | 183 | | | | 184 | | --- | --- | 185 | | [`options`] | `Object` with one or more of the following properties: | 186 | | [`options.node = (random)` ] | RFC "node" field as an `Array[6]` of byte values (per 4.1.6) | 187 | | [`options.clockseq = (random)`] | RFC "clock sequence" as a `Number` between 0 - 0x3fff | 188 | | [`options.msecs = (current time)`] | RFC "timestamp" field (`Number` of milliseconds, unix epoch) | 189 | | [`options.nsecs = 0`] | RFC "timestamp" field (`Number` of nanoseconds to add to `msecs`, should be 0-10,000) | 190 | | [`options.random = (random)`] | `Array` of 16 random bytes (0-255) used to generate other fields, above | 191 | | [`options.rng`] | Alternative to `options.random`, a `Function` that returns an `Array` of 16 random bytes (0-255) | 192 | | [`buffer`] | `Uint8Array` or `Uint8Array` subtype (e.g. Node.js `Buffer`). If provided, binary UUID is written into the array, starting at `offset` | 193 | | [`offset` = 0] | `Number` Index to start writing UUID bytes in `buffer` | 194 | | _returns_ | UUID `String` if no `buffer` is specified, otherwise returns `buffer` | 195 | | _throws_ | `Error` if more than 10M UUIDs/sec are requested | 196 | 197 | <!-- prettier-ignore --> 198 | > [!NOTE] 199 | > The default [node id](https://datatracker.ietf.org/doc/html/rfc9562#section-5.1) (the last 12 digits in the UUID) is generated once, randomly, on process startup, and then remains unchanged for the duration of the process. 200 | 201 | <!-- prettier-ignore --> 202 | > [!NOTE] 203 | > `options.random` and `options.rng` are only meaningful on the very first call to `v1()`, where they may be passed to initialize the internal `node` and `clockseq` fields. 204 | 205 | Example: 206 | 207 | ```javascript --run 208 | import { v1 as uuidv1 } from 'uuid'; 209 | 210 | uuidv1(); // RESULT 211 | ``` 212 | 213 | Example using `options`: 214 | 215 | ```javascript --run 216 | import { v1 as uuidv1 } from 'uuid'; 217 | 218 | const options = { 219 | node: Uint8Array.of(0x01, 0x23, 0x45, 0x67, 0x89, 0xab), 220 | clockseq: 0x1234, 221 | msecs: new Date('2011-11-01').getTime(), 222 | nsecs: 5678, 223 | }; 224 | uuidv1(options); // RESULT 225 | ``` 226 | 227 | ### uuid.v1ToV6(uuid) 228 | 229 | Convert a UUID from version 1 to version 6 230 | 231 | ```javascript --run 232 | import { v1ToV6 } from 'uuid'; 233 | 234 | v1ToV6('92f62d9e-22c4-11ef-97e9-325096b39f47'); // RESULT 235 | ``` 236 | 237 | ### uuid.v3(name, namespace[, buffer[, offset]]) 238 | 239 | Create an RFC version 3 (namespace w/ MD5) UUID 240 | 241 | API is identical to `v5()`, but uses "v3" instead. 242 | 243 | <!-- prettier-ignore --> 244 | > [!IMPORTANT] 245 | > Per the RFC, "_If backward compatibility is not an issue, SHA-1 [Version 5] is preferred_." 246 | 247 | ### uuid.v4([options[, buffer[, offset]]]) 248 | 249 | Create an RFC version 4 (random) UUID 250 | 251 | | | | 252 | | --- | --- | 253 | | [`options`] | `Object` with one or more of the following properties: | 254 | | [`options.random`] | `Array` of 16 random bytes (0-255) | 255 | | [`options.rng`] | Alternative to `options.random`, a `Function` that returns an `Array` of 16 random bytes (0-255) | 256 | | [`buffer`] | `Uint8Array` or `Uint8Array` subtype (e.g. Node.js `Buffer`). If provided, binary UUID is written into the array, starting at `offset` | 257 | | [`offset` = 0] | `Number` Index to start writing UUID bytes in `buffer` | 258 | | _returns_ | UUID `String` if no `buffer` is specified, otherwise returns `buffer` | 259 | 260 | Example: 261 | 262 | ```javascript --run 263 | import { v4 as uuidv4 } from 'uuid'; 264 | 265 | uuidv4(); // RESULT 266 | ``` 267 | 268 | Example using predefined `random` values: 269 | 270 | ```javascript --run 271 | import { v4 as uuidv4 } from 'uuid'; 272 | 273 | const v4options = { 274 | random: Uint8Array.of( 275 | 0x10, 276 | 0x91, 277 | 0x56, 278 | 0xbe, 279 | 0xc4, 280 | 0xfb, 281 | 0xc1, 282 | 0xea, 283 | 0x71, 284 | 0xb4, 285 | 0xef, 286 | 0xe1, 287 | 0x67, 288 | 0x1c, 289 | 0x58, 290 | 0x36 291 | ), 292 | }; 293 | uuidv4(v4options); // RESULT 294 | ``` 295 | 296 | ### uuid.v5(name, namespace[, buffer[, offset]]) 297 | 298 | Create an RFC version 5 (namespace w/ SHA-1) UUID 299 | 300 | | | | 301 | | --- | --- | 302 | | `name` | `String \| Array` | 303 | | `namespace` | `String \| Array[16]` Namespace UUID | 304 | | [`buffer`] | `Uint8Array` or `Uint8Array` subtype (e.g. Node.js `Buffer`). If provided, binary UUID is written into the array, starting at `offset` | 305 | | [`offset` = 0] | `Number` Index to start writing UUID bytes in `buffer` | 306 | | _returns_ | UUID `String` if no `buffer` is specified, otherwise returns `buffer` | 307 | 308 | <!-- prettier-ignore --> 309 | > [!NOTE] 310 | > The RFC `DNS` and `URL` namespaces are available as `v5.DNS` and `v5.URL`. 311 | 312 | Example with custom namespace: 313 | 314 | ```javascript --run 315 | import { v5 as uuidv5 } from 'uuid'; 316 | 317 | // Define a custom namespace. Readers, create your own using something like 318 | // https://www.uuidgenerator.net/ 319 | const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341'; 320 | 321 | uuidv5('Hello, World!', MY_NAMESPACE); // RESULT 322 | ``` 323 | 324 | Example with RFC `URL` namespace: 325 | 326 | ```javascript --run 327 | import { v5 as uuidv5 } from 'uuid'; 328 | 329 | uuidv5('https://www.w3.org/', uuidv5.URL); // RESULT 330 | ``` 331 | 332 | ### uuid.v6([options[, buffer[, offset]]]) 333 | 334 | Create an RFC version 6 (timestamp, reordered) UUID 335 | 336 | This method takes the same arguments as uuid.v1(). 337 | 338 | ```javascript --run 339 | import { v6 as uuidv6 } from 'uuid'; 340 | 341 | uuidv6(); // RESULT 342 | ``` 343 | 344 | Example using `options`: 345 | 346 | ```javascript --run 347 | import { v6 as uuidv6 } from 'uuid'; 348 | 349 | const options = { 350 | node: [0x01, 0x23, 0x45, 0x67, 0x89, 0xab], 351 | clockseq: 0x1234, 352 | msecs: new Date('2011-11-01').getTime(), 353 | nsecs: 5678, 354 | }; 355 | uuidv6(options); // RESULT 356 | ``` 357 | 358 | ### uuid.v6ToV1(uuid) 359 | 360 | Convert a UUID from version 6 to version 1 361 | 362 | ```javascript --run 363 | import { v6ToV1 } from 'uuid'; 364 | 365 | v6ToV1('1ef22c49-2f62-6d9e-97e9-325096b39f47'); // RESULT 366 | ``` 367 | 368 | ### uuid.v7([options[, buffer[, offset]]]) 369 | 370 | Create an RFC version 7 (random) UUID 371 | 372 | | | | 373 | | --- | --- | 374 | | [`options`] | `Object` with one or more of the following properties: | 375 | | [`options.msecs = (current time)`] | RFC "timestamp" field (`Number` of milliseconds, unix epoch) | 376 | | [`options.random = (random)`] | `Array` of 16 random bytes (0-255) used to generate other fields, above | 377 | | [`options.rng`] | Alternative to `options.random`, a `Function` that returns an `Array` of 16 random bytes (0-255) | 378 | | [`options.seq = (random)`] | 32-bit sequence `Number` between 0 - 0xffffffff. This may be provided to help ensure uniqueness for UUIDs generated within the same millisecond time interval. Default = random value. | 379 | | [`buffer`] | `Uint8Array` or `Uint8Array` subtype (e.g. Node.js `Buffer`). If provided, binary UUID is written into the array, starting at `offset` | 380 | | [`offset` = 0] | `Number` Index to start writing UUID bytes in `buffer` | 381 | | _returns_ | UUID `String` if no `buffer` is specified, otherwise returns `buffer` | 382 | 383 | Example: 384 | 385 | ```javascript --run 386 | import { v7 as uuidv7 } from 'uuid'; 387 | 388 | uuidv7(); // RESULT 389 | ``` 390 | 391 | ### ~~uuid.v8()~~ 392 | 393 | **_"Intentionally left blank"_** 394 | 395 | <!-- prettier-ignore --> 396 | > [!NOTE] 397 | > Version 8 (experimental) UUIDs are "[for experimental or vendor-specific use cases](https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-8)". The RFC does not define a creation algorithm for them, which is why this package does not offer a `v8()` method. The `validate()` and `version()` methods do work with such UUIDs, however. 398 | 399 | ### uuid.validate(str) 400 | 401 | Test a string to see if it is a valid UUID 402 | 403 | | | | 404 | | --------- | --------------------------------------------------- | 405 | | `str` | `String` to validate | 406 | | _returns_ | `true` if string is a valid UUID, `false` otherwise | 407 | 408 | Example: 409 | 410 | ```javascript --run 411 | import { validate as uuidValidate } from 'uuid'; 412 | 413 | uuidValidate('not a UUID'); // RESULT 414 | uuidValidate('6ec0bd7f-11c0-43da-975e-2a8ad9ebae0b'); // RESULT 415 | ``` 416 | 417 | Using `validate` and `version` together it is possible to do per-version validation, e.g. validate for only v4 UUIds. 418 | 419 | ```javascript --run 420 | import { version as uuidVersion } from 'uuid'; 421 | import { validate as uuidValidate } from 'uuid'; 422 | 423 | function uuidValidateV4(uuid) { 424 | return uuidValidate(uuid) && uuidVersion(uuid) === 4; 425 | } 426 | 427 | const v1Uuid = 'd9428888-122b-11e1-b85c-61cd3cbb3210'; 428 | const v4Uuid = '109156be-c4fb-41ea-b1b4-efe1671c5836'; 429 | 430 | uuidValidateV4(v4Uuid); // RESULT 431 | uuidValidateV4(v1Uuid); // RESULT 432 | ``` 433 | 434 | ### uuid.version(str) 435 | 436 | Detect RFC version of a UUID 437 | 438 | | | | 439 | | --------- | ---------------------------------------- | 440 | | `str` | A valid UUID `String` | 441 | | _returns_ | `Number` The RFC version of the UUID | 442 | | _throws_ | `TypeError` if `str` is not a valid UUID | 443 | 444 | Example: 445 | 446 | ```javascript --run 447 | import { version as uuidVersion } from 'uuid'; 448 | 449 | uuidVersion('45637ec4-c85f-11ea-87d0-0242ac130003'); // RESULT 450 | uuidVersion('6ec0bd7f-11c0-43da-975e-2a8ad9ebae0b'); // RESULT 451 | ``` 452 | 453 | <!-- prettier-ignore --> 454 | > [!NOTE] 455 | > This method returns `0` for the `NIL` UUID, and `15` for the `MAX` UUID. 456 | 457 | ## Command Line 458 | 459 | UUIDs can be generated from the command line using `uuid`. 460 | 461 | ```shell 462 | $ npx uuid 463 | ddeb27fb-d9a0-4624-be4d-4615062daed4 464 | ``` 465 | 466 | The default is to generate version 4 UUIDS, however the other versions are supported. Type `uuid --help` for details: 467 | 468 | ```shell 469 | $ npx uuid --help 470 | 471 | Usage: 472 | uuid 473 | uuid v1 474 | uuid v3 <name> <namespace uuid> 475 | uuid v4 476 | uuid v5 <name> <namespace uuid> 477 | uuid v7 478 | uuid --help 479 | 480 | Note: <namespace uuid> may be "URL" or "DNS" to use the corresponding UUIDs 481 | defined by RFC9562 482 | ``` 483 | 484 | ## `options` Handling for Timestamp UUIDs 485 | 486 | Prior to `uuid@11`, it was possible for `options` state to interfere with the internal state used to ensure uniqueness of timestamp-based UUIDs (the `v1()`, `v6()`, and `v7()` methods). Starting with `uuid@11`, this issue has been addressed by using the presence of the `options` argument as a flag to select between two possible behaviors: 487 | 488 | - Without `options`: Internal state is utilized to improve UUID uniqueness. 489 | - With `options`: Internal state is **NOT** used and, instead, appropriate defaults are applied as needed. 490 | 491 | ## Support 492 | 493 | **Browsers**: `uuid` [builds are tested](/uuidjs/uuid/blob/main/wdio.conf.js) against the latest version of desktop Chrome, Safari, Firefox, and Edge. Mobile versions of these same browsers are expected to work but aren't currently tested. 494 | 495 | **Node**: `uuid` [builds are tested](https://github.com/uuidjs/uuid/blob/main/.github/workflows/ci.yml#L26-L27) against node ([LTS releases](https://github.com/nodejs/Release)), plus one prior. E.g. `node@18` is in maintainence mode, and `node@22` is the current LTS release. So `uuid` supports `node@16`-`node@22`. 496 | 497 | **Typescript**: TS versions released within the past two years are supported. [source](https://github.com/microsoft/TypeScript/issues/49088#issuecomment-2468723715) 498 | 499 | ## Known issues 500 | 501 | <!-- This header is referenced as an anchor in src/rng-browser.ts --> 502 | 503 | ### "getRandomValues() not supported" 504 | 505 | This error occurs in environments where the standard [`crypto.getRandomValues()`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues) API is not supported. This issue can be resolved by adding an appropriate polyfill: 506 | 507 | #### React Native / Expo 508 | 509 | 1. Install [`react-native-get-random-values`](https://github.com/LinusU/react-native-get-random-values#readme) 510 | 1. Import it _before_ `uuid`. Since `uuid` might also appear as a transitive dependency of some other imports it's safest to just import `react-native-get-random-values` as the very first thing in your entry point: 511 | 512 | ```javascript 513 | import 'react-native-get-random-values'; 514 | import { v4 as uuidv4 } from 'uuid'; 515 | ``` 516 | -------------------------------------------------------------------------------- /bundlewatch.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ci": { 3 | "repoBranchBase": "main", 4 | "trackBranches": ["main"] 5 | }, 6 | "files": [ 7 | { "path": "./examples/browser-rollup/dist/v1-size.js", "maxSize": "1.0 kB" }, 8 | { "path": "./examples/browser-rollup/dist/v3-size.js", "maxSize": "2.1 kB" }, 9 | { "path": "./examples/browser-rollup/dist/v4-size.js", "maxSize": "0.7 kB" }, 10 | { "path": "./examples/browser-rollup/dist/v5-size.js", "maxSize": "1.5 kB" }, 11 | { "path": "./examples/browser-rollup/dist/v6-size.js", "maxSize": "1.6 kB" }, 12 | { "path": "./examples/browser-rollup/dist/v7-size.js", "maxSize": "0.8 kB" }, 13 | { "path": "./examples/browser-webpack/dist/v1-size.js", "maxSize": "1.0 kB" }, 14 | { "path": "./examples/browser-webpack/dist/v3-size.js", "maxSize": "2.1 kB" }, 15 | { "path": "./examples/browser-webpack/dist/v4-size.js", "maxSize": "0.7 kB" }, 16 | { "path": "./examples/browser-webpack/dist/v5-size.js", "maxSize": "1.5 kB" }, 17 | { "path": "./examples/browser-webpack/dist/v6-size.js", "maxSize": "1.6 kB" }, 18 | { "path": "./examples/browser-webpack/dist/v7-size.js", "maxSize": "0.8 kB" } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js'; 2 | import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; 3 | import globals from 'globals'; 4 | import neostandard from 'neostandard'; 5 | import tseslint from 'typescript-eslint'; 6 | 7 | const neostandardConfig = neostandard({ semi: true, noStyle: true }); 8 | 9 | export default [ 10 | js.configs.recommended, 11 | ...tseslint.configs.recommended, 12 | ...neostandardConfig, 13 | eslintPluginPrettierRecommended, 14 | { 15 | languageOptions: { 16 | ecmaVersion: 'latest', 17 | sourceType: 'module', 18 | globals: { 19 | ...globals.browser, 20 | ...globals.commonjs, 21 | ...globals.jest, 22 | ...globals.node, 23 | }, 24 | }, 25 | }, 26 | { 27 | rules: { 28 | '@typescript-eslint/no-redeclare': 'error', 29 | '@typescript-eslint/no-require-imports': 'off', 30 | 'no-redeclare': 'off', 31 | 'no-var': ['error'], 32 | curly: ['error', 'all'], 33 | }, 34 | }, 35 | { 36 | ignores: ['eslint.config.cjs', '**/dist/', 'node_modules/'], 37 | }, 38 | ]; 39 | -------------------------------------------------------------------------------- /examples/benchmark/README.md: -------------------------------------------------------------------------------- 1 | # uuid Benchmark 2 | 3 | ``` 4 | npm install 5 | ``` 6 | 7 | ## Node.js 8 | 9 | To run the benchmark in Node.js, run `npm test`. 10 | 11 | ## Browser 12 | 13 | To run the benchmark in the browser run `npm run start`, open `benchmark.html`, and check the console. 14 | 15 | Example output (`uuid@8.0.0`, MacBook Pro (Retina, 13-inch, Early 2015), 3.1 GHz Dual-Core Intel Core i7): 16 | 17 | ``` 18 | Starting. Tests take ~1 minute to run ... 19 | uuidv1() x 1,306,861 ops/sec ±2.62% (85 runs sampled) 20 | uuidv1() fill existing array x 4,750,515 ops/sec ±2.76% (88 runs sampled) 21 | uuidv4() x 302,174 ops/sec ±3.06% (81 runs sampled) 22 | uuidv4() fill existing array x 359,703 ops/sec ±3.67% (82 runs sampled) 23 | uuidv3() x 105,667 ops/sec ±3.84% (79 runs sampled) 24 | uuidv5() x 110,886 ops/sec ±2.55% (81 runs sampled) 25 | Fastest is uuidv1() fill existing array 26 | ``` 27 | -------------------------------------------------------------------------------- /examples/benchmark/benchmark.html: -------------------------------------------------------------------------------- 1 | <!doctype html> 2 | <title>UUID Benchmark 3 |

Please open the Developer Console to view output

4 | 5 | -------------------------------------------------------------------------------- /examples/benchmark/benchmark.js: -------------------------------------------------------------------------------- 1 | export default function benchmark(uuid, Benchmark) { 2 | console.log('Starting. Tests take ~1 minute to run ...'); 3 | 4 | function testParseAndStringify() { 5 | const suite = new Benchmark.Suite({ 6 | onError(event) { 7 | console.error(event.target.error); 8 | }, 9 | }); 10 | 11 | const BYTES = [ 12 | 0x0f, 0x5a, 0xbc, 0xd1, 0xc1, 0x94, 0x47, 0xf3, 0x90, 0x5b, 0x2d, 0xf7, 0x26, 0x3a, 0x08, 13 | 0x4b, 14 | ]; 15 | 16 | suite 17 | .add('uuid.stringify()', function () { 18 | uuid.stringify(BYTES); 19 | }) 20 | .add('uuid.parse()', function () { 21 | uuid.parse('0f5abcd1-c194-47f3-905b-2df7263a084b'); 22 | }) 23 | .on('cycle', function (event) { 24 | console.log(event.target.toString()); 25 | }) 26 | .on('complete', function () { 27 | console.log('---\n'); 28 | }) 29 | .run(); 30 | } 31 | 32 | function testGeneration() { 33 | const array = new Array(16); 34 | 35 | const suite = new Benchmark.Suite({ 36 | onError(event) { 37 | console.error(event.target.error); 38 | }, 39 | }); 40 | 41 | suite 42 | .add('uuid.v1()', function () { 43 | uuid.v1(); 44 | }) 45 | .add('uuid.v1() fill existing array', function () { 46 | try { 47 | uuid.v1(null, array, 0); 48 | } catch { 49 | // The spec (https://datatracker.ietf.org/doc/html/rfc9562#name-timestamp-considerations) defines that only 10M/s v1 50 | // UUIDs can be generated on a single node. This library throws an error if we hit that limit 51 | // (which can happen on modern hardware and modern Node.js versions). 52 | } 53 | }) 54 | .add('uuid.v4()', function () { 55 | uuid.v4(); 56 | }) 57 | .add('uuid.v4() fill existing array', function () { 58 | uuid.v4(null, array, 0); 59 | }) 60 | .add('uuid.v4() without native generation', function () { 61 | uuid.v4({}); // passing an object instead of null bypasses native.randomUUID 62 | }) 63 | .add('uuid.v3()', function () { 64 | uuid.v3('hello.example.com', uuid.v3.DNS); 65 | }) 66 | .add('uuid.v5()', function () { 67 | uuid.v5('hello.example.com', uuid.v5.DNS); 68 | }) 69 | .add('uuid.v6()', function () { 70 | uuid.v6(); 71 | }) 72 | .add('uuid.v7()', function () { 73 | uuid.v7(); 74 | }) 75 | .add('uuid.v7() fill existing array', function () { 76 | uuid.v7(null, array, 0); 77 | }) 78 | .add('uuid.v7() with defined time', function () { 79 | uuid.v7({ 80 | msecs: 1645557742000, 81 | }); 82 | }) 83 | .on('cycle', function (event) { 84 | console.log(event.target.toString()); 85 | }) 86 | .on('complete', function () { 87 | console.log('Fastest is ' + this.filter('fastest').map('name')); 88 | console.log('---\n'); 89 | }) 90 | .run(); 91 | } 92 | 93 | function testV6Conversion() { 94 | const suite = new Benchmark.Suite({ 95 | onError(event) { 96 | console.error(event.target.error); 97 | }, 98 | }); 99 | 100 | const V1_ID = 'f1207660-21d2-11ef-8c4f-419efbd44d48'; 101 | const V6_ID = '1ef21d2f-1207-6660-8c4f-419efbd44d48'; 102 | 103 | suite 104 | .add('uuid.v1ToV6()', function () { 105 | uuid.v1ToV6(V1_ID); 106 | }) 107 | .add('uuid.v6ToV1()', function () { 108 | uuid.v6ToV1(V6_ID); 109 | }) 110 | .on('cycle', function (event) { 111 | console.log(event.target.toString()); 112 | }) 113 | .run(); 114 | } 115 | 116 | testParseAndStringify(); 117 | testGeneration(); 118 | testV6Conversion(); 119 | } 120 | -------------------------------------------------------------------------------- /examples/benchmark/browser.js: -------------------------------------------------------------------------------- 1 | import * as uuid from './node_modules/uuid/dist/esm-browser/index.js'; 2 | import './node_modules/lodash/lodash.js'; 3 | import './node_modules/benchmark/benchmark.js'; 4 | 5 | import benchmark from './benchmark.js'; 6 | 7 | benchmark(uuid, window.Benchmark); 8 | -------------------------------------------------------------------------------- /examples/benchmark/node.js: -------------------------------------------------------------------------------- 1 | import * as uuid from 'uuid'; 2 | import Benchmark from 'benchmark'; 3 | 4 | import benchmark from './benchmark.js'; 5 | 6 | benchmark(uuid, Benchmark); 7 | -------------------------------------------------------------------------------- /examples/benchmark/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uuid-benchmark", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "true", 7 | "start": "npm run build && npx http-server . -o", 8 | "pretest": "rm -fr node_modules && npm install --no-package-lock", 9 | "test": "node node.js" 10 | }, 11 | "dependencies": { 12 | "uuid": "file:../../.build/uuid.tgz" 13 | }, 14 | "devDependencies": { 15 | "benchmark": "^2.1.4" 16 | }, 17 | "type": "module" 18 | } 19 | -------------------------------------------------------------------------------- /examples/browser-esmodules/README.md: -------------------------------------------------------------------------------- 1 | # uuid example Browser with native ECMAScript Modules 2 | 3 | ``` 4 | npm install 5 | npm start 6 | ``` 7 | 8 | Then navigate to `example.html`. 9 | -------------------------------------------------------------------------------- /examples/browser-esmodules/example.html: -------------------------------------------------------------------------------- 1 | 2 | UUID esmodule native example 3 |

Please open the Developer Console to view output

4 | 5 | -------------------------------------------------------------------------------- /examples/browser-esmodules/example.js: -------------------------------------------------------------------------------- 1 | import * as uuid from './node_modules/uuid/dist/esm-browser/index.js'; 2 | import { 3 | MAX as MAX_UUID, 4 | NIL as NIL_UUID, 5 | parse as uuidParse, 6 | stringify as uuidStringify, 7 | validate as uuidValidate, 8 | version as uuidVersion, 9 | v1 as uuidv1, 10 | v1ToV6 as uuidv1ToV6, 11 | v3 as uuidv3, 12 | v4 as uuidv4, 13 | v5 as uuidv5, 14 | v6 as uuidv6, 15 | v6ToV1 as uuidv6ToV1, 16 | v7 as uuidv7, 17 | } from './node_modules/uuid/dist/esm-browser/index.js'; 18 | 19 | // Import attribute syntax is still awaiting finalization. In the meantime we 20 | // use dynamic import to allows to specifyg both "assert" and "with" clauses. 21 | // See https://github.com/tc39/proposal-import-attributes 22 | const pkg = await import('./node_modules/uuid/package.json', { 23 | assert: { type: 'json' }, 24 | with: { type: 'json' }, 25 | }); 26 | 27 | console.log('pkg', pkg); 28 | 29 | console.log('uuidv1()', uuidv1()); 30 | 31 | console.log('uuidv4()', uuidv4()); 32 | 33 | console.log('uuidv7()', uuidv7()); 34 | 35 | // ... using predefined DNS namespace (for domain names) 36 | console.log('uuidv3() DNS', uuidv3('hello.example.com', uuidv3.DNS)); 37 | 38 | // ... using predefined URL namespace (for, well, URLs) 39 | console.log('uuidv3() URL', uuidv3('http://example.com/hello', uuidv3.URL)); 40 | 41 | // ... using a custom namespace 42 | // 43 | // Note: Custom namespaces should be a UUID string specific to your application! 44 | // E.g. the one here was generated using this modules `uuid` CLI. 45 | const MY_NAMESPACE = '55238d15-c926-4598-b49d-cf4e913ba13c'; 46 | console.log('uuidv3() MY_NAMESPACE', uuidv3('Hello, World!', MY_NAMESPACE)); 47 | 48 | // ... using predefined DNS namespace (for domain names) 49 | console.log('uuidv5() DNS', uuidv5('hello.example.com', uuidv5.DNS)); 50 | 51 | // ... using predefined URL namespace (for, well, URLs) 52 | console.log('uuidv5() URL', uuidv5('http://example.com/hello', uuidv5.URL)); 53 | 54 | // ... using a custom namespace 55 | // 56 | // Note: Custom namespaces should be a UUID string specific to your application! 57 | // E.g. the one here was generated using this modules `uuid` CLI. 58 | // const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341'; 59 | console.log('uuidv5() MY_NAMESPACE', uuidv5('Hello, World!', MY_NAMESPACE)); 60 | 61 | console.log('uuidv6()', uuidv6()); 62 | 63 | // v6 <-> v1 conversion 64 | const V1_ID = 'f1207660-21d2-11ef-8c4f-419efbd44d48'; 65 | const V6_ID = '1ef21d2f-1207-6660-8c4f-419efbd44d48'; 66 | console.log('uuidv1ToV6()', uuidv1ToV6(V1_ID)); 67 | console.log('uuidv6ToV1()', uuidv6ToV1(V6_ID)); 68 | 69 | // Utility functions 70 | console.log('NIL_UUID', NIL_UUID); 71 | console.log('MAX_UUID', MAX_UUID); 72 | console.log('uuidParse()', uuidParse(MY_NAMESPACE)); 73 | console.log('uuidStringify()', uuidStringify(uuidParse(MY_NAMESPACE))); 74 | console.log('uuidValidate()', uuidValidate(MY_NAMESPACE)); 75 | console.log('uuidVersion()', uuidVersion(MY_NAMESPACE)); 76 | 77 | console.log('Same with default export'); 78 | 79 | console.log('uuid.v1()', uuid.v1()); 80 | console.log('uuid.v4()', uuid.v4()); 81 | console.log('uuid.v7()', uuid.v7()); 82 | console.log('uuid.v3() DNS', uuid.v3('hello.example.com', uuid.v3.DNS)); 83 | console.log('uuid.v3() URL', uuid.v3('http://example.com/hello', uuid.v3.URL)); 84 | console.log('uuid.v3() MY_NAMESPACE', uuid.v3('Hello, World!', MY_NAMESPACE)); 85 | console.log('uuid.v5() DNS', uuid.v5('hello.example.com', uuid.v5.DNS)); 86 | console.log('uuid.v5() URL', uuid.v5('http://example.com/hello', uuid.v5.URL)); 87 | console.log('uuid.v5() MY_NAMESPACE', uuid.v5('Hello, World!', MY_NAMESPACE)); 88 | console.log('uuid.v6()', uuid.v6()); 89 | 90 | console.log('uuid.v1ToV6()', uuid.v1ToV6(V1_ID)); 91 | console.log('uuid.v6ToV1()', uuid.v6ToV1(V6_ID)); 92 | 93 | console.log('uuid.NIL', uuid.NIL); 94 | console.log('uuid.MAX', uuid.MAX); 95 | console.log('uuid.parse()', uuid.parse(MY_NAMESPACE)); 96 | console.log('uuid.stringify()', uuid.stringify(uuid.parse(MY_NAMESPACE))); 97 | console.log('uuid.validate()', uuid.validate(MY_NAMESPACE)); 98 | console.log('uuid.version()', uuid.version(MY_NAMESPACE)); 99 | 100 | // Some tools like react-native need to introspect the package.json file 101 | console.log('pkg.name', pkg.name); 102 | -------------------------------------------------------------------------------- /examples/browser-esmodules/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uuid-example-browser-esmodules", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "true", 7 | "start": "npm run build && npx http-server . -o" 8 | }, 9 | "dependencies": { 10 | "uuid": "file:../../.build/uuid.tgz" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/browser-rollup/README.md: -------------------------------------------------------------------------------- 1 | # uuid example Browser with rollup.js 2 | 3 | ``` 4 | npm install 5 | npm start 6 | ``` 7 | 8 | Then navigate to `example-*.html`. 9 | 10 | The `example-v{1,4,7}.js` demonstrate that treeshaking works as expected: 11 | 12 | ``` 13 | $ du -sh dist/* 14 | 20K dist/all.js 15 | 8.0K dist/v1.js 16 | 4.0K dist/v4.js 17 | 4.0K dist/v7.js 18 | ``` 19 | -------------------------------------------------------------------------------- /examples/browser-rollup/example-all.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UUID esmodule webpack example 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/browser-rollup/example-all.js: -------------------------------------------------------------------------------- 1 | import * as uuid from 'uuid'; 2 | import { 3 | MAX as MAX_UUID, 4 | NIL as NIL_UUID, 5 | parse as uuidParse, 6 | stringify as uuidStringify, 7 | validate as uuidValidate, 8 | version as uuidVersion, 9 | v1 as uuidv1, 10 | v1ToV6 as uuidv1ToV6, 11 | v3 as uuidv3, 12 | v4 as uuidv4, 13 | v5 as uuidv5, 14 | v6 as uuidv6, 15 | v6ToV1 as uuidv6ToV1, 16 | v7 as uuidv7, 17 | } from 'uuid'; 18 | 19 | import testpage from '../utils/testpage'; 20 | 21 | testpage(function (addTest, done) { 22 | addTest('Named exports'); 23 | 24 | addTest('uuidv1()', uuidv1()); 25 | 26 | addTest('uuidv4()', uuidv4()); 27 | 28 | addTest('uuidv7()', uuidv7()); 29 | 30 | // ... using predefined DNS namespace (for domain names) 31 | addTest('uuidv3() DNS', uuidv3('hello.example.com', uuidv3.DNS)); 32 | 33 | // ... using predefined URL namespace (for, well, URLs) 34 | addTest('uuidv3() URL', uuidv3('http://example.com/hello', uuidv3.URL)); 35 | 36 | // ... using a custom namespace 37 | // 38 | // Note: Custom namespaces should be a UUID string specific to your application! 39 | // E.g. the one here was generated using this modules `uuid` CLI. 40 | const MY_NAMESPACE = '55238d15-c926-4598-b49d-cf4e913ba13c'; 41 | addTest('uuidv3() MY_NAMESPACE', uuidv3('Hello, World!', MY_NAMESPACE)); 42 | 43 | // ... using predefined DNS namespace (for domain names) 44 | addTest('uuidv5() DNS', uuidv5('hello.example.com', uuidv5.DNS)); 45 | 46 | // ... using predefined URL namespace (for, well, URLs) 47 | addTest('uuidv5() URL', uuidv5('http://example.com/hello', uuidv5.URL)); 48 | 49 | // ... using a custom namespace 50 | // 51 | // Note: Custom namespaces should be a UUID string specific to your application! 52 | // E.g. the one here was generated using this modules `uuid` CLI. 53 | // const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341'; 54 | addTest('uuidv5() MY_NAMESPACE', uuidv5('Hello, World!', MY_NAMESPACE)); 55 | 56 | addTest('uuidv6()', uuidv6()); 57 | 58 | // v6 <-> v1 conversion 59 | const V1_ID = 'f1207660-21d2-11ef-8c4f-419efbd44d48'; 60 | const V6_ID = '1ef21d2f-1207-6660-8c4f-419efbd44d48'; 61 | addTest('uuidv1ToV6()', uuidv1ToV6(V1_ID)); 62 | addTest('uuidv6ToV1()', uuidv6ToV1(V6_ID)); 63 | 64 | // Utility functions 65 | addTest('NIL_UUID', NIL_UUID); 66 | addTest('MAX_UUID', MAX_UUID); 67 | addTest('uuidParse()', uuidParse(MY_NAMESPACE)); 68 | addTest('uuidStringify()', uuidStringify(uuidParse(MY_NAMESPACE))); 69 | addTest('uuidValidate()', uuidValidate(MY_NAMESPACE)); 70 | addTest('uuidVersion()', uuidVersion(MY_NAMESPACE)); 71 | 72 | addTest('Default export'); 73 | 74 | addTest('uuid.v1()', uuid.v1()); 75 | addTest('uuid.v4()', uuid.v4()); 76 | addTest('uuid.v7()', uuid.v7()); 77 | addTest('uuid.v3() DNS', uuid.v3('hello.example.com', uuid.v3.DNS)); 78 | addTest('uuid.v3() URL', uuid.v3('http://example.com/hello', uuid.v3.URL)); 79 | addTest('uuid.v3() MY_NAMESPACE', uuid.v3('Hello, World!', MY_NAMESPACE)); 80 | addTest('uuid.v5() DNS', uuid.v5('hello.example.com', uuid.v5.DNS)); 81 | addTest('uuid.v5() URL', uuid.v5('http://example.com/hello', uuid.v5.URL)); 82 | addTest('uuid.v5() MY_NAMESPACE', uuid.v5('Hello, World!', MY_NAMESPACE)); 83 | addTest('uuid.v6()', uuid.v6()); 84 | 85 | addTest('uuid.v1ToV6()', uuid.v1ToV6(V1_ID)); 86 | addTest('uuid.v6ToV1()', uuid.v6ToV1(V6_ID)); 87 | 88 | addTest('uuid.NIL', uuid.NIL); 89 | addTest('uuid.MAX', uuid.MAX); 90 | addTest('uuid.parse()', uuid.parse(MY_NAMESPACE)); 91 | addTest('uuid.stringify()', uuid.stringify(uuid.parse(MY_NAMESPACE))); 92 | addTest('uuid.validate()', uuid.validate(MY_NAMESPACE)); 93 | addTest('uuid.version()', uuid.version(MY_NAMESPACE)); 94 | 95 | done(); 96 | }); 97 | -------------------------------------------------------------------------------- /examples/browser-rollup/example-v1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UUID esmodule webpack example 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/browser-rollup/example-v1.js: -------------------------------------------------------------------------------- 1 | import { v1 as uuidv1 } from 'uuid'; 2 | 3 | import testpage from '../utils/testpage'; 4 | 5 | testpage(function (addTest, done) { 6 | addTest('uuidv1()', uuidv1()); 7 | done(); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/browser-rollup/example-v4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UUID esmodule webpack example 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/browser-rollup/example-v4.js: -------------------------------------------------------------------------------- 1 | import { v4 as uuidv4 } from 'uuid'; 2 | 3 | import testpage from '../utils/testpage'; 4 | 5 | testpage(function (addTest, done) { 6 | addTest('uuidv4()', uuidv4()); 7 | done(); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/browser-rollup/example-v7.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UUID esmodule webpack example 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/browser-rollup/example-v7.js: -------------------------------------------------------------------------------- 1 | import { v7 as uuidv7 } from 'uuid'; 2 | 3 | import testpage from '../utils/testpage'; 4 | 5 | testpage(function (addTest, done) { 6 | addTest('uuidv7()', uuidv7()); 7 | done(); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/browser-rollup/example.html: -------------------------------------------------------------------------------- 1 | 2 | UUID esmodule webpack example 3 |

Please open the Developer Console to view output

4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/browser-rollup/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uuid-example-browser-rollup", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "rm -fr node_modules && npm install --no-package-lock && rm -rf dist && rollup -c", 7 | "start": "npm run build && npx http-server . -o" 8 | }, 9 | "dependencies": { 10 | "uuid": "file:../../.build/uuid.tgz" 11 | }, 12 | "devDependencies": { 13 | "@rollup/plugin-node-resolve": "^15.2.3", 14 | "@rollup/plugin-terser": "0.4.4", 15 | "rollup": "^4.19.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/browser-rollup/rollup.config.js: -------------------------------------------------------------------------------- 1 | const { nodeResolve } = require('@rollup/plugin-node-resolve'); 2 | const terser = require('@rollup/plugin-terser'); 3 | 4 | const plugins = [nodeResolve({ browser: true }), terser()]; 5 | module.exports = [ 6 | { 7 | input: './example-all.js', 8 | output: { 9 | file: 'dist/all.js', 10 | format: 'iife', 11 | globals: { crypto: 'crypto' }, 12 | }, 13 | plugins, 14 | }, 15 | { 16 | input: './example-v1.js', 17 | output: { 18 | file: 'dist/v1.js', 19 | format: 'iife', 20 | globals: { crypto: 'crypto' }, 21 | }, 22 | plugins, 23 | }, 24 | { 25 | input: './example-v4.js', 26 | output: { 27 | file: 'dist/v4.js', 28 | format: 'iife', 29 | globals: { crypto: 'crypto' }, 30 | }, 31 | plugins, 32 | }, 33 | { 34 | input: './example-v7.js', 35 | output: { 36 | file: 'dist/v7.js', 37 | format: 'iife', 38 | globals: { crypto: 'crypto' }, 39 | }, 40 | plugins, 41 | }, 42 | 43 | { 44 | input: './size-v1.js', 45 | output: { 46 | file: 'dist/v1-size.js', 47 | format: 'cjs', 48 | }, 49 | plugins, 50 | }, 51 | { 52 | input: './size-v3.js', 53 | output: { 54 | file: 'dist/v3-size.js', 55 | format: 'cjs', 56 | }, 57 | plugins, 58 | }, 59 | { 60 | input: './size-v4.js', 61 | output: { 62 | file: 'dist/v4-size.js', 63 | format: 'cjs', 64 | }, 65 | plugins, 66 | }, 67 | { 68 | input: './size-v5.js', 69 | output: { 70 | file: 'dist/v5-size.js', 71 | format: 'cjs', 72 | }, 73 | plugins, 74 | }, 75 | { 76 | input: './size-v6.js', 77 | output: { 78 | file: 'dist/v6-size.js', 79 | format: 'cjs', 80 | }, 81 | plugins, 82 | }, 83 | { 84 | input: './size-v7.js', 85 | output: { 86 | file: 'dist/v7-size.js', 87 | format: 'cjs', 88 | }, 89 | plugins, 90 | }, 91 | ]; 92 | -------------------------------------------------------------------------------- /examples/browser-rollup/size-v1.js: -------------------------------------------------------------------------------- 1 | import { v1 as uuidv1 } from 'uuid'; 2 | 3 | uuidv1(); 4 | -------------------------------------------------------------------------------- /examples/browser-rollup/size-v3.js: -------------------------------------------------------------------------------- 1 | import { v3 as uuidv3 } from 'uuid'; 2 | 3 | uuidv3(); 4 | -------------------------------------------------------------------------------- /examples/browser-rollup/size-v4.js: -------------------------------------------------------------------------------- 1 | import { v4 as uuidv4 } from 'uuid'; 2 | 3 | uuidv4(); 4 | -------------------------------------------------------------------------------- /examples/browser-rollup/size-v5.js: -------------------------------------------------------------------------------- 1 | import { v5 as uuidv5 } from 'uuid'; 2 | 3 | uuidv5(); 4 | -------------------------------------------------------------------------------- /examples/browser-rollup/size-v6.js: -------------------------------------------------------------------------------- 1 | import { v6 as uuidv6 } from 'uuid'; 2 | 3 | uuidv6(); 4 | -------------------------------------------------------------------------------- /examples/browser-rollup/size-v7.js: -------------------------------------------------------------------------------- 1 | import { v7 as uuidv7 } from 'uuid'; 2 | 3 | uuidv7(); 4 | -------------------------------------------------------------------------------- /examples/browser-webpack/README.md: -------------------------------------------------------------------------------- 1 | # uuid example Browser with Webpack 2 | 3 | ``` 4 | npm install 5 | npm start 6 | ``` 7 | 8 | Then navigate to `example-*.html`. 9 | 10 | The `example-v{1,4}.js` demonstrate that treeshaking works as expected (webpack output below): 11 | 12 | ``` 13 | Asset Size Chunks Chunk Names 14 | all.js 8.54 KiB 0 [emitted] all 15 | v1.js 2.6 KiB 1 [emitted] v1 16 | v4.js 2 KiB 2 [emitted] v4 17 | ``` 18 | -------------------------------------------------------------------------------- /examples/browser-webpack/example-all-require.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UUID esmodule webpack example 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/browser-webpack/example-all-require.js: -------------------------------------------------------------------------------- 1 | const uuid = require('uuid'); 2 | const { 3 | NIL: NIL_UUID, 4 | MAX: MAX_UUID, 5 | parse: uuidParse, 6 | stringify: uuidStringify, 7 | v1: uuidv1, 8 | v1ToV6: uuidv1ToV6, 9 | v3: uuidv3, 10 | v4: uuidv4, 11 | v5: uuidv5, 12 | v6: uuidv6, 13 | v6ToV1: uuidv6ToV1, 14 | v7: uuidv7, 15 | validate: uuidValidate, 16 | version: uuidVersion, 17 | } = uuid; 18 | 19 | const { default: testpage } = require('../utils/testpage'); 20 | 21 | testpage(function (addTest, done) { 22 | addTest('Named exports'); 23 | 24 | addTest('uuidv1()', uuidv1()); 25 | 26 | addTest('uuidv4()', uuidv4()); 27 | 28 | addTest('uuidv7()', uuidv7()); 29 | 30 | // ... using predefined DNS namespace (for domain names) 31 | addTest('uuidv3() DNS', uuidv3('hello.example.com', uuidv3.DNS)); 32 | 33 | // ... using predefined URL namespace (for, well, URLs) 34 | addTest('uuidv3() URL', uuidv3('http://example.com/hello', uuidv3.URL)); 35 | 36 | // ... using a custom namespace 37 | // 38 | // Note: Custom namespaces should be a UUID string specific to your application! 39 | // E.g. the one here was generated using this modules `uuid` CLI. 40 | const MY_NAMESPACE = '55238d15-c926-4598-b49d-cf4e913ba13c'; 41 | addTest('uuidv3() MY_NAMESPACE', uuidv3('Hello, World!', MY_NAMESPACE)); 42 | 43 | // ... using predefined DNS namespace (for domain names) 44 | addTest('uuidv5() DNS', uuidv5('hello.example.com', uuidv5.DNS)); 45 | 46 | // ... using predefined URL namespace (for, well, URLs) 47 | addTest('uuidv5() URL', uuidv5('http://example.com/hello', uuidv5.URL)); 48 | 49 | // v6 <-> v1 conversion 50 | const V1_ID = 'f1207660-21d2-11ef-8c4f-419efbd44d48'; 51 | const V6_ID = '1ef21d2f-1207-6660-8c4f-419efbd44d48'; 52 | addTest('uuidv1ToV6()', uuidv1ToV6(V1_ID)); 53 | addTest('uuidv6ToV1()', uuidv6ToV1(V6_ID)); 54 | 55 | // ... using a custom namespace 56 | // 57 | // Note: Custom namespaces should be a UUID string specific to your application! 58 | // E.g. the one here was generated using this modules `uuid` CLI. 59 | // const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341'; 60 | addTest('uuidv5() MY_NAMESPACE', uuidv5('Hello, World!', MY_NAMESPACE)); 61 | 62 | addTest('uuidv6()', uuidv6()); 63 | 64 | // Utility functions 65 | addTest('NIL_UUID', NIL_UUID); 66 | addTest('MAX_UUID', MAX_UUID); 67 | addTest('uuidParse()', uuidParse(MY_NAMESPACE)); 68 | addTest('uuidStringify()', uuidStringify(uuidParse(MY_NAMESPACE))); 69 | addTest('uuidValidate()', uuidValidate(MY_NAMESPACE)); 70 | addTest('uuidVersion()', uuidVersion(MY_NAMESPACE)); 71 | 72 | addTest('Default export'); 73 | 74 | addTest('uuid.v1()', uuid.v1()); 75 | addTest('uuid.v4()', uuid.v4()); 76 | addTest('uuid.v7()', uuid.v7()); 77 | addTest('uuid.v3() DNS', uuid.v3('hello.example.com', uuid.v3.DNS)); 78 | addTest('uuid.v3() URL', uuid.v3('http://example.com/hello', uuid.v3.URL)); 79 | addTest('uuid.v3() MY_NAMESPACE', uuid.v3('Hello, World!', MY_NAMESPACE)); 80 | addTest('uuid.v5() DNS', uuid.v5('hello.example.com', uuid.v5.DNS)); 81 | addTest('uuid.v5() URL', uuid.v5('http://example.com/hello', uuid.v5.URL)); 82 | addTest('uuid.v5() MY_NAMESPACE', uuid.v5('Hello, World!', MY_NAMESPACE)); 83 | addTest('uuid.v6()', uuid.v6()); 84 | 85 | addTest('uuid.v1ToV6()', uuid.v1ToV6(V1_ID)); 86 | addTest('uuid.v6ToV1()', uuid.v6ToV1(V6_ID)); 87 | 88 | addTest('uuid.NIL', uuid.NIL); 89 | addTest('uuid.MAX', uuid.MAX); 90 | addTest('uuid.parse()', uuid.parse(MY_NAMESPACE)); 91 | addTest('uuid.stringify()', uuid.stringify(uuid.parse(MY_NAMESPACE))); 92 | addTest('uuid.validate()', uuid.validate(MY_NAMESPACE)); 93 | addTest('uuid.version()', uuid.version(MY_NAMESPACE)); 94 | 95 | done(); 96 | }); 97 | -------------------------------------------------------------------------------- /examples/browser-webpack/example-all.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UUID esmodule webpack example 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/browser-webpack/example-all.js: -------------------------------------------------------------------------------- 1 | import * as uuid from 'uuid'; 2 | import { 3 | MAX as MAX_UUID, 4 | NIL as NIL_UUID, 5 | parse as uuidParse, 6 | stringify as uuidStringify, 7 | validate as uuidValidate, 8 | version as uuidVersion, 9 | v1 as uuidv1, 10 | v1ToV6 as uuidv1ToV6, 11 | v3 as uuidv3, 12 | v4 as uuidv4, 13 | v5 as uuidv5, 14 | v6 as uuidv6, 15 | v6ToV1 as uuidv6ToV1, 16 | v7 as uuidv7, 17 | } from 'uuid'; 18 | 19 | import testpage from '../utils/testpage'; 20 | 21 | testpage(function (addTest, done) { 22 | addTest('Named exports'); 23 | 24 | addTest('uuidv1()', uuidv1()); 25 | 26 | addTest('uuidv4()', uuidv4()); 27 | 28 | addTest('uuidv7()', uuidv7()); 29 | 30 | // ... using predefined DNS namespace (for domain names) 31 | addTest('uuidv3() DNS', uuidv3('hello.example.com', uuidv3.DNS)); 32 | 33 | // ... using predefined URL namespace (for, well, URLs) 34 | addTest('uuidv3() URL', uuidv3('http://example.com/hello', uuidv3.URL)); 35 | 36 | // ... using a custom namespace 37 | // 38 | // Note: Custom namespaces should be a UUID string specific to your application! 39 | // E.g. the one here was generated using this modules `uuid` CLI. 40 | const MY_NAMESPACE = '55238d15-c926-4598-b49d-cf4e913ba13c'; 41 | addTest('uuidv3() MY_NAMESPACE', uuidv3('Hello, World!', MY_NAMESPACE)); 42 | 43 | // ... using predefined DNS namespace (for domain names) 44 | addTest('uuidv5() DNS', uuidv5('hello.example.com', uuidv5.DNS)); 45 | 46 | // ... using predefined URL namespace (for, well, URLs) 47 | addTest('uuidv5() URL', uuidv5('http://example.com/hello', uuidv5.URL)); 48 | 49 | // ... using a custom namespace 50 | // 51 | // Note: Custom namespaces should be a UUID string specific to your application! 52 | // E.g. the one here was generated using this modules `uuid` CLI. 53 | // const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341'; 54 | addTest('uuidv5() MY_NAMESPACE', uuidv5('Hello, World!', MY_NAMESPACE)); 55 | 56 | addTest('uuidv6()', uuidv6()); 57 | 58 | // v6 <-> v1 conversion 59 | const V1_ID = 'f1207660-21d2-11ef-8c4f-419efbd44d48'; 60 | const V6_ID = '1ef21d2f-1207-6660-8c4f-419efbd44d48'; 61 | addTest('uuidv1ToV6()', uuidv1ToV6(V1_ID)); 62 | addTest('uuidv6ToV1()', uuidv6ToV1(V6_ID)); 63 | 64 | // Utility functions 65 | addTest('NIL_UUID', NIL_UUID); 66 | addTest('MAX_UUID', MAX_UUID); 67 | addTest('uuidParse()', uuidParse(MY_NAMESPACE)); 68 | addTest('uuidStringify()', uuidStringify(uuidParse(MY_NAMESPACE))); 69 | addTest('uuidValidate()', uuidValidate(MY_NAMESPACE)); 70 | addTest('uuidVersion()', uuidVersion(MY_NAMESPACE)); 71 | 72 | addTest('Default export'); 73 | 74 | addTest('uuid.v1()', uuid.v1()); 75 | addTest('uuid.v4()', uuid.v4()); 76 | addTest('uuid.v7()', uuid.v7()); 77 | addTest('uuid.v3() DNS', uuid.v3('hello.example.com', uuid.v3.DNS)); 78 | addTest('uuid.v3() URL', uuid.v3('http://example.com/hello', uuid.v3.URL)); 79 | addTest('uuid.v3() MY_NAMESPACE', uuid.v3('Hello, World!', MY_NAMESPACE)); 80 | addTest('uuid.v5() DNS', uuid.v5('hello.example.com', uuid.v5.DNS)); 81 | addTest('uuid.v5() URL', uuid.v5('http://example.com/hello', uuid.v5.URL)); 82 | addTest('uuid.v5() MY_NAMESPACE', uuid.v5('Hello, World!', MY_NAMESPACE)); 83 | addTest('uuid.v6()', uuid.v6()); 84 | 85 | addTest('uuid.v1ToV6()', uuid.v1ToV6(V1_ID)); 86 | addTest('uuid.v6ToV1()', uuid.v6ToV1(V6_ID)); 87 | 88 | addTest('uuid.NIL', uuid.NIL); 89 | addTest('uuid.MAX', uuid.MAX); 90 | addTest('uuid.parse()', uuid.parse(MY_NAMESPACE)); 91 | addTest('uuid.stringify()', uuid.stringify(uuid.parse(MY_NAMESPACE))); 92 | addTest('uuid.validate()', uuid.validate(MY_NAMESPACE)); 93 | addTest('uuid.version()', uuid.version(MY_NAMESPACE)); 94 | 95 | done(); 96 | }); 97 | -------------------------------------------------------------------------------- /examples/browser-webpack/example-v1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UUID esmodule webpack example 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/browser-webpack/example-v1.js: -------------------------------------------------------------------------------- 1 | import { v1 as uuidv1 } from 'uuid'; 2 | 3 | import testpage from '../utils/testpage'; 4 | 5 | testpage(function (addTest, done) { 6 | addTest('uuidv1()', uuidv1()); 7 | 8 | done(); 9 | }); 10 | -------------------------------------------------------------------------------- /examples/browser-webpack/example-v4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UUID esmodule webpack example 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/browser-webpack/example-v4.js: -------------------------------------------------------------------------------- 1 | import { v4 as uuidv4 } from 'uuid'; 2 | 3 | import testpage from '../utils/testpage'; 4 | 5 | testpage(function (addTest, done) { 6 | addTest('uuidv4()', uuidv4()); 7 | 8 | done(); 9 | }); 10 | -------------------------------------------------------------------------------- /examples/browser-webpack/example-v7.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UUID esmodule webpack example 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/browser-webpack/example-v7.js: -------------------------------------------------------------------------------- 1 | import { v7 as uuidv7 } from 'uuid'; 2 | 3 | import testpage from '../utils/testpage'; 4 | 5 | testpage(function (addTest, done) { 6 | addTest('uuidv7()', uuidv7()); 7 | 8 | done(); 9 | }); 10 | -------------------------------------------------------------------------------- /examples/browser-webpack/example.html: -------------------------------------------------------------------------------- 1 | 2 | UUID esmodule webpack example 3 |

Please open the Developer Console to view output

4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/browser-webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uuid-example-browser-webpack", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "rm -fr node_modules && npm install --no-package-lock && rm -rf dist && webpack", 7 | "start": "npm run build && npx http-server . -o" 8 | }, 9 | "dependencies": { 10 | "uuid": "file:../../.build/uuid.tgz" 11 | }, 12 | "devDependencies": { 13 | "webpack": "5.93.0", 14 | "webpack-cli": "5.1.4" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/browser-webpack/size-v1.js: -------------------------------------------------------------------------------- 1 | import { v1 as uuidv1 } from 'uuid'; 2 | 3 | uuidv1(); 4 | -------------------------------------------------------------------------------- /examples/browser-webpack/size-v3.js: -------------------------------------------------------------------------------- 1 | import { v3 as uuidv3 } from 'uuid'; 2 | 3 | uuidv3(); 4 | -------------------------------------------------------------------------------- /examples/browser-webpack/size-v4.js: -------------------------------------------------------------------------------- 1 | import { v4 as uuidv4 } from 'uuid'; 2 | 3 | uuidv4(); 4 | -------------------------------------------------------------------------------- /examples/browser-webpack/size-v5.js: -------------------------------------------------------------------------------- 1 | import { v5 as uuidv5 } from 'uuid'; 2 | 3 | uuidv5(); 4 | -------------------------------------------------------------------------------- /examples/browser-webpack/size-v6.js: -------------------------------------------------------------------------------- 1 | import { v6 as uuidv6 } from 'uuid'; 2 | 3 | uuidv6(); 4 | -------------------------------------------------------------------------------- /examples/browser-webpack/size-v7.js: -------------------------------------------------------------------------------- 1 | import { v7 as uuidv7 } from 'uuid'; 2 | 3 | uuidv7(); 4 | -------------------------------------------------------------------------------- /examples/browser-webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | resolve: { 3 | extensions: ['*', '.js'], 4 | fallback: { crypto: false }, 5 | }, 6 | entry: { 7 | all: './example-all.js', 8 | allRequire: './example-all-require.js', 9 | v1: './example-v1.js', 10 | v4: './example-v4.js', 11 | v7: './example-v7.js', 12 | 13 | 'v1-size': './size-v1.js', 14 | 'v3-size': './size-v3.js', 15 | 'v4-size': './size-v4.js', 16 | 'v5-size': './size-v5.js', 17 | 'v6-size': './size-v6.js', 18 | 'v7-size': './size-v7.js', 19 | }, 20 | // Webpack now produces builds that are incompatible with IE11: 21 | // https://webpack.js.org/migrate/5/#turn-off-es2015-syntax-in-runtime-code-if-necessary 22 | target: ['web', 'es5'], 23 | output: { 24 | filename: '[name].js', 25 | }, 26 | mode: 'production', 27 | }; 28 | -------------------------------------------------------------------------------- /examples/node-commonjs/README.md: -------------------------------------------------------------------------------- 1 | # uuid example Node.js CommonJS 2 | 3 | ``` 4 | npm install 5 | npm test 6 | ``` 7 | -------------------------------------------------------------------------------- /examples/node-commonjs/example.js: -------------------------------------------------------------------------------- 1 | const { 2 | NIL: NIL_UUID, 3 | MAX: MAX_UUID, 4 | parse: uuidParse, 5 | stringify: uuidStringify, 6 | v1: uuidv1, 7 | v1ToV6: uuidv1ToV6, 8 | v3: uuidv3, 9 | v4: uuidv4, 10 | v5: uuidv5, 11 | v6: uuidv6, 12 | v6ToV1: uuidv6ToV1, 13 | v7: uuidv7, 14 | validate: uuidValidate, 15 | version: uuidVersion, 16 | } = require('uuid'); 17 | const uuid = require('uuid'); 18 | const pkg = require('uuid/package.json'); 19 | 20 | console.log('uuidv1()', uuidv1()); 21 | 22 | console.log('uuidv4()', uuidv4()); 23 | 24 | console.log('uuidv7()', uuidv7()); 25 | 26 | // ... using predefined DNS namespace (for domain names) 27 | console.log('uuidv3() DNS', uuidv3('hello.example.com', uuidv3.DNS)); 28 | 29 | // ... using predefined URL namespace (for, well, URLs) 30 | console.log('uuidv3() URL', uuidv3('http://example.com/hello', uuidv3.URL)); 31 | 32 | // ... using a custom namespace 33 | // 34 | // Note: Custom namespaces should be a UUID string specific to your application! 35 | // E.g. the one here was generated using this modules `uuid` CLI. 36 | const MY_NAMESPACE = '55238d15-c926-4598-b49d-cf4e913ba13c'; 37 | console.log('uuidv3() MY_NAMESPACE', uuidv3('Hello, World!', MY_NAMESPACE)); 38 | 39 | // ... using predefined DNS namespace (for domain names) 40 | console.log('uuidv5() DNS', uuidv5('hello.example.com', uuidv5.DNS)); 41 | 42 | // ... using predefined URL namespace (for, well, URLs) 43 | console.log('uuidv5() URL', uuidv5('http://example.com/hello', uuidv5.URL)); 44 | 45 | // ... using a custom namespace 46 | // 47 | // Note: Custom namespaces should be a UUID string specific to your application! 48 | // E.g. the one here was generated using this modules `uuid` CLI. 49 | // const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341'; 50 | console.log('uuidv5() MY_NAMESPACE', uuidv5('Hello, World!', MY_NAMESPACE)); 51 | 52 | console.log('uuidv6()', uuidv6()); 53 | 54 | // v6 <-> v1 conversion 55 | const V1_ID = 'f1207660-21d2-11ef-8c4f-419efbd44d48'; 56 | const V6_ID = '1ef21d2f-1207-6660-8c4f-419efbd44d48'; 57 | console.log('uuidv1ToV6()', uuidv1ToV6(V1_ID)); 58 | console.log('uuidv6ToV1()', uuidv6ToV1(V6_ID)); 59 | 60 | // Utility functions 61 | console.log('NIL_UUID', NIL_UUID); 62 | console.log('MAX_UUID', MAX_UUID); 63 | console.log('uuidParse()', uuidParse(MY_NAMESPACE)); 64 | console.log('uuidStringify()', uuidStringify(uuidParse(MY_NAMESPACE))); 65 | console.log('uuidValidate()', uuidValidate(MY_NAMESPACE)); 66 | console.log('uuidVersion()', uuidVersion(MY_NAMESPACE)); 67 | 68 | console.log('Same with default export'); 69 | 70 | console.log('uuid.v1()', uuid.v1()); 71 | console.log('uuid.v4()', uuid.v4()); 72 | console.log('uuid.v7()', uuid.v7()); 73 | console.log('uuid.v3() DNS', uuid.v3('hello.example.com', uuid.v3.DNS)); 74 | console.log('uuid.v3() URL', uuid.v3('http://example.com/hello', uuid.v3.URL)); 75 | console.log('uuid.v3() MY_NAMESPACE', uuid.v3('Hello, World!', MY_NAMESPACE)); 76 | console.log('uuid.v5() DNS', uuid.v5('hello.example.com', uuid.v5.DNS)); 77 | console.log('uuid.v5() URL', uuid.v5('http://example.com/hello', uuid.v5.URL)); 78 | console.log('uuid.v5() MY_NAMESPACE', uuid.v5('Hello, World!', MY_NAMESPACE)); 79 | console.log('uuid.v6()', uuid.v6()); 80 | 81 | console.log('uuid.v1ToV6()', uuid.v1ToV6(V1_ID)); 82 | console.log('uuid.v6ToV1()', uuid.v6ToV1(V6_ID)); 83 | 84 | console.log('uuid.NIL', uuid.NIL); 85 | console.log('uuid.MAX', uuid.MAX); 86 | console.log('uuid.parse()', uuid.parse(MY_NAMESPACE)); 87 | console.log('uuid.stringify()', uuid.stringify(uuid.parse(MY_NAMESPACE))); 88 | console.log('uuid.validate()', uuid.validate(MY_NAMESPACE)); 89 | console.log('uuid.version()', uuid.version(MY_NAMESPACE)); 90 | 91 | // Some tools like react-native need to introspect the package.json file 92 | console.log('pkg.name', pkg.name); 93 | -------------------------------------------------------------------------------- /examples/node-commonjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uuid-example-node-commonjs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "pretest": "rm -fr node_modules && npm install --no-package-lock", 7 | "test": "node example.js" 8 | }, 9 | "dependencies": { 10 | "uuid": "file:../../.build/uuid.tgz" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/node-esmodules/README.md: -------------------------------------------------------------------------------- 1 | # uuid example Node.js ESModules 2 | 3 | ``` 4 | npm install 5 | npm test 6 | ``` 7 | -------------------------------------------------------------------------------- /examples/node-esmodules/example.mjs: -------------------------------------------------------------------------------- 1 | import * as uuid from 'uuid'; 2 | import { 3 | MAX as MAX_UUID, 4 | NIL as NIL_UUID, 5 | parse as uuidParse, 6 | stringify as uuidStringify, 7 | validate as uuidValidate, 8 | version as uuidVersion, 9 | v1 as uuidv1, 10 | v1ToV6 as uuidv1ToV6, 11 | v3 as uuidv3, 12 | v4 as uuidv4, 13 | v5 as uuidv5, 14 | v6 as uuidv6, 15 | v6ToV1 as uuidv6ToV1, 16 | v7 as uuidv7, 17 | } from 'uuid'; 18 | 19 | // Import attribute syntax is still awaiting finalization. In the meantime we 20 | // use dynamic import to allows to specifyg both "assert" and "with" clauses. 21 | // See https://github.com/tc39/proposal-import-attributes 22 | const pkg = await import('uuid/package.json', { assert: { type: 'json' }, with: { type: 'json' } }); 23 | 24 | console.log('uuidv1()', uuidv1()); 25 | 26 | console.log('uuidv4()', uuidv4()); 27 | 28 | console.log('uuidv7()', uuidv7()); 29 | 30 | // ... using predefined DNS namespace (for domain names) 31 | console.log('uuidv3() DNS', uuidv3('hello.example.com', uuidv3.DNS)); 32 | 33 | // ... using predefined URL namespace (for, well, URLs) 34 | console.log('uuidv3() URL', uuidv3('http://example.com/hello', uuidv3.URL)); 35 | 36 | // ... using a custom namespace 37 | // 38 | // Note: Custom namespaces should be a UUID string specific to your application! 39 | // E.g. the one here was generated using this modules `uuid` CLI. 40 | const MY_NAMESPACE = '55238d15-c926-4598-b49d-cf4e913ba13c'; 41 | console.log('uuidv3() MY_NAMESPACE', uuidv3('Hello, World!', MY_NAMESPACE)); 42 | 43 | // ... using predefined DNS namespace (for domain names) 44 | console.log('uuidv5() DNS', uuidv5('hello.example.com', uuidv5.DNS)); 45 | 46 | // ... using predefined URL namespace (for, well, URLs) 47 | console.log('uuidv5() URL', uuidv5('http://example.com/hello', uuidv5.URL)); 48 | 49 | // ... using a custom namespace 50 | // 51 | // Note: Custom namespaces should be a UUID string specific to your application! 52 | // E.g. the one here was generated using this modules `uuid` CLI. 53 | // const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341'; 54 | console.log('uuidv5() MY_NAMESPACE', uuidv5('Hello, World!', MY_NAMESPACE)); 55 | 56 | console.log('uuidv6()', uuidv6()); 57 | 58 | // v6 <-> v1 conversion 59 | const V1_ID = 'f1207660-21d2-11ef-8c4f-419efbd44d48'; 60 | const V6_ID = '1ef21d2f-1207-6660-8c4f-419efbd44d48'; 61 | console.log('uuidv1ToV6()', uuidv1ToV6(V1_ID)); 62 | console.log('uuidv6ToV1()', uuidv6ToV1(V6_ID)); 63 | 64 | // Utility functions 65 | console.log('NIL_UUID', NIL_UUID); 66 | console.log('MAX_UUID', MAX_UUID); 67 | console.log('uuidParse()', uuidParse(MY_NAMESPACE)); 68 | console.log('uuidStringify()', uuidStringify(uuidParse(MY_NAMESPACE))); 69 | console.log('uuidValidate()', uuidValidate(MY_NAMESPACE)); 70 | console.log('uuidVersion()', uuidVersion(MY_NAMESPACE)); 71 | 72 | console.log('Same with default export'); 73 | 74 | console.log('uuid.v1()', uuid.v1()); 75 | console.log('uuid.v4()', uuid.v4()); 76 | console.log('uuid.v7()', uuid.v7()); 77 | console.log('uuid.v3() DNS', uuid.v3('hello.example.com', uuid.v3.DNS)); 78 | console.log('uuid.v3() URL', uuid.v3('http://example.com/hello', uuid.v3.URL)); 79 | console.log('uuid.v3() MY_NAMESPACE', uuid.v3('Hello, World!', MY_NAMESPACE)); 80 | console.log('uuid.v5() DNS', uuid.v5('hello.example.com', uuid.v5.DNS)); 81 | console.log('uuid.v5() URL', uuid.v5('http://example.com/hello', uuid.v5.URL)); 82 | console.log('uuid.v5() MY_NAMESPACE', uuid.v5('Hello, World!', MY_NAMESPACE)); 83 | console.log('uuid.v6()', uuid.v6()); 84 | 85 | console.log('uuid.v1ToV6()', uuid.v1ToV6(V1_ID)); 86 | console.log('uuid.v6ToV1()', uuid.v6ToV1(V6_ID)); 87 | 88 | console.log('uuid.NIL', uuid.NIL); 89 | console.log('uuid.MAX', uuid.MAX); 90 | console.log('uuid.parse()', uuid.parse(MY_NAMESPACE)); 91 | console.log('uuid.stringify()', uuid.stringify(uuid.parse(MY_NAMESPACE))); 92 | console.log('uuid.validate()', uuid.validate(MY_NAMESPACE)); 93 | console.log('uuid.version()', uuid.version(MY_NAMESPACE)); 94 | 95 | // Some tools like react-native need to introspect the package.json file 96 | console.log('pkg.name', pkg.default.name); 97 | -------------------------------------------------------------------------------- /examples/node-esmodules/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uuid-example-node-esmodules", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "test:package": "( node --version | grep -vq 'v16' ) || ( node --experimental-json-modules package.mjs )", 7 | "test:example": "node example.mjs", 8 | "pretest": "rm -fr node_modules && npm install --no-package-lock", 9 | "test": "npm-run-all test:*" 10 | }, 11 | "dependencies": { 12 | "uuid": "file:../../.build/uuid.tgz" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/node-esmodules/package.mjs: -------------------------------------------------------------------------------- 1 | import pkg from 'uuid/package.json' assert { type: 'json' }; 2 | 3 | // Some tools like react-native need to introspect the package.json file 4 | console.log('pkg.name', pkg.name); 5 | -------------------------------------------------------------------------------- /examples/node-jest/README.md: -------------------------------------------------------------------------------- 1 | # uuid example Node.js Jest 2 | 3 | ``` 4 | npm install 5 | npm test 6 | ``` 7 | -------------------------------------------------------------------------------- /examples/node-jest/jsdom.test.js: -------------------------------------------------------------------------------- 1 | /** @jest-environment jsdom */ 2 | 3 | const uuid = require('uuid'); 4 | 5 | test('uuidv4()', () => { 6 | const val = uuid.v4(); 7 | expect(uuid.version(val)).toBe(4); 8 | }); 9 | 10 | test('uuidv7()', () => { 11 | const val = uuid.v7(); 12 | expect(uuid.version(val)).toBe(7); 13 | }); 14 | -------------------------------------------------------------------------------- /examples/node-jest/node.test.js: -------------------------------------------------------------------------------- 1 | const uuid = require('uuid'); 2 | 3 | test('uuidv4()', () => { 4 | const val = uuid.v4(); 5 | expect(uuid.version(val)).toBe(4); 6 | }); 7 | 8 | test('uuidv7()', () => { 9 | const val = uuid.v7(); 10 | expect(uuid.version(val)).toBe(7); 11 | }); 12 | -------------------------------------------------------------------------------- /examples/node-jest/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uuid-example-node-jest", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "pretest": "rm -fr node_modules && npm install --no-package-lock", 7 | "test": "jest" 8 | }, 9 | "dependencies": { 10 | "uuid": "file:../../.build/uuid.tgz" 11 | }, 12 | "devDependencies": { 13 | "jest": "^29.7.0", 14 | "jest-environment-jsdom": "^29.7.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/node-webpack/README.md: -------------------------------------------------------------------------------- 1 | # uuid example Node.js with Webpack 2 | 3 | ``` 4 | npm install 5 | npm test 6 | ``` 7 | 8 | This will run webpack and execute the resulting bundles in `./dist`. 9 | -------------------------------------------------------------------------------- /examples/node-webpack/example-all.js: -------------------------------------------------------------------------------- 1 | import * as uuid from 'uuid'; 2 | import { v1 as uuidv1, v3 as uuidv3, v4 as uuidv4, v5 as uuidv5 } from 'uuid'; 3 | 4 | console.log('uuidv1()', uuidv1()); 5 | console.log('uuidv4()', uuidv4()); 6 | 7 | // ... using predefined DNS namespace (for domain names) 8 | console.log('uuidv3() DNS', uuidv3('hello.example.com', uuidv3.DNS)); 9 | 10 | // ... using predefined URL namespace (for, well, URLs) 11 | console.log('uuidv3() URL', uuidv3('http://example.com/hello', uuidv3.URL)); 12 | 13 | // ... using a custom namespace 14 | // 15 | // Note: Custom namespaces should be a UUID string specific to your application! 16 | // E.g. the one here was generated using this modules `uuid` CLI. 17 | const MY_NAMESPACE = '55238d15-c926-4598-b49d-cf4e913ba13c'; 18 | console.log('uuidv3() MY_NAMESPACE', uuidv3('Hello, World!', MY_NAMESPACE)); 19 | 20 | // ... using predefined DNS namespace (for domain names) 21 | console.log('uuidv5() DNS', uuidv5('hello.example.com', uuidv5.DNS)); 22 | 23 | // ... using predefined URL namespace (for, well, URLs) 24 | console.log('uuidv5() URL', uuidv5('http://example.com/hello', uuidv5.URL)); 25 | 26 | // ... using a custom namespace 27 | // 28 | // Note: Custom namespaces should be a UUID string specific to your application! 29 | // E.g. the one here was generated using this modules `uuid` CLI. 30 | // const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341'; 31 | console.log('uuidv5() MY_NAMESPACE', uuidv5('Hello, World!', MY_NAMESPACE)); 32 | 33 | console.log('Same with default export'); 34 | 35 | console.log('uuid.v1()', uuid.v1()); 36 | console.log('uuid.v4()', uuid.v4()); 37 | console.log('uuid.v3() DNS', uuid.v3('hello.example.com', uuid.v3.DNS)); 38 | console.log('uuid.v3() URL', uuid.v3('http://example.com/hello', uuid.v3.URL)); 39 | console.log('uuid.v3() MY_NAMESPACE', uuid.v3('Hello, World!', MY_NAMESPACE)); 40 | console.log('uuid.v5() DNS', uuid.v5('hello.example.com', uuid.v5.DNS)); 41 | console.log('uuid.v5() URL', uuid.v5('http://example.com/hello', uuid.v5.URL)); 42 | console.log('uuid.v5() MY_NAMESPACE', uuid.v5('Hello, World!', MY_NAMESPACE)); 43 | -------------------------------------------------------------------------------- /examples/node-webpack/example-v1.js: -------------------------------------------------------------------------------- 1 | import { v1 as uuidv1 } from 'uuid'; 2 | 3 | console.log('uuidv1()', uuidv1()); 4 | -------------------------------------------------------------------------------- /examples/node-webpack/example-v4.js: -------------------------------------------------------------------------------- 1 | import { v4 as uuidv4 } from 'uuid'; 2 | 3 | console.log('uuidv4()', uuidv4()); 4 | -------------------------------------------------------------------------------- /examples/node-webpack/example-v7.js: -------------------------------------------------------------------------------- 1 | import { v7 as uuidv7 } from 'uuid'; 2 | 3 | console.log('uuidv7()', uuidv7()); 4 | -------------------------------------------------------------------------------- /examples/node-webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uuid-example-node-webpack", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "rm -rf dist && webpack", 7 | "pretest": "rm -fr node_modules && npm install --no-package-lock", 8 | "test": "npm run build && node dist/v1.js && node dist/v4.js && node dist/v7.js && node dist/all.js" 9 | }, 10 | "dependencies": { 11 | "uuid": "file:../../.build/uuid.tgz" 12 | }, 13 | "devDependencies": { 14 | "webpack": "^5.74.0", 15 | "webpack-cli": "^4.10.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/node-webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | resolve: { 3 | extensions: ['*', '.js'], 4 | }, 5 | entry: { 6 | all: './example-all.js', 7 | v1: './example-v1.js', 8 | v4: './example-v4.js', 9 | v7: './example-v7.js', 10 | }, 11 | output: { 12 | filename: '[name].js', 13 | }, 14 | mode: 'production', 15 | target: 'node', 16 | }; 17 | -------------------------------------------------------------------------------- /examples/typescript/README.md: -------------------------------------------------------------------------------- 1 | # uuid test for typescript support (not really an example) 2 | 3 | ``` 4 | npm install 5 | npm test 6 | ``` 7 | 8 | This runs `tsc` using the oldest version of TypeScript supported by this project -------------------------------------------------------------------------------- /examples/typescript/buffer.test.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/ban-ts-comment */ 2 | import { v1 } from 'uuid'; 3 | 4 | v1(undefined, new Uint8Array(16)) satisfies Uint8Array; 5 | v1(undefined, Buffer.alloc(16)) satisfies Buffer; 6 | 7 | // @ts-expect-error 8 | v1(undefined, new Uint8Array(16)) satisfies Buffer; 9 | -------------------------------------------------------------------------------- /examples/typescript/index.ts: -------------------------------------------------------------------------------- 1 | import * as uuid from 'uuid'; 2 | 3 | console.log(uuid); 4 | -------------------------------------------------------------------------------- /examples/typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "uuid": "file:../../.build/uuid.tgz" 4 | }, 5 | "scripts": { 6 | "pretest": "rm -fr node_modules && npm install --no-package-lock", 7 | "test": "npx -y --package=typescript@5.0.4 -- tsc --noEmit" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /examples/utils/testpage.js: -------------------------------------------------------------------------------- 1 | export default function test(callback) { 2 | const style = document.createElement('style'); 3 | style.appendChild( 4 | document.createTextNode( 5 | [ 6 | 'body {font-family: monospace;}', 7 | 'dt, dd {display: inline-block; margin: 0;}', 8 | 'dt {min-width: 15em;}', 9 | ].join('\n') 10 | ) 11 | ); 12 | document.body.appendChild(style); 13 | 14 | function addTest(title, result) { 15 | // join() result if it's array-like 16 | if (result instanceof Uint8Array || Array.isArray(result)) { 17 | result = Array.prototype.join.apply(result); 18 | } 19 | 20 | let el; 21 | if (result === undefined) { 22 | el = document.createElement('h2'); 23 | el.innerHTML = title; 24 | } else { 25 | el = document.createElement('div'); 26 | el.className = 'test_result'; 27 | el.innerHTML = '
' + title + '
:
' + result + '
'; 28 | } 29 | 30 | document.body.appendChild(el); 31 | } 32 | 33 | function done() { 34 | const div = document.createElement('h2'); 35 | div.id = 'done'; 36 | document.body.appendChild(div); 37 | } 38 | 39 | window.onload = function () { 40 | callback(addTest, done); 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uuid", 3 | "version": "11.1.0", 4 | "description": "RFC9562 UUIDs", 5 | "type": "module", 6 | "funding": [ 7 | "https://github.com/sponsors/broofa", 8 | "https://github.com/sponsors/ctavan" 9 | ], 10 | "commitlint": { 11 | "extends": [ 12 | "@commitlint/config-conventional" 13 | ] 14 | }, 15 | "keywords": [ 16 | "uuid", 17 | "guid", 18 | "rfc4122", 19 | "rfc9562" 20 | ], 21 | "license": "MIT", 22 | "bin": { 23 | "uuid": "./dist/esm/bin/uuid" 24 | }, 25 | "sideEffects": false, 26 | "main": "./dist/cjs/index.js", 27 | "exports": { 28 | ".": { 29 | "node": { 30 | "import": "./dist/esm/index.js", 31 | "require": "./dist/cjs/index.js" 32 | }, 33 | "browser": { 34 | "import": "./dist/esm-browser/index.js", 35 | "require": "./dist/cjs-browser/index.js" 36 | }, 37 | "default": "./dist/esm-browser/index.js" 38 | }, 39 | "./package.json": "./package.json" 40 | }, 41 | "module": "./dist/esm/index.js", 42 | "browser": { 43 | "./dist/esm/index.js": "./dist/esm-browser/index.js", 44 | "./dist/cjs/index.js": "./dist/cjs-browser/index.js" 45 | }, 46 | "files": [ 47 | "dist", 48 | "!dist/**/test" 49 | ], 50 | "devDependencies": { 51 | "@babel/eslint-parser": "7.27.1", 52 | "@commitlint/cli": "19.8.0", 53 | "@commitlint/config-conventional": "19.8.0", 54 | "@eslint/js": "9.26.0", 55 | "bundlewatch": "0.4.1", 56 | "commander": "13.1.0", 57 | "eslint": "9.26.0", 58 | "eslint-config-prettier": "10.1.2", 59 | "eslint-plugin-prettier": "5.4.0", 60 | "globals": "16.0.0", 61 | "husky": "9.1.7", 62 | "jest": "29.7.0", 63 | "lint-staged": "15.5.2", 64 | "neostandard": "0.12.1", 65 | "npm-run-all": "4.1.5", 66 | "prettier": "3.5.3", 67 | "release-please": "17.0.0", 68 | "runmd": "1.4.1", 69 | "standard-version": "9.5.0", 70 | "typescript": "5.0.4", 71 | "typescript-eslint": "8.32.0" 72 | }, 73 | "optionalDevDependencies": { 74 | "@wdio/browserstack-service": "9.2.1", 75 | "@wdio/cli": "9.2.1", 76 | "@wdio/jasmine-framework": "9.2.1", 77 | "@wdio/local-runner": "9.2.1", 78 | "@wdio/spec-reporter": "9.1.3", 79 | "@wdio/static-server-service": "9.1.3" 80 | }, 81 | "scripts": { 82 | "build": "./scripts/build.sh", 83 | "build:watch": "tsc --watch -p tsconfig.esm.json", 84 | "bundlewatch": "npm run pretest:browser && bundlewatch --config bundlewatch.config.json", 85 | "docs:diff": "npm run docs && git diff --quiet README.md", 86 | "docs": "npm run build && npx runmd --output=README.md README_js.md", 87 | "eslint:check": "eslint src/ test/ examples/ *.[jt]s", 88 | "eslint:fix": "eslint --fix src/ test/ examples/ *.[jt]s", 89 | "examples:browser:rollup:build": "cd examples/browser-rollup && npm run build", 90 | "examples:browser:webpack:build": "cd examples/browser-webpack && npm run build", 91 | "examples:node:commonjs:test": "cd examples/node-commonjs && npm test", 92 | "examples:node:esmodules:test": "cd examples/node-esmodules && npm test", 93 | "examples:node:jest:test": "cd examples/node-jest && npm test", 94 | "examples:node:typescript:test": "cd examples/typescript && npm test", 95 | "lint": "npm run eslint:check && npm run prettier:check", 96 | "md": "runmd --watch --output=README.md README_js.md", 97 | "prepack": "npm run build -- --no-pack", 98 | "prepare": "husky", 99 | "prepublishOnly": "npm run build", 100 | "pretest:benchmark": "npm run build", 101 | "pretest:browser": "./scripts/iodd && npm run build && npm-run-all --parallel examples:browser:**", 102 | "pretest:node": "npm run build", 103 | "pretest": "npm run build", 104 | "prettier:check": "prettier --check .", 105 | "prettier:fix": "prettier --write .", 106 | "release": "standard-version --no-verify", 107 | "test:benchmark": "cd examples/benchmark && npm test", 108 | "test:browser": "wdio run ./wdio.conf.js", 109 | "test:node": "npm-run-all --parallel examples:node:**", 110 | "test:watch": "node --test --enable-source-maps --watch dist/esm/test/*.js", 111 | "test": "node --test --enable-source-maps dist/esm/test/*.js" 112 | }, 113 | "repository": { 114 | "type": "git", 115 | "url": "https://github.com/uuidjs/uuid.git" 116 | }, 117 | "lint-staged": { 118 | "*": [ 119 | "prettier --no-error-on-unmatched-pattern --write" 120 | ], 121 | "*.{js,jsx}": [ 122 | "eslint --no-error-on-unmatched-pattern --fix" 123 | ] 124 | }, 125 | "standard-version": { 126 | "scripts": { 127 | "postchangelog": "prettier --write CHANGELOG.md" 128 | } 129 | }, 130 | "packageManager": "npm@11.3.0" 131 | } 132 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | arrowParens: 'always', 3 | printWidth: 100, 4 | proseWrap: 'never', 5 | singleQuote: true, 6 | trailingComma: 'es5', 7 | }; 8 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | # This script generates 4 builds, as follows: 4 | # - dist/esm: ESM build for Node.js 5 | # - dist/esm-browser: ESM build for the Browser 6 | # - dist/cjs: CommonJS build for Node.js 7 | # - dist/cjs-browser: CommonJS build for the Browser 8 | # 9 | # Note: that the "preferred" build for testing (local and CI) is the ESM build, 10 | # except where we specifically test the other builds 11 | 12 | set -e # exit on error 13 | 14 | # Change to project root 15 | ROOT="$(pwd)/$(dirname "$0")/.." 16 | cd "$ROOT" || exit 1 17 | 18 | # Prep TS output dir 19 | DIST_DIR="$ROOT/dist" 20 | rm -rf "$DIST_DIR" 21 | mkdir -p "$DIST_DIR" 22 | 23 | # Build each module type 24 | for MODULE_TYPE in esm cjs; do 25 | echo "Building ${MODULE_TYPE}" 26 | 27 | NODE_DIST_DIR="$DIST_DIR/${MODULE_TYPE}" 28 | BROWSER_DIST_DIR="$DIST_DIR/${MODULE_TYPE}-browser" 29 | 30 | tsc -p tsconfig.${MODULE_TYPE}.json 31 | 32 | # Clone files for browser builds 33 | cp -pr ${NODE_DIST_DIR} ${BROWSER_DIST_DIR} 34 | 35 | # Remove browser files from non-browser builds 36 | for FILE in ${NODE_DIST_DIR}/*-browser*;do 37 | rm -f $FILE 38 | done 39 | 40 | # Move browser files into place for browser builds 41 | ( 42 | # Temporarily cd into BROWSER_DIST_DIR to avoid having to deal with 43 | # "-browser" appearing in both the dir name and file name of FILE's full 44 | # path 45 | cd ${BROWSER_DIST_DIR} 46 | 47 | for FILE in *-browser*;do 48 | mv $FILE ${FILE/-browser/} 49 | done 50 | ) 51 | 52 | # esm/cjs-specific logic 53 | if [ "$MODULE_TYPE" = "esm" ]; then 54 | # ESM: copy bin files to dist 55 | cp -pr "$DIST_DIR/../src/bin" "$NODE_DIST_DIR" 56 | else 57 | # CJS: Add package.json that specifies type: commonjs 58 | echo "{\"type\":\"commonjs\"}" > "$NODE_DIST_DIR/package.json" 59 | echo "{\"type\":\"commonjs\"}" > "$BROWSER_DIST_DIR/package.json" 60 | fi 61 | done 62 | 63 | if [ "${1-}" != "--no-pack" ]; then 64 | # Prep tarball dir 65 | BUILD_DIR="$ROOT/.build" 66 | rm -rf "$BUILD_DIR" 67 | mkdir -p "$BUILD_DIR" 68 | 69 | # Create tarball for local installation (in tests and examples) 70 | echo "Packing tarball" 71 | npm pack --pack-destination "$BUILD_DIR" > /dev/null 2>&1 72 | mv $BUILD_DIR/uuid-*.tgz $BUILD_DIR/uuid.tgz 73 | fi 74 | 75 | echo "-- fin --" -------------------------------------------------------------------------------- /scripts/iodd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { program } from 'commander'; 4 | import { spawn } from 'node:child_process'; 5 | import fs from 'node:fs/promises'; 6 | import path from 'node:path'; 7 | 8 | program 9 | .name('iodd') 10 | .description( 11 | '(I)nstall (o)ptional (d)ev (d)ependencies from package.json#optionalDevDependencies' 12 | ) 13 | .option('-v, --verbose', 'Output npm install output to stdout/stderr') 14 | .option('-r, --required', 'Exit with non-zero code if dependencies fail to install') 15 | .argument('[packagePath]', 'Path to package.json file', './package.json') 16 | .action(main); 17 | 18 | async function main(packagePath, options) { 19 | // Get list of optional dependencies from package.json 20 | const json = await fs.readFile(path.join(process.cwd(), packagePath)); 21 | const packageJson = JSON.parse(json); 22 | const { optionalDevDependencies: deps } = packageJson; 23 | 24 | if (!deps) { 25 | console.error(`No optional dependencies found in ${packagePath}`); 26 | process.exit(1); 27 | } 28 | 29 | const packageRefs = Object.entries(deps).map(([name, version]) => `${name}@${version}`); 30 | 31 | // Install optional dependencies with child_process running npm 32 | const args = ['install', '--no-save', ...packageRefs]; 33 | console.log('Running: ', 'npm', args.join(' ')); 34 | 35 | const cp = spawn('npm', args); 36 | 37 | if (options.verbose) { 38 | cp.stdout.pipe(process.stdout); 39 | cp.stderr.pipe(process.stderr); 40 | } 41 | 42 | const exitCode = await new Promise((resolve) => { 43 | cp.on('close', resolve); 44 | }); 45 | 46 | if (exitCode !== 0) { 47 | console.error('Dependencies failed to install'); 48 | 49 | if (options.required) { 50 | process.exit(exitCode); 51 | } 52 | } 53 | } 54 | 55 | program.parseAsync(); 56 | -------------------------------------------------------------------------------- /src/bin/uuid: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import '../uuid-bin.js'; 3 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export type * from './types.js'; 2 | export { default as MAX } from './max.js'; 3 | export { default as NIL } from './nil.js'; 4 | export { default as parse } from './parse.js'; 5 | export { default as stringify } from './stringify.js'; 6 | export { default as v1 } from './v1.js'; 7 | export { default as v1ToV6 } from './v1ToV6.js'; 8 | export { default as v3 } from './v3.js'; 9 | export { default as v4 } from './v4.js'; 10 | export { default as v5 } from './v5.js'; 11 | export { default as v6 } from './v6.js'; 12 | export { default as v6ToV1 } from './v6ToV1.js'; 13 | export { default as v7 } from './v7.js'; 14 | export { default as validate } from './validate.js'; 15 | export { default as version } from './version.js'; 16 | -------------------------------------------------------------------------------- /src/max.ts: -------------------------------------------------------------------------------- 1 | export default 'ffffffff-ffff-ffff-ffff-ffffffffffff'; 2 | -------------------------------------------------------------------------------- /src/md5-browser.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Browser-compatible JavaScript MD5 3 | * 4 | * Modification of JavaScript MD5 5 | * https://github.com/blueimp/JavaScript-MD5 6 | * 7 | * Copyright 2011, Sebastian Tschan 8 | * https://blueimp.net 9 | * 10 | * Licensed under the MIT license: 11 | * https://opensource.org/licenses/MIT 12 | * 13 | * Based on 14 | * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message 15 | * Digest Algorithm, as defined in RFC 1321. 16 | * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 17 | * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet 18 | * Distributed under the BSD License 19 | * See http://pajhome.org.uk/crypt/md5 for more info. 20 | */ 21 | function md5(bytes: Uint8Array) { 22 | const words = uint8ToUint32(bytes); 23 | 24 | const md5Bytes = wordsToMd5(words, bytes.length * 8); 25 | return uint32ToUint8(md5Bytes); 26 | } 27 | 28 | /* 29 | * Convert an array of little-endian words to an array of bytes 30 | */ 31 | function uint32ToUint8(input: Uint32Array) { 32 | // Note: On little endian platforms we could simply return `new 33 | // Uint8Array(input.buffer)` here, but that that won't work on big-endian 34 | // systems. (That said, there's code below that appears to already assume 35 | // little-endian, so maybe this is a moot point? Either way, keeping the 36 | // existing code for now to be safe.) 37 | const bytes = new Uint8Array(input.length * 4); 38 | for (let i = 0; i < input.length * 4; i++) { 39 | bytes[i] = (input[i >> 2] >>> ((i % 4) * 8)) & 0xff; 40 | } 41 | return bytes; 42 | } 43 | 44 | /** 45 | * Calculate output length with padding and bit length 46 | */ 47 | function getOutputLength(inputLength8: number) { 48 | return (((inputLength8 + 64) >>> 9) << 4) + 14 + 1; 49 | } 50 | 51 | /* 52 | * Calculate the MD5 of an array of little-endian words, and a bit length. 53 | */ 54 | function wordsToMd5(x: Uint32Array, len: number) { 55 | /* append padding */ 56 | const xpad = new Uint32Array(getOutputLength(len)).fill(0); 57 | xpad.set(x); 58 | xpad[len >> 5] |= 0x80 << len % 32; 59 | xpad[xpad.length - 1] = len; 60 | x = xpad; 61 | 62 | let a = 1732584193; 63 | let b = -271733879; 64 | let c = -1732584194; 65 | let d = 271733878; 66 | 67 | for (let i = 0; i < x.length; i += 16) { 68 | const olda = a; 69 | const oldb = b; 70 | const oldc = c; 71 | const oldd = d; 72 | 73 | a = md5ff(a, b, c, d, x[i], 7, -680876936); 74 | d = md5ff(d, a, b, c, x[i + 1], 12, -389564586); 75 | c = md5ff(c, d, a, b, x[i + 2], 17, 606105819); 76 | b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330); 77 | a = md5ff(a, b, c, d, x[i + 4], 7, -176418897); 78 | d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426); 79 | c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341); 80 | b = md5ff(b, c, d, a, x[i + 7], 22, -45705983); 81 | a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416); 82 | d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417); 83 | c = md5ff(c, d, a, b, x[i + 10], 17, -42063); 84 | b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162); 85 | a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682); 86 | d = md5ff(d, a, b, c, x[i + 13], 12, -40341101); 87 | c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290); 88 | b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329); 89 | 90 | a = md5gg(a, b, c, d, x[i + 1], 5, -165796510); 91 | d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632); 92 | c = md5gg(c, d, a, b, x[i + 11], 14, 643717713); 93 | b = md5gg(b, c, d, a, x[i], 20, -373897302); 94 | a = md5gg(a, b, c, d, x[i + 5], 5, -701558691); 95 | d = md5gg(d, a, b, c, x[i + 10], 9, 38016083); 96 | c = md5gg(c, d, a, b, x[i + 15], 14, -660478335); 97 | b = md5gg(b, c, d, a, x[i + 4], 20, -405537848); 98 | a = md5gg(a, b, c, d, x[i + 9], 5, 568446438); 99 | d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690); 100 | c = md5gg(c, d, a, b, x[i + 3], 14, -187363961); 101 | b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501); 102 | a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467); 103 | d = md5gg(d, a, b, c, x[i + 2], 9, -51403784); 104 | c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473); 105 | b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734); 106 | 107 | a = md5hh(a, b, c, d, x[i + 5], 4, -378558); 108 | d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463); 109 | c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562); 110 | b = md5hh(b, c, d, a, x[i + 14], 23, -35309556); 111 | a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060); 112 | d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353); 113 | c = md5hh(c, d, a, b, x[i + 7], 16, -155497632); 114 | b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640); 115 | a = md5hh(a, b, c, d, x[i + 13], 4, 681279174); 116 | d = md5hh(d, a, b, c, x[i], 11, -358537222); 117 | c = md5hh(c, d, a, b, x[i + 3], 16, -722521979); 118 | b = md5hh(b, c, d, a, x[i + 6], 23, 76029189); 119 | a = md5hh(a, b, c, d, x[i + 9], 4, -640364487); 120 | d = md5hh(d, a, b, c, x[i + 12], 11, -421815835); 121 | c = md5hh(c, d, a, b, x[i + 15], 16, 530742520); 122 | b = md5hh(b, c, d, a, x[i + 2], 23, -995338651); 123 | 124 | a = md5ii(a, b, c, d, x[i], 6, -198630844); 125 | d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415); 126 | c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905); 127 | b = md5ii(b, c, d, a, x[i + 5], 21, -57434055); 128 | a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571); 129 | d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606); 130 | c = md5ii(c, d, a, b, x[i + 10], 15, -1051523); 131 | b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799); 132 | a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359); 133 | d = md5ii(d, a, b, c, x[i + 15], 10, -30611744); 134 | c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380); 135 | b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649); 136 | a = md5ii(a, b, c, d, x[i + 4], 6, -145523070); 137 | d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379); 138 | c = md5ii(c, d, a, b, x[i + 2], 15, 718787259); 139 | b = md5ii(b, c, d, a, x[i + 9], 21, -343485551); 140 | 141 | a = safeAdd(a, olda); 142 | b = safeAdd(b, oldb); 143 | c = safeAdd(c, oldc); 144 | d = safeAdd(d, oldd); 145 | } 146 | 147 | return Uint32Array.of(a, b, c, d); 148 | } 149 | 150 | /* 151 | * Convert Uint8Array to Uint32 array (little-endian) 152 | */ 153 | function uint8ToUint32(input: Uint8Array) { 154 | if (input.length === 0) { 155 | return new Uint32Array(); 156 | } 157 | 158 | const output = new Uint32Array(getOutputLength(input.length * 8)).fill(0); 159 | for (let i = 0; i < input.length; i++) { 160 | output[i >> 2] |= (input[i] & 0xff) << ((i % 4) * 8); 161 | } 162 | return output; 163 | } 164 | 165 | /* 166 | * Add integers, wrapping at 2^32. This uses 16-bit operations internally 167 | * to work around bugs in some JS interpreters. 168 | */ 169 | function safeAdd(x: number, y: number) { 170 | const lsw = (x & 0xffff) + (y & 0xffff); 171 | const msw = (x >> 16) + (y >> 16) + (lsw >> 16); 172 | return (msw << 16) | (lsw & 0xffff); 173 | } 174 | 175 | /* 176 | * Bitwise rotate a 32-bit number to the left. 177 | */ 178 | function bitRotateLeft(num: number, cnt: number) { 179 | return (num << cnt) | (num >>> (32 - cnt)); 180 | } 181 | 182 | /* 183 | * These functions implement the four basic operations the algorithm uses. 184 | */ 185 | function md5cmn(q: number, a: number, b: number, x: number, s: number, t: number) { 186 | return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b); 187 | } 188 | 189 | function md5ff(a: number, b: number, c: number, d: number, x: number, s: number, t: number) { 190 | return md5cmn((b & c) | (~b & d), a, b, x, s, t); 191 | } 192 | 193 | function md5gg(a: number, b: number, c: number, d: number, x: number, s: number, t: number) { 194 | return md5cmn((b & d) | (c & ~d), a, b, x, s, t); 195 | } 196 | 197 | function md5hh(a: number, b: number, c: number, d: number, x: number, s: number, t: number) { 198 | return md5cmn(b ^ c ^ d, a, b, x, s, t); 199 | } 200 | 201 | function md5ii(a: number, b: number, c: number, d: number, x: number, s: number, t: number) { 202 | return md5cmn(c ^ (b | ~d), a, b, x, s, t); 203 | } 204 | 205 | export default md5; 206 | -------------------------------------------------------------------------------- /src/md5.ts: -------------------------------------------------------------------------------- 1 | import { createHash } from 'crypto'; 2 | 3 | function md5(bytes: Uint8Array) { 4 | if (Array.isArray(bytes)) { 5 | bytes = Buffer.from(bytes); 6 | } else if (typeof bytes === 'string') { 7 | bytes = Buffer.from(bytes, 'utf8'); 8 | } 9 | 10 | return createHash('md5').update(bytes).digest(); 11 | } 12 | 13 | export default md5; 14 | -------------------------------------------------------------------------------- /src/native-browser.ts: -------------------------------------------------------------------------------- 1 | const randomUUID = 2 | typeof crypto !== 'undefined' && crypto.randomUUID && crypto.randomUUID.bind(crypto); 3 | 4 | export default { randomUUID }; 5 | -------------------------------------------------------------------------------- /src/native.ts: -------------------------------------------------------------------------------- 1 | import { randomUUID } from 'crypto'; 2 | 3 | export default { randomUUID }; 4 | -------------------------------------------------------------------------------- /src/nil.ts: -------------------------------------------------------------------------------- 1 | export default '00000000-0000-0000-0000-000000000000'; 2 | -------------------------------------------------------------------------------- /src/parse.ts: -------------------------------------------------------------------------------- 1 | import validate from './validate.js'; 2 | 3 | function parse(uuid: string) { 4 | if (!validate(uuid)) { 5 | throw TypeError('Invalid UUID'); 6 | } 7 | 8 | let v; 9 | return Uint8Array.of( 10 | (v = parseInt(uuid.slice(0, 8), 16)) >>> 24, 11 | (v >>> 16) & 0xff, 12 | (v >>> 8) & 0xff, 13 | v & 0xff, 14 | 15 | // Parse ........-####-....-....-............ 16 | (v = parseInt(uuid.slice(9, 13), 16)) >>> 8, 17 | v & 0xff, 18 | 19 | // Parse ........-....-####-....-............ 20 | (v = parseInt(uuid.slice(14, 18), 16)) >>> 8, 21 | v & 0xff, 22 | 23 | // Parse ........-....-....-####-............ 24 | (v = parseInt(uuid.slice(19, 23), 16)) >>> 8, 25 | v & 0xff, 26 | 27 | // Parse ........-....-....-....-############ 28 | // (Use "/" to avoid 32-bit truncation when bit-shifting high-order bytes) 29 | ((v = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000) & 0xff, 30 | (v / 0x100000000) & 0xff, 31 | (v >>> 24) & 0xff, 32 | (v >>> 16) & 0xff, 33 | (v >>> 8) & 0xff, 34 | v & 0xff 35 | ); 36 | } 37 | 38 | export default parse; 39 | -------------------------------------------------------------------------------- /src/regex.ts: -------------------------------------------------------------------------------- 1 | export default /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i; 2 | -------------------------------------------------------------------------------- /src/rng-browser.ts: -------------------------------------------------------------------------------- 1 | // Unique ID creation requires a high quality random # generator. In the browser we therefore 2 | // require the crypto API and do not support built-in fallback to lower quality random number 3 | // generators (like Math.random()). 4 | 5 | let getRandomValues: typeof crypto.getRandomValues | undefined; 6 | 7 | const rnds8 = new Uint8Array(16); 8 | 9 | export default function rng() { 10 | // lazy load so that environments that need to polyfill have a chance to do so 11 | if (!getRandomValues) { 12 | if (typeof crypto === 'undefined' || !crypto.getRandomValues) { 13 | throw new Error( 14 | 'crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported' 15 | ); 16 | } 17 | 18 | getRandomValues = crypto.getRandomValues.bind(crypto); 19 | } 20 | 21 | return getRandomValues(rnds8); 22 | } 23 | -------------------------------------------------------------------------------- /src/rng.ts: -------------------------------------------------------------------------------- 1 | import { randomFillSync } from 'crypto'; 2 | 3 | const rnds8Pool = new Uint8Array(256); // # of random values to pre-allocate 4 | let poolPtr = rnds8Pool.length; 5 | 6 | export default function rng() { 7 | if (poolPtr > rnds8Pool.length - 16) { 8 | randomFillSync(rnds8Pool); 9 | poolPtr = 0; 10 | } 11 | return rnds8Pool.slice(poolPtr, (poolPtr += 16)); 12 | } 13 | -------------------------------------------------------------------------------- /src/sha1-browser.ts: -------------------------------------------------------------------------------- 1 | // Adapted from Chris Veness' SHA1 code at 2 | // http://www.movable-type.co.uk/scripts/sha1.html 3 | function f(s: 0 | 1 | 2 | 3, x: number, y: number, z: number) { 4 | switch (s) { 5 | case 0: 6 | return (x & y) ^ (~x & z); 7 | case 1: 8 | return x ^ y ^ z; 9 | case 2: 10 | return (x & y) ^ (x & z) ^ (y & z); 11 | case 3: 12 | return x ^ y ^ z; 13 | } 14 | } 15 | 16 | function ROTL(x: number, n: number) { 17 | return (x << n) | (x >>> (32 - n)); 18 | } 19 | 20 | function sha1(bytes: Uint8Array): Uint8Array { 21 | const K = [0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6]; 22 | const H = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]; 23 | 24 | const newBytes = new Uint8Array(bytes.length + 1); 25 | newBytes.set(bytes); 26 | newBytes[bytes.length] = 0x80; 27 | bytes = newBytes; 28 | 29 | const l = bytes.length / 4 + 2; 30 | const N = Math.ceil(l / 16); 31 | const M: Uint32Array[] = new Array(N); 32 | 33 | for (let i = 0; i < N; ++i) { 34 | const arr = new Uint32Array(16); 35 | 36 | for (let j = 0; j < 16; ++j) { 37 | arr[j] = 38 | (bytes[i * 64 + j * 4] << 24) | 39 | (bytes[i * 64 + j * 4 + 1] << 16) | 40 | (bytes[i * 64 + j * 4 + 2] << 8) | 41 | bytes[i * 64 + j * 4 + 3]; 42 | } 43 | 44 | M[i] = arr; 45 | } 46 | 47 | M[N - 1][14] = ((bytes.length - 1) * 8) / Math.pow(2, 32); 48 | M[N - 1][14] = Math.floor(M[N - 1][14]); 49 | M[N - 1][15] = ((bytes.length - 1) * 8) & 0xffffffff; 50 | 51 | for (let i = 0; i < N; ++i) { 52 | const W = new Uint32Array(80); 53 | 54 | for (let t = 0; t < 16; ++t) { 55 | W[t] = M[i][t]; 56 | } 57 | 58 | for (let t = 16; t < 80; ++t) { 59 | W[t] = ROTL(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1); 60 | } 61 | 62 | let a = H[0]; 63 | let b = H[1]; 64 | let c = H[2]; 65 | let d = H[3]; 66 | let e = H[4]; 67 | 68 | for (let t = 0; t < 80; ++t) { 69 | const s = Math.floor(t / 20) as 0 | 1 | 2 | 3; 70 | const T = (ROTL(a, 5) + f(s, b, c, d) + e + K[s] + W[t]) >>> 0; 71 | e = d; 72 | d = c; 73 | c = ROTL(b, 30) >>> 0; 74 | b = a; 75 | a = T; 76 | } 77 | 78 | H[0] = (H[0] + a) >>> 0; 79 | H[1] = (H[1] + b) >>> 0; 80 | H[2] = (H[2] + c) >>> 0; 81 | H[3] = (H[3] + d) >>> 0; 82 | H[4] = (H[4] + e) >>> 0; 83 | } 84 | 85 | // Note: Uint8Array.of() does `& 0xff` for each value 86 | return Uint8Array.of( 87 | H[0] >> 24, 88 | H[0] >> 16, 89 | H[0] >> 8, 90 | H[0], 91 | H[1] >> 24, 92 | H[1] >> 16, 93 | H[1] >> 8, 94 | H[1], 95 | H[2] >> 24, 96 | H[2] >> 16, 97 | H[2] >> 8, 98 | H[2], 99 | H[3] >> 24, 100 | H[3] >> 16, 101 | H[3] >> 8, 102 | H[3], 103 | H[4] >> 24, 104 | H[4] >> 16, 105 | H[4] >> 8, 106 | H[4] 107 | ); 108 | } 109 | 110 | export default sha1; 111 | -------------------------------------------------------------------------------- /src/sha1.ts: -------------------------------------------------------------------------------- 1 | import { createHash } from 'crypto'; 2 | 3 | function sha1(bytes: Uint8Array) { 4 | if (Array.isArray(bytes)) { 5 | bytes = Buffer.from(bytes); 6 | } else if (typeof bytes === 'string') { 7 | bytes = Buffer.from(bytes, 'utf8'); 8 | } 9 | 10 | return createHash('sha1').update(bytes).digest(); 11 | } 12 | 13 | export default sha1; 14 | -------------------------------------------------------------------------------- /src/stringify.ts: -------------------------------------------------------------------------------- 1 | import validate from './validate.js'; 2 | 3 | /** 4 | * Convert array of 16 byte values to UUID string format of the form: 5 | * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 6 | */ 7 | const byteToHex: string[] = []; 8 | 9 | for (let i = 0; i < 256; ++i) { 10 | byteToHex.push((i + 0x100).toString(16).slice(1)); 11 | } 12 | 13 | export function unsafeStringify(arr: Uint8Array, offset = 0): string { 14 | // Note: Be careful editing this code! It's been tuned for performance 15 | // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434 16 | // 17 | // Note to future-self: No, you can't remove the `toLowerCase()` call. 18 | // REF: https://github.com/uuidjs/uuid/pull/677#issuecomment-1757351351 19 | return ( 20 | byteToHex[arr[offset + 0]] + 21 | byteToHex[arr[offset + 1]] + 22 | byteToHex[arr[offset + 2]] + 23 | byteToHex[arr[offset + 3]] + 24 | '-' + 25 | byteToHex[arr[offset + 4]] + 26 | byteToHex[arr[offset + 5]] + 27 | '-' + 28 | byteToHex[arr[offset + 6]] + 29 | byteToHex[arr[offset + 7]] + 30 | '-' + 31 | byteToHex[arr[offset + 8]] + 32 | byteToHex[arr[offset + 9]] + 33 | '-' + 34 | byteToHex[arr[offset + 10]] + 35 | byteToHex[arr[offset + 11]] + 36 | byteToHex[arr[offset + 12]] + 37 | byteToHex[arr[offset + 13]] + 38 | byteToHex[arr[offset + 14]] + 39 | byteToHex[arr[offset + 15]] 40 | ).toLowerCase(); 41 | } 42 | 43 | function stringify(arr: Uint8Array, offset = 0) { 44 | const uuid = unsafeStringify(arr, offset); 45 | 46 | // Consistency check for valid UUID. If this throws, it's likely due to one 47 | // of the following: 48 | // - One or more input array values don't map to a hex octet (leading to 49 | // "undefined" in the uuid) 50 | // - Invalid input values for the RFC `version` or `variant` fields 51 | if (!validate(uuid)) { 52 | throw TypeError('Stringified UUID is invalid'); 53 | } 54 | 55 | return uuid; 56 | } 57 | 58 | export default stringify; 59 | -------------------------------------------------------------------------------- /src/test/parse.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'node:assert/strict'; 2 | import { describe, test } from 'node:test'; 3 | import parse from '../parse.js'; 4 | import stringify from '../stringify.js'; 5 | import uuidv4 from '../v4.js'; 6 | 7 | // Deterministic PRNG for reproducible tests 8 | // See https://stackoverflow.com/a/47593316/109538 9 | function splitmix32(a: number) { 10 | return function () { 11 | a |= 0; 12 | a = (a + 0x9e3779b9) | 0; 13 | let t = a ^ (a >>> 16); 14 | t = Math.imul(t, 0x21f0aaad); 15 | t = t ^ (t >>> 15); 16 | t = Math.imul(t, 0x735a2d97); 17 | return ((t = t ^ (t >>> 15)) >>> 0) / 4294967296; 18 | }; 19 | } 20 | const rand = splitmix32(0x12345678); 21 | 22 | function rng(bytes = new Uint8Array(16)) { 23 | for (let i = 0; i < 16; i++) { 24 | bytes[i] = rand() * 256; 25 | } 26 | 27 | return bytes; 28 | } 29 | 30 | describe('parse', () => { 31 | test('String -> bytes parsing', () => { 32 | assert.deepStrictEqual( 33 | parse('0f5abcd1-c194-47f3-905b-2df7263a084b'), 34 | Uint8Array.from([ 35 | 0x0f, 0x5a, 0xbc, 0xd1, 0xc1, 0x94, 0x47, 0xf3, 0x90, 0x5b, 0x2d, 0xf7, 0x26, 0x3a, 0x08, 36 | 0x4b, 37 | ]) 38 | ); 39 | }); 40 | 41 | test('String -> bytes -> string symmetry for assorted uuids', () => { 42 | for (let i = 0; i < 1000; i++) { 43 | const uuid = uuidv4({ rng }); 44 | assert.equal(stringify(parse(uuid)), uuid); 45 | } 46 | }); 47 | 48 | test('Case neutrality', () => { 49 | // Verify upper/lower case neutrality 50 | assert.deepStrictEqual( 51 | parse('0f5abcd1-c194-47f3-905b-2df7263a084b'), 52 | parse('0f5abcd1-c194-47f3-905b-2df7263a084b'.toUpperCase()) 53 | ); 54 | }); 55 | 56 | test('Null UUID case', () => { 57 | assert.deepStrictEqual( 58 | parse('00000000-0000-0000-0000-000000000000'), 59 | Uint8Array.from(new Array(16).fill(0)) 60 | ); 61 | }); 62 | 63 | test('UUID validation', () => { 64 | // @ts-expect-error testing invalid input 65 | assert.throws(() => parse()); 66 | 67 | assert.throws(() => parse('invalid uuid')); 68 | assert.throws(() => parse('zyxwvuts-rqpo-nmlk-jihg-fedcba000000')); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /src/test/rng.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'node:assert/strict'; 2 | import { describe, test } from 'node:test'; 3 | import rng from '../rng.js'; 4 | 5 | describe('rng', () => { 6 | test('Node.js RNG', () => { 7 | const bytes = rng(); 8 | assert.equal(bytes.length, 16); 9 | 10 | for (let i = 0; i < bytes.length; ++i) { 11 | assert.equal(typeof bytes[i], 'number'); 12 | } 13 | }); 14 | 15 | // Test of whatwgRNG missing for now since with esmodules we can no longer manipulate the 16 | // require.cache. 17 | }); 18 | -------------------------------------------------------------------------------- /src/test/stringify.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'node:assert/strict'; 2 | import { describe, test } from 'node:test'; 3 | import stringify, { unsafeStringify } from '../stringify.js'; 4 | 5 | const BYTES = Uint8Array.of( 6 | 0x0f, 7 | 0x5a, 8 | 0xbc, 9 | 0xd1, 10 | 0xc1, 11 | 0x94, 12 | 0x47, 13 | 0xf3, 14 | 0x90, 15 | 0x5b, 16 | 0x2d, 17 | 0xf7, 18 | 0x26, 19 | 0x3a, 20 | 0x08, 21 | 0x4b 22 | ); 23 | 24 | describe('stringify', () => { 25 | test('Stringify Array (unsafe)', () => { 26 | assert.equal(unsafeStringify(BYTES), '0f5abcd1-c194-47f3-905b-2df7263a084b'); 27 | }); 28 | 29 | test('Stringify w/ offset (unsafe)', () => { 30 | const bytes = new Uint8Array(19).fill(0); 31 | bytes.set(BYTES, 3); 32 | assert.equal(unsafeStringify(bytes, 3), '0f5abcd1-c194-47f3-905b-2df7263a084b'); 33 | }); 34 | 35 | test('Stringify Array (safe)', () => { 36 | assert.equal(stringify(BYTES), '0f5abcd1-c194-47f3-905b-2df7263a084b'); 37 | }); 38 | 39 | test('Throws on not enough values (safe)', () => { 40 | const bytes = BYTES.slice(0, 15); 41 | assert.throws(() => stringify(bytes)); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /src/test/test_constants.ts: -------------------------------------------------------------------------------- 1 | import MAX from '../max.js'; 2 | import NIL from '../nil.js'; 3 | 4 | // Table of [uuid value, expected validate(), [expected version()]] 5 | export const TESTS = [ 6 | // constants 7 | { value: NIL, expectedValidate: true, expectedVersion: 0 }, 8 | { value: MAX, expectedValidate: true, expectedVersion: 15 }, 9 | 10 | // each version, with either all 0's or all 1's in settable bits 11 | { value: '00000000-0000-1000-8000-000000000000', expectedValidate: true, expectedVersion: 1 }, 12 | { value: 'ffffffff-ffff-1fff-8fff-ffffffffffff', expectedValidate: true, expectedVersion: 1 }, 13 | { value: '00000000-0000-2000-8000-000000000000', expectedValidate: true, expectedVersion: 2 }, 14 | { value: 'ffffffff-ffff-2fff-bfff-ffffffffffff', expectedValidate: true, expectedVersion: 2 }, 15 | { value: '00000000-0000-3000-8000-000000000000', expectedValidate: true, expectedVersion: 3 }, 16 | { value: 'ffffffff-ffff-3fff-bfff-ffffffffffff', expectedValidate: true, expectedVersion: 3 }, 17 | { value: '00000000-0000-4000-8000-000000000000', expectedValidate: true, expectedVersion: 4 }, 18 | { value: 'ffffffff-ffff-4fff-bfff-ffffffffffff', expectedValidate: true, expectedVersion: 4 }, 19 | { value: '00000000-0000-5000-8000-000000000000', expectedValidate: true, expectedVersion: 5 }, 20 | { value: 'ffffffff-ffff-5fff-bfff-ffffffffffff', expectedValidate: true, expectedVersion: 5 }, 21 | { value: '00000000-0000-6000-8000-000000000000', expectedValidate: true, expectedVersion: 6 }, 22 | { value: 'ffffffff-ffff-6fff-bfff-ffffffffffff', expectedValidate: true, expectedVersion: 6 }, 23 | { value: '00000000-0000-7000-8000-000000000000', expectedValidate: true, expectedVersion: 7 }, 24 | { value: 'ffffffff-ffff-7fff-bfff-ffffffffffff', expectedValidate: true, expectedVersion: 7 }, 25 | { value: '00000000-0000-8000-8000-000000000000', expectedValidate: true, expectedVersion: 8 }, 26 | { value: 'ffffffff-ffff-8fff-bfff-ffffffffffff', expectedValidate: true, expectedVersion: 8 }, 27 | { value: '00000000-0000-9000-8000-000000000000', expectedValidate: false }, 28 | { value: 'ffffffff-ffff-9fff-bfff-ffffffffffff', expectedValidate: false }, 29 | { value: '00000000-0000-a000-8000-000000000000', expectedValidate: false }, 30 | { value: 'ffffffff-ffff-afff-bfff-ffffffffffff', expectedValidate: false }, 31 | { value: '00000000-0000-b000-8000-000000000000', expectedValidate: false }, 32 | { value: 'ffffffff-ffff-bfff-bfff-ffffffffffff', expectedValidate: false }, 33 | { value: '00000000-0000-c000-8000-000000000000', expectedValidate: false }, 34 | { value: 'ffffffff-ffff-cfff-bfff-ffffffffffff', expectedValidate: false }, 35 | { value: '00000000-0000-d000-8000-000000000000', expectedValidate: false }, 36 | { value: 'ffffffff-ffff-dfff-bfff-ffffffffffff', expectedValidate: false }, 37 | { value: '00000000-0000-e000-8000-000000000000', expectedValidate: false }, 38 | { value: 'ffffffff-ffff-efff-bfff-ffffffffffff', expectedValidate: false }, 39 | 40 | // selection of normal, valid UUIDs 41 | { value: 'd9428888-122b-11e1-b85c-61cd3cbb3210', expectedValidate: true, expectedVersion: 1 }, 42 | { value: '000003e8-2363-21ef-b200-325096b39f47', expectedValidate: true, expectedVersion: 2 }, 43 | { value: 'a981a0c2-68b1-35dc-bcfc-296e52ab01ec', expectedValidate: true, expectedVersion: 3 }, 44 | { value: '109156be-c4fb-41ea-b1b4-efe1671c5836', expectedValidate: true, expectedVersion: 4 }, 45 | { value: '90123e1c-7512-523e-bb28-76fab9f2f73d', expectedValidate: true, expectedVersion: 5 }, 46 | { value: '1ef21d2f-1207-6660-8c4f-419efbd44d48', expectedValidate: true, expectedVersion: 6 }, 47 | { value: '017f22e2-79b0-7cc3-98c4-dc0c0c07398f', expectedValidate: true, expectedVersion: 7 }, 48 | { value: '0d8f23a0-697f-83ae-802e-48f3756dd581', expectedValidate: true, expectedVersion: 8 }, 49 | 50 | // all variant octet values 51 | { value: '00000000-0000-1000-0000-000000000000', expectedValidate: false }, 52 | { value: '00000000-0000-1000-1000-000000000000', expectedValidate: false }, 53 | { value: '00000000-0000-1000-2000-000000000000', expectedValidate: false }, 54 | { value: '00000000-0000-1000-3000-000000000000', expectedValidate: false }, 55 | { value: '00000000-0000-1000-4000-000000000000', expectedValidate: false }, 56 | { value: '00000000-0000-1000-5000-000000000000', expectedValidate: false }, 57 | { value: '00000000-0000-1000-6000-000000000000', expectedValidate: false }, 58 | { value: '00000000-0000-1000-7000-000000000000', expectedValidate: false }, 59 | { value: '00000000-0000-1000-8000-000000000000', expectedValidate: true, expectedVersion: 1 }, 60 | { value: '00000000-0000-1000-9000-000000000000', expectedValidate: true, expectedVersion: 1 }, 61 | { value: '00000000-0000-1000-a000-000000000000', expectedValidate: true, expectedVersion: 1 }, 62 | { value: '00000000-0000-1000-b000-000000000000', expectedValidate: true, expectedVersion: 1 }, 63 | { value: '00000000-0000-1000-c000-000000000000', expectedValidate: false }, 64 | { value: '00000000-0000-1000-d000-000000000000', expectedValidate: false }, 65 | { value: '00000000-0000-1000-e000-000000000000', expectedValidate: false }, 66 | { value: '00000000-0000-1000-f000-000000000000', expectedValidate: false }, 67 | 68 | // invalid strings 69 | { value: '00000000000000000000000000000000', expectedValidate: false }, // unhyphenated NIL 70 | { value: '', expectedValidate: false }, 71 | { value: 'invalid uuid string', expectedValidate: false }, 72 | { 73 | value: '=Y00a-f*vb*-c-d#-p00f\b-g0h-#i^-j*3&-L00k-\nl---00n-fg000-00p-00r+', 74 | expectedValidate: false, 75 | }, 76 | 77 | // invalid types 78 | { value: undefined, expectedValidate: false }, 79 | { value: null, expectedValidate: false }, 80 | { value: 123, expectedValidate: false }, 81 | { value: /regex/, expectedValidate: false }, 82 | { value: new Date(0), expectedValidate: false }, 83 | { value: false, expectedValidate: false }, 84 | ]; 85 | 86 | // Add NIL and MAX UUIDs with 1-bit flipped in each position 87 | for (let charIndex = 0; charIndex < 36; charIndex++) { 88 | // Skip hyphens and version char 89 | if ( 90 | charIndex === 8 || 91 | charIndex === 13 || 92 | charIndex === 14 || // version char 93 | charIndex === 18 || 94 | charIndex === 23 95 | ) { 96 | continue; 97 | } 98 | 99 | const nilChars = NIL.split(''); 100 | const maxChars = MAX.split(''); 101 | 102 | for (let i = 0; i < 4; i++) { 103 | nilChars[charIndex] = (0x0 ^ (1 << i)).toString(16); 104 | // NIL UUIDs w/ a single 1-bit 105 | TESTS.push({ value: nilChars.join(''), expectedValidate: false }); 106 | 107 | // MAX UUIDs w/ a single 0-bit 108 | maxChars[charIndex] = (0xf ^ (1 << i)).toString(16); 109 | TESTS.push({ value: maxChars.join(''), expectedValidate: false }); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/test/v1.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'node:assert/strict'; 2 | import { describe, test } from 'node:test'; 3 | import parse from '../parse.js'; 4 | import v1, { updateV1State } from '../v1.js'; 5 | 6 | // Verify ordering of v1 ids created with explicit times 7 | const TIME = 1321644961388; // 2011-11-18 11:36:01.388-08:00 8 | 9 | // Fixture values for testing with the rfc v1 UUID example: 10 | // https://www.rfc-editor.org/rfc/rfc9562.html#name-example-of-a-uuidv1-value 11 | const RFC_V1 = 'c232ab00-9414-11ec-b3c8-9f68deced846'; 12 | const RFC_V1_BYTES = parse(RFC_V1); 13 | 14 | // `options` for producing the above RFC UUID 15 | const RFC_OPTIONS = { 16 | msecs: 0x17f22e279b0, 17 | nsecs: 0, 18 | clockseq: 0x33c8, 19 | node: Uint8Array.of(0x9f, 0x68, 0xde, 0xce, 0xd8, 0x46), 20 | }; 21 | 22 | // random bytes for producing the above RFC UUID 23 | const RFC_RANDOM = Uint8Array.of( 24 | // unused 25 | 0, 26 | 0, 27 | 0, 28 | 0, 29 | 0, 30 | 0, 31 | 0, 32 | 0, 33 | 34 | // clock seq 35 | RFC_OPTIONS.clockseq >> 8, 36 | RFC_OPTIONS.clockseq & 0xff, 37 | 38 | // node 39 | ...RFC_OPTIONS.node 40 | ); 41 | 42 | // Compare v1 timestamp fields chronologically 43 | function compareV1TimeField(a: string, b: string) { 44 | a = a.split('-').slice(0, 3).reverse().join(''); 45 | b = b.split('-').slice(0, 3).reverse().join(''); 46 | return a < b ? -1 : a > b ? 1 : 0; 47 | } 48 | 49 | describe('v1', () => { 50 | test('v1 sort order (default)', () => { 51 | const ids = [v1(), v1(), v1(), v1(), v1()]; 52 | 53 | const sorted = [...ids].sort(compareV1TimeField); 54 | assert.deepEqual(ids, sorted); 55 | }); 56 | 57 | // Verify ordering of v1 ids created with explicit times 58 | test('v1 sort order (time option)', () => { 59 | const ids = [ 60 | v1({ msecs: TIME - 10 * 3600 * 1000 }), 61 | v1({ msecs: TIME - 1 }), 62 | v1({ msecs: TIME }), 63 | v1({ msecs: TIME + 1 }), 64 | v1({ msecs: TIME + 28 * 24 * 3600 * 1000 }), 65 | ]; 66 | 67 | const sorted = [...ids].sort(compareV1TimeField); 68 | assert.deepEqual(ids, sorted); 69 | }); 70 | 71 | test('v1(options)', () => { 72 | assert.equal(v1({ msecs: RFC_OPTIONS.msecs, random: RFC_RANDOM }), RFC_V1, 'minimal options'); 73 | assert.equal(v1(RFC_OPTIONS), RFC_V1, 'full options'); 74 | }); 75 | 76 | test('v1(options) equality', () => { 77 | assert.notEqual(v1({ msecs: TIME }), v1({ msecs: TIME }), 'UUIDs with minimal options differ'); 78 | assert.equal(v1(RFC_OPTIONS), v1(RFC_OPTIONS), 'UUIDs with full options are identical'); 79 | }); 80 | 81 | test('fills one UUID into a buffer as expected', () => { 82 | const buffer = new Uint8Array(16); 83 | const result = v1(RFC_OPTIONS, buffer); 84 | assert.deepEqual(buffer, RFC_V1_BYTES); 85 | assert.strictEqual(buffer, result); 86 | }); 87 | 88 | test('fills two UUIDs into a buffer as expected', () => { 89 | const buffer = new Uint8Array(32); 90 | v1(RFC_OPTIONS, buffer, 0); 91 | v1(RFC_OPTIONS, buffer, 16); 92 | 93 | const expectedBuf = new Uint8Array(32); 94 | expectedBuf.set(RFC_V1_BYTES); 95 | expectedBuf.set(RFC_V1_BYTES, 16); 96 | 97 | assert.deepEqual(buffer, expectedBuf); 98 | }); 99 | 100 | test('v1() state transitions', () => { 101 | // Test fixture for internal state passed into updateV1State function 102 | const PRE_STATE = { 103 | msecs: 10, 104 | nsecs: 20, 105 | clockseq: 0x1234, 106 | node: Uint8Array.of(0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc), 107 | }; 108 | 109 | // Note: The test code, below, passes RFC_RANDOM as the `rnds` argument for 110 | // convenience. This allows us to test that fields have been initialized from 111 | // the rnds argument by testing for RFC_OPTIONS values in the output state. 112 | 113 | const tests = [ 114 | { 115 | title: 'initial state', 116 | state: {}, 117 | now: 10, 118 | expected: { 119 | msecs: 10, // -> now 120 | nsecs: 0, // -> init 121 | clockseq: RFC_OPTIONS.clockseq, // -> random 122 | node: RFC_OPTIONS.node, // -> random 123 | }, 124 | }, 125 | { 126 | title: 'same time interval', 127 | state: { ...PRE_STATE }, 128 | now: PRE_STATE.msecs, 129 | expected: { 130 | ...PRE_STATE, 131 | nsecs: 21, // -> +1 132 | }, 133 | }, 134 | { 135 | title: 'new time interval', 136 | state: { ...PRE_STATE }, 137 | now: PRE_STATE.msecs + 1, 138 | expected: { 139 | ...PRE_STATE, 140 | msecs: PRE_STATE.msecs + 1, // -> +1 141 | nsecs: 0, // -> init 142 | }, 143 | }, 144 | { 145 | title: 'same time interval (nsecs overflow)', 146 | state: { ...PRE_STATE, nsecs: 9999 }, 147 | now: PRE_STATE.msecs, 148 | expected: { 149 | ...PRE_STATE, 150 | nsecs: 0, // -> init 151 | clockseq: RFC_OPTIONS.clockseq, // -> init 152 | node: RFC_OPTIONS.node, // -> init 153 | }, 154 | }, 155 | { 156 | title: 'time regression', 157 | state: { ...PRE_STATE }, 158 | now: PRE_STATE.msecs - 1, 159 | expected: { 160 | ...PRE_STATE, 161 | msecs: PRE_STATE.msecs - 1, // -> now 162 | clockseq: RFC_OPTIONS.clockseq, // -> init 163 | node: RFC_OPTIONS.node, // -> init 164 | }, 165 | }, 166 | ]; 167 | for (const { title, state, now, expected } of tests) { 168 | assert.deepStrictEqual(updateV1State(state, now, RFC_RANDOM), expected, `Failed: ${title}`); 169 | } 170 | }); 171 | 172 | test('throws when option.random is too short', () => { 173 | const random = Uint8Array.of(16); 174 | const buffer = new Uint8Array(16).fill(0); 175 | assert.throws(() => { 176 | v1({ random }, buffer); 177 | }); 178 | }); 179 | 180 | test('throws when options.rng() is too short', () => { 181 | const buffer = new Uint8Array(16); 182 | const rng = () => Uint8Array.of(0); // length = 1 183 | assert.throws(() => { 184 | v1({ rng }, buffer); 185 | }); 186 | }); 187 | 188 | test('throws RangeError for out-of-range indexes', () => { 189 | const buf15 = new Uint8Array(15); 190 | const buf30 = new Uint8Array(30); 191 | assert.throws(() => v1({}, buf15)); 192 | assert.throws(() => v1({}, buf30, -1)); 193 | assert.throws(() => v1({}, buf30, 15)); 194 | }); 195 | }); 196 | -------------------------------------------------------------------------------- /src/test/v35.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'node:assert/strict'; 2 | import { describe, test } from 'node:test'; 3 | import md5 from '../md5.js'; 4 | import sha1 from '../sha1.js'; 5 | import v3 from '../v3.js'; 6 | import { stringToBytes } from '../v35.js'; 7 | import v5 from '../v5.js'; 8 | 9 | describe('v35', () => { 10 | const HASH_SAMPLES = [ 11 | { 12 | input: stringToBytes(''), 13 | sha1: 'da39a3ee5e6b4b0d3255bfef95601890afd80709', 14 | md5: 'd41d8cd98f00b204e9800998ecf8427e', 15 | }, 16 | 17 | // Extended ascii chars 18 | { 19 | input: stringToBytes( 20 | '\t\b\f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u00A1\u00A2\u00A3\u00A4\u00A5\u00A6\u00A7\u00A8\u00A9\u00AA\u00AB\u00AC\u00AE\u00AF\u00B0\u00B1\u00B2\u00B3\u00B4\u00B5\u00B6\u00B7\u00B8\u00B9\u00BA\u00BB\u00BC\u00BD\u00BE\u00BF\u00C0\u00C1\u00C2\u00C3\u00C4\u00C5\u00C6\u00C7\u00C8\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE\u00CF\u00D0\u00D1\u00D2\u00D3\u00D4\u00D5\u00D6\u00D7\u00D8\u00D9\u00DA\u00DB\u00DC\u00DD\u00DE\u00DF\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5\u00E6\u00E7\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF\u00F0\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6\u00F7\u00F8\u00F9\u00FA\u00FB\u00FC\u00FD\u00FE\u00FF' 21 | ), 22 | sha1: 'ca4a426a3d536f14cfd79011e79e10d64de950a0', 23 | md5: 'e8098ec21950f841731d28749129d3ee', 24 | }, 25 | 26 | // A sampling from the Unicode BMP 27 | { 28 | input: stringToBytes( 29 | '\u00A5\u0104\u018F\u0256\u02B1o\u0315\u038E\u0409\u0500\u0531\u05E1\u05B6\u0920\u0903\u09A4\u0983\u0A20\u0A02\u0AA0\u0A83\u0B06\u0C05\u0C03\u1401\u16A0' 30 | ), 31 | sha1: 'f2753ebc390e5f637e333c2a4179644a93ae9f65', 32 | md5: '231b309e277b6be8bb3d6c688b7f098b', 33 | }, 34 | ]; 35 | 36 | function hashToHex(hash: Uint8Array) { 37 | const chars = new Array(hash.length); 38 | for (let i = 0; i < hash.length; i++) { 39 | chars[i] = hash[i].toString(16).padStart(2, '0'); 40 | } 41 | return chars.join(''); 42 | } 43 | 44 | HASH_SAMPLES.forEach(function (sample, i) { 45 | test(`sha1(node) HASH_SAMPLES[${i}]`, () => { 46 | assert.equal(hashToHex(sha1(sample.input)), sample.sha1); 47 | }); 48 | }); 49 | 50 | HASH_SAMPLES.forEach(function (sample, i) { 51 | test(`md5(node) HASH_SAMPLES[${i}]`, () => { 52 | assert.equal(hashToHex(md5(sample.input)), sample.md5); 53 | }); 54 | }); 55 | 56 | test('v3', () => { 57 | // Expect to get the same results as http://tools.adjet.org/uuid-v3 58 | assert.strictEqual(v3('hello.example.com', v3.DNS), '9125a8dc-52ee-365b-a5aa-81b0b3681cf6'); 59 | 60 | assert.strictEqual( 61 | v3('http://example.com/hello', v3.URL), 62 | 'c6235813-3ba4-3801-ae84-e0a6ebb7d138' 63 | ); 64 | 65 | assert.strictEqual( 66 | v3('hello', '0f5abcd1-c194-47f3-905b-2df7263a084b'), 67 | 'a981a0c2-68b1-35dc-bcfc-296e52ab01ec' 68 | ); 69 | }); 70 | 71 | test('v3 namespace.toUpperCase', () => { 72 | assert.strictEqual( 73 | v3('hello.example.com', v3.DNS.toUpperCase()), 74 | '9125a8dc-52ee-365b-a5aa-81b0b3681cf6' 75 | ); 76 | 77 | assert.strictEqual( 78 | v3('http://example.com/hello', v3.URL.toUpperCase()), 79 | 'c6235813-3ba4-3801-ae84-e0a6ebb7d138' 80 | ); 81 | 82 | assert.strictEqual( 83 | v3('hello', '0f5abcd1-c194-47f3-905b-2df7263a084b'.toUpperCase()), 84 | 'a981a0c2-68b1-35dc-bcfc-296e52ab01ec' 85 | ); 86 | }); 87 | 88 | test('v3 namespace string validation', () => { 89 | assert.throws(() => { 90 | v3('hello.example.com', 'zyxwvuts-rqpo-nmlk-jihg-fedcba000000'); 91 | }); 92 | 93 | assert.throws(() => { 94 | v3('hello.example.com', 'invalid uuid value'); 95 | }); 96 | 97 | assert.ok(v3('hello.example.com', '00000000-0000-0000-0000-000000000000')); 98 | }); 99 | 100 | test('v3 namespace buffer validation', () => { 101 | assert.throws(() => { 102 | v3('hello.example.com', new Uint8Array(15)); 103 | }); 104 | 105 | assert.throws(() => { 106 | v3('hello.example.com', new Uint8Array(17)); 107 | }); 108 | 109 | assert.ok(v3('hello.example.com', new Uint8Array(16).fill(0))); 110 | }); 111 | 112 | test('v3 fill buffer', () => { 113 | let buf = new Uint8Array(16); 114 | 115 | const expectedUuid = Uint8Array.of( 116 | 0x91, 117 | 0x25, 118 | 0xa8, 119 | 0xdc, 120 | 0x52, 121 | 0xee, 122 | 0x36, 123 | 0x5b, 124 | 0xa5, 125 | 0xaa, 126 | 0x81, 127 | 0xb0, 128 | 0xb3, 129 | 0x68, 130 | 0x1c, 131 | 0xf6 132 | ); 133 | 134 | const result = v3('hello.example.com', v3.DNS, buf); 135 | 136 | assert.deepEqual(buf, expectedUuid); 137 | assert.strictEqual(result, buf); 138 | 139 | // test offsets as well 140 | buf = new Uint8Array(19).fill(0xaa); 141 | 142 | const expectedBuf = new Uint8Array(19).fill(0xaa); 143 | expectedBuf.set(expectedUuid, 3); 144 | 145 | v3('hello.example.com', v3.DNS, buf, 3); 146 | 147 | assert.deepEqual(buf, expectedBuf); 148 | }); 149 | 150 | test('v3 undefined/null', () => { 151 | // @ts-expect-error testing invalid input 152 | assert.throws(() => v3()); 153 | // @ts-expect-error testing invalid input 154 | assert.throws(() => v3('hello')); 155 | // @ts-expect-error testing invalid input 156 | assert.throws(() => v3('hello.example.com', undefined)); 157 | // @ts-expect-error testing invalid input 158 | assert.throws(() => v3('hello.example.com', null, new Uint8Array(16))); 159 | }); 160 | 161 | test('v5', () => { 162 | // Expect to get the same results as http://tools.adjet.org/uuid-v5 163 | assert.strictEqual(v5('hello.example.com', v5.DNS), 'fdda765f-fc57-5604-a269-52a7df8164ec'); 164 | 165 | assert.strictEqual( 166 | v5('http://example.com/hello', v5.URL), 167 | '3bbcee75-cecc-5b56-8031-b6641c1ed1f1' 168 | ); 169 | 170 | assert.strictEqual( 171 | v5('hello', '0f5abcd1-c194-47f3-905b-2df7263a084b'), 172 | '90123e1c-7512-523e-bb28-76fab9f2f73d' 173 | ); 174 | }); 175 | 176 | test('v5 namespace.toUpperCase', () => { 177 | // Expect to get the same results as http://tools.adjet.org/uuid-v5 178 | assert.strictEqual( 179 | v5('hello.example.com', v5.DNS.toUpperCase()), 180 | 'fdda765f-fc57-5604-a269-52a7df8164ec' 181 | ); 182 | 183 | assert.strictEqual( 184 | v5('http://example.com/hello', v5.URL.toUpperCase()), 185 | '3bbcee75-cecc-5b56-8031-b6641c1ed1f1' 186 | ); 187 | 188 | assert.strictEqual( 189 | v5('hello', '0f5abcd1-c194-47f3-905b-2df7263a084b'.toUpperCase()), 190 | '90123e1c-7512-523e-bb28-76fab9f2f73d' 191 | ); 192 | }); 193 | 194 | test('v5 namespace string validation', () => { 195 | assert.throws(() => { 196 | v5('hello.example.com', 'zyxwvuts-rqpo-nmlk-jihg-fedcba000000'); 197 | }); 198 | 199 | assert.throws(() => { 200 | v5('hello.example.com', 'invalid uuid value'); 201 | }); 202 | 203 | assert.ok(v5('hello.example.com', '00000000-0000-0000-0000-000000000000')); 204 | }); 205 | 206 | test('v5 namespace buffer validation', () => { 207 | assert.throws(() => { 208 | v5('hello.example.com', new Uint8Array(15)); 209 | }); 210 | 211 | assert.throws(() => { 212 | v5('hello.example.com', new Uint8Array(17)); 213 | }); 214 | 215 | assert.ok(v5('hello.example.com', new Uint8Array(16).fill(0))); 216 | }); 217 | 218 | test('v5 fill buffer', () => { 219 | let buf = new Uint8Array(16); 220 | 221 | const expectedUuid = Uint8Array.of( 222 | 0xfd, 223 | 0xda, 224 | 0x76, 225 | 0x5f, 226 | 0xfc, 227 | 0x57, 228 | 0x56, 229 | 0x04, 230 | 0xa2, 231 | 0x69, 232 | 0x52, 233 | 0xa7, 234 | 0xdf, 235 | 0x81, 236 | 0x64, 237 | 0xec 238 | ); 239 | 240 | const result = v5('hello.example.com', v5.DNS, buf); 241 | assert.deepEqual(buf, expectedUuid); 242 | assert.strictEqual(result, buf); 243 | 244 | // test offsets as well 245 | buf = new Uint8Array(19).fill(0xaa); 246 | 247 | const expectedBuf = new Uint8Array(19).fill(0xaa); 248 | expectedBuf.set(expectedUuid, 3); 249 | 250 | v5('hello.example.com', v5.DNS, buf, 3); 251 | 252 | assert.deepEqual(buf, expectedBuf); 253 | }); 254 | 255 | test('v5 undefined/null', () => { 256 | // @ts-expect-error testing invalid input 257 | assert.throws(() => v5()); 258 | // @ts-expect-error testing invalid input 259 | assert.throws(() => v5('hello')); 260 | // @ts-expect-error testing invalid input 261 | assert.throws(() => v5('hello.example.com', undefined)); 262 | // @ts-expect-error testing invalid input 263 | assert.throws(() => v5('hello.example.com', null, new Uint8Array(16))); 264 | }); 265 | 266 | test('v3/v5 constants', () => { 267 | assert.strictEqual(v3.DNS, '6ba7b810-9dad-11d1-80b4-00c04fd430c8'); 268 | assert.strictEqual(v3.URL, '6ba7b811-9dad-11d1-80b4-00c04fd430c8'); 269 | assert.strictEqual(v5.DNS, '6ba7b810-9dad-11d1-80b4-00c04fd430c8'); 270 | assert.strictEqual(v5.URL, '6ba7b811-9dad-11d1-80b4-00c04fd430c8'); 271 | }); 272 | }); 273 | -------------------------------------------------------------------------------- /src/test/v4.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'node:assert/strict'; 2 | import { describe, test } from 'node:test'; 3 | import native from '../native.js'; 4 | import v4 from '../v4.js'; 5 | 6 | const randomBytesFixture = Uint8Array.of( 7 | 0x10, 8 | 0x91, 9 | 0x56, 10 | 0xbe, 11 | 0xc4, 12 | 0xfb, 13 | 0xc1, 14 | 0xea, 15 | 0x71, 16 | 0xb4, 17 | 0xef, 18 | 0xe1, 19 | 0x67, 20 | 0x1c, 21 | 0x58, 22 | 0x36 23 | ); 24 | 25 | const expectedBytes = Uint8Array.of( 26 | 16, 27 | 145, 28 | 86, 29 | 190, 30 | 196, 31 | 251, 32 | 65, 33 | 234, 34 | 177, 35 | 180, 36 | 239, 37 | 225, 38 | 103, 39 | 28, 40 | 88, 41 | 54 42 | ); 43 | 44 | describe('v4', () => { 45 | test('subsequent UUIDs are different', () => { 46 | const id1 = v4(); 47 | const id2 = v4(); 48 | 49 | assert.ok(id1 !== id2); 50 | }); 51 | 52 | test('should uses native randomUUID() if no option is passed', async () => { 53 | // TODO: `mock` is not supported until node@18, so we feature-detect it 54 | // here. Once node@16 drops off our support matrix, we can just 55 | // static-import it normally 56 | const mock = (await import('node:test')).default.mock; 57 | if (!mock) { 58 | return; 59 | } 60 | 61 | const mockRandomUUID = mock.method(native, 'randomUUID'); 62 | 63 | assert.equal(mockRandomUUID.mock.callCount(), 0); 64 | v4(); 65 | assert.equal(mockRandomUUID.mock.callCount(), 1); 66 | 67 | mock.restoreAll(); 68 | }); 69 | 70 | test('should not use native randomUUID() if an option is passed', async () => { 71 | // TODO: `mock` is not supported until node@18, so we feature-detect it 72 | // here. Once node@16 drops off our support matrix, we can just 73 | // static-import it normally 74 | const mock = (await import('node:test')).default.mock; 75 | if (!mock) { 76 | return; 77 | } 78 | 79 | const mockRandomUUID = mock.method(native, 'randomUUID'); 80 | 81 | assert.equal(mockRandomUUID.mock.callCount(), 0); 82 | v4({}); 83 | assert.equal(mockRandomUUID.mock.callCount(), 0); 84 | 85 | mock.restoreAll(); 86 | }); 87 | 88 | test('explicit options.random produces expected result', () => { 89 | const id = v4({ random: randomBytesFixture }); 90 | assert.strictEqual(id, '109156be-c4fb-41ea-b1b4-efe1671c5836'); 91 | }); 92 | 93 | test('explicit options.rng produces expected result', () => { 94 | const id = v4({ rng: () => randomBytesFixture }); 95 | assert.strictEqual(id, '109156be-c4fb-41ea-b1b4-efe1671c5836'); 96 | }); 97 | 98 | test('fills one UUID into a buffer as expected', () => { 99 | const buffer = new Uint8Array(16); 100 | const result = v4({ random: randomBytesFixture }, buffer); 101 | 102 | assert.deepEqual(buffer, expectedBytes); 103 | assert.strictEqual(buffer, result); 104 | }); 105 | 106 | test('fills two UUIDs into a buffer as expected', () => { 107 | const buffer = new Uint8Array(32); 108 | v4({ random: randomBytesFixture }, buffer, 0); 109 | v4({ random: randomBytesFixture }, buffer, 16); 110 | 111 | const expectedBuf = new Uint8Array(32); 112 | expectedBuf.set(expectedBytes); 113 | expectedBuf.set(expectedBytes, 16); 114 | 115 | assert.deepEqual(buffer, expectedBuf); 116 | }); 117 | 118 | test('throws when option.random is too short', () => { 119 | const random = Uint8Array.of(16); 120 | const buffer = new Uint8Array(16).fill(0); 121 | assert.throws(() => { 122 | v4({ random }, buffer); 123 | }); 124 | }); 125 | 126 | test('throws when options.rng() is too short', () => { 127 | const buffer = new Uint8Array(16); 128 | const rng = () => Uint8Array.of(0); // length = 1 129 | assert.throws(() => { 130 | v4({ rng }, buffer); 131 | }); 132 | }); 133 | 134 | test('throws RangeError for out-of-range indexes', () => { 135 | const buf15 = new Uint8Array(15); 136 | const buf30 = new Uint8Array(30); 137 | assert.throws(() => v4({}, buf15)); 138 | assert.throws(() => v4({}, buf30, -1)); 139 | assert.throws(() => v4({}, buf30, 15)); 140 | }); 141 | }); 142 | -------------------------------------------------------------------------------- /src/test/v6.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'node:assert/strict'; 2 | import { describe, test } from 'node:test'; 3 | import v1ToV6 from '../v1ToV6.js'; 4 | import v6 from '../v6.js'; 5 | import v6ToV1 from '../v6ToV1.js'; 6 | 7 | describe('v6', () => { 8 | const V1_ID = 'f1207660-21d2-11ef-8c4f-419efbd44d48'; 9 | const V6_ID = '1ef21d2f-1207-6660-8c4f-419efbd44d48'; 10 | 11 | const fullOptions = { 12 | msecs: 0x133b891f705, 13 | nsecs: 0x1538, 14 | clockseq: 0x385c, 15 | node: Uint8Array.of(0x61, 0xcd, 0x3c, 0xbb, 0x32, 0x10), 16 | }; 17 | 18 | const EXPECTED_BYTES = Uint8Array.of( 19 | 0x1e, 20 | 0x11, 21 | 0x22, 22 | 0xbd, 23 | 0x94, 24 | 0x28, 25 | 0x68, 26 | 0x88, 27 | 0xb8, 28 | 0x5c, 29 | 0x61, 30 | 0xcd, 31 | 0x3c, 32 | 0xbb, 33 | 0x32, 34 | 0x10 35 | ); 36 | 37 | test('default behavior', () => { 38 | // Verify explicit options produce expected id 39 | const id = v6(); 40 | assert.ok( 41 | /[0-9a-f]{8}-[0-9a-f]{4}-6[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/.test(id), 42 | 43 | 'id is valid v6 UUID' 44 | ); 45 | }); 46 | 47 | test('default behavior (binary type)', () => { 48 | const buffer = new Uint8Array(16); 49 | const result = v6(fullOptions, buffer); 50 | assert.deepEqual(buffer, EXPECTED_BYTES); 51 | assert.strictEqual(buffer, result); 52 | }); 53 | 54 | test('all options', () => { 55 | // Verify explicit options produce expected id 56 | const id = v6(fullOptions); 57 | assert.equal(id, '1e1122bd-9428-6888-b85c-61cd3cbb3210'); 58 | }); 59 | 60 | test('sort by creation time', () => { 61 | // Verify ids sort by creation time 62 | const ids = []; 63 | for (let i = 0; i < 5; i++) { 64 | ids.push(v6({ msecs: i * 1000 })); 65 | } 66 | assert.deepEqual(ids, ids.slice().sort()); 67 | }); 68 | 69 | test('creating at array offset', () => { 70 | const buffer = new Uint8Array(32); 71 | v6(fullOptions, buffer, 0); 72 | v6(fullOptions, buffer, 16); 73 | 74 | const expectedBuf = new Uint8Array(32); 75 | expectedBuf.set(EXPECTED_BYTES, 0); 76 | expectedBuf.set(EXPECTED_BYTES, 16); 77 | 78 | assert.deepEqual(buffer, expectedBuf); 79 | }); 80 | 81 | test('v1 -> v6 conversion', () => { 82 | const id = v1ToV6(V1_ID); 83 | assert.equal(id, V6_ID); 84 | }); 85 | 86 | test('v6 -> v1 conversion', () => { 87 | const id = v6ToV1(V6_ID); 88 | assert.equal(id, V1_ID); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /src/test/v7.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'node:assert/strict'; 2 | import { describe, test } from 'node:test'; 3 | import parse from '../parse.js'; 4 | import stringify from '../stringify.js'; 5 | import { Version7Options } from '../types.js'; 6 | import v7, { updateV7State } from '../v7.js'; 7 | 8 | // Fixture values for testing with the rfc v7 UUID example: 9 | // https://www.rfc-editor.org/rfc/rfc9562.html#name-example-of-a-uuidv7-value 10 | const RFC_V7 = '017f22e2-79b0-7cc3-98c4-dc0c0c07398f'; 11 | const RFC_V7_BYTES = parse('017f22e2-79b0-7cc3-98c4-dc0c0c07398f'); 12 | const RFC_MSECS = 0x17f22e279b0; 13 | 14 | // `option.seq` for the above RFC uuid 15 | const RFC_SEQ = (0x0cc3 << 20) | (0x98c4dc >> 2); 16 | 17 | // `option,random` for the above RFC uuid 18 | const RFC_RANDOM = Uint8Array.of( 19 | 0x10, 20 | 0x91, 21 | 0x56, 22 | 0xbe, 23 | 0xc4, 24 | 0xfb, 25 | 0x0c, 26 | 0xc3, 27 | 0x18, 28 | 0xc4, 29 | 0x6c, 30 | 0x0c, 31 | 0x0c, 32 | 0x07, 33 | 0x39, 34 | 0x8f 35 | ); 36 | 37 | describe('v7', () => { 38 | test('subsequent UUIDs are different', () => { 39 | const id1 = v7(); 40 | const id2 = v7(); 41 | assert.ok(id1 !== id2); 42 | }); 43 | 44 | test('explicit options.random and options.msecs produces expected result', () => { 45 | const id = v7({ 46 | random: RFC_RANDOM, 47 | msecs: RFC_MSECS, 48 | seq: RFC_SEQ, 49 | }); 50 | assert.strictEqual(id, RFC_V7); 51 | }); 52 | 53 | test('explicit options.rng produces expected result', () => { 54 | const id = v7({ 55 | rng: () => RFC_RANDOM, 56 | msecs: RFC_MSECS, 57 | seq: RFC_SEQ, 58 | }); 59 | assert.strictEqual(id, RFC_V7); 60 | }); 61 | 62 | test('explicit options.msecs produces expected result', () => { 63 | const id = v7({ 64 | msecs: RFC_MSECS, 65 | }); 66 | assert.strictEqual(id.indexOf('017f22e2'), 0); 67 | }); 68 | 69 | test('fills one UUID into a buffer as expected', () => { 70 | const buffer = new Uint8Array(16); 71 | const result = v7( 72 | { 73 | random: RFC_RANDOM, 74 | msecs: RFC_MSECS, 75 | seq: RFC_SEQ, 76 | }, 77 | buffer 78 | ); 79 | stringify(buffer); 80 | 81 | assert.deepEqual(buffer, RFC_V7_BYTES); 82 | assert.strictEqual(buffer, result); 83 | }); 84 | 85 | test('fills two UUIDs into a buffer as expected', () => { 86 | const buffer = new Uint8Array(32); 87 | 88 | v7( 89 | { 90 | random: RFC_RANDOM, 91 | msecs: RFC_MSECS, 92 | seq: RFC_SEQ, 93 | }, 94 | buffer, 95 | 0 96 | ); 97 | v7( 98 | { 99 | random: RFC_RANDOM, 100 | msecs: RFC_MSECS, 101 | seq: RFC_SEQ, 102 | }, 103 | buffer, 104 | 16 105 | ); 106 | const expected = new Uint8Array(32); 107 | expected.set(RFC_V7_BYTES); 108 | expected.set(RFC_V7_BYTES, 16); 109 | assert.deepEqual(buffer, expected); 110 | }); 111 | 112 | // 113 | // monotonic and lexicographical sorting tests 114 | // 115 | 116 | test('lexicographical sorting is preserved', () => { 117 | let id; 118 | let prior; 119 | let msecs = RFC_MSECS; 120 | for (let i = 0; i < 20000; ++i) { 121 | if (i % 1500 === 0) { 122 | // every 1500 runs increment msecs so seq is 123 | // reinitialized, simulating passage of time 124 | msecs += 1; 125 | } 126 | 127 | id = v7({ msecs, seq: i }); 128 | 129 | if (prior !== undefined) { 130 | assert.ok(prior < id, `${prior} < ${id}`); 131 | } 132 | 133 | prior = id; 134 | } 135 | }); 136 | 137 | test('can supply seq', () => { 138 | let seq = 0x12345; 139 | let uuid = v7({ 140 | msecs: RFC_MSECS, 141 | seq, 142 | }); 143 | 144 | assert.strictEqual(uuid.substr(0, 25), '017f22e2-79b0-7000-848d-1'); 145 | 146 | seq = 0x6fffffff; 147 | uuid = v7({ 148 | msecs: RFC_MSECS, 149 | seq, 150 | }); 151 | 152 | assert.strictEqual(uuid.substring(0, 25), '017f22e2-79b0-76ff-bfff-f'); 153 | }); 154 | 155 | test('internal seq is reset upon timestamp change', () => { 156 | v7({ 157 | msecs: RFC_MSECS, 158 | seq: 0x6fffffff, 159 | }); 160 | 161 | const uuid = v7({ 162 | msecs: RFC_MSECS + 1, 163 | }); 164 | 165 | assert.ok(uuid.indexOf('fff') !== 15); 166 | }); 167 | 168 | test('v7() state transitions', () => { 169 | const tests = [ 170 | { 171 | title: 'new time interval', 172 | state: { msecs: 1, seq: 123 }, 173 | now: 2, 174 | expected: { 175 | msecs: 2, // time interval should update 176 | seq: 0x6c318c4, // sequence should be randomized 177 | }, 178 | }, 179 | { 180 | title: 'same time interval', 181 | state: { msecs: 1, seq: 123 }, 182 | now: 1, 183 | expected: { 184 | msecs: 1, // timestamp unchanged 185 | seq: 124, // sequence increments 186 | }, 187 | }, 188 | { 189 | title: 'same time interval (sequence rollover)', 190 | state: { msecs: 1, seq: 0xffffffff }, 191 | now: 1, 192 | expected: { 193 | msecs: 2, // timestamp increments 194 | seq: 0, // sequence rolls over 195 | }, 196 | }, 197 | { 198 | title: 'time regression', 199 | state: { msecs: 2, seq: 123 }, 200 | now: 1, 201 | expected: { 202 | msecs: 2, // timestamp unchanged 203 | seq: 124, // sequence increments 204 | }, 205 | }, 206 | { 207 | title: 'time regression (sequence rollover)', 208 | state: { msecs: 2, seq: 0xffffffff }, 209 | now: 1, 210 | expected: { 211 | // timestamp increments (crazy, right? The system clock goes backwards 212 | // but the UUID timestamp moves forward? Weird, but it's what's 213 | // required to maintain monotonicity... and this is why we have unit 214 | // tests!) 215 | msecs: 3, 216 | seq: 0, // sequence rolls over 217 | }, 218 | }, 219 | ]; 220 | for (const { title, state, now, expected } of tests) { 221 | assert.deepStrictEqual(updateV7State(state, now, RFC_RANDOM), expected, `Failed: ${title}`); 222 | } 223 | }); 224 | 225 | test('flipping bits changes the result', () => { 226 | // convert uint8array to BigInt (BE) 227 | const asBigInt = (buf: Uint8Array) => buf.reduce((acc, v) => (acc << 8n) | BigInt(v), 0n); 228 | 229 | // convert the given number of bits (LE) to number 230 | const asNumber = (bits: number, data: bigint) => Number(BigInt.asUintN(bits, data)); 231 | 232 | // flip the nth bit (BE) in a BigInt 233 | const flip = (data: bigint, n: number) => data ^ (1n << BigInt(127 - n)); 234 | 235 | // Extract v7 `options` from a (BigInt) UUID 236 | const optionsFrom = (data: bigint): Version7Options => { 237 | const ms = asNumber(48, data >> 80n); 238 | const hi = asNumber(12, data >> 64n); 239 | const lo = asNumber(20, data >> 42n); 240 | const r = BigInt.asUintN(42, data); 241 | return { 242 | msecs: ms, 243 | seq: (hi << 20) | lo, 244 | random: Uint8Array.from([ 245 | ...Array(10).fill(0), 246 | ...Array(6) 247 | .fill(0) 248 | .map((_, i) => asNumber(8, r >> (BigInt(i) * 8n))) 249 | .reverse(), 250 | ]), 251 | }; 252 | }; 253 | const buf = new Uint8Array(16); 254 | const data = asBigInt(v7({}, buf)); 255 | const id = stringify(buf); 256 | const reserved = [48, 49, 50, 51, 64, 65]; 257 | for (let i = 0; i < 128; ++i) { 258 | if (reserved.includes(i)) { 259 | continue; // skip bits used for version and variant 260 | } 261 | const flipped = flip(data, i); 262 | assert.strictEqual( 263 | asBigInt(v7(optionsFrom(flipped), buf)).toString(16), 264 | flipped.toString(16), 265 | `Unequal uuids at bit ${i}` 266 | ); 267 | assert.notStrictEqual(stringify(buf), id); 268 | } 269 | }); 270 | 271 | test('throws when option.random is too short', () => { 272 | const random = Uint8Array.of(16); 273 | const buffer = new Uint8Array(16).fill(0); 274 | assert.throws(() => { 275 | v7({ random }, buffer); 276 | }); 277 | }); 278 | 279 | test('throws when options.rng() is too short', () => { 280 | const buffer = new Uint8Array(16); 281 | const rng = () => Uint8Array.of(0); // length = 1 282 | assert.throws(() => { 283 | v7({ rng }, buffer); 284 | }); 285 | }); 286 | 287 | test('throws RangeError for out-of-range indexes', () => { 288 | const buf15 = new Uint8Array(15); 289 | const buf30 = new Uint8Array(30); 290 | assert.throws(() => v7({}, buf15)); 291 | assert.throws(() => v7({}, buf30, -1)); 292 | assert.throws(() => v7({}, buf30, 15)); 293 | }); 294 | }); 295 | -------------------------------------------------------------------------------- /src/test/validate.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'node:assert/strict'; 2 | import { describe, test } from 'node:test'; 3 | import validate from '../validate.js'; 4 | import { TESTS } from './test_constants.js'; 5 | 6 | describe('validate()', () => { 7 | test('TESTS cases', () => { 8 | for (const { value, expectedValidate } of TESTS) { 9 | assert.strictEqual( 10 | validate(value), 11 | expectedValidate, 12 | `validate(${value}) should be ${expectedValidate}` 13 | ); 14 | } 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/test/version.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'node:assert/strict'; 2 | import { describe, test } from 'node:test'; 3 | import version from '../version.js'; 4 | import { TESTS } from './test_constants.js'; 5 | 6 | describe('version()', () => { 7 | test('TESTS cases', () => { 8 | for (const { value, expectedValidate, expectedVersion } of TESTS) { 9 | try { 10 | // @ts-expect-error testing invalid input 11 | const actualVersion = version(value); 12 | 13 | assert.ok(expectedValidate, `version(${value}) should throw`); 14 | assert.strictEqual(actualVersion, expectedVersion); 15 | } catch { 16 | assert.ok(!expectedValidate, `version(${value}) threw unexpectedly`); 17 | } 18 | } 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export type UUIDTypes = string | TBuf; 2 | 3 | export type Version1Options = { 4 | node?: Uint8Array; 5 | clockseq?: number; 6 | random?: Uint8Array; 7 | rng?: () => Uint8Array; 8 | msecs?: number; 9 | nsecs?: number; 10 | _v6?: boolean; // Internal use only! 11 | }; 12 | 13 | export type Version4Options = { 14 | random?: Uint8Array; 15 | rng?: () => Uint8Array; 16 | }; 17 | 18 | export type Version6Options = Version1Options; 19 | 20 | export type Version7Options = { 21 | random?: Uint8Array; 22 | msecs?: number; 23 | seq?: number; 24 | rng?: () => Uint8Array; 25 | }; 26 | -------------------------------------------------------------------------------- /src/uuid-bin.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'node:assert/strict'; 2 | 3 | import v1 from './v1.js'; 4 | import v3 from './v3.js'; 5 | import v4 from './v4.js'; 6 | import v5 from './v5.js'; 7 | import v6 from './v6.js'; 8 | import v7 from './v7.js'; 9 | 10 | function usage() { 11 | console.log('Usage:'); 12 | console.log(' uuid'); 13 | console.log(' uuid v1'); 14 | console.log(' uuid v3 '); 15 | console.log(' uuid v4'); 16 | console.log(' uuid v5 '); 17 | console.log(' uuid v6'); 18 | console.log(' uuid v7'); 19 | console.log(' uuid --help'); 20 | console.log( 21 | '\nNote: may be "URL" or "DNS" to use the corresponding UUIDs defined by RFC9562' 22 | ); 23 | } 24 | 25 | const args = process.argv.slice(2); 26 | 27 | if (args.indexOf('--help') >= 0) { 28 | usage(); 29 | process.exit(0); 30 | } 31 | 32 | const version = args.shift() || 'v4'; 33 | 34 | switch (version) { 35 | case 'v1': 36 | console.log(v1()); 37 | break; 38 | 39 | case 'v3': { 40 | const name = args.shift(); 41 | let namespace = args.shift(); 42 | 43 | assert.ok(name != null, 'v3 name not specified'); 44 | assert.ok(namespace != null, 'v3 namespace not specified'); 45 | 46 | if (namespace === 'URL') { 47 | namespace = v3.URL; 48 | } 49 | 50 | if (namespace === 'DNS') { 51 | namespace = v3.DNS; 52 | } 53 | 54 | console.log(v3(name, namespace)); 55 | break; 56 | } 57 | 58 | case 'v4': 59 | console.log(v4()); 60 | break; 61 | 62 | case 'v5': { 63 | const name = args.shift(); 64 | let namespace = args.shift(); 65 | 66 | assert.ok(name != null, 'v5 name not specified'); 67 | assert.ok(namespace != null, 'v5 namespace not specified'); 68 | 69 | if (namespace === 'URL') { 70 | namespace = v5.URL; 71 | } 72 | 73 | if (namespace === 'DNS') { 74 | namespace = v5.DNS; 75 | } 76 | 77 | console.log(v5(name, namespace)); 78 | break; 79 | } 80 | 81 | case 'v6': 82 | console.log(v6()); 83 | break; 84 | 85 | case 'v7': 86 | console.log(v7()); 87 | break; 88 | 89 | default: 90 | usage(); 91 | process.exit(1); 92 | } 93 | -------------------------------------------------------------------------------- /src/v1.ts: -------------------------------------------------------------------------------- 1 | import rng from './rng.js'; 2 | import { unsafeStringify } from './stringify.js'; 3 | import { UUIDTypes, Version1Options } from './types.js'; 4 | 5 | // **`v1()` - Generate time-based UUID** 6 | // 7 | // Inspired by https://github.com/LiosK/UUID.js 8 | // and http://docs.python.org/library/uuid.html 9 | 10 | type V1State = { 11 | node?: Uint8Array; // node id (47-bit random) 12 | clockseq?: number; // sequence number (14-bit) 13 | 14 | // v1 & v6 timestamps are a pain to deal with. They specify time from the 15 | // Gregorian epoch in 100ns intervals, which requires values with 57+ bits of 16 | // precision. But that's outside the precision of IEEE754 floats (i.e. JS 17 | // numbers). To work around this, we represent them internally using 'msecs' 18 | // (milliseconds since unix epoch) and 'nsecs' (100-nanoseconds offset from 19 | // `msecs`). 20 | 21 | msecs?: number; // timestamp (milliseconds, unix epoch) 22 | nsecs?: number; // timestamp (100-nanoseconds offset from 'msecs') 23 | }; 24 | 25 | const _state: V1State = {}; 26 | 27 | function v1(options?: Version1Options, buf?: undefined, offset?: number): string; 28 | function v1( 29 | options: Version1Options | undefined, 30 | buf: Buf, 31 | offset?: number 32 | ): Buf; 33 | function v1( 34 | options?: Version1Options, 35 | buf?: TBuf, 36 | offset?: number 37 | ): UUIDTypes { 38 | let bytes: Uint8Array; 39 | 40 | // Extract _v6 flag from options, clearing options if appropriate 41 | const isV6 = options?._v6 ?? false; 42 | if (options) { 43 | const optionsKeys = Object.keys(options); 44 | if (optionsKeys.length === 1 && optionsKeys[0] === '_v6') { 45 | options = undefined; 46 | } 47 | } 48 | 49 | if (options) { 50 | // With options: Make UUID independent of internal state 51 | bytes = v1Bytes( 52 | options.random ?? options.rng?.() ?? rng(), 53 | options.msecs, 54 | options.nsecs, 55 | options.clockseq, 56 | options.node, 57 | buf, 58 | offset 59 | ); 60 | } else { 61 | // Without options: Make UUID from internal state 62 | const now = Date.now(); 63 | const rnds = rng(); 64 | 65 | updateV1State(_state, now, rnds); 66 | 67 | // Geenerate UUID. Note that v6 uses random values for `clockseq` and 68 | // `node`. 69 | // 70 | // https://www.rfc-editor.org/rfc/rfc9562.html#section-5.6-4 71 | bytes = v1Bytes( 72 | rnds, 73 | _state.msecs, 74 | _state.nsecs, 75 | // v6 UUIDs get random `clockseq` and `node` for every UUID 76 | // https://www.rfc-editor.org/rfc/rfc9562.html#section-5.6-4 77 | isV6 ? undefined : _state.clockseq, 78 | isV6 ? undefined : _state.node, 79 | buf, 80 | offset 81 | ); 82 | } 83 | 84 | return buf ?? unsafeStringify(bytes); 85 | } 86 | 87 | // (Private!) Do not use. This method is only exported for testing purposes 88 | // and may change without notice. 89 | export function updateV1State(state: V1State, now: number, rnds: Uint8Array) { 90 | state.msecs ??= -Infinity; 91 | state.nsecs ??= 0; 92 | 93 | // Update timestamp 94 | if (now === state.msecs) { 95 | // Same msec-interval = simulate higher clock resolution by bumping `nsecs` 96 | // https://www.rfc-editor.org/rfc/rfc9562.html#section-6.1-2.6 97 | state.nsecs++; 98 | 99 | // Check for `nsecs` overflow (nsecs is capped at 10K intervals / msec) 100 | if (state.nsecs >= 10000) { 101 | // Prior to uuid@11 this would throw an error, however the RFCs allow for 102 | // changing the node in this case. This slightly breaks monotonicity at 103 | // msec granularity, but that's not a significant concern. 104 | // https://www.rfc-editor.org/rfc/rfc9562.html#section-6.1-2.16 105 | state.node = undefined; 106 | state.nsecs = 0; 107 | } 108 | } else if (now > state.msecs) { 109 | // Reset nsec counter when clock advances to a new msec interval 110 | state.nsecs = 0; 111 | } else if (now < state.msecs) { 112 | // Handle clock regression 113 | // https://www.rfc-editor.org/rfc/rfc9562.html#section-6.1-2.7 114 | // 115 | // Note: Unsetting node here causes both it and clockseq to be randomized, 116 | // below. 117 | state.node = undefined; 118 | } 119 | 120 | // Init node and clock sequence (do this after timestamp update which may 121 | // reset the node) https://www.rfc-editor.org/rfc/rfc9562.html#section-5.1-7 122 | // 123 | // Note: 124 | if (!state.node) { 125 | state.node = rnds.slice(10, 16); 126 | 127 | // Set multicast bit 128 | // https://www.rfc-editor.org/rfc/rfc9562.html#section-6.10-3 129 | state.node[0] |= 0x01; // Set multicast bit 130 | 131 | // Clock sequence must be randomized 132 | // https://www.rfc-editor.org/rfc/rfc9562.html#section-5.1-8 133 | state.clockseq = ((rnds[8] << 8) | rnds[9]) & 0x3fff; 134 | } 135 | 136 | state.msecs = now; 137 | 138 | return state; 139 | } 140 | 141 | function v1Bytes( 142 | rnds: Uint8Array, 143 | msecs?: number, 144 | nsecs?: number, 145 | clockseq?: number, 146 | node?: Uint8Array, 147 | buf?: Uint8Array, 148 | offset = 0 149 | ) { 150 | if (rnds.length < 16) { 151 | throw new Error('Random bytes length must be >= 16'); 152 | } 153 | 154 | // Defaults 155 | if (!buf) { 156 | buf = new Uint8Array(16); 157 | offset = 0; 158 | } else { 159 | if (offset < 0 || offset + 16 > buf.length) { 160 | throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`); 161 | } 162 | } 163 | 164 | msecs ??= Date.now(); 165 | nsecs ??= 0; 166 | clockseq ??= ((rnds[8] << 8) | rnds[9]) & 0x3fff; 167 | node ??= rnds.slice(10, 16); 168 | 169 | // Offset to Gregorian epoch 170 | // https://www.rfc-editor.org/rfc/rfc9562.html#section-5.1-1 171 | msecs += 12219292800000; 172 | 173 | // `time_low` 174 | const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000; 175 | buf[offset++] = (tl >>> 24) & 0xff; 176 | buf[offset++] = (tl >>> 16) & 0xff; 177 | buf[offset++] = (tl >>> 8) & 0xff; 178 | buf[offset++] = tl & 0xff; 179 | 180 | // `time_mid` 181 | const tmh = ((msecs / 0x100000000) * 10000) & 0xfffffff; 182 | buf[offset++] = (tmh >>> 8) & 0xff; 183 | buf[offset++] = tmh & 0xff; 184 | 185 | // `time_high_and_version` 186 | buf[offset++] = ((tmh >>> 24) & 0xf) | 0x10; // include version 187 | buf[offset++] = (tmh >>> 16) & 0xff; 188 | 189 | // `clock_seq_hi_and_reserved` | variant 190 | buf[offset++] = (clockseq >>> 8) | 0x80; 191 | 192 | // `clock_seq_low` 193 | buf[offset++] = clockseq & 0xff; 194 | 195 | // `node` 196 | for (let n = 0; n < 6; ++n) { 197 | buf[offset++] = node[n]; 198 | } 199 | 200 | return buf; 201 | } 202 | 203 | export default v1; 204 | -------------------------------------------------------------------------------- /src/v1ToV6.ts: -------------------------------------------------------------------------------- 1 | import { UUIDTypes } from './types.js'; 2 | import parse from './parse.js'; 3 | import { unsafeStringify } from './stringify.js'; 4 | 5 | /** 6 | * Convert a v1 UUID to a v6 UUID 7 | * 8 | * @param {string|Uint8Array} uuid - The v1 UUID to convert to v6 9 | * @returns {string|Uint8Array} The v6 UUID as the same type as the `uuid` arg 10 | * (string or Uint8Array) 11 | */ 12 | export default function v1ToV6(uuid: string): string; 13 | export default function v1ToV6(uuid: Uint8Array): Uint8Array; 14 | export default function v1ToV6(uuid: string | Uint8Array): UUIDTypes { 15 | const v1Bytes = typeof uuid === 'string' ? parse(uuid) : uuid; 16 | 17 | const v6Bytes = _v1ToV6(v1Bytes); 18 | 19 | return typeof uuid === 'string' ? unsafeStringify(v6Bytes) : v6Bytes; 20 | } 21 | 22 | // Do the field transformation needed for v1 -> v6 23 | function _v1ToV6(v1Bytes: Uint8Array) { 24 | return Uint8Array.of( 25 | ((v1Bytes[6] & 0x0f) << 4) | ((v1Bytes[7] >> 4) & 0x0f), 26 | ((v1Bytes[7] & 0x0f) << 4) | ((v1Bytes[4] & 0xf0) >> 4), 27 | ((v1Bytes[4] & 0x0f) << 4) | ((v1Bytes[5] & 0xf0) >> 4), 28 | ((v1Bytes[5] & 0x0f) << 4) | ((v1Bytes[0] & 0xf0) >> 4), 29 | 30 | ((v1Bytes[0] & 0x0f) << 4) | ((v1Bytes[1] & 0xf0) >> 4), 31 | ((v1Bytes[1] & 0x0f) << 4) | ((v1Bytes[2] & 0xf0) >> 4), 32 | 33 | 0x60 | (v1Bytes[2] & 0x0f), 34 | v1Bytes[3], 35 | 36 | v1Bytes[8], 37 | v1Bytes[9], 38 | v1Bytes[10], 39 | v1Bytes[11], 40 | v1Bytes[12], 41 | v1Bytes[13], 42 | v1Bytes[14], 43 | v1Bytes[15] 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /src/v3.ts: -------------------------------------------------------------------------------- 1 | import { UUIDTypes } from './types.js'; 2 | import md5 from './md5.js'; 3 | import v35, { DNS, URL } from './v35.js'; 4 | 5 | export { DNS, URL } from './v35.js'; 6 | 7 | function v3( 8 | value: string | Uint8Array, 9 | namespace: UUIDTypes, 10 | buf?: undefined, 11 | offset?: number 12 | ): string; 13 | function v3( 14 | value: string | Uint8Array, 15 | namespace: UUIDTypes, 16 | buf: TBuf, 17 | offset?: number 18 | ): TBuf; 19 | function v3( 20 | value: string | Uint8Array, 21 | namespace: UUIDTypes, 22 | buf?: TBuf, 23 | offset?: number 24 | ): UUIDTypes { 25 | return v35(0x30, md5, value, namespace, buf, offset); 26 | } 27 | 28 | v3.DNS = DNS; 29 | v3.URL = URL; 30 | 31 | export default v3; 32 | -------------------------------------------------------------------------------- /src/v35.ts: -------------------------------------------------------------------------------- 1 | import { UUIDTypes } from './types.js'; 2 | import parse from './parse.js'; 3 | import { unsafeStringify } from './stringify.js'; 4 | 5 | export function stringToBytes(str: string) { 6 | // TODO: Use TextEncoder (see https://stackoverflow.com/a/48762658/109538) 7 | str = unescape(encodeURIComponent(str)); 8 | 9 | const bytes = new Uint8Array(str.length); 10 | 11 | for (let i = 0; i < str.length; ++i) { 12 | bytes[i] = str.charCodeAt(i); 13 | } 14 | 15 | return bytes; 16 | } 17 | 18 | export const DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; 19 | export const URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8'; 20 | 21 | type HashFunction = (bytes: Uint8Array) => Uint8Array; 22 | 23 | export default function v35( 24 | version: 0x30 | 0x50, 25 | hash: HashFunction, 26 | value: string | Uint8Array, 27 | namespace: UUIDTypes, 28 | buf?: TBuf, 29 | offset?: number 30 | ): UUIDTypes { 31 | const valueBytes: Uint8Array = typeof value === 'string' ? stringToBytes(value) : value; 32 | const namespaceBytes: Uint8Array = typeof namespace === 'string' ? parse(namespace) : namespace; 33 | 34 | if (typeof namespace === 'string') { 35 | namespace = parse(namespace); 36 | } 37 | 38 | if (namespace?.length !== 16) { 39 | throw TypeError('Namespace must be array-like (16 iterable integer values, 0-255)'); 40 | } 41 | 42 | // Compute hash of namespace and value, Per 4.3 43 | // Future: Use spread syntax when supported on all platforms, e.g. `bytes = 44 | // hashfunc([...namespace, ... value])` 45 | let bytes = new Uint8Array(16 + valueBytes.length); 46 | bytes.set(namespaceBytes); 47 | bytes.set(valueBytes, namespaceBytes.length); 48 | bytes = hash(bytes); 49 | 50 | bytes[6] = (bytes[6] & 0x0f) | version; 51 | bytes[8] = (bytes[8] & 0x3f) | 0x80; 52 | 53 | if (buf) { 54 | offset = offset || 0; 55 | 56 | for (let i = 0; i < 16; ++i) { 57 | buf[offset + i] = bytes[i]; 58 | } 59 | 60 | return buf; 61 | } 62 | 63 | return unsafeStringify(bytes); 64 | } 65 | -------------------------------------------------------------------------------- /src/v4.ts: -------------------------------------------------------------------------------- 1 | import native from './native.js'; 2 | import rng from './rng.js'; 3 | import { unsafeStringify } from './stringify.js'; 4 | import { UUIDTypes, Version4Options } from './types.js'; 5 | 6 | function v4(options?: Version4Options, buf?: undefined, offset?: number): string; 7 | function v4( 8 | options: Version4Options | undefined, 9 | buf: TBuf, 10 | offset?: number 11 | ): TBuf; 12 | function v4( 13 | options?: Version4Options, 14 | buf?: TBuf, 15 | offset?: number 16 | ): UUIDTypes { 17 | if (native.randomUUID && !buf && !options) { 18 | return native.randomUUID(); 19 | } 20 | 21 | options = options || {}; 22 | 23 | const rnds = options.random ?? options.rng?.() ?? rng(); 24 | if (rnds.length < 16) { 25 | throw new Error('Random bytes length must be >= 16'); 26 | } 27 | 28 | // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` 29 | rnds[6] = (rnds[6] & 0x0f) | 0x40; 30 | rnds[8] = (rnds[8] & 0x3f) | 0x80; 31 | 32 | // Copy bytes to buffer, if provided 33 | if (buf) { 34 | offset = offset || 0; 35 | if (offset < 0 || offset + 16 > buf.length) { 36 | throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`); 37 | } 38 | 39 | for (let i = 0; i < 16; ++i) { 40 | buf[offset + i] = rnds[i]; 41 | } 42 | 43 | return buf; 44 | } 45 | 46 | return unsafeStringify(rnds); 47 | } 48 | 49 | export default v4; 50 | -------------------------------------------------------------------------------- /src/v5.ts: -------------------------------------------------------------------------------- 1 | import { UUIDTypes } from './types.js'; 2 | import sha1 from './sha1.js'; 3 | import v35, { DNS, URL } from './v35.js'; 4 | 5 | export { DNS, URL } from './v35.js'; 6 | 7 | function v5( 8 | value: string | Uint8Array, 9 | namespace: UUIDTypes, 10 | buf?: undefined, 11 | offset?: number 12 | ): string; 13 | function v5( 14 | value: string | Uint8Array, 15 | namespace: UUIDTypes, 16 | buf: TBuf, 17 | offset?: number 18 | ): TBuf; 19 | function v5( 20 | value: string | Uint8Array, 21 | namespace: UUIDTypes, 22 | buf?: TBuf, 23 | offset?: number 24 | ): UUIDTypes { 25 | return v35(0x50, sha1, value, namespace, buf, offset); 26 | } 27 | 28 | v5.DNS = DNS; 29 | v5.URL = URL; 30 | 31 | export default v5; 32 | -------------------------------------------------------------------------------- /src/v6.ts: -------------------------------------------------------------------------------- 1 | import { UUIDTypes, Version6Options } from './types.js'; 2 | import { unsafeStringify } from './stringify.js'; 3 | import v1 from './v1.js'; 4 | import v1ToV6 from './v1ToV6.js'; 5 | 6 | function v6(options?: Version6Options, buf?: undefined, offset?: number): string; 7 | function v6( 8 | options: Version6Options | undefined, 9 | buf: TBuf, 10 | offset?: number 11 | ): TBuf; 12 | function v6( 13 | options?: Version6Options, 14 | buf?: TBuf, 15 | offset?: number 16 | ): UUIDTypes { 17 | options ??= {}; 18 | offset ??= 0; 19 | 20 | // v6 is v1 with different field layout, so we start with a v1 UUID, albeit 21 | // with slightly different behavior around how the clock_seq and node fields 22 | // are randomized, which is why we call v1 with _v6: true. 23 | let bytes = v1({ ...options, _v6: true }, new Uint8Array(16)); 24 | 25 | // Reorder the fields to v6 layout. 26 | bytes = v1ToV6(bytes); 27 | 28 | // Return as a byte array if requested 29 | if (buf) { 30 | for (let i = 0; i < 16; i++) { 31 | buf[offset + i] = bytes[i]; 32 | } 33 | return buf; 34 | } 35 | 36 | return unsafeStringify(bytes); 37 | } 38 | 39 | export default v6; 40 | -------------------------------------------------------------------------------- /src/v6ToV1.ts: -------------------------------------------------------------------------------- 1 | import { UUIDTypes } from './types.js'; 2 | import parse from './parse.js'; 3 | import { unsafeStringify } from './stringify.js'; 4 | 5 | /** 6 | * Convert a v6 UUID to a v1 UUID 7 | * 8 | * @param {string|Uint8Array} uuid - The v6 UUID to convert to v6 9 | * @returns {string|Uint8Array} The v1 UUID as the same type as the `uuid` arg 10 | * (string or Uint8Array) 11 | */ 12 | export default function v6ToV1(uuid: string): string; 13 | export default function v6ToV1(uuid: Uint8Array): Uint8Array; 14 | export default function v6ToV1(uuid: UUIDTypes): UUIDTypes { 15 | const v6Bytes = typeof uuid === 'string' ? parse(uuid) : uuid; 16 | 17 | const v1Bytes = _v6ToV1(v6Bytes); 18 | 19 | return typeof uuid === 'string' ? unsafeStringify(v1Bytes) : v1Bytes; 20 | } 21 | 22 | // Do the field transformation needed for v6 -> v1 23 | function _v6ToV1(v6Bytes: Uint8Array) { 24 | return Uint8Array.of( 25 | ((v6Bytes[3] & 0x0f) << 4) | ((v6Bytes[4] >> 4) & 0x0f), 26 | ((v6Bytes[4] & 0x0f) << 4) | ((v6Bytes[5] & 0xf0) >> 4), 27 | ((v6Bytes[5] & 0x0f) << 4) | (v6Bytes[6] & 0x0f), 28 | v6Bytes[7], 29 | 30 | ((v6Bytes[1] & 0x0f) << 4) | ((v6Bytes[2] & 0xf0) >> 4), 31 | ((v6Bytes[2] & 0x0f) << 4) | ((v6Bytes[3] & 0xf0) >> 4), 32 | 33 | 0x10 | ((v6Bytes[0] & 0xf0) >> 4), 34 | ((v6Bytes[0] & 0x0f) << 4) | ((v6Bytes[1] & 0xf0) >> 4), 35 | 36 | v6Bytes[8], 37 | v6Bytes[9], 38 | v6Bytes[10], 39 | v6Bytes[11], 40 | v6Bytes[12], 41 | v6Bytes[13], 42 | v6Bytes[14], 43 | v6Bytes[15] 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /src/v7.ts: -------------------------------------------------------------------------------- 1 | import rng from './rng.js'; 2 | import { unsafeStringify } from './stringify.js'; 3 | import { UUIDTypes, Version7Options } from './types.js'; 4 | 5 | type V7State = { 6 | msecs?: number; // time, milliseconds 7 | seq?: number; // sequence number (32-bits) 8 | }; 9 | 10 | const _state: V7State = {}; 11 | 12 | function v7(options?: Version7Options, buf?: undefined, offset?: number): string; 13 | function v7( 14 | options: Version7Options | undefined, 15 | buf: TBuf, 16 | offset?: number 17 | ): TBuf; 18 | function v7( 19 | options?: Version7Options, 20 | buf?: TBuf, 21 | offset?: number 22 | ): UUIDTypes { 23 | let bytes: Uint8Array; 24 | 25 | if (options) { 26 | // With options: Make UUID independent of internal state 27 | bytes = v7Bytes( 28 | options.random ?? options.rng?.() ?? rng(), 29 | options.msecs, 30 | options.seq, 31 | buf, 32 | offset 33 | ); 34 | } else { 35 | // No options: Use internal state 36 | const now = Date.now(); 37 | const rnds = rng(); 38 | 39 | updateV7State(_state, now, rnds); 40 | 41 | bytes = v7Bytes(rnds, _state.msecs, _state.seq, buf, offset); 42 | } 43 | 44 | return buf ?? unsafeStringify(bytes); 45 | } 46 | 47 | // (Private!) Do not use. This method is only exported for testing purposes 48 | // and may change without notice. 49 | export function updateV7State(state: V7State, now: number, rnds: Uint8Array) { 50 | state.msecs ??= -Infinity; 51 | state.seq ??= 0; 52 | 53 | if (now > state.msecs) { 54 | // Time has moved on! Pick a new random sequence number 55 | state.seq = (rnds[6] << 23) | (rnds[7] << 16) | (rnds[8] << 8) | rnds[9]; 56 | state.msecs = now; 57 | } else { 58 | // Bump sequence counter w/ 32-bit rollover 59 | state.seq = (state.seq + 1) | 0; 60 | 61 | // In case of rollover, bump timestamp to preserve monotonicity. This is 62 | // allowed by the RFC and should self-correct as the system clock catches 63 | // up. See https://www.rfc-editor.org/rfc/rfc9562.html#section-6.2-9.4 64 | if (state.seq === 0) { 65 | state.msecs++; 66 | } 67 | } 68 | 69 | return state; 70 | } 71 | 72 | function v7Bytes(rnds: Uint8Array, msecs?: number, seq?: number, buf?: Uint8Array, offset = 0) { 73 | if (rnds.length < 16) { 74 | throw new Error('Random bytes length must be >= 16'); 75 | } 76 | 77 | if (!buf) { 78 | buf = new Uint8Array(16); 79 | offset = 0; 80 | } else { 81 | if (offset < 0 || offset + 16 > buf.length) { 82 | throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`); 83 | } 84 | } 85 | 86 | // Defaults 87 | msecs ??= Date.now(); 88 | seq ??= ((rnds[6] * 0x7f) << 24) | (rnds[7] << 16) | (rnds[8] << 8) | rnds[9]; 89 | 90 | // byte 0-5: timestamp (48 bits) 91 | buf[offset++] = (msecs / 0x10000000000) & 0xff; 92 | buf[offset++] = (msecs / 0x100000000) & 0xff; 93 | buf[offset++] = (msecs / 0x1000000) & 0xff; 94 | buf[offset++] = (msecs / 0x10000) & 0xff; 95 | buf[offset++] = (msecs / 0x100) & 0xff; 96 | buf[offset++] = msecs & 0xff; 97 | 98 | // byte 6: `version` (4 bits) | sequence bits 28-31 (4 bits) 99 | buf[offset++] = 0x70 | ((seq >>> 28) & 0x0f); 100 | 101 | // byte 7: sequence bits 20-27 (8 bits) 102 | buf[offset++] = (seq >>> 20) & 0xff; 103 | 104 | // byte 8: `variant` (2 bits) | sequence bits 14-19 (6 bits) 105 | buf[offset++] = 0x80 | ((seq >>> 14) & 0x3f); 106 | 107 | // byte 9: sequence bits 6-13 (8 bits) 108 | buf[offset++] = (seq >>> 6) & 0xff; 109 | 110 | // byte 10: sequence bits 0-5 (6 bits) | random (2 bits) 111 | buf[offset++] = ((seq << 2) & 0xff) | (rnds[10] & 0x03); 112 | 113 | // bytes 11-15: random (40 bits) 114 | buf[offset++] = rnds[11]; 115 | buf[offset++] = rnds[12]; 116 | buf[offset++] = rnds[13]; 117 | buf[offset++] = rnds[14]; 118 | buf[offset++] = rnds[15]; 119 | 120 | return buf; 121 | } 122 | 123 | export default v7; 124 | -------------------------------------------------------------------------------- /src/validate.ts: -------------------------------------------------------------------------------- 1 | import REGEX from './regex.js'; 2 | 3 | function validate(uuid: unknown) { 4 | return typeof uuid === 'string' && REGEX.test(uuid); 5 | } 6 | 7 | export default validate; 8 | -------------------------------------------------------------------------------- /src/version.ts: -------------------------------------------------------------------------------- 1 | import validate from './validate.js'; 2 | 3 | function version(uuid: string) { 4 | if (!validate(uuid)) { 5 | throw TypeError('Invalid UUID'); 6 | } 7 | 8 | return parseInt(uuid.slice(14, 15), 16); 9 | } 10 | 11 | export default version; 12 | -------------------------------------------------------------------------------- /test/browser/browser.spec.js: -------------------------------------------------------------------------------- 1 | /* global browser:false, $:false, $$:false */ 2 | 3 | const v1Regex = /^[0-9A-F]{8}-[0-9A-F]{4}-1[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i; 4 | const v4Regex = /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i; 5 | const v6Regex = /^[0-9A-F]{8}-[0-9A-F]{4}-6[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i; 6 | const v7Regex = /^[0-9A-F]{8}-[0-9A-F]{4}-7[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i; 7 | 8 | const V1_ID = 'f1207660-21d2-11ef-8c4f-419efbd44d48'; 9 | const V6_ID = '1ef21d2f-1207-6660-8c4f-419efbd44d48'; 10 | 11 | const v1 = (result) => expect(result).toMatch(v1Regex); 12 | const v4 = (result) => expect(result).toMatch(v4Regex); 13 | const v6 = (result) => expect(result).toMatch(v6Regex); 14 | const v7 = (result) => expect(result).toMatch(v7Regex); 15 | const v3dns = (result) => expect(result).toBe('9125a8dc-52ee-365b-a5aa-81b0b3681cf6'); 16 | const v3url = (result) => expect(result).toBe('c6235813-3ba4-3801-ae84-e0a6ebb7d138'); 17 | const v3custom = (result) => expect(result).toBe('f5a52d34-dcd7-30f7-b581-0112fab43d0c'); 18 | const v5dns = (result) => expect(result).toBe('fdda765f-fc57-5604-a269-52a7df8164ec'); 19 | const v5url = (result) => expect(result).toBe('3bbcee75-cecc-5b56-8031-b6641c1ed1f1'); 20 | const v5custom = (result) => expect(result).toBe('c49c5142-4d9a-5940-a926-612ede0ec632'); 21 | 22 | const v1ToV6 = (result) => expect(result).toBe(V6_ID); 23 | const v6ToV1 = (result) => expect(result).toBe(V1_ID); 24 | 25 | const nil = (result) => expect(result).toBe('00000000-0000-0000-0000-000000000000'); 26 | const max = (result) => expect(result).toBe('ffffffff-ffff-ffff-ffff-ffffffffffff'); 27 | const parse = (result) => 28 | expect(result).toEqual('85,35,141,21,201,38,69,152,180,157,207,78,145,59,161,60'); 29 | const stringify = (result) => expect(result).toBe('55238d15-c926-4598-b49d-cf4e913ba13c'); 30 | const validate = (result) => expect(result).toBe('true'); 31 | const version = (result) => expect(result).toBe('4'); 32 | 33 | const expectations = { 34 | 'uuidv1()': v1, 35 | 'uuidv4()': v4, 36 | 'uuidv7()': v7, 37 | 'uuidv3() DNS': v3dns, 38 | 'uuidv3() URL': v3url, 39 | 'uuidv3() MY_NAMESPACE': v3custom, 40 | 'uuidv5() DNS': v5dns, 41 | 'uuidv5() URL': v5url, 42 | 'uuidv5() MY_NAMESPACE': v5custom, 43 | 'uuidv6()': v6, 44 | 45 | 'uuidv1ToV6()': v1ToV6, 46 | 'uuidv6ToV1()': v6ToV1, 47 | 48 | NIL_UUID: nil, 49 | MAX_UUID: max, 50 | 'uuidParse()': parse, 51 | 'uuidStringify()': stringify, 52 | 'uuidValidate()': validate, 53 | 'uuidVersion()': version, 54 | 55 | 'uuid.v1()': v1, 56 | 'uuid.v4()': v4, 57 | 'uuid.v7()': v7, 58 | 'uuid.v3() DNS': v3dns, 59 | 'uuid.v3() URL': v3url, 60 | 'uuid.v3() MY_NAMESPACE': v3custom, 61 | 'uuid.v5() DNS': v5dns, 62 | 'uuid.v5() URL': v5url, 63 | 'uuid.v5() MY_NAMESPACE': v5custom, 64 | 'uuid.v6()': v6, 65 | 66 | 'uuid.v1ToV6()': v1ToV6, 67 | 'uuid.v6ToV1()': v6ToV1, 68 | 69 | 'uuid.NIL': nil, 70 | 'uuid.MAX': max, 71 | 'uuid.parse()': parse, 72 | 'uuid.stringify()': stringify, 73 | 'uuid.validate()': validate, 74 | 'uuid.version()': version, 75 | }; 76 | const expectationTitles = Object.keys(expectations); 77 | 78 | const PORT = 9000; 79 | 80 | describe('BrowserStack Local Testing', () => { 81 | async function testExpectations(path, titleFilter) { 82 | const url = `http://127.0.0.1:${PORT}/${path}`; 83 | await browser.url(url); 84 | 85 | await browser.waitUntil(async () => $('#done').isExisting(), 30000); 86 | 87 | const elements = await $$('.test_result'); 88 | 89 | // Unfortunately the WebDriver API is not thread safe and we cannot use Promise.all() to 90 | // query it in parallel: 91 | // https://github.com/SeleniumHQ/selenium/issues/422#issuecomment-90629726 92 | const titles = []; 93 | 94 | for (let i = 0; i < elements.length; ++i) { 95 | const element = elements[i]; 96 | const titleEl = await element.$('dt'); 97 | const title = await titleEl.getText(); 98 | const resultEl = await element.$('dd'); 99 | const result = await resultEl.getText(); 100 | 101 | if (!expectations[title]) { 102 | throw new Error(`Unexpected title: ${title}`); 103 | } 104 | 105 | expectations[title](result); 106 | titles.push(title); 107 | } 108 | 109 | // Confirm the expected titles are all present 110 | const expectedTitles = expectationTitles.filter(titleFilter); 111 | expect(titles.length).toEqual(expectedTitles.length); 112 | expect(titles.sort()).toEqual(expectedTitles.sort()); 113 | } 114 | 115 | describe('webpack', () => { 116 | it('renders all', async () => testExpectations('browser-webpack/example-all.html', () => true)); 117 | 118 | it('renders v1 only', async () => 119 | testExpectations('browser-webpack/example-v1.html', (title) => title.includes('uuidv1()'))); 120 | 121 | it('renders v4 only', async () => 122 | testExpectations('browser-webpack/example-v4.html', (title) => title.includes('uuidv4()'))); 123 | }); 124 | 125 | describe('rollup', () => { 126 | it('renders all', async () => testExpectations('browser-rollup/example-all.html', () => true)); 127 | 128 | it('renders v1 only', async () => 129 | testExpectations('browser-rollup/example-v1.html', (title) => title.includes('uuidv1()'))); 130 | 131 | it('renders v4 only', async () => 132 | testExpectations('browser-rollup/example-v4.html', (title) => title.includes('uuidv4('))); 133 | }); 134 | }); 135 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "removeComments": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "target": "ES2022" 8 | }, 9 | "exclude": ["dist", "examples"] 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "CommonJS", 5 | "moduleResolution": "Node", 6 | "outDir": "./dist/cjs" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.esm.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "NodeNext", 5 | "moduleResolution": "NodeNext", 6 | "outDir": "./dist/esm" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | // NOTE: Do not extend or alter this file. This file is only here to point 2 | // VSCode to the correct config file. (There's no way of configuring the path 3 | // VSCode uses for the tsconfig.json file) 4 | { "extends": "./tsconfig.esm.json" } 5 | -------------------------------------------------------------------------------- /wdio.conf.js: -------------------------------------------------------------------------------- 1 | const PORT = 9000; 2 | const PROJECT = process.env.GITHUB_REPOSITORY || 'node-uuid'; 3 | const GITHUB_SHA = process.env.GITHUB_SHA || ''; 4 | const GITHUB_REF = process.env.GITHUB_REF || ''; 5 | const BUILD = GITHUB_SHA || GITHUB_REF ? `${GITHUB_REF} ${GITHUB_SHA}` : 'manual build'; 6 | 7 | const commonCapabilities = { 8 | projectName: PROJECT, 9 | buildName: BUILD, 10 | sessionName: 'browser test', 11 | resolution: '1024x768', 12 | }; 13 | 14 | const capabilities = [ 15 | // Chrome Latest 16 | { 17 | browserName: 'Chrome', 18 | browserVersion: 'latest', 19 | 20 | 'bstack:options': { 21 | ...commonCapabilities, 22 | os: 'Windows', 23 | osVersion: '11', 24 | }, 25 | }, 26 | 27 | // Firefox Latest 28 | { 29 | browserName: 'Firefox', 30 | browserVersion: 'latest', 31 | 32 | 'bstack:options': { 33 | ...commonCapabilities, 34 | os: 'Windows', 35 | osVersion: '11', 36 | }, 37 | }, 38 | 39 | // Edge Latest 40 | { 41 | browserName: 'Edge', 42 | browserVersion: 'latest', 43 | 44 | 'bstack:options': { 45 | ...commonCapabilities, 46 | os: 'Windows', 47 | osVersion: '11', 48 | }, 49 | }, 50 | 51 | // Safari Latest 52 | { 53 | browserName: 'Safari', 54 | browserVersion: 'latest', 55 | 56 | 'bstack:options': { 57 | ...commonCapabilities, 58 | os: 'OS X', 59 | osVersion: 'Monterey', 60 | }, 61 | }, 62 | ]; 63 | 64 | export const config = { 65 | specs: ['./test/browser/browser.spec.js'], 66 | 67 | user: process.env.BROWSERSTACK_USER, 68 | key: process.env.BROWSERSTACK_ACCESS_KEY, 69 | 70 | services: [ 71 | [ 72 | 'static-server', 73 | { 74 | folders: [{ mount: '/', path: './examples' }], 75 | port: PORT, 76 | }, 77 | ], 78 | [ 79 | 'browserstack', 80 | { 81 | browserstackLocal: true, 82 | }, 83 | ], 84 | ], 85 | 86 | runner: 'local', 87 | maxInstances: 5, 88 | 89 | capabilities, 90 | 91 | logLevel: 'warn', 92 | bail: 1, 93 | waitforTimeout: 10000, 94 | connectionRetryTimeout: 90000, 95 | connectionRetryCount: 3, 96 | framework: 'jasmine', 97 | jasmineOpts: { 98 | defaultTimeoutInterval: 120000, 99 | }, 100 | reporters: ['spec'], 101 | }; 102 | --------------------------------------------------------------------------------