typescript

This commit is contained in:
Sebastian Korotkiewicz 2024-02-06 07:26:25 +01:00
parent 6d78355cec
commit ef9042afa0
Signed by: skorotkiewicz
GPG key ID: 5BDC557B496BDB0D
16 changed files with 3668 additions and 97 deletions

7
.gitignore vendored
View file

@ -1,5 +1,2 @@
a.js node_modules/
b.js dist/
c.js
d.js
decode_d.js

1
.npmignore Normal file
View file

@ -0,0 +1 @@
node_modules/

37
README.md Normal file
View 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
View file

@ -0,0 +1,3 @@
module.exports = {
presets: ["@babel/preset-env", "@babel/preset-typescript"],
};

View file

@ -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);

View file

@ -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
View file

@ -0,0 +1,3 @@
module.exports = {
testEnvironment: "jsdom",
};

48
package.json Normal file
View 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
View 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()],
},
];

View 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);
})();
});
});

View file

@ -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);
})();

View file

@ -0,0 +1 @@
export { Snowflake, decodeSnowflake } from "./Snowflake";

1
src/components/index.ts Normal file
View file

@ -0,0 +1 @@
export { Snowflake, decodeSnowflake } from "./Snowflake";

1
src/index.ts Normal file
View file

@ -0,0 +1 @@
export * from "./components";

19
tsconfig.json Normal file
View 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
}
}

3476
yarn.lock Normal file

File diff suppressed because it is too large Load diff