mirror of
https://github.com/hanzoai/uuid.git
synced 2026-09-24 17:04:02 +00:00
uuid v7 (#681)
This commit is contained in:
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../v7.js
|
||||
@@ -2,3 +2,4 @@
|
||||
dist/
|
||||
node_modules/
|
||||
README.md
|
||||
*.sh
|
||||
@@ -3,3 +3,4 @@ Christoph Tavan <[email protected]>
|
||||
AJ ONeal <[email protected]>
|
||||
Vincent Voyer <[email protected]>
|
||||
Roman Shtylman <[email protected]>
|
||||
Patrick McCarren <[email protected]>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
For the creation of [RFC4122](https://www.ietf.org/rfc/rfc4122.txt) UUIDs
|
||||
|
||||
- **Complete** - Support for RFC4122 version 1, 3, 4, and 5 UUIDs
|
||||
- **Complete** - Support for RFC4122 version 1, 3, 4, 5, and 7 UUIDs
|
||||
- **Cross-platform** - Support for ...
|
||||
- CommonJS, [ECMAScript Modules](#ecmascript-modules) and [CDN builds](#cdn-builds)
|
||||
- NodeJS 12+ ([LTS releases](https://github.com/nodejs/Release))
|
||||
@@ -59,6 +59,7 @@ For timestamp UUIDs, namespace UUIDs, and other options read on ...
|
||||
| [`uuid.v3()`](#uuidv3name-namespace-buffer-offset) | Create a version 3 (namespace w/ MD5) UUID | |
|
||||
| [`uuid.v4()`](#uuidv4options-buffer-offset) | Create a version 4 (random) UUID | |
|
||||
| [`uuid.v5()`](#uuidv5name-namespace-buffer-offset) | Create a version 5 (namespace w/ SHA-1) UUID | |
|
||||
| [`uuid.v7()`](#uuidv7options-buffer-offset) | Create a version 7 (Unix Epoch time-based) UUID | `experimental support` |
|
||||
| [`uuid.validate()`](#uuidvalidatestr) | Test a string to see if it is a valid UUID | New in `[email protected]` |
|
||||
| [`uuid.version()`](#uuidversionstr) | Detect RFC version of a UUID | New in `[email protected]` |
|
||||
|
||||
@@ -251,6 +252,29 @@ import { v5 as uuidv5 } from 'uuid';
|
||||
uuidv5('https://www.w3.org/', uuidv5.URL); // ⇨ 'c106a26a-21bb-5538-8bf2-57095d1976c1'
|
||||
```
|
||||
|
||||
### uuid.v7([options[, buffer[, offset]]])
|
||||
|
||||
Create an RFC version 7 (random) UUID
|
||||
|
||||
| | |
|
||||
| --- | --- |
|
||||
| [`options`] | `Object` with one or more of the following properties: |
|
||||
| [`options.msecs`] | RFC "timestamp" field (`Number` of milliseconds, unix epoch) |
|
||||
| [`options.random`] | `Array` of 16 random bytes (0-255) |
|
||||
| [`options.rng`] | Alternative to `options.random`, a `Function` that returns an `Array` of 16 random bytes (0-255) |
|
||||
| [`options.seq`] | 31 bit monotonic sequence counter as `Number` between 0 - 0x7fffffff |
|
||||
| [`buffer`] | `Array \| Buffer` If specified, uuid will be written here in byte-form, starting at `offset` |
|
||||
| [`offset` = 0] | `Number` Index to start writing UUID bytes in `buffer` |
|
||||
| _returns_ | UUID `String` if no `buffer` is specified, otherwise returns `buffer` |
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
import { v7 as uuidv7 } from 'uuid';
|
||||
|
||||
uuidv7(); // ⇨ '01695553-c90c-7aad-9bdd-330d7b3dcb6d'
|
||||
```
|
||||
|
||||
### uuid.validate(str)
|
||||
|
||||
Test a string to see if it is a valid UUID
|
||||
@@ -325,6 +349,7 @@ Usage:
|
||||
uuid v3 <name> <namespace uuid>
|
||||
uuid v4
|
||||
uuid v5 <name> <namespace uuid>
|
||||
uuid v7
|
||||
uuid --help
|
||||
|
||||
Note: <namespace uuid> may be "URL" or "DNS" to use the corresponding UUIDs
|
||||
|
||||
+26
-1
@@ -21,7 +21,7 @@ require('crypto').randomUUID = undefined;
|
||||
|
||||
For the creation of [RFC4122](https://www.ietf.org/rfc/rfc4122.txt) UUIDs
|
||||
|
||||
- **Complete** - Support for RFC4122 version 1, 3, 4, and 5 UUIDs
|
||||
- **Complete** - Support for RFC4122 version 1, 3, 4, 5, and 7 UUIDs
|
||||
- **Cross-platform** - Support for ...
|
||||
- CommonJS, [ECMAScript Modules](#ecmascript-modules) and [CDN builds](#cdn-builds)
|
||||
- NodeJS 12+ ([LTS releases](https://github.com/nodejs/Release))
|
||||
@@ -73,6 +73,7 @@ For timestamp UUIDs, namespace UUIDs, and other options read on ...
|
||||
| [`uuid.v3()`](#uuidv3name-namespace-buffer-offset) | Create a version 3 (namespace w/ MD5) UUID | |
|
||||
| [`uuid.v4()`](#uuidv4options-buffer-offset) | Create a version 4 (random) UUID | |
|
||||
| [`uuid.v5()`](#uuidv5name-namespace-buffer-offset) | Create a version 5 (namespace w/ SHA-1) UUID | |
|
||||
| [`uuid.v7()`](#uuidv7options-buffer-offset) | Create a version 7 (Unix Epoch time-based) UUID | `experimental support` |
|
||||
| [`uuid.validate()`](#uuidvalidatestr) | Test a string to see if it is a valid UUID | New in `uuid@8.3` |
|
||||
| [`uuid.version()`](#uuidversionstr) | Detect RFC version of a UUID | New in `uuid@8.3` |
|
||||
|
||||
@@ -259,6 +260,29 @@ import { v5 as uuidv5 } from 'uuid';
|
||||
uuidv5('https://www.w3.org/', uuidv5.URL); // RESULT
|
||||
```
|
||||
|
||||
### uuid.v7([options[, buffer[, offset]]])
|
||||
|
||||
Create an RFC version 7 (random) UUID
|
||||
|
||||
| | |
|
||||
| --- | --- |
|
||||
| [`options`] | `Object` with one or more of the following properties: |
|
||||
| [`options.msecs`] | RFC "timestamp" field (`Number` of milliseconds, unix epoch) |
|
||||
| [`options.random`] | `Array` of 16 random bytes (0-255) |
|
||||
| [`options.rng`] | Alternative to `options.random`, a `Function` that returns an `Array` of 16 random bytes (0-255) |
|
||||
| [`options.seq`] | 31 bit monotonic sequence counter as `Number` between 0 - 0x7fffffff |
|
||||
| [`buffer`] | `Array \| Buffer` If specified, uuid will be written here in byte-form, starting at `offset` |
|
||||
| [`offset` = 0] | `Number` Index to start writing UUID bytes in `buffer` |
|
||||
| _returns_ | UUID `String` if no `buffer` is specified, otherwise returns `buffer` |
|
||||
|
||||
Example:
|
||||
|
||||
```javascript --run
|
||||
import { v7 as uuidv7 } from 'uuid';
|
||||
|
||||
uuidv7(); // RESULT
|
||||
```
|
||||
|
||||
### uuid.validate(str)
|
||||
|
||||
Test a string to see if it is a valid UUID
|
||||
@@ -333,6 +357,7 @@ Usage:
|
||||
uuid v3 <name> <namespace uuid>
|
||||
uuid v4
|
||||
uuid v5 <name> <namespace uuid>
|
||||
uuid v7
|
||||
uuid --help
|
||||
|
||||
Note: <namespace uuid> may be "URL" or "DNS" to use the corresponding UUIDs
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
"path": "./examples/browser-rollup/dist/v5-size.js",
|
||||
"maxSize": "1.5 kB"
|
||||
},
|
||||
{ "path": "./examples/browser-rollup/dist/v7-size.js", "maxSize": "0.8 kB" },
|
||||
|
||||
{
|
||||
"path": "./examples/browser-webpack/dist/v1-size.js",
|
||||
"maxSize": "1.0 kB"
|
||||
@@ -35,6 +37,7 @@
|
||||
{
|
||||
"path": "./examples/browser-webpack/dist/v5-size.js",
|
||||
"maxSize": "1.5 kB"
|
||||
}
|
||||
},
|
||||
{ "path": "./examples/browser-webpack/dist/v7-size.js", "maxSize": "0.8 kB" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -57,12 +57,26 @@ export default function benchmark(uuid, Benchmark) {
|
||||
.add('uuid.v4() fill existing array', function () {
|
||||
uuid.v4(null, array, 0);
|
||||
})
|
||||
.add('uuid.v4() without native generation', function () {
|
||||
uuid.v4({}); // passing an object instead of null bypasses native.randomUUID
|
||||
})
|
||||
.add('uuid.v3()', function () {
|
||||
uuid.v3('hello.example.com', uuid.v3.DNS);
|
||||
})
|
||||
.add('uuid.v5()', function () {
|
||||
uuid.v5('hello.example.com', uuid.v5.DNS);
|
||||
})
|
||||
.add('uuid.v7()', function () {
|
||||
uuid.v7();
|
||||
})
|
||||
.add('uuid.v7() fill existing array', function () {
|
||||
uuid.v7(null, array, 0);
|
||||
})
|
||||
.add('uuid.v7() with defined time', function () {
|
||||
uuid.v7({
|
||||
msecs: 1645557742000,
|
||||
});
|
||||
})
|
||||
.on('cycle', function (event) {
|
||||
console.log(event.target.toString());
|
||||
})
|
||||
|
||||
Generated
+15
-11
@@ -15,20 +15,24 @@
|
||||
}
|
||||
},
|
||||
"../../.local/uuid": {
|
||||
"version": "8.3.2",
|
||||
"version": "9.0.1",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.18.9",
|
||||
"@babel/core": "7.18.9",
|
||||
"@babel/cli": "7.18.10",
|
||||
"@babel/core": "7.18.10",
|
||||
"@babel/eslint-parser": "7.18.9",
|
||||
"@babel/preset-env": "7.18.9",
|
||||
"@babel/preset-env": "7.18.10",
|
||||
"@commitlint/cli": "17.0.3",
|
||||
"@commitlint/config-conventional": "17.0.3",
|
||||
"bundlewatch": "0.3.3",
|
||||
"eslint": "8.20.0",
|
||||
"eslint": "8.21.0",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"eslint-config-standard": "17.0.0",
|
||||
"eslint-plugin-import": "2.26.0",
|
||||
@@ -42,7 +46,7 @@
|
||||
"optional-dev-dependency": "2.0.1",
|
||||
"prettier": "2.7.1",
|
||||
"random-seed": "0.3.0",
|
||||
"runmd": "1.3.6",
|
||||
"runmd": "1.3.9",
|
||||
"standard-version": "9.5.0"
|
||||
}
|
||||
},
|
||||
@@ -99,14 +103,14 @@
|
||||
"uuid": {
|
||||
"version": "file:../../.local/uuid",
|
||||
"requires": {
|
||||
"@babel/cli": "7.18.9",
|
||||
"@babel/core": "7.18.9",
|
||||
"@babel/cli": "7.18.10",
|
||||
"@babel/core": "7.18.10",
|
||||
"@babel/eslint-parser": "7.18.9",
|
||||
"@babel/preset-env": "7.18.9",
|
||||
"@babel/preset-env": "7.18.10",
|
||||
"@commitlint/cli": "17.0.3",
|
||||
"@commitlint/config-conventional": "17.0.3",
|
||||
"bundlewatch": "0.3.3",
|
||||
"eslint": "8.20.0",
|
||||
"eslint": "8.21.0",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"eslint-config-standard": "17.0.0",
|
||||
"eslint-plugin-import": "2.26.0",
|
||||
@@ -120,7 +124,7 @@
|
||||
"optional-dev-dependency": "2.0.1",
|
||||
"prettier": "2.7.1",
|
||||
"random-seed": "0.3.0",
|
||||
"runmd": "1.3.6",
|
||||
"runmd": "1.3.9",
|
||||
"standard-version": "9.5.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
v3 as uuidv3,
|
||||
v4 as uuidv4,
|
||||
v5 as uuidv5,
|
||||
v7 as uuidv7,
|
||||
validate as uuidValidate,
|
||||
version as uuidVersion,
|
||||
} from './node_modules/uuid/dist/esm-browser/index.js';
|
||||
@@ -15,6 +16,8 @@ console.log('uuidv1()', uuidv1());
|
||||
|
||||
console.log('uuidv4()', uuidv4());
|
||||
|
||||
console.log('uuidv7()', uuidv7());
|
||||
|
||||
// ... using predefined DNS namespace (for domain names)
|
||||
console.log('uuidv3() DNS', uuidv3('hello.example.com', uuidv3.DNS));
|
||||
|
||||
@@ -52,6 +55,7 @@ console.log('Same with default export');
|
||||
|
||||
console.log('uuid.v1()', uuid.v1());
|
||||
console.log('uuid.v4()', uuid.v4());
|
||||
console.log('uuid.v7()', uuid.v7());
|
||||
console.log('uuid.v3() DNS', uuid.v3('hello.example.com', uuid.v3.DNS));
|
||||
console.log('uuid.v3() URL', uuid.v3('http://example.com/hello', uuid.v3.URL));
|
||||
console.log('uuid.v3() MY_NAMESPACE', uuid.v3('Hello, World!', MY_NAMESPACE));
|
||||
|
||||
@@ -7,11 +7,12 @@ npm start
|
||||
|
||||
Then navigate to `example-*.html`.
|
||||
|
||||
The `example-v{1,4}.js` demonstrate that treeshaking works as expected:
|
||||
The `example-v{1,4,7}.js` demonstrate that treeshaking works as expected:
|
||||
|
||||
```
|
||||
$ du -sh dist/*
|
||||
20K dist/all.js
|
||||
8.0K dist/v1.js
|
||||
4.0K dist/v4.js
|
||||
4.0K dist/v7.js
|
||||
```
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
v3 as uuidv3,
|
||||
v4 as uuidv4,
|
||||
v5 as uuidv5,
|
||||
v7 as uuidv7,
|
||||
validate as uuidValidate,
|
||||
version as uuidVersion,
|
||||
} from 'uuid';
|
||||
@@ -20,6 +21,8 @@ testpage(function (addTest, done) {
|
||||
|
||||
addTest('uuidv4()', uuidv4());
|
||||
|
||||
addTest('uuidv7()', uuidv7());
|
||||
|
||||
// ... using predefined DNS namespace (for domain names)
|
||||
addTest('uuidv3() DNS', uuidv3('hello.example.com', uuidv3.DNS));
|
||||
|
||||
@@ -57,6 +60,7 @@ testpage(function (addTest, done) {
|
||||
|
||||
addTest('uuid.v1()', uuid.v1());
|
||||
addTest('uuid.v4()', uuid.v4());
|
||||
addTest('uuid.v7()', uuid.v7());
|
||||
addTest('uuid.v3() DNS', uuid.v3('hello.example.com', uuid.v3.DNS));
|
||||
addTest('uuid.v3() URL', uuid.v3('http://example.com/hello', uuid.v3.URL));
|
||||
addTest('uuid.v3() MY_NAMESPACE', uuid.v3('Hello, World!', MY_NAMESPACE));
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>UUID esmodule webpack example</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="text/javascript" src="./dist/v7.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,8 @@
|
||||
import { v7 as uuidv7 } from 'uuid';
|
||||
|
||||
import testpage from '../utils/testpage';
|
||||
|
||||
testpage(function (addTest, done) {
|
||||
addTest('uuidv7()', uuidv7());
|
||||
done();
|
||||
});
|
||||
@@ -3,4 +3,5 @@
|
||||
<p>Please open the Developer Console to view output</p>
|
||||
<script type="text/javascript" src="./dist/v1.js"></script>
|
||||
<script type="text/javascript" src="./dist/v4.js"></script>
|
||||
<script type="text/javascript" src="./dist/v7.js"></script>
|
||||
<script type="text/javascript" src="./dist/all.js"></script>
|
||||
|
||||
@@ -27,6 +27,14 @@ module.exports = [
|
||||
},
|
||||
plugins,
|
||||
},
|
||||
{
|
||||
input: './example-v7.js',
|
||||
output: {
|
||||
file: 'dist/v7.js',
|
||||
format: 'iife',
|
||||
},
|
||||
plugins,
|
||||
},
|
||||
|
||||
{
|
||||
input: './size-v1.js',
|
||||
@@ -60,4 +68,12 @@ module.exports = [
|
||||
},
|
||||
plugins,
|
||||
},
|
||||
{
|
||||
input: './size-v7.js',
|
||||
output: {
|
||||
file: 'dist/v7-size.js',
|
||||
format: 'cjs',
|
||||
},
|
||||
plugins,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import { v7 as uuidv7 } from 'uuid';
|
||||
|
||||
uuidv7();
|
||||
@@ -7,6 +7,7 @@ const {
|
||||
v3: uuidv3,
|
||||
v4: uuidv4,
|
||||
v5: uuidv5,
|
||||
v7: uuidv7,
|
||||
validate: uuidValidate,
|
||||
version: uuidVersion,
|
||||
} = uuid;
|
||||
@@ -20,6 +21,8 @@ testpage(function (addTest, done) {
|
||||
|
||||
addTest('uuidv4()', uuidv4());
|
||||
|
||||
addTest('uuidv7()', uuidv7());
|
||||
|
||||
// ... using predefined DNS namespace (for domain names)
|
||||
addTest('uuidv3() DNS', uuidv3('hello.example.com', uuidv3.DNS));
|
||||
|
||||
@@ -57,6 +60,7 @@ testpage(function (addTest, done) {
|
||||
|
||||
addTest('uuid.v1()', uuid.v1());
|
||||
addTest('uuid.v4()', uuid.v4());
|
||||
addTest('uuid.v7()', uuid.v7());
|
||||
addTest('uuid.v3() DNS', uuid.v3('hello.example.com', uuid.v3.DNS));
|
||||
addTest('uuid.v3() URL', uuid.v3('http://example.com/hello', uuid.v3.URL));
|
||||
addTest('uuid.v3() MY_NAMESPACE', uuid.v3('Hello, World!', MY_NAMESPACE));
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
v3 as uuidv3,
|
||||
v4 as uuidv4,
|
||||
v5 as uuidv5,
|
||||
v7 as uuidv7,
|
||||
validate as uuidValidate,
|
||||
version as uuidVersion,
|
||||
} from 'uuid';
|
||||
@@ -20,6 +21,8 @@ testpage(function (addTest, done) {
|
||||
|
||||
addTest('uuidv4()', uuidv4());
|
||||
|
||||
addTest('uuidv7()', uuidv7());
|
||||
|
||||
// ... using predefined DNS namespace (for domain names)
|
||||
addTest('uuidv3() DNS', uuidv3('hello.example.com', uuidv3.DNS));
|
||||
|
||||
@@ -57,6 +60,7 @@ testpage(function (addTest, done) {
|
||||
|
||||
addTest('uuid.v1()', uuid.v1());
|
||||
addTest('uuid.v4()', uuid.v4());
|
||||
addTest('uuid.v7()', uuid.v7());
|
||||
addTest('uuid.v3() DNS', uuid.v3('hello.example.com', uuid.v3.DNS));
|
||||
addTest('uuid.v3() URL', uuid.v3('http://example.com/hello', uuid.v3.URL));
|
||||
addTest('uuid.v3() MY_NAMESPACE', uuid.v3('Hello, World!', MY_NAMESPACE));
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>UUID esmodule webpack example</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="text/javascript" src="./dist/v7.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,9 @@
|
||||
import { v7 as uuidv7 } from 'uuid';
|
||||
|
||||
import testpage from '../utils/testpage';
|
||||
|
||||
testpage(function (addTest, done) {
|
||||
addTest('uuidv7()', uuidv7());
|
||||
|
||||
done();
|
||||
});
|
||||
@@ -3,4 +3,5 @@
|
||||
<p>Please open the Developer Console to view output</p>
|
||||
<script type="text/javascript" src="./dist/v1.js"></script>
|
||||
<script type="text/javascript" src="./dist/v4.js"></script>
|
||||
<script type="text/javascript" src="./dist/v7.js"></script>
|
||||
<script type="text/javascript" src="./dist/all.js"></script>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import { v7 as uuidv7 } from 'uuid';
|
||||
|
||||
uuidv7();
|
||||
@@ -7,11 +7,13 @@ module.exports = {
|
||||
allRequire: './example-all-require.js',
|
||||
v1: './example-v1.js',
|
||||
v4: './example-v4.js',
|
||||
v7: './example-v7.js',
|
||||
|
||||
'v1-size': './size-v1.js',
|
||||
'v3-size': './size-v3.js',
|
||||
'v4-size': './size-v4.js',
|
||||
'v5-size': './size-v5.js',
|
||||
'v7-size': './size-v7.js',
|
||||
},
|
||||
// Webpack now produces builds that are incompatible with IE11:
|
||||
// https://webpack.js.org/migrate/5/#turn-off-es2015-syntax-in-runtime-code-if-necessary
|
||||
|
||||
@@ -6,6 +6,7 @@ const {
|
||||
v3: uuidv3,
|
||||
v4: uuidv4,
|
||||
v5: uuidv5,
|
||||
v7: uuidv7,
|
||||
validate: uuidValidate,
|
||||
version: uuidVersion,
|
||||
} = require('uuid');
|
||||
@@ -16,6 +17,8 @@ console.log('uuidv1()', uuidv1());
|
||||
|
||||
console.log('uuidv4()', uuidv4());
|
||||
|
||||
console.log('uuidv7()', uuidv7());
|
||||
|
||||
// ... using predefined DNS namespace (for domain names)
|
||||
console.log('uuidv3() DNS', uuidv3('hello.example.com', uuidv3.DNS));
|
||||
|
||||
@@ -53,6 +56,7 @@ console.log('Same with default export');
|
||||
|
||||
console.log('uuid.v1()', uuid.v1());
|
||||
console.log('uuid.v4()', uuid.v4());
|
||||
console.log('uuid.v7()', uuid.v7());
|
||||
console.log('uuid.v3() DNS', uuid.v3('hello.example.com', uuid.v3.DNS));
|
||||
console.log('uuid.v3() URL', uuid.v3('http://example.com/hello', uuid.v3.URL));
|
||||
console.log('uuid.v3() MY_NAMESPACE', uuid.v3('Hello, World!', MY_NAMESPACE));
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
v3 as uuidv3,
|
||||
v4 as uuidv4,
|
||||
v5 as uuidv5,
|
||||
v7 as uuidv7,
|
||||
validate as uuidValidate,
|
||||
version as uuidVersion,
|
||||
} from 'uuid';
|
||||
@@ -15,6 +16,8 @@ console.log('uuidv1()', uuidv1());
|
||||
|
||||
console.log('uuidv4()', uuidv4());
|
||||
|
||||
console.log('uuidv7()', uuidv7());
|
||||
|
||||
// ... using predefined DNS namespace (for domain names)
|
||||
console.log('uuidv3() DNS', uuidv3('hello.example.com', uuidv3.DNS));
|
||||
|
||||
@@ -52,6 +55,7 @@ console.log('Same with default export');
|
||||
|
||||
console.log('uuid.v1()', uuid.v1());
|
||||
console.log('uuid.v4()', uuid.v4());
|
||||
console.log('uuid.v7()', uuid.v7());
|
||||
console.log('uuid.v3() DNS', uuid.v3('hello.example.com', uuid.v3.DNS));
|
||||
console.log('uuid.v3() URL', uuid.v3('http://example.com/hello', uuid.v3.URL));
|
||||
console.log('uuid.v3() MY_NAMESPACE', uuid.v3('Hello, World!', MY_NAMESPACE));
|
||||
|
||||
@@ -6,3 +6,8 @@ test('uuidv4()', () => {
|
||||
const val = uuid.v4();
|
||||
expect(uuid.version(val)).toBe(4);
|
||||
});
|
||||
|
||||
test('uuidv7()', () => {
|
||||
const val = uuid.v7();
|
||||
expect(uuid.version(val)).toBe(7);
|
||||
});
|
||||
|
||||
@@ -4,3 +4,8 @@ test('uuidv4()', () => {
|
||||
const val = uuid.v4();
|
||||
expect(uuid.version(val)).toBe(4);
|
||||
});
|
||||
|
||||
test('uuidv7()', () => {
|
||||
const val = uuid.v7();
|
||||
expect(uuid.version(val)).toBe(7);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import { v7 as uuidv7 } from 'uuid';
|
||||
|
||||
console.log('uuidv7()', uuidv7());
|
||||
@@ -4,7 +4,7 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "rm -rf dist && webpack",
|
||||
"test": "npm run build && node dist/v1.js && node dist/v4.js && node dist/all.js"
|
||||
"test": "npm run build && node dist/v1.js && node dist/v4.js && node dist/v7.js && node dist/all.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": "file:../../.local/uuid"
|
||||
|
||||
@@ -6,6 +6,7 @@ module.exports = {
|
||||
all: './example-all.js',
|
||||
v1: './example-v1.js',
|
||||
v4: './example-v4.js',
|
||||
v7: './example-v7.js',
|
||||
},
|
||||
output: {
|
||||
filename: '[name].js',
|
||||
|
||||
+2
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "uuid",
|
||||
"version": "9.0.1",
|
||||
"description": "RFC4122 (v1, v4, and v5) UUIDs",
|
||||
"description": "RFC4122 (v1, v3, v4, v5, and v7) UUIDs",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
@@ -98,6 +98,7 @@
|
||||
"eslint:fix": "eslint --fix src/ test/ examples/ *.js",
|
||||
"pretest": "[ -n $CI ] || npm run build",
|
||||
"test": "BABEL_ENV=commonjsNode node --throw-deprecation node_modules/.bin/jest test/unit/",
|
||||
"test:matching": "BABEL_ENV=commonjsNode node --throw-deprecation node_modules/.bin/jest test/unit/ -t",
|
||||
"pretest:browser": "optional-dev-dependency && npm run build && npm-run-all --parallel examples:browser:**",
|
||||
"test:browser": "wdio run ./wdio.conf.js",
|
||||
"pretest:node": "npm run build",
|
||||
|
||||
@@ -2,6 +2,7 @@ export { default as v1 } from './v1.js';
|
||||
export { default as v3 } from './v3.js';
|
||||
export { default as v4 } from './v4.js';
|
||||
export { default as v5 } from './v5.js';
|
||||
export { default as v7 } from './v7.js';
|
||||
export { default as NIL } from './nil.js';
|
||||
export { default as version } from './version.js';
|
||||
export { default as validate } from './validate.js';
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
export default /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;
|
||||
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)$/i;
|
||||
|
||||
@@ -4,6 +4,7 @@ import v1 from './v1.js';
|
||||
import v3 from './v3.js';
|
||||
import v4 from './v4.js';
|
||||
import v5 from './v5.js';
|
||||
import v7 from './v7.js';
|
||||
|
||||
function usage() {
|
||||
console.log('Usage:');
|
||||
@@ -12,6 +13,7 @@ function usage() {
|
||||
console.log(' uuid v3 <name> <namespace uuid>');
|
||||
console.log(' uuid v4');
|
||||
console.log(' uuid v5 <name> <namespace uuid>');
|
||||
console.log(' uuid v7');
|
||||
console.log(' uuid --help');
|
||||
console.log(
|
||||
'\nNote: <namespace uuid> may be "URL" or "DNS" to use the corresponding UUIDs defined by RFC4122'
|
||||
@@ -74,6 +76,10 @@ switch (version) {
|
||||
break;
|
||||
}
|
||||
|
||||
case 'v7':
|
||||
console.log(v7());
|
||||
break;
|
||||
|
||||
default:
|
||||
usage();
|
||||
process.exit(1);
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
import rng from './rng.js';
|
||||
import { unsafeStringify } from './stringify.js';
|
||||
|
||||
/**
|
||||
* UUID V7 - Unix Epoch time-based UUID
|
||||
*
|
||||
* The IETF has published RFC9562, introducing 3 new UUID versions (6,7,8). This
|
||||
* implementation of V7 is based on the accepted, though not yet approved,
|
||||
* revisions.
|
||||
*
|
||||
* RFC 9562:https://www.rfc-editor.org/rfc/rfc9562.html Universally Unique
|
||||
* IDentifiers (UUIDs)
|
||||
|
||||
*
|
||||
* Sample V7 value:
|
||||
* https://www.rfc-editor.org/rfc/rfc9562.html#name-example-of-a-uuidv7-value
|
||||
*
|
||||
* Monotonic Bit Layout: RFC rfc9562.6.2 Method 1, Dedicated Counter Bits ref:
|
||||
* https://www.rfc-editor.org/rfc/rfc9562.html#section-6.2-5.1
|
||||
*
|
||||
* 0 1 2 3 0 1 2 3 4 5 6
|
||||
* 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | unix_ts_ms |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | unix_ts_ms | ver | seq_hi |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* |var| seq_low | rand |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | rand |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*
|
||||
* seq is a 31 bit serialized counter; comprised of 12 bit seq_hi and 19 bit
|
||||
* seq_low, and randomly initialized upon timestamp change. 31 bit counter size
|
||||
* was selected as any bitwise operations in node are done as _signed_ 32 bit
|
||||
* ints. we exclude the sign bit.
|
||||
*/
|
||||
|
||||
let _seqLow = null;
|
||||
let _seqHigh = null;
|
||||
let _msecs = 0;
|
||||
|
||||
function v7(options, buf, offset) {
|
||||
options = options || {};
|
||||
|
||||
// initialize buffer and pointer
|
||||
let i = (buf && offset) || 0;
|
||||
const b = buf || new Uint8Array(16);
|
||||
|
||||
// rnds is Uint8Array(16) filled with random bytes
|
||||
const rnds = options.random || (options.rng || rng)();
|
||||
|
||||
// milliseconds since unix epoch, 1970-01-01 00:00
|
||||
const msecs = options.msecs !== undefined ? options.msecs : Date.now();
|
||||
|
||||
// seq is user provided 31 bit counter
|
||||
let seq = options.seq !== undefined ? options.seq : null;
|
||||
|
||||
// initialize local seq high/low parts
|
||||
let seqHigh = _seqHigh;
|
||||
let seqLow = _seqLow;
|
||||
|
||||
// check if clock has advanced and user has not provided msecs
|
||||
if (msecs > _msecs && options.msecs === undefined) {
|
||||
_msecs = msecs;
|
||||
|
||||
// unless user provided seq, reset seq parts
|
||||
if (seq !== null) {
|
||||
seqHigh = null;
|
||||
seqLow = null;
|
||||
}
|
||||
}
|
||||
|
||||
// if we have a user provided seq
|
||||
if (seq !== null) {
|
||||
// trim provided seq to 31 bits of value, avoiding overflow
|
||||
if (seq > 0x7fffffff) {
|
||||
seq = 0x7fffffff;
|
||||
}
|
||||
|
||||
// split provided seq into high/low parts
|
||||
seqHigh = (seq >>> 19) & 0xfff;
|
||||
seqLow = seq & 0x7ffff;
|
||||
}
|
||||
|
||||
// randomly initialize seq
|
||||
if (seqHigh === null || seqLow === null) {
|
||||
seqHigh = rnds[6] & 0x7f;
|
||||
seqHigh = (seqHigh << 8) | rnds[7];
|
||||
|
||||
seqLow = rnds[8] & 0x3f; // pad for var
|
||||
seqLow = (seqLow << 8) | rnds[9];
|
||||
seqLow = (seqLow << 5) | (rnds[10] >>> 3);
|
||||
}
|
||||
|
||||
// increment seq if within msecs window
|
||||
if (msecs + 10000 > _msecs && seq === null) {
|
||||
if (++seqLow > 0x7ffff) {
|
||||
seqLow = 0;
|
||||
|
||||
if (++seqHigh > 0xfff) {
|
||||
seqHigh = 0;
|
||||
|
||||
// increment internal _msecs. this allows us to continue incrementing
|
||||
// while staying monotonic. Note, once we hit 10k milliseconds beyond system
|
||||
// clock, we will reset breaking monotonicity (after (2^31)*10000 generations)
|
||||
_msecs++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// resetting; we have advanced more than
|
||||
// 10k milliseconds beyond system clock
|
||||
_msecs = msecs;
|
||||
}
|
||||
|
||||
_seqHigh = seqHigh;
|
||||
_seqLow = seqLow;
|
||||
|
||||
// [bytes 0-5] 48 bits of local timestamp
|
||||
b[i++] = (_msecs / 0x10000000000) & 0xff;
|
||||
b[i++] = (_msecs / 0x100000000) & 0xff;
|
||||
b[i++] = (_msecs / 0x1000000) & 0xff;
|
||||
b[i++] = (_msecs / 0x10000) & 0xff;
|
||||
b[i++] = (_msecs / 0x100) & 0xff;
|
||||
b[i++] = _msecs & 0xff;
|
||||
|
||||
// [byte 6] - set 4 bits of version (7) with first 4 bits seq_hi
|
||||
b[i++] = ((seqHigh >>> 4) & 0x0f) | 0x70;
|
||||
|
||||
// [byte 7] remaining 8 bits of seq_hi
|
||||
b[i++] = seqHigh & 0xff;
|
||||
|
||||
// [byte 8] - variant (2 bits), first 6 bits seq_low
|
||||
b[i++] = ((seqLow >>> 13) & 0x3f) | 0x80;
|
||||
|
||||
// [byte 9] 8 bits seq_low
|
||||
b[i++] = (seqLow >>> 5) & 0xff;
|
||||
|
||||
// [byte 10] remaining 5 bits seq_low, 3 bits random
|
||||
b[i++] = ((seqLow << 3) & 0xff) | (rnds[10] & 0x07);
|
||||
|
||||
// [bytes 11-15] always random
|
||||
b[i++] = rnds[11];
|
||||
b[i++] = rnds[12];
|
||||
b[i++] = rnds[13];
|
||||
b[i++] = rnds[14];
|
||||
b[i++] = rnds[15];
|
||||
|
||||
return buf || unsafeStringify(b);
|
||||
}
|
||||
|
||||
export default v7;
|
||||
@@ -0,0 +1,172 @@
|
||||
import assert from 'assert';
|
||||
import v7 from '../../src/v7.js';
|
||||
|
||||
/**
|
||||
* fixture bit layout:
|
||||
* ref: https://www.rfc-editor.org/rfc/rfc9562.html#name-example-of-a-uuidv7-value
|
||||
*
|
||||
* expectedBytes was calculated using this script:
|
||||
* https://gist.github.com/d5382ac3a1ce4ba9ba40a90d9da8cbf1
|
||||
*
|
||||
* -------------------------------
|
||||
* field bits value
|
||||
* -------------------------------
|
||||
* unix_ts_ms 48 0x17F22E279B0
|
||||
* ver 4 0x7
|
||||
* rand_a 12 0xCC3
|
||||
* var 2 b10
|
||||
* rand_b 62 b01, 0x8C4DC0C0C07398F
|
||||
* -------------------------------
|
||||
* total 128
|
||||
* -------------------------------
|
||||
* final: 017f22e2-79b0-7cc3-98c4-dc0c0c07398f
|
||||
*/
|
||||
|
||||
describe('v7', () => {
|
||||
const msecsFixture = 1645557742000;
|
||||
const seqFixture = 0x661b189b;
|
||||
|
||||
const randomBytesFixture = [
|
||||
0x10, 0x91, 0x56, 0xbe, 0xc4, 0xfb, 0x0c, 0xc3, 0x18, 0xc4, 0xdc, 0x0c, 0x0c, 0x07, 0x39, 0x8f,
|
||||
];
|
||||
|
||||
const expectedBytes = [1, 127, 34, 226, 121, 176, 124, 195, 152, 196, 220, 12, 12, 7, 57, 143];
|
||||
|
||||
test('subsequent UUIDs are different', () => {
|
||||
const id1 = v7();
|
||||
const id2 = v7();
|
||||
assert(id1 !== id2);
|
||||
});
|
||||
|
||||
test('explicit options.random and options.msecs produces expected result', () => {
|
||||
const id = v7({
|
||||
random: randomBytesFixture,
|
||||
msecs: msecsFixture,
|
||||
seq: seqFixture,
|
||||
});
|
||||
assert.strictEqual(id, '017f22e2-79b0-7cc3-98c4-dc0c0c07398f');
|
||||
});
|
||||
|
||||
test('explicit options.rng produces expected result', () => {
|
||||
const id = v7({
|
||||
rng: () => randomBytesFixture,
|
||||
msecs: msecsFixture,
|
||||
seq: seqFixture,
|
||||
});
|
||||
assert.strictEqual(id, '017f22e2-79b0-7cc3-98c4-dc0c0c07398f');
|
||||
});
|
||||
|
||||
test('explicit options.msecs produces expected result', () => {
|
||||
const id = v7({
|
||||
msecs: msecsFixture,
|
||||
});
|
||||
assert.strictEqual(id.indexOf('017f22e2'), 0);
|
||||
});
|
||||
|
||||
test('fills one UUID into a buffer as expected', () => {
|
||||
const buffer = [];
|
||||
const result = v7(
|
||||
{
|
||||
random: randomBytesFixture,
|
||||
msecs: msecsFixture,
|
||||
seq: seqFixture,
|
||||
},
|
||||
buffer
|
||||
);
|
||||
assert.deepEqual(buffer, expectedBytes);
|
||||
assert.strictEqual(buffer, result);
|
||||
});
|
||||
|
||||
test('fills two UUIDs into a buffer as expected', () => {
|
||||
const buffer = [];
|
||||
v7(
|
||||
{
|
||||
random: randomBytesFixture,
|
||||
msecs: msecsFixture,
|
||||
seq: seqFixture,
|
||||
},
|
||||
buffer,
|
||||
0
|
||||
);
|
||||
v7(
|
||||
{
|
||||
random: randomBytesFixture,
|
||||
msecs: msecsFixture,
|
||||
seq: seqFixture,
|
||||
},
|
||||
buffer,
|
||||
16
|
||||
);
|
||||
assert.deepEqual(buffer, expectedBytes.concat(expectedBytes));
|
||||
});
|
||||
|
||||
//
|
||||
// monotonic and lexicographical sorting tests
|
||||
//
|
||||
|
||||
test('lexicographical sorting is preserved', () => {
|
||||
let id;
|
||||
let prior;
|
||||
let msecs = msecsFixture;
|
||||
for (let i = 0; i < 20000; ++i) {
|
||||
if (i % 1500 === 0) {
|
||||
// every 1500 runs increment msecs so seq is
|
||||
// reinitialized, simulating passage of time
|
||||
msecs += 1;
|
||||
}
|
||||
|
||||
id = v7({ msecs });
|
||||
|
||||
if (i > 0) {
|
||||
assert(prior < id, `${prior} < ${id}`);
|
||||
}
|
||||
|
||||
prior = id;
|
||||
}
|
||||
});
|
||||
|
||||
test('handles seq rollover', () => {
|
||||
const msecs = msecsFixture;
|
||||
const a = v7({
|
||||
msecs,
|
||||
seq: 0x7fffffff,
|
||||
});
|
||||
|
||||
v7({ msecs });
|
||||
|
||||
const c = v7({ msecs });
|
||||
|
||||
assert(a < c, `${a} < ${c}`);
|
||||
});
|
||||
|
||||
test('can supply seq', () => {
|
||||
let seq = 0x12345;
|
||||
let uuid = v7({
|
||||
msecs: msecsFixture,
|
||||
seq,
|
||||
});
|
||||
|
||||
assert.strictEqual(uuid.substr(0, 25), '017f22e2-79b0-7000-891a-2');
|
||||
|
||||
seq = 0x6fffffff;
|
||||
uuid = v7({
|
||||
msecs: msecsFixture,
|
||||
seq,
|
||||
});
|
||||
|
||||
assert.strictEqual(uuid.substr(0, 25), '017f22e2-79b0-7fff-bfff-f');
|
||||
});
|
||||
|
||||
test('internal seq is reset upon timestamp change', () => {
|
||||
v7({
|
||||
msecs: msecsFixture,
|
||||
seq: 0x6fffffff,
|
||||
});
|
||||
|
||||
const uuid = v7({
|
||||
msecs: msecsFixture + 1,
|
||||
});
|
||||
|
||||
assert(uuid.indexOf('fff') !== 15);
|
||||
});
|
||||
});
|
||||
@@ -6,14 +6,32 @@ describe('validate', () => {
|
||||
test('validate uuid', () => {
|
||||
assert.strictEqual(validate(NIL), true);
|
||||
|
||||
// test valid UUID versions
|
||||
|
||||
// v1
|
||||
assert.strictEqual(validate('d9428888-122b-11e1-b85c-61cd3cbb3210'), true);
|
||||
|
||||
assert.strictEqual(validate('109156be-c4fb-41ea-b1b4-efe1671c5836'), true);
|
||||
|
||||
// v3
|
||||
assert.strictEqual(validate('a981a0c2-68b1-35dc-bcfc-296e52ab01ec'), true);
|
||||
|
||||
// v4
|
||||
assert.strictEqual(validate('109156be-c4fb-41ea-b1b4-efe1671c5836'), true);
|
||||
|
||||
// v5
|
||||
assert.strictEqual(validate('90123e1c-7512-523e-bb28-76fab9f2f73d'), true);
|
||||
|
||||
// v7
|
||||
assert.strictEqual(validate('017f22e2-79b0-7cc3-98c4-dc0c0c07398f'), true);
|
||||
|
||||
// test invalid/unsupported UUID versions
|
||||
[0, 2, 6, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f'].forEach((v) => {
|
||||
assert.strictEqual(
|
||||
validate('12300000-0000-' + v + '000-0000-000000000000'),
|
||||
false,
|
||||
'version ' + v + ' should not be valid'
|
||||
);
|
||||
});
|
||||
|
||||
assert.strictEqual(validate(), false);
|
||||
|
||||
assert.strictEqual(validate(''), false);
|
||||
|
||||
@@ -14,6 +14,8 @@ describe('version', () => {
|
||||
|
||||
assert.strictEqual(version('90123e1c-7512-523e-bb28-76fab9f2f73d'), 5);
|
||||
|
||||
assert.strictEqual(version('017f22e2-79b0-7cc3-98c4-dc0c0c07398f'), 7);
|
||||
|
||||
assert.throws(() => version());
|
||||
|
||||
assert.throws(() => version(''));
|
||||
|
||||
@@ -3,6 +3,7 @@ export const v1 = uuid.v1;
|
||||
export const v3 = uuid.v3;
|
||||
export const v4 = uuid.v4;
|
||||
export const v5 = uuid.v5;
|
||||
export const v7 = uuid.v7;
|
||||
export const NIL = uuid.NIL;
|
||||
export const version = uuid.version;
|
||||
export const validate = uuid.validate;
|
||||
|
||||
Reference in New Issue
Block a user