Skip to content

Commit 1b5c20c

Browse files
committed
initial commit
0 parents  commit 1b5c20c

File tree

15 files changed

+4192
-0
lines changed

15 files changed

+4192
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.DS_Store
2+
node_modules/
3+
dist/

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
src/

.prettierrc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"semi": true,
3+
"singleQuote": false,
4+
"arrowParens": "always",
5+
"bracketSpacing": true,
6+
"printWidth": 70,
7+
"tabWidth": 2,
8+
"trailingComma": "all"
9+
}

CONTRIBUTORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Richard Marks <[email protected]>

LICENSE.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2021 Richard Marks <[email protected]>
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# Optimal ID
2+
<a id="toc"></a>
3+
4+
## Table of Contents
5+
6+
- [About](#about)
7+
- [Installation](#installation)
8+
- [API Reference](#api-reference)
9+
- [Usage](#usage)
10+
- [License](#license)
11+
12+
## About
13+
14+
`optimal-id` is a small optimized library that generates an optimized string based id value suitable for nosql database unique keys.
15+
16+
It is based upon the research and works of [Peter Zaitsev](https://www.percona.com/blog/2007/03/13/to-uuid-or-not-to-uuid/) and [Karthik Appigatla](https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/).
17+
18+
Assuming a standard v1 UUID of `"4a6b9f70-b678-11eb-a7b6-032e6afcbc8e"`
19+
20+
This code provides the string `"11ebb6784a6b9f70a7b6032e6afcbc8e"`
21+
22+
- rearranges bytes of timestamp for ordered ids
23+
- removes separator dash characters because we don't need to waste 4 bytes per id
24+
- zero third-party dependency libraries for a smaller payload
25+
26+
## Installation
27+
28+
Install the package into your project.
29+
30+
### Using NPM
31+
32+
```bash
33+
npm install --save optimal-id
34+
```
35+
36+
### Using Yarn Package Manager
37+
38+
```bash
39+
yarn add optimal-id
40+
```
41+
42+
> [Return to Table of Contents](#toc)
43+
44+
## API Reference
45+
46+
- `optimal-id` - module exported from npm package
47+
- `optId`- interface exported from module
48+
- `generate` - function that returns optimal ids
49+
```typescript
50+
optId.generate(numIds?: number): OptimalId[]
51+
```
52+
- `OptimalId` - type exported from module if using TypeScript
53+
54+
> [Return to Table of Contents](#toc)
55+
56+
## Usage
57+
58+
### Using JavaScript ES5
59+
60+
```javascript
61+
var optId = require('optimal-id').optId;
62+
63+
// generate one id
64+
var id = optId.generate()[0];
65+
66+
// generate multiple ids
67+
var NUM_IDS_TO_GENERATE = 100;
68+
var ids = optId.generate(NUM_IDS_TO_GENERATE);
69+
```
70+
71+
or
72+
73+
### Using JavaScript ECMAScript 2015 or later
74+
75+
```javascript
76+
const { optId } = require('optimal-id');
77+
78+
// generate one id
79+
const [id] = optId.generate();
80+
81+
// generate multiple ids
82+
const NUM_IDS_TO_GENERATE = 100;
83+
const ids = optId.generate(NUM_IDS_TO_GENERATE);
84+
```
85+
86+
or
87+
88+
### Using Typescript
89+
90+
```typescript
91+
import { optId, OptimalId } from 'optimal-id';
92+
93+
// generate one id
94+
const [id]: OptimalId[] = optId.generate();
95+
96+
// generate multiple ids
97+
const NUM_IDS_TO_GENERATE = 100;
98+
const ids: OptimalId[] = optId.generate(NUM_IDS_TO_GENERATE);
99+
```
100+
101+
> [Return to Table of Contents](#toc)
102+
## License
103+
104+
This library has been developed by Richard Marks, is copyright 2021, and licensed under the MIT License.
105+
106+
See [LICENSE.md](./LICENSE.md) for full legal details.
107+
108+
> [Return to Table of Contents](#toc)
109+
110+
----
111+
112+
**This library depends on Node.js being built with support for the `crypto` module.**

jest.config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module.exports = {
2+
preset: "ts-jest",
3+
testEnvironment: "node",
4+
};

package.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "optimal-id",
3+
"version": "1.0.0",
4+
"description": "generates an optimized string based id value suitable for nosql database unique keys",
5+
"main": "dist/main.js",
6+
"repository": {
7+
"type": "git",
8+
"url": "https://github.com/RichardMarks/optimal-id.git"
9+
},
10+
"author": "Richard Marks <[email protected]>",
11+
"license": "MIT",
12+
"private": false,
13+
"devDependencies": {
14+
"@types/jest": "^26.0.23",
15+
"jest": "^26.6.3",
16+
"prettier": "^2.3.0",
17+
"ts-jest": "^26.5.6",
18+
"typescript": "^4.2.4"
19+
},
20+
"scripts": {
21+
"prepublish": "yarn build",
22+
"build": "tsc",
23+
"clean": "rm -rf ./dist",
24+
"test": "jest --passWithNoTests"
25+
}
26+
}

src/index.spec.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { optId } from "./index";
2+
3+
declare global {
4+
namespace jest {
5+
interface Matchers<R> {
6+
toBeDistinct(): R;
7+
toMatchPattern(pattern: RegExp): R;
8+
}
9+
}
10+
}
11+
12+
beforeAll(() => {
13+
expect.extend({
14+
toBeDistinct(received) {
15+
const pass =
16+
Array.isArray(received) &&
17+
new Set(received).size === received.length;
18+
if (pass) {
19+
return {
20+
message: () => `expected [${received}] array is unique`,
21+
pass: true,
22+
};
23+
} else {
24+
return {
25+
message: () =>
26+
`expected [${received}] array is not to unique`,
27+
pass: false,
28+
};
29+
}
30+
},
31+
toMatchPattern(received, pattern: RegExp) {
32+
if (typeof received !== "string") {
33+
return {
34+
message: () => `expected [${received}] is not a string`,
35+
pass: false,
36+
};
37+
}
38+
const pass = pattern.test(received);
39+
if (pass) {
40+
return {
41+
message: () =>
42+
`expected [${received}] string matches pattern`,
43+
pass: true,
44+
};
45+
} else {
46+
return {
47+
message: () =>
48+
`expected [${received}] string does not match pattern`,
49+
pass: false,
50+
};
51+
}
52+
},
53+
});
54+
});
55+
56+
it("exports correct interface", () => {
57+
expect(optId).toBeDefined();
58+
expect(optId).toHaveProperty("generate");
59+
});
60+
61+
it("generates an id", () => {
62+
expect(optId.generate()).toHaveLength(1);
63+
});
64+
65+
it("generates an id that is 32 characters in length", () => {
66+
const [id] = optId.generate();
67+
expect(id).toHaveLength(32);
68+
});
69+
70+
it("generates an id that only contains valid hex characters", () => {
71+
const [id] = optId.generate();
72+
expect(id).toMatchPattern(/^[a-f0-9]{32}$/);
73+
});
74+
75+
it("generates n ids", () => {
76+
const ids = optId.generate(10);
77+
expect(ids).toHaveLength(10);
78+
});
79+
80+
it("generates n ids that are unique", () => {
81+
const ids = optId.generate(10);
82+
expect(ids).toBeDistinct();
83+
});
84+
85+
it("generates n ids that are in ascending order", () => {
86+
const ids = optId.generate(20);
87+
88+
const sortedIds = [...ids].sort((a, b) => {
89+
a = a
90+
.match(/[a-f0-9]{0,2}/g)!
91+
.reverse()
92+
.join("");
93+
b = b
94+
.match(/[a-f0-9]{0,2}/g)!
95+
.reverse()
96+
.join("");
97+
return a < b ? -1 : a > b ? 1 : 0;
98+
});
99+
100+
expect(ids).toStrictEqual(sortedIds);
101+
});

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export type { OptimalId } from "./lib/types";
2+
export { optId } from "./lib/opt-id";

0 commit comments

Comments
 (0)