@eliware/snowflake 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +128 -0
- package/index.cjs +37 -0
- package/index.d.ts +3 -0
- package/index.mjs +34 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Eli Sterling, eliware.org
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# [](https://discord.gg/M6aTR9eTwN)
|
|
2
|
+
|
|
3
|
+
## @eliware/snowflake [](https://www.npmjs.com/package/@eliware/snowflake)[](LICENSE)[](https://github.com/eliware/snowflake/actions)
|
|
4
|
+
|
|
5
|
+
> A snowflake for Node.js (Insert Brief Description)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
- [Features](#features)
|
|
12
|
+
- [Installation](#installation)
|
|
13
|
+
- [Usage](#usage)
|
|
14
|
+
- [ESM Example](#esm-example)
|
|
15
|
+
- [CommonJS Example](#commonjs-example)
|
|
16
|
+
- [API](#api)
|
|
17
|
+
- [TypeScript](#typescript)
|
|
18
|
+
- [Testing](#testing)
|
|
19
|
+
- [License](#license)
|
|
20
|
+
|
|
21
|
+
## Features
|
|
22
|
+
|
|
23
|
+
- Minimal, dependency-light Snowflake ID generator compatible with Discord-style bit layout.
|
|
24
|
+
- BigInt-safe implementation to preserve full 64-bit IDs.
|
|
25
|
+
- Works in both ESM and CommonJS environments.
|
|
26
|
+
- Configurable epoch and worker ID via a factory function.
|
|
27
|
+
- Includes TypeScript declarations and basic tests.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
Install from npm:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install @eliware/snowflake
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
### ESM Example
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
// ESM (index.mjs)
|
|
43
|
+
import generate, { createSnowflakeGenerator } from './index.mjs';
|
|
44
|
+
|
|
45
|
+
// default generator (workerId: 0)
|
|
46
|
+
console.log('id:', generate());
|
|
47
|
+
|
|
48
|
+
// create a generator for a specific worker ID
|
|
49
|
+
const gen = createSnowflakeGenerator({ workerId: 1 });
|
|
50
|
+
console.log('worker 1 id:', gen());
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### CommonJS Example
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
// CommonJS (index.cjs)
|
|
57
|
+
const generate = require('./index.cjs');
|
|
58
|
+
const { createSnowflakeGenerator } = require('./index.cjs');
|
|
59
|
+
|
|
60
|
+
console.log('id:', generate());
|
|
61
|
+
|
|
62
|
+
const gen = createSnowflakeGenerator({ workerId: 2 });
|
|
63
|
+
console.log('worker 2 id:', gen());
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## API
|
|
67
|
+
|
|
68
|
+
### createSnowflakeGenerator(options?) => () => string
|
|
69
|
+
|
|
70
|
+
- options.epoch: number | bigint (default: 1420070400000) — base epoch in milliseconds (Discord epoch: 2015-01-01T00:00:00.000Z).
|
|
71
|
+
- options.workerId: number (default: 0) — numeric worker/node ID (10-bit value).
|
|
72
|
+
|
|
73
|
+
Returns a zero-argument function that generates a new Snowflake ID (as a decimal string) each call.
|
|
74
|
+
|
|
75
|
+
Behavior notes:
|
|
76
|
+
- Timestamp uses (Date.now() - epoch) shifted left 22 bits.
|
|
77
|
+
- Worker ID occupies 10 bits, sequence counter occupies 12 bits.
|
|
78
|
+
- The generator waits for the next millisecond if more than 4095 IDs are requested in the same millisecond.
|
|
79
|
+
|
|
80
|
+
### Default export: generate()
|
|
81
|
+
|
|
82
|
+
A ready-to-use generator (workerId 0). It returns a decimal string representation of the Snowflake ID.
|
|
83
|
+
|
|
84
|
+
## TypeScript
|
|
85
|
+
|
|
86
|
+
Type definitions are included (index.d.ts). Example signatures:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
export function createSnowflakeGenerator(options?: { epoch?: number | bigint; workerId?: number }): () => string;
|
|
90
|
+
export const generate: () => string;
|
|
91
|
+
export default generate;
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Testing
|
|
95
|
+
|
|
96
|
+
A small Jest test suite is included (index.test.mjs / index.test.cjs) that verifies:
|
|
97
|
+
|
|
98
|
+
- Generated IDs are numeric strings parseable to BigInt.
|
|
99
|
+
- Consecutive IDs are monotonically increasing.
|
|
100
|
+
- Worker ID bits are encoded correctly.
|
|
101
|
+
- Timestamp portion decodes to a recent time.
|
|
102
|
+
|
|
103
|
+
Run tests with:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm test
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Support
|
|
112
|
+
|
|
113
|
+
For help, questions, or to chat with the author and community, visit:
|
|
114
|
+
|
|
115
|
+
[](https://discord.gg/M6aTR9eTwN)[](https://discord.gg/M6aTR9eTwN)
|
|
116
|
+
|
|
117
|
+
**[eliware.org on Discord](https://discord.gg/M6aTR9eTwN)**
|
|
118
|
+
|
|
119
|
+
## License
|
|
120
|
+
|
|
121
|
+
[MIT © 2025 Eli Sterling, eliware.org](LICENSE)
|
|
122
|
+
|
|
123
|
+
## Links
|
|
124
|
+
|
|
125
|
+
- [Home Page](https://eliware.org)
|
|
126
|
+
- [GitHub](https://github.com/eliware/snowflake)
|
|
127
|
+
- [npm](https://www.npmjs.com/package/@eliware/snowflake)
|
|
128
|
+
- [Discord](https://discord.gg/M6aTR9eTwN)
|
package/index.cjs
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Snowflake generator (CommonJS)
|
|
2
|
+
const DEFAULT_EPOCH = 1420070400000n; // Discord epoch: 2015-01-01T00:00:00.000Z
|
|
3
|
+
|
|
4
|
+
function createSnowflakeGenerator({ epoch = DEFAULT_EPOCH, workerId = 0 } = {}) {
|
|
5
|
+
const _epoch = BigInt(epoch);
|
|
6
|
+
const _workerId = BigInt(workerId) & 0x3ffn; // 10 bits
|
|
7
|
+
let lastTimestamp = 0n;
|
|
8
|
+
let sequence = 0n;
|
|
9
|
+
|
|
10
|
+
return function generate() {
|
|
11
|
+
let now = BigInt(Date.now());
|
|
12
|
+
|
|
13
|
+
if (now === lastTimestamp) {
|
|
14
|
+
sequence = (sequence + 1n) & 0xfffn; // 12 bits
|
|
15
|
+
if (sequence === 0n) {
|
|
16
|
+
// Sequence overflow in the same millisecond — wait for next ms
|
|
17
|
+
while (now <= lastTimestamp) {
|
|
18
|
+
now = BigInt(Date.now());
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
} else {
|
|
22
|
+
sequence = 0n;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
lastTimestamp = now;
|
|
26
|
+
|
|
27
|
+
const id = ((now - _epoch) << 22n) | (_workerId << 12n) | sequence;
|
|
28
|
+
return id.toString();
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Default generator (workerId 0)
|
|
33
|
+
const generate = createSnowflakeGenerator();
|
|
34
|
+
|
|
35
|
+
module.exports = generate;
|
|
36
|
+
module.exports.generate = generate;
|
|
37
|
+
module.exports.createSnowflakeGenerator = createSnowflakeGenerator;
|
package/index.d.ts
ADDED
package/index.mjs
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Snowflake generator (ESM)
|
|
2
|
+
const DEFAULT_EPOCH = 1420070400000n; // Discord epoch: 2015-01-01T00:00:00.000Z
|
|
3
|
+
|
|
4
|
+
export function createSnowflakeGenerator({ epoch = DEFAULT_EPOCH, workerId = 0 } = {}) {
|
|
5
|
+
const _epoch = BigInt(epoch);
|
|
6
|
+
const _workerId = BigInt(workerId) & 0x3ffn; // 10 bits
|
|
7
|
+
let lastTimestamp = 0n;
|
|
8
|
+
let sequence = 0n;
|
|
9
|
+
|
|
10
|
+
return function generate() {
|
|
11
|
+
let now = BigInt(Date.now());
|
|
12
|
+
|
|
13
|
+
if (now === lastTimestamp) {
|
|
14
|
+
sequence = (sequence + 1n) & 0xfffn; // 12 bits
|
|
15
|
+
if (sequence === 0n) {
|
|
16
|
+
// Sequence overflow in the same millisecond — wait for next ms
|
|
17
|
+
while (now <= lastTimestamp) {
|
|
18
|
+
now = BigInt(Date.now());
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
} else {
|
|
22
|
+
sequence = 0n;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
lastTimestamp = now;
|
|
26
|
+
|
|
27
|
+
const id = ((now - _epoch) << 22n) | (_workerId << 12n) | sequence;
|
|
28
|
+
return id.toString();
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Default generator (workerId 0)
|
|
33
|
+
export const generate = createSnowflakeGenerator();
|
|
34
|
+
export default generate;
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@eliware/snowflake",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "1.1.1",
|
|
5
|
+
"description": "A minimal Node.js Snowflake ID generator (Discord-compatible). Provides BigInt-safe generators for ESM and CommonJS with configurable epoch and worker ID.",
|
|
6
|
+
"main": "index.cjs",
|
|
7
|
+
"module": "index.mjs",
|
|
8
|
+
"types": "index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"index.mjs",
|
|
11
|
+
"index.cjs",
|
|
12
|
+
"index.d.ts",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"import": "./index.mjs",
|
|
19
|
+
"require": "./index.cjs",
|
|
20
|
+
"types": "./index.d.ts"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"test": "npx --node-options=\"--experimental-vm-modules --no-warnings\" jest --detectOpenHandles --silent"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/eliware/snowflake.git"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"snowflake",
|
|
32
|
+
"snowflake-id",
|
|
33
|
+
"id-generator",
|
|
34
|
+
"discord",
|
|
35
|
+
"unique-id",
|
|
36
|
+
"bigint",
|
|
37
|
+
"nodejs",
|
|
38
|
+
"esm",
|
|
39
|
+
"cjs"
|
|
40
|
+
],
|
|
41
|
+
"author": "Eli Sterling, eliware.org <eli@eliware.org>",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/eliware/snowflake/issues"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/eliware/snowflake#readme",
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"jest": "^30.0.4"
|
|
49
|
+
},
|
|
50
|
+
"jest": {
|
|
51
|
+
"testEnvironment": "node",
|
|
52
|
+
"testMatch": [
|
|
53
|
+
"**/?(*.)+(spec|test).[jt]s?(x)",
|
|
54
|
+
"**/?(*.)+(spec|test).mjs",
|
|
55
|
+
"**/?(*.)+(spec|test).cjs"
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"@eliware/common": "^1.1.1"
|
|
60
|
+
}
|
|
61
|
+
}
|