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
|
||||
b.js
|
||||
c.js
|
||||
d.js
|
||||
decode_d.js
|
||||
node_modules/
|
||||
dist/
|
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 {
|
||||
constructor(machineId) {
|
||||
export class Snowflake {
|
||||
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) {
|
||||
throw new Error("Machine ID must be in the range 0-1023.");
|
||||
}
|
||||
|
@ -7,13 +14,9 @@ class Snowflake {
|
|||
this.machineId = BigInt(machineId);
|
||||
this.sequence = BigInt(0);
|
||||
this.lastTimestamp = BigInt(-1);
|
||||
|
||||
this.timestampShift = 22n;
|
||||
this.machineIdShift = 12n;
|
||||
this.sequenceMask = 0xfffn;
|
||||
}
|
||||
|
||||
async generate() {
|
||||
async generate(): Promise<string> {
|
||||
let timestamp = BigInt(Date.now());
|
||||
|
||||
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 timestampShift = 22n;
|
||||
const machineIdShift = 12n;
|
||||
const sequenceMask = 0xfffn;
|
||||
|
||||
const timestamp = (id >> timestampShift) & 0x1ffffffffffn; // Adjusting the shift
|
||||
const timestamp = (id >> timestampShift) & 0x1ffffffffffn;
|
||||
const machineId = (id >> machineIdShift) & 0x3ffn;
|
||||
const sequence = id & sequenceMask;
|
||||
|
||||
const date = new Date(Number(timestamp)); // Converting BigInt to Number, then to Date
|
||||
const date = new Date(Number(timestamp));
|
||||
|
||||
return {
|
||||
timestamp: date.toISOString(),
|
||||
|
@ -58,15 +67,3 @@ function decodeSnowflake(idStr) {
|
|||
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