typescript
This commit is contained in:
parent
6d78355cec
commit
ef9042afa0
16 changed files with 3668 additions and 97 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -1,5 +1,2 @@
|
||||||
a.js
|
node_modules/
|
||||||
b.js
|
dist/
|
||||||
c.js
|
|
||||||
d.js
|
|
||||||
decode_d.js
|
|
1
.npmignore
Normal file
1
.npmignore
Normal file
|
@ -0,0 +1 @@
|
||||||
|
node_modules/
|
37
README.md
Normal file
37
README.md
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
# snowflakeid
|
||||||
|
|
||||||
|
> Snowflake ID is a unique identifier commonly used in distributed systems to generate unique IDs with a timestamp component. It is designed to ensure uniqueness, even in distributed and highly concurrent environments.
|
||||||
|
|
||||||
|
The Snowflake ID typically consists of three components:
|
||||||
|
|
||||||
|
1. **Timestamp:** Representing the time when the ID was generated.
|
||||||
|
2. **Machine ID:** Identifying the machine or node that generated the ID.
|
||||||
|
3. **Sequence Number:** Ensuring uniqueness in cases of multiple IDs generated within the same millisecond.
|
||||||
|
|
||||||
|
By combining these components, Snowflake IDs provide a reliable way to generate globally unique identifiers, making them valuable for applications such as distributed databases, messaging systems, and more.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```
|
||||||
|
yarn add snowflakeid
|
||||||
|
or
|
||||||
|
npm i snowflakeid
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { Snowflake, decodeSnowflake } from "snowflakeid";
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const machineId = 1; // machine ID (0-1023)
|
||||||
|
const snowflake = new Snowflake(machineId);
|
||||||
|
|
||||||
|
const id1 = await snowflake.generate();
|
||||||
|
console.log("encodeID", id1);
|
||||||
|
|
||||||
|
const decoded = decodeSnowflake(id1);
|
||||||
|
console.log("decodeID", decoded);
|
||||||
|
})();
|
||||||
|
};
|
||||||
|
```
|
3
babel.config.js
Normal file
3
babel.config.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = {
|
||||||
|
presets: ["@babel/preset-env", "@babel/preset-typescript"],
|
||||||
|
};
|
23
decode.js
23
decode.js
|
@ -1,23 +0,0 @@
|
||||||
function decodeSnowflake(idStr) {
|
|
||||||
const id = BigInt(idStr);
|
|
||||||
const timestampShift = 22n;
|
|
||||||
const machineIdShift = 12n;
|
|
||||||
const sequenceMask = 0xfffn;
|
|
||||||
|
|
||||||
const timestamp = (id >> timestampShift) & 0x1ffffffffffn; // Adjusting the shift
|
|
||||||
const machineId = (id >> machineIdShift) & 0x3ffn;
|
|
||||||
const sequence = id & sequenceMask;
|
|
||||||
|
|
||||||
const date = new Date(Number(timestamp)); // Converting BigInt to Number, then to Date
|
|
||||||
|
|
||||||
return {
|
|
||||||
timestamp: date.toISOString(),
|
|
||||||
machineId: machineId.toString(),
|
|
||||||
sequence: sequence.toString(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use
|
|
||||||
const idStr = "7160488856901390336";
|
|
||||||
const decoded = decodeSnowflake(idStr);
|
|
||||||
console.log(decoded);
|
|
47
encode.js
47
encode.js
|
@ -1,47 +0,0 @@
|
||||||
class Snowflake {
|
|
||||||
constructor(machineId) {
|
|
||||||
if (machineId < 0 || machineId >= 1024) {
|
|
||||||
throw new Error("Machine ID must be in the range 0-1023.");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.machineId = BigInt(machineId);
|
|
||||||
this.sequence = BigInt(0);
|
|
||||||
this.lastTimestamp = BigInt(-1);
|
|
||||||
|
|
||||||
this.timestampShift = 22n;
|
|
||||||
this.machineIdShift = 12n;
|
|
||||||
this.sequenceMask = 0xfffn;
|
|
||||||
}
|
|
||||||
|
|
||||||
async generate() {
|
|
||||||
let timestamp = BigInt(Date.now());
|
|
||||||
|
|
||||||
if (timestamp === this.lastTimestamp) {
|
|
||||||
this.sequence = (this.sequence + 1n) & this.sequenceMask;
|
|
||||||
|
|
||||||
if (this.sequence === 0n) {
|
|
||||||
while (timestamp <= this.lastTimestamp) {
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
||||||
timestamp = BigInt(Date.now());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.sequence = 0n;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.lastTimestamp = timestamp;
|
|
||||||
|
|
||||||
const id =
|
|
||||||
((timestamp & 0x1ffffffffffn) << this.timestampShift) |
|
|
||||||
(this.machineId << this.machineIdShift) |
|
|
||||||
this.sequence;
|
|
||||||
|
|
||||||
return id.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const machineId = 1; // machine ID (0-1023)
|
|
||||||
const snowflake = new Snowflake(machineId);
|
|
||||||
|
|
||||||
const id1 = snowflake.generate();
|
|
||||||
console.log(id1);
|
|
3
jest.config.js
Normal file
3
jest.config.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = {
|
||||||
|
testEnvironment: "jsdom",
|
||||||
|
};
|
48
package.json
Normal file
48
package.json
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
{
|
||||||
|
"name": "snowflakeid",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Snowflake ID is a unique identifier commonly used in distributed systems to generate unique IDs with a timestamp component. It is designed to ensure uniqueness, even in distributed and highly concurrent environments.",
|
||||||
|
"scripts": {
|
||||||
|
"rollup": "rollup -c",
|
||||||
|
"test": "jest"
|
||||||
|
},
|
||||||
|
"author": "Sebastian Korotkiewicz",
|
||||||
|
"license": "MIT",
|
||||||
|
"keywords": [
|
||||||
|
"snowflake",
|
||||||
|
"encode",
|
||||||
|
"decode"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/skorotkiewicz/snowflake.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/skorotkiewicz/snowflake/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/skorotkiewicz/snowflake#readme",
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/preset-env": "^7.9.0",
|
||||||
|
"@babel/preset-typescript": "^7.9.0",
|
||||||
|
"@rollup/plugin-commonjs": "^21.1.0",
|
||||||
|
"@rollup/plugin-node-resolve": "^7.1.3",
|
||||||
|
"@rollup/plugin-typescript": "^8.3.2",
|
||||||
|
"@types/jest": "^27.4.1",
|
||||||
|
"jest": "^27.5.1",
|
||||||
|
"rollup": "^2.70.1",
|
||||||
|
"rollup-plugin-dts": "^4.2.1",
|
||||||
|
"tslib": "^2.3.1",
|
||||||
|
"typescript": "^4.6.3"
|
||||||
|
},
|
||||||
|
"main": "dist/cjs/index.js",
|
||||||
|
"module": "dist/esm/index.js",
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"dependencies": {},
|
||||||
|
"funding": {
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://paypal.me/skorotkiewicz"
|
||||||
|
}
|
||||||
|
}
|
35
rollup.config.js
Normal file
35
rollup.config.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import resolve from "@rollup/plugin-node-resolve";
|
||||||
|
import commonjs from "@rollup/plugin-commonjs";
|
||||||
|
import typescript from "@rollup/plugin-typescript";
|
||||||
|
import dts from "rollup-plugin-dts";
|
||||||
|
|
||||||
|
const packageJson = require("./package.json");
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
input: "src/index.ts",
|
||||||
|
output: [
|
||||||
|
{
|
||||||
|
file: packageJson.main,
|
||||||
|
format: "cjs",
|
||||||
|
sourcemap: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: packageJson.module,
|
||||||
|
format: "esm",
|
||||||
|
sourcemap: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
resolve(),
|
||||||
|
commonjs(),
|
||||||
|
typescript({ tsconfig: "./tsconfig.json" }),
|
||||||
|
],
|
||||||
|
external: ["react"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "dist/esm/index.d.ts",
|
||||||
|
output: [{ file: "dist/index.d.ts", format: "esm" }],
|
||||||
|
plugins: [dts()],
|
||||||
|
},
|
||||||
|
];
|
22
src/components/Snowflake/Snowflake.test.tsx
Normal file
22
src/components/Snowflake/Snowflake.test.tsx
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { Snowflake, decodeSnowflake } from "./Snowflake";
|
||||||
|
|
||||||
|
describe("Snowflake", () => {
|
||||||
|
test("test encode", () => {
|
||||||
|
(async () => {
|
||||||
|
const machineId = 1; // machine ID (0-1023)
|
||||||
|
const snowflake = new Snowflake(machineId);
|
||||||
|
|
||||||
|
const id1 = await snowflake.generate();
|
||||||
|
console.log("encodeID", id1);
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("test decode", () => {
|
||||||
|
(async () => {
|
||||||
|
const id1 = "7160500553955414016";
|
||||||
|
|
||||||
|
const decoded = decodeSnowflake(id1);
|
||||||
|
console.log("decodeID", decoded);
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,5 +1,12 @@
|
||||||
class Snowflake {
|
export class Snowflake {
|
||||||
constructor(machineId) {
|
private machineId: bigint;
|
||||||
|
private sequence: bigint;
|
||||||
|
private lastTimestamp: bigint;
|
||||||
|
private readonly timestampShift: bigint = 22n;
|
||||||
|
private readonly machineIdShift: bigint = 12n;
|
||||||
|
private readonly sequenceMask: bigint = 0xfffn;
|
||||||
|
|
||||||
|
constructor(machineId: number) {
|
||||||
if (machineId < 0 || machineId >= 1024) {
|
if (machineId < 0 || machineId >= 1024) {
|
||||||
throw new Error("Machine ID must be in the range 0-1023.");
|
throw new Error("Machine ID must be in the range 0-1023.");
|
||||||
}
|
}
|
||||||
|
@ -7,13 +14,9 @@ class Snowflake {
|
||||||
this.machineId = BigInt(machineId);
|
this.machineId = BigInt(machineId);
|
||||||
this.sequence = BigInt(0);
|
this.sequence = BigInt(0);
|
||||||
this.lastTimestamp = BigInt(-1);
|
this.lastTimestamp = BigInt(-1);
|
||||||
|
|
||||||
this.timestampShift = 22n;
|
|
||||||
this.machineIdShift = 12n;
|
|
||||||
this.sequenceMask = 0xfffn;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async generate() {
|
async generate(): Promise<string> {
|
||||||
let timestamp = BigInt(Date.now());
|
let timestamp = BigInt(Date.now());
|
||||||
|
|
||||||
if (timestamp === this.lastTimestamp) {
|
if (timestamp === this.lastTimestamp) {
|
||||||
|
@ -40,17 +43,23 @@ class Snowflake {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function decodeSnowflake(idStr) {
|
interface DecodedSnowflake {
|
||||||
|
timestamp: string;
|
||||||
|
machineId: string;
|
||||||
|
sequence: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decodeSnowflake(idStr: string): DecodedSnowflake {
|
||||||
const id = BigInt(idStr);
|
const id = BigInt(idStr);
|
||||||
const timestampShift = 22n;
|
const timestampShift = 22n;
|
||||||
const machineIdShift = 12n;
|
const machineIdShift = 12n;
|
||||||
const sequenceMask = 0xfffn;
|
const sequenceMask = 0xfffn;
|
||||||
|
|
||||||
const timestamp = (id >> timestampShift) & 0x1ffffffffffn; // Adjusting the shift
|
const timestamp = (id >> timestampShift) & 0x1ffffffffffn;
|
||||||
const machineId = (id >> machineIdShift) & 0x3ffn;
|
const machineId = (id >> machineIdShift) & 0x3ffn;
|
||||||
const sequence = id & sequenceMask;
|
const sequence = id & sequenceMask;
|
||||||
|
|
||||||
const date = new Date(Number(timestamp)); // Converting BigInt to Number, then to Date
|
const date = new Date(Number(timestamp));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
timestamp: date.toISOString(),
|
timestamp: date.toISOString(),
|
||||||
|
@ -58,15 +67,3 @@ function decodeSnowflake(idStr) {
|
||||||
sequence: sequence.toString(),
|
sequence: sequence.toString(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use
|
|
||||||
(async () => {
|
|
||||||
const machineId = 1; // machine ID (0-1023)
|
|
||||||
const snowflake = new Snowflake(machineId);
|
|
||||||
|
|
||||||
const id1 = await snowflake.generate();
|
|
||||||
console.log("encodeID", id1);
|
|
||||||
|
|
||||||
const decoded = decodeSnowflake(id1);
|
|
||||||
console.log("decodeID", decoded);
|
|
||||||
})();
|
|
1
src/components/Snowflake/index.ts
Normal file
1
src/components/Snowflake/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { Snowflake, decodeSnowflake } from "./Snowflake";
|
1
src/components/index.ts
Normal file
1
src/components/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { Snowflake, decodeSnowflake } from "./Snowflake";
|
1
src/index.ts
Normal file
1
src/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export * from "./components";
|
19
tsconfig.json
Normal file
19
tsconfig.json
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2021",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
"jsx": "react",
|
||||||
|
"module": "ESNext",
|
||||||
|
"declaration": true,
|
||||||
|
"declarationDir": "types",
|
||||||
|
"sourceMap": false,
|
||||||
|
"outDir": "dist",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"emitDeclarationOnly": true
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue