simpleflakes 3.0.0 → 3.0.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 +297 -66
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,100 +9,299 @@
|
|
|
9
9
|
[](https://www.npmjs.com/package/simpleflakes)
|
|
10
10
|
[](https://www.typescriptlang.org/)
|
|
11
11
|
[](https://github.com/leodutra/simpleflakes)
|
|
12
|
-
<!-- [![codacy quality][codacy-quality-badge]][codacy-quality-link]
|
|
13
|
-
[![codacy coverage][codacy-coverage-badge]][codacy-coverage-link] -->
|
|
14
12
|
[![coveralls status][coveralls-badge]][coveralls-link] [](https://app.fossa.io/projects/git%2Bgithub.com%2Fleodutra%2Fsimpleflakes?ref=badge_shield)
|
|
15
13
|
|
|
16
|
-
|
|
14
|
+
> **Fast, reliable, distributed 64-bit ID generation for Node.js** ⚡
|
|
15
|
+
> Zero dependencies • TypeScript-ready • 8.8M+ ops/sec performance
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
This port is test-driven and no release goes out without tests.
|
|
20
|
-
**Also, this library does not rely on low-level bindings, with OpenSSL, libgmp or anything beyond pure JavaScript.**
|
|
17
|
+
## ✨ Features
|
|
21
18
|
|
|
22
|
-
|
|
19
|
+
- 🚀 **Ultra-fast**: 8.8M+ operations per second
|
|
20
|
+
- 🔢 **64-bit time-ordered IDs**: Globally unique, sortable by creation time
|
|
21
|
+
- 📦 **Zero dependencies**: Pure JavaScript implementation
|
|
22
|
+
- 🏷️ **TypeScript-first**: Full type safety and IntelliSense support
|
|
23
|
+
- 🌐 **Universal**: Works with CommonJS, ES Modules, and TypeScript
|
|
24
|
+
- ⚖️ **Lightweight**: Tiny bundle size, tree-shakable
|
|
25
|
+
- 🧪 **Battle-tested**: 100% test coverage, production-ready
|
|
26
|
+
- 🔄 **Snowflake compatible**: Drop-in replacement for Twitter Snowflake
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
## 🏗️ What is Simpleflake?
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
Simpleflake generates **unique 64-bit integers** that are:
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
1. **Time-ordered** - IDs generated later are numerically larger
|
|
33
|
+
2. **Distributed-safe** - No coordination needed between multiple generators
|
|
34
|
+
3. **Compact** - Fits in a 64-bit integer (vs UUID's 128 bits)
|
|
35
|
+
4. **URL-friendly** - Can be represented as short strings
|
|
36
|
+
|
|
37
|
+
Perfect for database primary keys, distributed system IDs, and anywhere you need fast, unique identifiers.
|
|
38
|
+
|
|
39
|
+
[See the original presentation!](http://akmanalp.com/simpleflake_presentation/)
|
|
40
|
+
|
|
41
|
+
## 📦 Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install simpleflakes
|
|
30
45
|
```
|
|
31
46
|
|
|
32
|
-
|
|
33
|
-
|
|
47
|
+
## 🚀 Quick Start
|
|
48
|
+
|
|
49
|
+
### JavaScript (CommonJS)
|
|
50
|
+
```javascript
|
|
34
51
|
const { simpleflake } = require('simpleflakes');
|
|
35
52
|
|
|
36
|
-
|
|
53
|
+
// Generate a unique ID
|
|
54
|
+
const id = simpleflake();
|
|
55
|
+
console.log(id); // 4234673179811182512n (BigInt)
|
|
37
56
|
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
|
|
57
|
+
// Convert to different formats
|
|
58
|
+
console.log(id.toString()); // "4234673179811182512"
|
|
59
|
+
console.log(id.toString(16)); // "3ac494d21e84f7b0" (hex)
|
|
60
|
+
console.log(id.toString(36)); // "20rfh5bt4k0g" (base36 - shortest)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### TypeScript / ES Modules
|
|
64
|
+
```typescript
|
|
65
|
+
import { simpleflake, parseSimpleflake, type SimpleFlakeStruct } from 'simpleflakes';
|
|
44
66
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
67
|
+
// Generate with full type safety
|
|
68
|
+
const id: bigint = simpleflake();
|
|
69
|
+
|
|
70
|
+
// Parse the ID to extract timestamp and random bits
|
|
71
|
+
const parsed: SimpleFlakeStruct = parseSimpleflake(id);
|
|
72
|
+
console.log(parsed.timestamp); // "1693244847123" (Unix timestamp as string)
|
|
73
|
+
console.log(parsed.randomBits); // "4567234" (Random component as string)
|
|
49
74
|
```
|
|
50
75
|
|
|
51
|
-
|
|
52
|
-
The library is written in TypeScript and includes full type definitions:
|
|
76
|
+
## 🎯 Advanced Usage
|
|
53
77
|
|
|
54
|
-
|
|
55
|
-
|
|
78
|
+
### Custom Parameters
|
|
79
|
+
```javascript
|
|
80
|
+
// Generate with custom timestamp and random bits
|
|
81
|
+
const customId = simpleflake(
|
|
82
|
+
Date.now(), // timestamp (default: Date.now())
|
|
83
|
+
12345, // random bits (default: 23-bit random)
|
|
84
|
+
Date.UTC(2000, 0, 1) // epoch (default: Year 2000)
|
|
85
|
+
);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Working with Binary Data
|
|
89
|
+
```javascript
|
|
90
|
+
import { binary, extractBits } from 'simpleflakes';
|
|
56
91
|
|
|
57
|
-
|
|
58
|
-
const flakeId: bigint = simpleflake();
|
|
92
|
+
const id = simpleflake();
|
|
59
93
|
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
console.log(parsed.randomBits); // string
|
|
94
|
+
// View binary representation
|
|
95
|
+
console.log(binary(id));
|
|
96
|
+
// Output: "0011101011000100100100110100001000011110100001001111011110110000"
|
|
64
97
|
|
|
65
|
-
//
|
|
66
|
-
const
|
|
67
|
-
const
|
|
98
|
+
// Extract specific bit ranges
|
|
99
|
+
const timestampBits = extractBits(id, 23n, 41n); // Extract 41 bits starting at position 23
|
|
100
|
+
const randomBits = extractBits(id, 0n, 23n); // Extract first 23 bits
|
|
68
101
|
```
|
|
69
102
|
|
|
70
|
-
|
|
103
|
+
### Batch Generation
|
|
104
|
+
```javascript
|
|
105
|
+
// Generate multiple IDs efficiently
|
|
106
|
+
function generateBatch(count) {
|
|
107
|
+
const ids = [];
|
|
108
|
+
for (let i = 0; i < count; i++) {
|
|
109
|
+
ids.push(simpleflake());
|
|
110
|
+
}
|
|
111
|
+
return ids;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const batch = generateBatch(1000);
|
|
115
|
+
console.log(`Generated ${batch.length} unique IDs`);
|
|
116
|
+
```
|
|
71
117
|
|
|
118
|
+
## 🔬 ID Structure
|
|
72
119
|
|
|
73
|
-
|
|
74
|
-
```js
|
|
75
|
-
// Main flake function and its defaults
|
|
76
|
-
simpleflake(
|
|
77
|
-
timestamp = Date.now(),
|
|
78
|
-
random_bits = 23-bit random,
|
|
79
|
-
epoch = Date.UTC(2000, 0, 1)
|
|
80
|
-
)
|
|
120
|
+
Each 64-bit simpleflake ID contains:
|
|
81
121
|
|
|
82
|
-
|
|
83
|
-
|
|
122
|
+
```
|
|
123
|
+
|-- 41 bits ---|-- 23 bits --|
|
|
124
|
+
| Timestamp | Random |
|
|
125
|
+
| (milliseconds)| (0-8388607) |
|
|
126
|
+
```
|
|
84
127
|
|
|
85
|
-
|
|
86
|
-
|
|
128
|
+
- **41 bits timestamp**: Milliseconds since epoch (Year 2000)
|
|
129
|
+
- **23 bits random**: Random number for uniqueness within the same millisecond
|
|
130
|
+
- **Total**: 64 bits = fits in a signed 64-bit integer
|
|
87
131
|
|
|
88
|
-
|
|
89
|
-
|
|
132
|
+
This gives you:
|
|
133
|
+
- **69+ years** of timestamp range (until year 2069)
|
|
134
|
+
- **8.3 million** unique IDs per millisecond
|
|
135
|
+
- **Sortable by creation time** when converted to integers
|
|
90
136
|
|
|
91
|
-
|
|
92
|
-
simpleflake.parseSimpleflake(flake)
|
|
137
|
+
## ⚡ Performance
|
|
93
138
|
|
|
94
|
-
|
|
95
|
-
simpleflake.simpleflakeStruct
|
|
139
|
+
Simpleflakes is optimized for speed:
|
|
96
140
|
|
|
97
|
-
|
|
98
|
-
|
|
141
|
+
```javascript
|
|
142
|
+
// Benchmark results (operations per second)
|
|
143
|
+
simpleflake() // ~8.8M ops/sec
|
|
144
|
+
parseSimpleflake() // ~3.9M ops/sec
|
|
145
|
+
binary() // ~26M ops/sec
|
|
99
146
|
```
|
|
100
147
|
|
|
101
|
-
|
|
148
|
+
Perfect for high-throughput applications requiring millions of IDs per second.
|
|
149
|
+
|
|
150
|
+
## 🏛️ Architecture
|
|
151
|
+
|
|
152
|
+
### Why 64-bit IDs?
|
|
153
|
+
|
|
154
|
+
- **Database-friendly**: Most databases optimize for 64-bit integers
|
|
155
|
+
- **Memory efficient**: Half the size of UUIDs (128-bit)
|
|
156
|
+
- **Performance**: Integer operations are faster than string operations
|
|
157
|
+
- **Sortable**: Natural ordering by creation time
|
|
158
|
+
- **Compact URLs**: Shorter than UUIDs when base36-encoded
|
|
159
|
+
|
|
160
|
+
### Distributed Generation
|
|
161
|
+
|
|
162
|
+
No coordination required between multiple ID generators:
|
|
163
|
+
- **Clock skew tolerant**: Small time differences between servers are fine
|
|
164
|
+
- **Random collision protection**: 23 random bits provide 8.3M combinations per millisecond
|
|
165
|
+
- **High availability**: Each service can generate IDs independently
|
|
166
|
+
|
|
167
|
+
## 🧪 API Reference
|
|
168
|
+
|
|
169
|
+
### Core Functions
|
|
170
|
+
|
|
171
|
+
#### `simpleflake(timestamp?, randomBits?, epoch?): bigint`
|
|
172
|
+
Generates a unique 64-bit ID.
|
|
173
|
+
|
|
174
|
+
**Parameters:**
|
|
175
|
+
- `timestamp` (number, optional): Unix timestamp in milliseconds. Default: `Date.now()`
|
|
176
|
+
- `randomBits` (number, optional): Random bits (0-8388607). Default: random 23-bit number
|
|
177
|
+
- `epoch` (number, optional): Epoch start time. Default: `Date.UTC(2000, 0, 1)`
|
|
178
|
+
|
|
179
|
+
**Returns:** BigInt - The generated ID
|
|
180
|
+
|
|
181
|
+
```javascript
|
|
182
|
+
const id = simpleflake();
|
|
183
|
+
const customId = simpleflake(Date.now(), 12345, Date.UTC(2000, 0, 1));
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
#### `parseSimpleflake(flake): SimpleFlakeStruct`
|
|
187
|
+
Parses a simpleflake ID into its components.
|
|
188
|
+
|
|
189
|
+
**Parameters:**
|
|
190
|
+
- `flake` (bigint | string | number): The ID to parse
|
|
191
|
+
|
|
192
|
+
**Returns:** Object with `timestamp` and `randomBits` properties (both strings)
|
|
193
|
+
|
|
194
|
+
```javascript
|
|
195
|
+
const parsed = parseSimpleflake(4234673179811182512n);
|
|
196
|
+
console.log(parsed.timestamp); // "1693244847123"
|
|
197
|
+
console.log(parsed.randomBits); // "4567234"
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
#### `binary(value, padding?): string`
|
|
201
|
+
Converts a number to binary string representation.
|
|
202
|
+
|
|
203
|
+
**Parameters:**
|
|
204
|
+
- `value` (bigint | string | number): Value to convert
|
|
205
|
+
- `padding` (boolean, optional): Whether to pad to 64 bits. Default: `true`
|
|
206
|
+
|
|
207
|
+
**Returns:** String - Binary representation
|
|
208
|
+
|
|
209
|
+
```javascript
|
|
210
|
+
console.log(binary(42n)); // "0000000000000000000000000000000000000000000000000000000000101010"
|
|
211
|
+
console.log(binary(42n, false)); // "101010"
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
#### `extractBits(data, shift, length): bigint`
|
|
215
|
+
Extracts a portion of bits from a number.
|
|
216
|
+
|
|
217
|
+
**Parameters:**
|
|
218
|
+
- `data` (bigint | string | number): Source data
|
|
219
|
+
- `shift` (bigint): Starting bit position (0-based from right)
|
|
220
|
+
- `length` (bigint): Number of bits to extract
|
|
221
|
+
|
|
222
|
+
**Returns:** BigInt - Extracted bits as number
|
|
223
|
+
|
|
224
|
+
```javascript
|
|
225
|
+
const bits = extractBits(0b11110000n, 4n, 4n); // Extract 4 bits starting at position 4
|
|
226
|
+
console.log(bits); // 15n (0b1111)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Constants
|
|
230
|
+
|
|
231
|
+
#### `SIMPLEFLAKE_EPOCH: number`
|
|
232
|
+
The epoch start time (January 1, 2000 UTC) as Unix timestamp.
|
|
233
|
+
|
|
234
|
+
```javascript
|
|
235
|
+
import { SIMPLEFLAKE_EPOCH } from 'simpleflakes';
|
|
236
|
+
console.log(SIMPLEFLAKE_EPOCH); // 946684800000
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### TypeScript Types
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
interface SimpleFlakeStruct {
|
|
243
|
+
timestamp: string; // Unix timestamp as string
|
|
244
|
+
randomBits: string; // Random component as string
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## 🔄 Migration Guide
|
|
249
|
+
|
|
250
|
+
### From UUID
|
|
251
|
+
```javascript
|
|
252
|
+
// Before (UUID v4)
|
|
253
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
254
|
+
const id = uuidv4(); // "f47ac10b-58cc-4372-a567-0e02b2c3d479"
|
|
255
|
+
|
|
256
|
+
// After (Simpleflake)
|
|
257
|
+
import { simpleflake } from 'simpleflakes';
|
|
258
|
+
const id = simpleflake().toString(36); // "20rfh5bt4k0g" (shorter!)
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### From Twitter Snowflake
|
|
262
|
+
```javascript
|
|
263
|
+
// Simpleflake is backwards compatible with Snowflake structure
|
|
264
|
+
// Just different bit allocation:
|
|
265
|
+
// - Snowflake: 41 bits timestamp + 10 bits machine + 12 bits sequence
|
|
266
|
+
// - Simpleflake: 41 bits timestamp + 23 bits random
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## 📖 Use Cases
|
|
270
|
+
|
|
271
|
+
### Database Primary Keys
|
|
272
|
+
```javascript
|
|
273
|
+
// Perfect for database IDs - time-ordered and unique
|
|
274
|
+
const userId = simpleflake();
|
|
275
|
+
await db.users.create({ id: userId.toString(), name: "John" });
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Distributed System IDs
|
|
279
|
+
```javascript
|
|
280
|
+
// Each service can generate IDs independently
|
|
281
|
+
const serviceAId = simpleflake(); // Service A
|
|
282
|
+
const serviceBId = simpleflake(); // Service B
|
|
283
|
+
// No coordination needed, guaranteed unique across services
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Short URLs
|
|
287
|
+
```javascript
|
|
288
|
+
// Generate compact URL identifiers
|
|
289
|
+
const shortId = simpleflake().toString(36); // "20rfh5bt4k0g"
|
|
290
|
+
const url = `https://short.ly/${shortId}`;
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Event Tracking
|
|
294
|
+
```javascript
|
|
295
|
+
// Time-ordered event IDs for chronological processing
|
|
296
|
+
const eventId = simpleflake();
|
|
297
|
+
await analytics.track({ eventId, userId, action: "click" });
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## 🔧 Development
|
|
102
301
|
|
|
103
302
|
This project is written in TypeScript and includes comprehensive test coverage.
|
|
104
303
|
|
|
105
|
-
```
|
|
304
|
+
```bash
|
|
106
305
|
# Install dependencies
|
|
107
306
|
npm install
|
|
108
307
|
|
|
@@ -124,23 +323,55 @@ npm run test:ci
|
|
|
124
323
|
# Type checking
|
|
125
324
|
npm run type-check
|
|
126
325
|
|
|
326
|
+
# Run benchmarks
|
|
327
|
+
npm run benchmark
|
|
328
|
+
|
|
127
329
|
# Clean build artifacts
|
|
128
330
|
npm run clean
|
|
129
331
|
```
|
|
130
332
|
|
|
131
|
-
|
|
333
|
+
## 📚 References
|
|
334
|
+
|
|
335
|
+
- **[Original Presentation](http://akmanalp.com/simpleflake_presentation/)** - Introduction to the concept
|
|
336
|
+
- **[Python Implementation](https://simpleflake.readthedocs.org/en/latest/)** - Original reference implementation
|
|
337
|
+
- **[Twitter Snowflake](https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake.html)** - Similar distributed ID system
|
|
338
|
+
|
|
339
|
+
## 🆚 Comparison
|
|
340
|
+
|
|
341
|
+
| Feature | Simpleflakes | UUID v4 | Twitter Snowflake |
|
|
342
|
+
|---------|-------------|---------|------------------|
|
|
343
|
+
| **Size** | 64-bit | 128-bit | 64-bit |
|
|
344
|
+
| **Time-ordered** | ✅ Yes | ❌ No | ✅ Yes |
|
|
345
|
+
| **Distributed** | ✅ Yes | ✅ Yes | ⚠️ Needs config |
|
|
346
|
+
| **Dependencies** | ✅ Zero | ❌ crypto | ❌ System clock |
|
|
347
|
+
| **Performance** | 🚀 8.8M/sec | 🐌 ~2M/sec | 🚀 ~10M/sec |
|
|
348
|
+
| **URL-friendly** | ✅ Base36 | ❌ Long hex | ✅ Base36 |
|
|
349
|
+
| **Database-friendly** | ✅ Integer | ❌ String | ✅ Integer |
|
|
350
|
+
|
|
351
|
+
## 🤝 Contributing
|
|
352
|
+
|
|
353
|
+
1. Fork the repository
|
|
354
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
355
|
+
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
356
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
357
|
+
5. Open a Pull Request
|
|
358
|
+
|
|
359
|
+
## 📄 License
|
|
132
360
|
|
|
133
361
|
[MIT](https://raw.githubusercontent.com/leodutra/simpleflakes/master/LICENSE)
|
|
134
362
|
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## 🏷️ Credits
|
|
366
|
+
|
|
367
|
+
- Original concept by [Mali Akmanalp](http://akmanalp.com/)
|
|
368
|
+
- TypeScript port and optimizations by [Leo Dutra](https://github.com/leodutra)
|
|
369
|
+
- Inspired by [Twitter Snowflake](https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake.html)
|
|
370
|
+
|
|
135
371
|
[npm-badge]: https://img.shields.io/npm/v/simpleflakes.svg?style=flat
|
|
136
|
-
[codacy-coverage-badge]: https://api.codacy.com/project/badge/Coverage/f71ef817e5f14a9ab3b8b2cb6fabf51a
|
|
137
|
-
[codacy-quality-badge]: https://api.codacy.com/project/badge/Grade/f71ef817e5f14a9ab3b8b2cb6fabf51a
|
|
138
372
|
[coveralls-badge]: https://img.shields.io/coveralls/leodutra/simpleflakes.svg?style=flat
|
|
139
373
|
|
|
140
374
|
[npm-link]: https://www.npmjs.com/package/simpleflakes
|
|
141
|
-
[codacy-quality-link]: https://www.codacy.com/app/leodutra/simpleflakes
|
|
142
|
-
[codacy-coverage-link]: https://www.codacy.com/app/leodutra/simpleflakes?utm_source=github.com&utm_medium=referral&utm_content=leodutra/simpleflakes&utm_campaign=Badge_Coverage
|
|
143
375
|
[coveralls-link]: https://coveralls.io/github/leodutra/simpleflakes
|
|
144
376
|
|
|
145
|
-
|
|
146
377
|
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fleodutra%2Fsimpleflakes?ref=badge_large)
|
package/package.json
CHANGED