@toolkit-f/snowflake-id 1.0.0 → 1.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.
Files changed (2) hide show
  1. package/README.md +632 -0
  2. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,632 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/maulik-mk/Snowflake-Id/main/public/image.png" alt="Snowflake ID Logo" width="600" />
3
+ </p>
4
+
5
+
6
+ <p align="center">
7
+ <a href="https://www.npmjs.com/package/@toolkit-f/snowflake-id"><img src="https://img.shields.io/npm/v/@toolkit-f/snowflake-id.svg" alt="npm version"></a>
8
+ <a href="https://www.npmjs.com/package/@toolkit-f/snowflake-id"><img src="https://img.shields.io/npm/dm/@toolkit-f/snowflake-id.svg" alt="npm downloads"></a>
9
+ <a href="https://github.com/maulik-mk/Snowflake-Id/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@toolkit-f/snowflake-id.svg" alt="license"></a>
10
+ <img src="https://img.shields.io/badge/TypeScript-Ready-blue.svg" alt="TypeScript">
11
+ </p>
12
+
13
+ <p align="center">
14
+ A Snowflake ID generator for Node.js • No dependencies • TypeScript support
15
+ </p>
16
+
17
+ # Snowflake-ID
18
+
19
+ Snowflake IDs are 64-bit unique identifiers. Sortable by time and can be generated across multiple servers without coordination and collision.
20
+
21
+ ```
22
+ | 41 bits - Timestamp | 10 bits - Machine ID | 12 bits - Sequence |
23
+ ```
24
+
25
+ ### Features
26
+
27
+ - Generates unique IDs across distributed systems
28
+ - Works with CommonJS and ESM
29
+ - Full TypeScript support
30
+ - Parse IDs to extract timestamp, machine ID, and sequence
31
+
32
+ ---
33
+
34
+ ## Installation
35
+
36
+ ### npm
37
+ ```bash
38
+ npm install @toolkit-f/snowflake-id
39
+ ```
40
+
41
+ ### Yarn
42
+ ```bash
43
+ yarn add @toolkit-f/snowflake-id
44
+ ```
45
+
46
+ ### pnpm
47
+ ```bash
48
+ pnpm add @toolkit-f/snowflake-id
49
+ ```
50
+
51
+ ### Bun
52
+ ```bash
53
+ bun add @toolkit-f/snowflake-id
54
+ ```
55
+
56
+ ---
57
+
58
+ ## Quick Start
59
+
60
+ ### Generate IDs
61
+
62
+ ```typescript
63
+ import { SnowflakeGenerator } from '@toolkit-f/snowflake-id';
64
+
65
+ // Create a generator with a unique machine ID (0-1023)
66
+ const generator = new SnowflakeGenerator({ machineId: 1 });
67
+
68
+ // Generate as BigInt
69
+ const id = generator.nextId();
70
+ console.log(id); // 136941813297541120n
71
+
72
+ // Generate as String (recommended for JSON/APIs)
73
+ const idString = generator.nextIdString();
74
+ console.log(idString); // "136941813297541121"
75
+ ```
76
+
77
+ ### Parse Existing IDs
78
+
79
+ ```typescript
80
+ import { parseSnowflake } from '@toolkit-f/snowflake-id';
81
+
82
+ const parts = parseSnowflake('136941813297545217');
83
+ console.log(parts);
84
+ // {
85
+ // id: 136941813297545217n,
86
+ // timestamp: 2024-01-16T09:09:25.000Z,
87
+ // machineId: 1,
88
+ // sequence: 1
89
+ // }
90
+ ```
91
+
92
+ ### CommonJS Support
93
+
94
+ ```javascript
95
+ const { SnowflakeGenerator } = require('@toolkit-f/snowflake-id');
96
+
97
+ const generator = new SnowflakeGenerator({ machineId: 1 });
98
+ console.log(generator.nextIdString());
99
+ ```
100
+
101
+ ---
102
+
103
+ ## API Reference
104
+
105
+ ### Core Class: `SnowflakeGenerator`
106
+
107
+ #### Constructor
108
+
109
+ ```typescript
110
+ new SnowflakeGenerator(config: SnowflakeConfig)
111
+ ```
112
+
113
+ | Parameter | Type | Required | Default | Description |
114
+ |-----------|------|----------|---------|-------------|
115
+ | `machineId` | `number` | Yes | - | Unique ID for this machine/worker (0-1023) |
116
+ | `epoch` | `number` | No | `1704067200000` (Jan 1, 2024) | Custom epoch in milliseconds |
117
+ | `clockMoveBackAction` | `'throw'` \| `'wait'` | No | `'throw'` | Behavior when system clock drifts backwards |
118
+
119
+ **Example:**
120
+ ```typescript
121
+ import { SnowflakeGenerator } from '@toolkit-f/snowflake-id';
122
+
123
+ const generator = new SnowflakeGenerator({
124
+ machineId: 5,
125
+ epoch: 1609459200000, // Jan 1, 2021
126
+ clockMoveBackAction: 'wait' // Wait instead of throwing error
127
+ });
128
+ ```
129
+
130
+ ---
131
+
132
+ #### `nextId(): bigint`
133
+
134
+ Generates the next unique ID as a `bigint`.
135
+
136
+ ```typescript
137
+ const generator = new SnowflakeGenerator({ machineId: 1 });
138
+
139
+ console.log(generator.nextId());
140
+ // 136941813297541120n
141
+
142
+ console.log(generator.nextId());
143
+ // 136941813297541121n
144
+
145
+ console.log(generator.nextId());
146
+ // 136941813297545216n
147
+ ```
148
+
149
+ ---
150
+
151
+ #### `nextIdString(): string`
152
+
153
+ Generates the next unique ID as a `string`.
154
+
155
+ ```typescript
156
+ const generator = new SnowflakeGenerator({ machineId: 1 });
157
+
158
+ console.log(generator.nextIdString());
159
+ // "136941813297541120"
160
+
161
+ console.log(generator.nextIdString());
162
+ // "136941813297541121"
163
+ ```
164
+
165
+ ---
166
+
167
+ #### `getMachineId(): number`
168
+
169
+ Returns the configured machine ID.
170
+
171
+ ```typescript
172
+ const generator = new SnowflakeGenerator({ machineId: 42 });
173
+
174
+ console.log(generator.getMachineId());
175
+ // 42
176
+ ```
177
+
178
+ ---
179
+
180
+ #### `getEpoch(): number`
181
+
182
+ Returns the configured epoch timestamp.
183
+
184
+ ```typescript
185
+ const generator = new SnowflakeGenerator({ machineId: 1 });
186
+
187
+ console.log(generator.getEpoch());
188
+ // 1704067200000
189
+ ```
190
+
191
+ ---
192
+
193
+ #### `getSequence(): number`
194
+
195
+ Returns the current sequence number.
196
+
197
+ ```typescript
198
+ const generator = new SnowflakeGenerator({ machineId: 1 });
199
+
200
+ generator.nextId();
201
+ console.log(generator.getSequence());
202
+ // 0
203
+
204
+ generator.nextId();
205
+ console.log(generator.getSequence());
206
+ // 1
207
+ ```
208
+
209
+ ---
210
+
211
+ #### `getLastTimestamp(): number`
212
+
213
+ Returns the timestamp of the last generated ID.
214
+
215
+ ```typescript
216
+ const generator = new SnowflakeGenerator({ machineId: 1 });
217
+
218
+ generator.nextId();
219
+ console.log(generator.getLastTimestamp());
220
+ // 1737017365000
221
+ ```
222
+
223
+ ---
224
+
225
+ ### Factory Function: `createGenerator`
226
+
227
+ ```typescript
228
+ createGenerator(config: SnowflakeConfig): SnowflakeGenerator
229
+ ```
230
+
231
+ Alternative way to create a generator instance.
232
+
233
+ ```typescript
234
+ import { createGenerator } from '@toolkit-f/snowflake-id';
235
+
236
+ const generator = createGenerator({ machineId: 1 });
237
+
238
+ console.log(generator.nextId());
239
+ // 136941813297541120n
240
+ ```
241
+
242
+ ---
243
+
244
+ ### Utility Functions
245
+
246
+ #### `parseSnowflake(id, epoch?): SnowflakeParts`
247
+
248
+ Deconstructs a Snowflake ID into its components.
249
+
250
+ | Parameter | Type | Required | Default | Description |
251
+ |-----------|------|----------|---------|-------------|
252
+ | `id` | `bigint` \| `string` | Yes | - | The Snowflake ID to parse |
253
+ | `epoch` | `number` | No | `1704067200000` | Custom epoch used during generation |
254
+
255
+ **Returns:** `SnowflakeParts`
256
+
257
+ ```typescript
258
+ import { parseSnowflake } from '@toolkit-f/snowflake-id';
259
+
260
+ const parts = parseSnowflake('136941813297545217');
261
+ console.log(parts);
262
+ // {
263
+ // id: 136941813297545217n,
264
+ // timestamp: 2024-01-16T09:09:25.000Z,
265
+ // timestampMs: 1737017365000,
266
+ // machineId: 1,
267
+ // sequence: 1
268
+ // }
269
+
270
+ const parts2 = parseSnowflake(136941813297545217n);
271
+ console.log(parts2);
272
+ // {
273
+ // id: 136941813297545217n,
274
+ // timestamp: 2024-01-16T09:09:25.000Z,
275
+ // timestampMs: 1737017365000,
276
+ // machineId: 1,
277
+ // sequence: 1
278
+ // }
279
+ ```
280
+
281
+ ---
282
+
283
+ #### `stringifySnowflakeParts(parts): SnowflakePartsJSON`
284
+
285
+ Converts SnowflakeParts to a JSON-serializable format.
286
+
287
+ ```typescript
288
+ import { parseSnowflake, stringifySnowflakeParts } from '@toolkit-f/snowflake-id';
289
+
290
+ const parts = parseSnowflake('136941813297545217');
291
+ const json = stringifySnowflakeParts(parts);
292
+
293
+ console.log(json);
294
+ // {
295
+ // id: "136941813297545217",
296
+ // timestamp: "2024-01-16T09:09:25.000Z",
297
+ // timestampMs: 1737017365000,
298
+ // machineId: 1,
299
+ // sequence: 1
300
+ // }
301
+
302
+ console.log(JSON.stringify(json));
303
+ // '{"id":"136941813297545217","timestamp":"2024-01-16T09:09:25.000Z","timestampMs":1737017365000,"machineId":1,"sequence":1}'
304
+ ```
305
+
306
+ ---
307
+
308
+ #### `getTimestamp(id, epoch?): Date`
309
+
310
+ Extracts the Date object from a Snowflake ID.
311
+
312
+ | Parameter | Type | Required | Default | Description |
313
+ |-----------|------|----------|---------|-------------|
314
+ | `id` | `bigint` \| `string` | Yes | - | The Snowflake ID |
315
+ | `epoch` | `number` | No | `1704067200000` | Custom epoch |
316
+
317
+ ```typescript
318
+ import { getTimestamp } from '@toolkit-f/snowflake-id';
319
+
320
+ console.log(getTimestamp('136941813297545217'));
321
+ // 2024-01-16T09:09:25.000Z
322
+
323
+ console.log(getTimestamp(136941813297545217n));
324
+ // 2024-01-16T09:09:25.000Z
325
+ ```
326
+
327
+ ---
328
+
329
+ #### `getMachineId(id): number`
330
+
331
+ Extracts the machine ID from a Snowflake ID.
332
+
333
+ | Parameter | Type | Required | Description |
334
+ |-----------|------|----------|-------------|
335
+ | `id` | `bigint` \| `string` | Yes | The Snowflake ID |
336
+
337
+ ```typescript
338
+ import { getMachineId } from '@toolkit-f/snowflake-id';
339
+
340
+ console.log(getMachineId('136941813297545217'));
341
+ // 1
342
+
343
+ console.log(getMachineId(136941813297545217n));
344
+ // 1
345
+ ```
346
+
347
+ ---
348
+
349
+ #### `getSequence(id): number`
350
+
351
+ Extracts the sequence number from a Snowflake ID.
352
+
353
+ | Parameter | Type | Required | Description |
354
+ |-----------|------|----------|-------------|
355
+ | `id` | `bigint` \| `string` | Yes | The Snowflake ID |
356
+
357
+ ```typescript
358
+ import { getSequence } from '@toolkit-f/snowflake-id';
359
+
360
+ console.log(getSequence('136941813297545217'));
361
+ // 1
362
+
363
+ console.log(getSequence(136941813297545217n));
364
+ // 1
365
+ ```
366
+
367
+ ---
368
+
369
+ #### `isValidSnowflake(id, epoch?, relaxed?): boolean`
370
+
371
+ Validates if a value is a valid Snowflake ID.
372
+
373
+ | Parameter | Type | Required | Default | Description |
374
+ |-----------|------|----------|---------|-------------|
375
+ | `id` | `unknown` | Yes | - | Value to validate |
376
+ | `epoch` | `number` | No | `1704067200000` | Custom epoch |
377
+ | `relaxed` | `boolean` | No | `false` | Skip timestamp range check |
378
+
379
+ ```typescript
380
+ import { isValidSnowflake } from '@toolkit-f/snowflake-id';
381
+
382
+ console.log(isValidSnowflake('136941813297545217'));
383
+ // true
384
+
385
+ console.log(isValidSnowflake(136941813297545217n));
386
+ // true
387
+
388
+ console.log(isValidSnowflake('invalid'));
389
+ // false
390
+
391
+ console.log(isValidSnowflake('abc123'));
392
+ // false
393
+
394
+ console.log(isValidSnowflake(-1n));
395
+ // false
396
+
397
+ // Relaxed mode (skip timestamp validation)
398
+ console.log(isValidSnowflake('999999999999999999999', undefined, true));
399
+ // true
400
+ ```
401
+
402
+ ---
403
+
404
+ #### `snowflakeToString(id): string`
405
+
406
+ Converts a bigint Snowflake ID to string.
407
+
408
+ | Parameter | Type | Required | Description |
409
+ |-----------|------|----------|-------------|
410
+ | `id` | `bigint` | Yes | The Snowflake ID |
411
+
412
+ ```typescript
413
+ import { snowflakeToString } from '@toolkit-f/snowflake-id';
414
+
415
+ console.log(snowflakeToString(136941813297545217n));
416
+ // "136941813297545217"
417
+ ```
418
+
419
+ ---
420
+
421
+ #### `stringToSnowflake(str): bigint`
422
+
423
+ Converts a string Snowflake ID to bigint.
424
+
425
+ | Parameter | Type | Required | Description |
426
+ |-----------|------|----------|-------------|
427
+ | `str` | `string` | Yes | The Snowflake ID string |
428
+
429
+ ```typescript
430
+ import { stringToSnowflake } from '@toolkit-f/snowflake-id';
431
+
432
+ console.log(stringToSnowflake('136941813297545217'));
433
+ // 136941813297545217n
434
+
435
+ // Throws error for invalid input
436
+ stringToSnowflake('invalid');
437
+ // Error: Invalid Snowflake ID string: "invalid"
438
+ ```
439
+
440
+ ---
441
+
442
+ #### `compareSnowflakes(a, b): -1 | 0 | 1`
443
+
444
+ Compares two Snowflake IDs. Useful for sorting.
445
+
446
+ | Parameter | Type | Required | Description |
447
+ |-----------|------|----------|-------------|
448
+ | `a` | `bigint` \| `string` | Yes | First Snowflake ID |
449
+ | `b` | `bigint` \| `string` | Yes | Second Snowflake ID |
450
+
451
+ **Returns:** `-1` if a < b, `0` if a === b, `1` if a > b
452
+
453
+ ```typescript
454
+ import { compareSnowflakes } from '@toolkit-f/snowflake-id';
455
+
456
+ console.log(compareSnowflakes('136941813297545216', '136941813297545217'));
457
+ // -1
458
+
459
+ console.log(compareSnowflakes('136941813297545217', '136941813297545217'));
460
+ // 0
461
+
462
+ console.log(compareSnowflakes('136941813297545218', '136941813297545217'));
463
+ // 1
464
+
465
+ // Sorting example
466
+ const ids = ['136941813297545218', '136941813297545216', '136941813297545217'];
467
+ ids.sort(compareSnowflakes);
468
+ console.log(ids);
469
+ // ['136941813297545216', '136941813297545217', '136941813297545218']
470
+ ```
471
+
472
+ ---
473
+
474
+ #### `snowflakeFromTimestamp(date, machineId?, sequence?, epoch?): bigint`
475
+
476
+ Creates a Snowflake ID from a specific timestamp. Useful for database range queries.
477
+
478
+ | Parameter | Type | Required | Default | Description |
479
+ |-----------|------|----------|---------|-------------|
480
+ | `date` | `Date` \| `number` | Yes | - | Timestamp to create ID from |
481
+ | `machineId` | `number` | No | `0` | Machine ID (0-1023) |
482
+ | `sequence` | `number` | No | `0` | Sequence number (0-4095) |
483
+ | `epoch` | `number` | No | `1704067200000` | Custom epoch |
484
+
485
+ ```typescript
486
+ import { snowflakeFromTimestamp } from '@toolkit-f/snowflake-id';
487
+
488
+ // From Date object
489
+ const id1 = snowflakeFromTimestamp(new Date('2024-06-15T12:00:00.000Z'));
490
+ console.log(id1);
491
+ // 59918327808000000n
492
+
493
+ // From timestamp number
494
+ const id2 = snowflakeFromTimestamp(1718452800000);
495
+ console.log(id2);
496
+ // 59918327808000000n
497
+
498
+ // With machine ID and sequence
499
+ const id3 = snowflakeFromTimestamp(new Date('2024-06-15T12:00:00.000Z'), 5, 10);
500
+ console.log(id3);
501
+ // 59918327808020490n
502
+
503
+ // Database range query example
504
+ const startOfDay = snowflakeFromTimestamp(new Date('2024-06-15T00:00:00.000Z'));
505
+ const endOfDay = snowflakeFromTimestamp(new Date('2024-06-15T23:59:59.999Z'));
506
+ console.log(`SELECT * FROM items WHERE id >= ${startOfDay} AND id <= ${endOfDay}`);
507
+ // SELECT * FROM items WHERE id >= 59914215014400000 AND id <= 60010106326016000
508
+ ```
509
+
510
+ ---
511
+
512
+ ### Exported Constants
513
+
514
+ ```typescript
515
+ import {
516
+ DEFAULT_EPOCH,
517
+ MAX_MACHINE_ID,
518
+ MAX_SEQUENCE,
519
+ MACHINE_ID_SHIFT,
520
+ TIMESTAMP_SHIFT
521
+ } from '@toolkit-f/snowflake-id';
522
+
523
+ console.log(DEFAULT_EPOCH);
524
+ // 1704067200000 (Jan 1, 2024 00:00:00 UTC)
525
+
526
+ console.log(MAX_MACHINE_ID);
527
+ // 1023
528
+
529
+ console.log(MAX_SEQUENCE);
530
+ // 4095n
531
+
532
+ console.log(MACHINE_ID_SHIFT);
533
+ // 12n
534
+
535
+ console.log(TIMESTAMP_SHIFT);
536
+ // 22n
537
+ ```
538
+
539
+ ---
540
+
541
+ ### TypeScript Types
542
+
543
+ ```typescript
544
+ import type {
545
+ SnowflakeConfig,
546
+ SnowflakeParts,
547
+ SnowflakePartsJSON
548
+ } from '@toolkit-f/snowflake-id';
549
+
550
+ // SnowflakeConfig
551
+ interface SnowflakeConfig {
552
+ machineId: number; // 0-1023
553
+ epoch?: number; // Custom epoch (ms)
554
+ clockMoveBackAction?: 'throw' | 'wait';
555
+ }
556
+
557
+ // SnowflakeParts
558
+ interface SnowflakeParts {
559
+ id: bigint;
560
+ timestamp: Date;
561
+ timestampMs: number;
562
+ machineId: number;
563
+ sequence: number;
564
+ }
565
+
566
+ // SnowflakePartsJSON
567
+ interface SnowflakePartsJSON {
568
+ id: string;
569
+ timestamp: string;
570
+ timestampMs: number;
571
+ machineId: number;
572
+ sequence: number;
573
+ }
574
+ ```
575
+
576
+ ---
577
+
578
+ ## Error Handling
579
+
580
+ ```typescript
581
+ import { SnowflakeGenerator, stringToSnowflake, parseSnowflake } from '@toolkit-f/snowflake-id';
582
+
583
+ // Invalid machine ID
584
+ try {
585
+ new SnowflakeGenerator({ machineId: 2000 });
586
+ } catch (e) {
587
+ console.log(e.message);
588
+ // "machineId must be integer 0-1023, got 2000"
589
+ }
590
+
591
+ // Future epoch
592
+ try {
593
+ new SnowflakeGenerator({ machineId: 1, epoch: Date.now() + 100000 });
594
+ } catch (e) {
595
+ console.log(e.message);
596
+ // "epoch cannot be in the future"
597
+ }
598
+
599
+ // Invalid string ID
600
+ try {
601
+ stringToSnowflake('not-a-number');
602
+ } catch (e) {
603
+ console.log(e.message);
604
+ // 'Invalid Snowflake ID string: "not-a-number"'
605
+ }
606
+
607
+ // Invalid parse input
608
+ try {
609
+ parseSnowflake('abc123');
610
+ } catch (e) {
611
+ console.log(e.message);
612
+ // 'Invalid Snowflake ID: "abc123"'
613
+ }
614
+ ```
615
+
616
+ ---
617
+
618
+ ## 🤝 Contributing
619
+
620
+ Contributions are welcome! Please feel free to submit a [Pull Request](https://github.com/maulik-mk/Snowflake-Id/pulls).
621
+
622
+ 1. Fork the repository
623
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
624
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
625
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
626
+ 5. Open a Pull Request
627
+
628
+ ---
629
+
630
+ ## 🪪 License
631
+
632
+ [MIT](https://github.com/maulik-mk/Snowflake-Id/blob/main/LICENSE) © [Maulik M. K.](https://github.com/maulik-mk)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toolkit-f/snowflake-id",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },