simpleflakes 4.0.0 → 4.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/README.md +35 -11
- package/dist/simpleflakes.d.ts +1 -1
- package/dist/simpleflakes.d.ts.map +1 -1
- package/dist/simpleflakes.js +1 -1
- package/dist/simpleflakes.js.map +1 -1
- package/package.json +18 -13
package/README.md
CHANGED
|
@@ -5,19 +5,18 @@
|
|
|
5
5
|
[![coveralls status][coveralls-badge]][coveralls-link]
|
|
6
6
|
[](https://www.npmjs.com/package/simpleflakes)
|
|
7
7
|
[](https://bundlephobia.com/package/simpleflakes)
|
|
8
|
-
[](https://www.npmjs.com/package/simpleflakes)
|
|
9
8
|
[](https://www.typescriptlang.org/)
|
|
10
9
|
[](https://nodejs.org/)
|
|
11
|
-
[](https://github.com/leodutra/simpleflakes/actions/workflows/ci.yml)
|
|
12
11
|
[](https://github.com/leodutra/simpleflakes)
|
|
13
|
-
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fleodutra%2Fsimpleflakes?ref=badge_shield)
|
|
12
|
+
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fleodutra%2Fsimpleflakes?ref=badge_shield&issueType=license)
|
|
14
13
|
|
|
15
|
-
> **Fast, lightweight, and reliable distributed 64-bit ID generation for Node.js**
|
|
16
|
-
> Zero dependencies • TypeScript-ready •
|
|
14
|
+
> **Fast, lightweight, and reliable distributed 64-bit ID generation for Node.js and the web**
|
|
15
|
+
> Zero dependencies • TypeScript-ready • 10M+ ops/sec performance
|
|
17
16
|
|
|
18
17
|
## Features
|
|
19
18
|
|
|
20
|
-
- ⚡ **
|
|
19
|
+
- ⚡ **10M+ ops/sec** - Ultra-fast performance
|
|
21
20
|
- 🔢 **Time-oriented 64-bit IDs** - Globally unique, sortable by creation time
|
|
22
21
|
- 0️⃣ **Zero dependencies** - Pure JavaScript, lightweight bundle
|
|
23
22
|
- 🏷️ **TypeScript-ready** - Full type safety and universal module support
|
|
@@ -49,6 +48,8 @@ Simpleflake generates **unique 64-bit integers** that are:
|
|
|
49
48
|
|
|
50
49
|
Perfect for database primary keys, distributed system IDs, and anywhere you need fast, unique identifiers.
|
|
51
50
|
|
|
51
|
+
Works in Node.js and modern browsers with BigInt and Web Crypto support.
|
|
52
|
+
|
|
52
53
|
## References
|
|
53
54
|
|
|
54
55
|
- **[Original Presentation](http://akmanalp.com/simpleflake_presentation/)** - Introduction to the concept by Mali Akmanalp
|
|
@@ -156,13 +157,29 @@ This gives you:
|
|
|
156
157
|
|
|
157
158
|
This library is optimized for speed:
|
|
158
159
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
simpleflake()
|
|
162
|
-
|
|
163
|
-
binary()
|
|
160
|
+
| Operation | Ops/sec | Time/op |
|
|
161
|
+
|-----------|---------|---------|
|
|
162
|
+
| `simpleflake()` | 11,468,350 | 87.20 ns |
|
|
163
|
+
| `simpleflake(timestamp, randomBits, epoch)` | 14,557,764 | 68.69 ns |
|
|
164
|
+
| `binary()` | 22,794,523 | 43.87 ns |
|
|
165
|
+
| `BigInt()` | 15,933,159 | 62.76 ns |
|
|
166
|
+
| `parseSimpleflake()` | 6,722,882 | 148.75 ns |
|
|
167
|
+
|
|
168
|
+
Benchmark command:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
npm run benchmark
|
|
164
172
|
```
|
|
165
173
|
|
|
174
|
+
Benchmark environment:
|
|
175
|
+
|
|
176
|
+
- Node.js v24.14.1
|
|
177
|
+
- Linux 6.6.87.2-microsoft-standard-WSL2 (WSL2)
|
|
178
|
+
- AMD Ryzen 7 5800X3D 8-Core Processor
|
|
179
|
+
- 8 cores / 16 threads
|
|
180
|
+
|
|
181
|
+
Results will vary across CPUs, Node.js versions, power profiles, and native Linux vs. WSL2 environments.
|
|
182
|
+
|
|
166
183
|
Perfect for high-throughput applications requiring millions of IDs per second.
|
|
167
184
|
|
|
168
185
|
## Architecture
|
|
@@ -182,6 +199,13 @@ No coordination required between multiple ID generators:
|
|
|
182
199
|
- **Random collision protection**: 23 random bits provide 8.3M combinations per millisecond
|
|
183
200
|
- **High availability**: Each service can generate IDs independently
|
|
184
201
|
|
|
202
|
+
### Randomness Considerations
|
|
203
|
+
|
|
204
|
+
- **Cryptographic randomness by default**: simpleflake() uses Web Crypto in browsers and Node's built-in crypto implementation in Node.js.
|
|
205
|
+
- **No Math.random() fallback**: this keeps the default entropy source strong and avoids silently weakening collision resistance.
|
|
206
|
+
- **Runtime requirements**: modern browsers need `crypto.getRandomValues()` and Node.js needs version 16 or newer.
|
|
207
|
+
- **Deterministic or legacy environments**: pass `randomBits` explicitly if you need repeatable output or if you are targeting a runtime without crypto support.
|
|
208
|
+
|
|
185
209
|
## Use Cases
|
|
186
210
|
|
|
187
211
|
### Database Primary Keys
|
package/dist/simpleflakes.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ declare const SIMPLEFLAKE_EPOCH = 946684800000n;
|
|
|
2
2
|
/**
|
|
3
3
|
* Generates a simpleflake ID
|
|
4
4
|
* @param ts - Timestamp in milliseconds (defaults to current time)
|
|
5
|
-
* @param randomBits - Random bits for the ID (defaults to a random value)
|
|
5
|
+
* @param randomBits - Random bits for the ID (defaults to a cryptographically secure random value)
|
|
6
6
|
* @param epoch - Epoch timestamp in milliseconds (defaults to SIMPLEFLAKE_EPOCH)
|
|
7
7
|
* @returns Generated simpleflake as a BigInt
|
|
8
8
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"simpleflakes.d.ts","sourceRoot":"","sources":["../src/simpleflakes.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,iBAAiB,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"simpleflakes.d.ts","sourceRoot":"","sources":["../src/simpleflakes.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,iBAAiB,gBAAgB,CAAC;AA2DxC;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,EAAE,GAAE,MAAM,GAAG,MAAmB,EAChC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,EAC5B,KAAK,GAAE,MAAM,GAAG,MAA0B,GACzC,MAAM,CAOR;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CACpB,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAC/B,OAAO,GAAE,OAAc,GACtB,MAAM,CAKR;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAC9B,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,MAAM,EAAE,MAAM,GAAG,MAAM,GACtB,MAAM,CAKR;AAED;;GAEG;AACH,qBAAa,iBAAiB;IAC5B,SAAwB,SAAS,EAAE,MAAM,CAAC;IAC1C,SAAwB,UAAU,EAAE,MAAM,CAAC;gBAE/B,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;CAOlD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAC9B,iBAAiB,CAWnB;AAGD,OAAO,EAAE,iBAAiB,EAAE,CAAC;;;;;;;;;AAG7B,wBAOE"}
|
package/dist/simpleflakes.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),function(e,t){for(var r in t)Object.defineProperty(e,r,{enumerable:!0,get:Object.getOwnPropertyDescriptor(t,r).get})}(exports,{get SIMPLEFLAKE_EPOCH(){return SIMPLEFLAKE_EPOCH},get SimpleflakeStruct(){return SimpleflakeStruct},get binary(){return binary},get default(){return _default},get extractBits(){return extractBits},get parseSimpleflake(){return parseSimpleflake},get simpleflake(){return simpleflake}});const SIMPLEFLAKE_EPOCH=946684800000n;let randomBuffer=new Uint32Array(1024),randomBufferIndex=1024;function toBigInt(e){return"bigint"==typeof e?e:BigInt(e)}function getRandomSource(){let e=globalThis.crypto;if(e)return e;try{return require("node:crypto").webcrypto}catch{throw Error("Cryptographically secure random values are unavailable in this environment.")}}function refillRandomBuffer(){getRandomSource().getRandomValues(randomBuffer),randomBufferIndex=0}function random23(){randomBufferIndex>=1024&&refillRandomBuffer();let e=randomBuffer[randomBufferIndex];return randomBufferIndex+=1,BigInt(8388607&e)}function simpleflake(e=Date.now(),t,r=SIMPLEFLAKE_EPOCH){return toBigInt(e)-toBigInt(r)<<23n|(null==t?random23():toBigInt(t))}function binary(e,t=!0){let r=toBigInt(e).toString(2);return t&&r.length<64?"0000000000000000000000000000000000000000000000000000000000000000".substr(0,64-r.length)+r:r}function extractBits(e,t,r){let n=toBigInt(t),i=toBigInt(r);return toBigInt(e)>>n&(1n<<i)-1n}class SimpleflakeStruct{constructor(e,t){if(null==e||null==t)throw Error("Missing argument for SimpleflakeStruct.");this.timestamp=e,this.randomBits=t}}function parseSimpleflake(e){return new SimpleflakeStruct(extractBits(e,23n,41n)+SIMPLEFLAKE_EPOCH,extractBits(e,0n,23n))}const _default={binary,extractBits,parseSimpleflake,SIMPLEFLAKE_EPOCH,simpleflake,SimpleflakeStruct};
|
|
2
2
|
|
|
3
3
|
//# sourceMappingURL=simpleflakes.js.map
|
package/dist/simpleflakes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/simpleflakes.ts"],"names":["SIMPLEFLAKE_EPOCH","SimpleflakeStruct","binary","extractBits","parseSimpleflake","simpleflake","
|
|
1
|
+
{"version":3,"sources":["../src/simpleflakes.ts"],"names":["SIMPLEFLAKE_EPOCH","SimpleflakeStruct","binary","extractBits","parseSimpleflake","simpleflake","randomBuffer","Uint32Array","randomBufferIndex","toBigInt","value","BigInt","getRandomSource","globalCrypto","globalThis","crypto","require","webcrypto","Error","refillRandomBuffer","getRandomValues","random23","ts","Date","now","randomBits","epoch","padding","binValue","toString","length","CACHE_64_BIT_ZEROS","substr","data","shift","shiftN","lengthN","timestamp","flake"],"mappings":"uMAsJSA,2BAAAA,uBAlCIC,2BAAAA,uBA/BGC,gBAAAA,YAoEhB,iBAAA,cAnDgBC,qBAAAA,iBAgCAC,0BAAAA,sBApEAC,qBAAAA,eAlEhB,MAAML,kBAAoB,YAAA,AAAa,CAAA,CAmBvC,IAAIM,aAAe,IAAIC,YAjBI,MAkBvBC,kBAlBuB,KAoB3B,SAASC,SAASC,CAA+B,EAC/C,MAAO,AAAiB,UAAjB,OAAOA,EAAqBA,EAAQC,OAAOD,EACpD,CAEA,SAASE,kBACP,IAAMC,EAAe,AAACC,WAEnBC,MAAM,CAET,GAAIF,EAAc,OAAOA,EAEzB,GAAI,CACF,OAAO,AAACG,QACN,eACAC,SAAS,AACb,CAAE,KAAM,CACN,MAAM,AAAIC,MACR,8EAEJ,CACF,CAEA,SAASC,qBACPP,kBAAkBQ,eAAe,CAACd,cAClCE,kBAAoB,CACtB,CAEA,SAASa,WACHb,mBAhDqB,MAiDvBW,qBAEF,IAAMT,EAAQJ,YAAY,CAACE,kBAAkB,CAG7C,OADAA,mBAAqB,EACdG,OAvDkB,AAuDXD,QAAAA,EAChB,CASO,SAASL,YACdiB,EAAsBC,KAAKC,GAAG,EAAE,CAChCC,CAA4B,CAC5BC,EAAyB1B,iBAAiB,EAI1C,OACE,AAAES,SAASa,GAAMb,SAASiB,IAlEM,EAAA,AAAG,CAAA,CAmElCD,CAAAA,AAAc,MAAdA,EAAqBJ,WAAaZ,SAASgB,EAAU,CAE1D,CAQO,SAASvB,OACdQ,CAA+B,CAC/BiB,EAAmB,CAAA,CAAI,EAEvB,IAAMC,EAAWnB,SAASC,GAAOmB,QAAQ,CAAC,GAC1C,OAAOF,GAAWC,EAASE,MAAM,CAAG,GA/EpC,AAgFIC,mEAAmBC,MAAM,CAAC,EAAG,GAAKJ,EAASE,MAAM,EAAIF,EACrDA,CACN,CASO,SAASzB,YACd8B,CAA8B,CAC9BC,CAAsB,CACtBJ,CAAuB,EAEvB,IAAMK,EAAS1B,SAASyB,GAClBE,EAAU3B,SAASqB,GAEzB,OAAO,AAACrB,SAASwB,IAASE,EAAW,AAAC,CAAA,CAAA,AAAE,CAAA,EAAIC,CAAM,EAAK,CAAA,AAAE,CAAA,AAC3D,CAKO,MAAMnC,kBAIX,YAAYoC,CAAiB,CAAEZ,CAAkB,CAAE,CACjD,GAAIY,AAAa,MAAbA,GAAqBZ,AAAc,MAAdA,EACvB,MAAM,AAAIP,MAAM,0CAElB,CAAA,IAAI,CAACmB,SAAS,CAAGA,EACjB,IAAI,CAACZ,UAAU,CAAGA,CACpB,CACF,CAOO,SAASrB,iBACdkC,CAA+B,EAE/B,OAAO,IAAIrC,kBAETE,YACEmC,EApI8B,EAAA,AAAG,CAAA,CAJF,EAAA,AAAG,CAAA,EA2IhCtC,kBAEJG,YAAYmC,EA1IiB,CAAA,AAAE,CAAA,CAFD,EAAA,AAAG,CAAA,EA8IrC,OAMA,SAAe,CACbpC,OACAC,YACAC,iBACAJ,kBACAK,YACAJ,iBACF","file":"simpleflakes.js","sourcesContent":["const SIMPLEFLAKE_EPOCH = 946684800000n; // Date.UTC(2000, 0, 1) == epoch ms, since 1 Jan 2000 00:00\nconst UNSIGNED_23BIT_MAX = 8388607; // (Math.pow(2, 23) - 1) >> 0\nconst RANDOM_BUFFER_SIZE = 1024;\n\nconst SIMPLEFLAKE_TIMESTAMP_LENGTH = 41n;\nconst SIMPLEFLAKE_RANDOM_LENGTH = 23n;\n\nconst SIMPLEFLAKE_RANDOM_SHIFT = 0n;\nconst SIMPLEFLAKE_TIMESTAMP_SHIFT = 23n;\n\nconst CACHE_64_BIT_ZEROS =\n \"0000000000000000000000000000000000000000000000000000000000000000\";\n\ninterface RandomSource {\n getRandomValues<T extends ArrayBufferView>(array: T): T;\n}\n\ndeclare const require: ((moduleName: string) => unknown) | undefined;\n\nlet randomBuffer = new Uint32Array(RANDOM_BUFFER_SIZE);\nlet randomBufferIndex = RANDOM_BUFFER_SIZE;\n\nfunction toBigInt(value: bigint | number | string): bigint {\n return typeof value === \"bigint\" ? value : BigInt(value);\n}\n\nfunction getRandomSource(): RandomSource {\n const globalCrypto = (globalThis as typeof globalThis & {\n crypto?: RandomSource;\n }).crypto;\n\n if (globalCrypto) return globalCrypto;\n\n try {\n return (require as (moduleName: string) => { webcrypto: RandomSource })(\n [\"node\", \"crypto\"].join(\":\")\n ).webcrypto;\n } catch {\n throw new Error(\n \"Cryptographically secure random values are unavailable in this environment.\"\n );\n }\n}\n\nfunction refillRandomBuffer(): void {\n getRandomSource().getRandomValues(randomBuffer);\n randomBufferIndex = 0;\n}\n\nfunction random23(): bigint {\n if (randomBufferIndex >= RANDOM_BUFFER_SIZE) {\n refillRandomBuffer();\n }\n const value = randomBuffer[randomBufferIndex]!;\n\n randomBufferIndex += 1;\n return BigInt(value & UNSIGNED_23BIT_MAX);\n}\n\n/**\n * Generates a simpleflake ID\n * @param ts - Timestamp in milliseconds (defaults to current time)\n * @param randomBits - Random bits for the ID (defaults to a cryptographically secure random value)\n * @param epoch - Epoch timestamp in milliseconds (defaults to SIMPLEFLAKE_EPOCH)\n * @returns Generated simpleflake as a BigInt\n */\nexport function simpleflake(\n ts: number | bigint = Date.now(),\n randomBits?: number | bigint,\n epoch: number | bigint = SIMPLEFLAKE_EPOCH\n): bigint {\n // Use bitwise OR instead of addition since bit ranges don't overlap\n\n return (\n ((toBigInt(ts) - toBigInt(epoch)) << SIMPLEFLAKE_TIMESTAMP_SHIFT) |\n (randomBits == null ? random23() : toBigInt(randomBits))\n );\n}\n\n/**\n * Converts a value to binary representation\n * @param value - The value to convert to binary\n * @param padding - Whether to pad to 64 bits (defaults to true)\n * @returns Binary string representation\n */\nexport function binary(\n value: bigint | number | string,\n padding: boolean = true\n): string {\n const binValue = toBigInt(value).toString(2);\n return padding && binValue.length < 64\n ? CACHE_64_BIT_ZEROS.substr(0, 64 - binValue.length) + binValue\n : binValue;\n}\n\n/**\n * Extracts bits from a data value\n * @param data - The data to extract bits from\n * @param shift - Number of bits to shift\n * @param length - Number of bits to extract\n * @returns Extracted bits as a BigInt\n */\nexport function extractBits(\n data: bigint | number | string,\n shift: bigint | number,\n length: bigint | number\n): bigint {\n const shiftN = toBigInt(shift);\n const lengthN = toBigInt(length);\n // Optimize: shift right first, then mask (avoids creating large bitmask)\n return (toBigInt(data) >> shiftN) & ((1n << lengthN) - 1n);\n}\n\n/**\n * Structure representing a parsed simpleflake\n */\nexport class SimpleflakeStruct {\n public declare readonly timestamp: bigint;\n public declare readonly randomBits: bigint;\n\n constructor(timestamp: bigint, randomBits: bigint) {\n if (timestamp == null || randomBits == null) {\n throw new Error(\"Missing argument for SimpleflakeStruct.\");\n }\n this.timestamp = timestamp;\n this.randomBits = randomBits;\n }\n}\n\n/**\n * Parses a simpleflake into its components\n * @param flake - The simpleflake to parse\n * @returns SimpleflakeStruct containing timestamp and random bits\n */\nexport function parseSimpleflake(\n flake: bigint | number | string\n): SimpleflakeStruct {\n return new SimpleflakeStruct(\n // timestamp\n extractBits(\n flake,\n SIMPLEFLAKE_TIMESTAMP_SHIFT,\n SIMPLEFLAKE_TIMESTAMP_LENGTH\n ) + SIMPLEFLAKE_EPOCH,\n // random bits\n extractBits(flake, SIMPLEFLAKE_RANDOM_SHIFT, SIMPLEFLAKE_RANDOM_LENGTH)\n );\n}\n\n// Export constants\nexport { SIMPLEFLAKE_EPOCH };\n\n// Default export for CommonJS compatibility\nexport default {\n binary,\n extractBits,\n parseSimpleflake,\n SIMPLEFLAKE_EPOCH,\n simpleflake,\n SimpleflakeStruct,\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "simpleflakes",
|
|
3
|
-
"version": "4.
|
|
4
|
-
"description": "Fast, lightweight, and reliable distributed 64-bit ID generation with zero dependencies for Node.js.",
|
|
3
|
+
"version": "4.1.1",
|
|
4
|
+
"description": "Fast, lightweight, and reliable distributed 64-bit ID generation with zero dependencies for Node.js and the web.",
|
|
5
5
|
"main": "dist/simpleflakes.js",
|
|
6
6
|
"types": "dist/simpleflakes.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -33,19 +33,24 @@
|
|
|
33
33
|
"keywords": [
|
|
34
34
|
"simpleflake",
|
|
35
35
|
"snowflake",
|
|
36
|
-
"
|
|
37
|
-
"id",
|
|
36
|
+
"snowflake-id",
|
|
37
|
+
"id-generator",
|
|
38
|
+
"unique-id",
|
|
39
|
+
"sortable-id",
|
|
40
|
+
"time-ordered",
|
|
41
|
+
"64-bit",
|
|
42
|
+
"bigint",
|
|
43
|
+
"distributed-id",
|
|
44
|
+
"database-id",
|
|
38
45
|
"uuid",
|
|
39
|
-
"sequential",
|
|
40
|
-
"monotonic",
|
|
41
|
-
"ksuid",
|
|
42
|
-
"nanoid",
|
|
43
46
|
"uuidv7",
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"typescript"
|
|
47
|
+
"nanoid",
|
|
48
|
+
"ksuid",
|
|
49
|
+
"webcrypto",
|
|
50
|
+
"zero-dependency",
|
|
51
|
+
"typescript",
|
|
52
|
+
"nodejs",
|
|
53
|
+
"browser"
|
|
49
54
|
],
|
|
50
55
|
"author": {
|
|
51
56
|
"name": "Leonardo Dutra",
|