@cdlab996/genid 1.3.0 → 1.4.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 CHANGED
@@ -3,17 +3,19 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@cdlab996/genid)](https://www.npmjs.com/package/@cdlab996/genid)
4
4
  [![license](https://img.shields.io/npm/l/@cdlab996/genid)](./LICENSE)
5
5
 
6
- 基于 Snowflake 算法的高性能分布式唯一 ID 生成器,支持漂移算法和时钟回拨处理。
6
+ High-performance distributed unique ID generator based on the Snowflake algorithm, with drift mode and clock-rollback handling.
7
7
 
8
- ## 特性
8
+ [中文文档](./README.zh-CN.md)
9
9
 
10
- - **漂移算法** - 高并发场景下突破每毫秒序列号上限,性能更优
11
- - **时钟回拨处理** - 使用保留序列号优雅降级,不阻塞 ID 生成
12
- - **灵活配置** - 支持自定义时间戳、节点 ID、序列号的位长度分配
13
- - **ID 验证** - 支持严格/宽松模式校验 ID 有效性
14
- - **运行监控** - 内置统计、解析和二进制格式化调试工具
10
+ ## Features
15
11
 
16
- ## 安装
12
+ - **Drift Algorithm** - Exceeds per-millisecond sequence limits under high concurrency for better throughput
13
+ - **Clock Rollback Handling** - Graceful degradation using reserved sequence numbers without blocking ID generation
14
+ - **Flexible Configuration** - Customize bit allocation for timestamp, worker ID, and sequence
15
+ - **ID Validation** - Strict and loose validation modes with `afterTime` support
16
+ - **Runtime Monitoring** - Built-in statistics, parsing, and binary formatting for debugging
17
+
18
+ ## Installation
17
19
 
18
20
  ```bash
19
21
  # npm
@@ -23,25 +25,25 @@ npm install @cdlab996/genid
23
25
  pnpm add @cdlab996/genid
24
26
  ```
25
27
 
26
- ## 快速开始
28
+ ## Quick Start
27
29
 
28
30
  ```typescript
29
31
  import { GenidOptimized } from '@cdlab996/genid'
30
32
 
31
- // 创建实例(每个 Worker/进程使用不同的 workerId
33
+ // Create an instance (use a different workerId for each worker/process)
32
34
  const genid = new GenidOptimized({ workerId: 1 })
33
35
 
34
- // 生成 ID
36
+ // Generate an ID
35
37
  const id = genid.nextId()
36
38
 
37
- // 批量生成
39
+ // Batch generate
38
40
  const ids = genid.nextBatch(1000)
39
41
 
40
- // 解析 ID
42
+ // Parse an ID
41
43
  const info = genid.parse(id)
42
44
  // => { timestamp: Date, timestampMs: 1609459200000, workerId: 1, sequence: 42 }
43
45
 
44
- // 验证 ID
46
+ // Validate an ID
45
47
  genid.isValid(id) // true
46
48
  ```
47
49
 
@@ -49,48 +51,52 @@ genid.isValid(id) // true
49
51
 
50
52
  ### `new GenidOptimized(options)`
51
53
 
52
- | 参数 | 类型 | 必填 | 默认值 | 说明 |
53
- | ------------------- | ------------- | :---: | ------------------ | --------------------------------------------- |
54
- | `workerId` | `number` | Yes | - | 工作节点 ID0 ~ 2^workerIdBitLength-1|
55
- | `method` | `GenidMethod` | | `DRIFT` | 算法:`DRIFT`(漂移)或 `TRADITIONAL`(传统) |
56
- | `baseTime` | `number` | | `1577836800000` | 起始时间戳,毫秒(默认 2020-01-01|
57
- | `workerIdBitLength` | `number` | | `6` | 节点 ID 位数(1-15|
58
- | `seqBitLength` | `number` | | `6` | 序列号位数(3-21|
59
- | `maxSeqNumber` | `number` | | `2^seqBitLength-1` | 最大序列号 |
60
- | `minSeqNumber` | `number` | | `5` | 最小序列号(0-4 保留用于时钟回拨) |
61
- | `topOverCostCount` | `number` | | `2000` | 最大漂移次数 |
54
+ | Parameter | Type | Required | Default | Description |
55
+ | ------------------- | ------------- | :------: | ------------------ | --------------------------------------------------- |
56
+ | `workerId` | `number` | Yes | - | Worker node ID (0 to 2^workerIdBitLength-1) |
57
+ | `method` | `GenidMethod` | | `DRIFT` | Algorithm: `DRIFT` or `TRADITIONAL` |
58
+ | `baseTime` | `number` | | `1577836800000` | Base timestamp in ms (default: 2020-01-01) |
59
+ | `workerIdBitLength` | `number` | | `6` | Bit length for worker ID (1-15) |
60
+ | `seqBitLength` | `number` | | `6` | Bit length for sequence (3-21) |
61
+ | `maxSeqNumber` | `number` | | `2^seqBitLength-1` | Maximum sequence number |
62
+ | `minSeqNumber` | `number` | | `5` | Minimum sequence number (0-4 reserved for rollback) |
63
+ | `topOverCostCount` | `number` | | `2000` | Maximum drift count |
62
64
 
63
- ### 生成 ID
65
+ ### Generating IDs
64
66
 
65
67
  ```typescript
66
- genid.nextId() // 返回 number | bigint(自动选择)
67
- genid.nextNumber() // 返回 number(超出安全整数范围抛错)
68
- genid.nextBigId() // 返回 bigint
69
- genid.nextBatch(100) // 批量生成 100 个 ID
70
- genid.nextBatch(100, true) // 批量生成 100 BigInt ID
68
+ genid.nextId() // Returns number | bigint (auto-selects)
69
+ genid.nextNumber() // Returns number (throws if exceeds safe integer range)
70
+ genid.nextBigId() // Returns bigint
71
+ genid.nextBatch(100) // Batch generate 100 IDs
72
+ genid.nextBatch(100, true) // Batch generate 100 BigInt IDs
71
73
  ```
72
74
 
73
- ### 解析与验证
75
+ ### Parsing & Validation
74
76
 
75
77
  ```typescript
76
- // 解析 ID 的组成部分
78
+ // Parse an ID into its components
77
79
  genid.parse(id)
78
80
  // => { timestamp: Date, timestampMs: number, workerId: number, sequence: number }
79
81
 
80
- // 宽松验证:检查 ID 格式是否有效
81
- genid.isValid(id) // true
82
- genid.isValid(12345) // false
83
- genid.isValid('invalid') // false
82
+ // Loose validation: checks structural validity
83
+ genid.isValid(id) // true
84
+ genid.isValid('invalid') // false
85
+
86
+ // Strict validation: requires workerId to match the current instance
87
+ genid.isValid(id, true) // true (generated by this instance)
88
+ genid.isValid(otherId, true) // false (generated by another instance)
84
89
 
85
- // 严格验证:要求 workerId 匹配当前实例
86
- genid.isValid(id, true) // true(本实例生成的 ID)
87
- genid.isValid(otherId, true) // false(其他实例生成的 ID)
90
+ // Time-bound validation: reject IDs generated before a given time
91
+ const startupTime = Date.now()
92
+ genid.isValid(id, { afterTime: startupTime }) // true
93
+ genid.isValid(id, { strictWorkerId: true, afterTime: startupTime }) // combined
88
94
  ```
89
95
 
90
- ### 统计与配置
96
+ ### Statistics & Configuration
91
97
 
92
98
  ```typescript
93
- // 获取运行统计
99
+ // Get runtime statistics
94
100
  genid.getStats()
95
101
  // => {
96
102
  // totalGenerated: 1000,
@@ -101,7 +107,7 @@ genid.getStats()
101
107
  // currentState: 'NORMAL' | 'OVER_COST'
102
108
  // }
103
109
 
104
- // 获取当前配置
110
+ // Get current configuration
105
111
  genid.getConfig()
106
112
  // => {
107
113
  // method: 'DRIFT',
@@ -116,24 +122,24 @@ genid.getConfig()
116
122
  // sequenceBits: 6
117
123
  // }
118
124
 
119
- // 重置统计
125
+ // Reset statistics
120
126
  genid.resetStats()
121
127
  ```
122
128
 
123
- ### 调试
129
+ ### Debugging
124
130
 
125
131
  ```typescript
126
132
  genid.formatBinary(id)
127
133
  // ID: 123456789012345
128
134
  // Binary (64-bit):
129
- // 0000000000011010... - 时间戳 (52 bits) = 2025-10-17T...
130
- // 000001 - 工作节点 ID (6 bits) = 1
131
- // 101010 - 序列号 (6 bits) = 42
135
+ // 0000000000011010... - Timestamp (52 bits) = 2025-10-17T...
136
+ // 000001 - Worker ID (6 bits) = 1
137
+ // 101010 - Sequence (6 bits) = 42
132
138
  ```
133
139
 
134
- ## 使用示例
140
+ ## Examples
135
141
 
136
- ### 自定义位分配
142
+ ### Custom Bit Allocation
137
143
 
138
144
  ```typescript
139
145
  import { GenidOptimized, GenidMethod } from '@cdlab996/genid'
@@ -142,81 +148,81 @@ const genid = new GenidOptimized({
142
148
  workerId: 1,
143
149
  method: GenidMethod.TRADITIONAL,
144
150
  baseTime: new Date('2024-01-01').valueOf(),
145
- workerIdBitLength: 10, // 支持 1024 个节点
146
- seqBitLength: 12, // 每毫秒 4096 ID
151
+ workerIdBitLength: 10, // Support 1024 nodes
152
+ seqBitLength: 12, // 4096 IDs per millisecond
147
153
  topOverCostCount: 5000,
148
154
  })
149
155
  ```
150
156
 
151
- ### 验证外部 ID
157
+ ### Validating External IDs
152
158
 
153
159
  ```typescript
154
- // 验证从数据库或 API 获取的 ID
160
+ // Validate IDs from a database or API
155
161
  const externalId = '123456789012345'
156
162
  if (genid.isValid(externalId)) {
157
163
  const info = genid.parse(externalId)
158
- console.log('生成时间:', info.timestamp)
159
- console.log('来自节点:', info.workerId)
164
+ console.log('Generated at:', info.timestamp)
165
+ console.log('From worker:', info.workerId)
160
166
  } else {
161
- console.error('无效 ID')
167
+ console.error('Invalid ID')
162
168
  }
163
169
  ```
164
170
 
165
- ### 性能监控
171
+ ### Performance Monitoring
166
172
 
167
173
  ```typescript
168
174
  setInterval(() => {
169
175
  const stats = genid.getStats()
170
- console.log(`速率: ${stats.avgPerSecond} ID/s | 漂移: ${stats.overCostCount} | 回拨: ${stats.turnBackCount}`)
176
+ console.log(`Rate: ${stats.avgPerSecond} ID/s | Drift: ${stats.overCostCount} | Rollback: ${stats.turnBackCount}`)
171
177
  }, 10000)
172
178
  ```
173
179
 
174
- ## 算法模式
180
+ ## Algorithm Modes
175
181
 
176
- | 模式 | 说明 | 适用场景 |
177
- | ----------------- | ---------------------------------------- | -------------------- |
178
- | **DRIFT**(默认) | 序列号耗尽时借用未来时间戳,避免等待 | 高频 ID 生成、高并发 |
179
- | **TRADITIONAL** | 严格按时间戳递增,序列号耗尽等待下一毫秒 | 对时间顺序严格要求 |
182
+ | Mode | Description | Use Case |
183
+ | ------------------- | ------------------------------------------------------------------ | ----------------------------- |
184
+ | **DRIFT** (default) | Borrows future timestamps when sequence is exhausted; avoids waits | High-frequency ID generation |
185
+ | **TRADITIONAL** | Strictly increasing timestamps; waits for next ms on exhaustion | Strict time-ordering required |
180
186
 
181
- ## 架构
187
+ ## Architecture
182
188
 
183
- ### ID 结构(64-bit
189
+ ### ID Structure (64-bit)
184
190
 
185
191
  ```
186
- |------------ 时间戳 ------------|-- 工作节点 ID --|-- 序列号 --|
187
- 42-52 bits 1-15 bits 3-21 bits
192
+ |------------ Timestamp ------------|-- Worker ID --|-- Sequence --|
193
+ 42-52 bits 1-15 bits 3-21 bits
188
194
  ```
189
195
 
190
- 默认配置:时间戳 52 bits(约 139 年)| 节点 ID 6 bits64 个节点)| 序列号 6 bits(每毫秒 59 个 ID)
196
+ Default: Timestamp 52 bits (~139 years) | Worker ID 6 bits (64 nodes) | Sequence 6 bits (59 IDs/ms)
191
197
 
192
- 序列号 `0-4` 保留用于时钟回拨,正常使用从 `5` 开始。
198
+ Sequence values `0-4` are reserved for clock rollback; normal generation starts at `5`.
193
199
 
194
- ### 核心流程
200
+ ### Core Flow
195
201
 
196
202
  ```mermaid
197
203
  graph TB
198
- A[开始生成 ID] --> B{是否处于漂移状态?}
204
+ A[Start ID Generation] --> B{In drift mode?}
199
205
 
200
- B -->|否| C[正常路径]
201
- B -->|是| D[漂移路径]
206
+ B -->|No| C[Normal Path]
207
+ B -->|Yes| D[Drift Path]
202
208
 
203
- C --> E{检测时钟}
204
- E -->|时钟回拨| F[使用保留序列号 0-4]
205
- E -->|时间前进| G[重置序列号]
206
- E -->|同一毫秒| H{序列号是否溢出?}
209
+ C --> E{Check Clock}
210
+ E -->|Clock Rollback| F[Use Reserved Sequence 0-4]
211
+ E -->|Time Advanced| G[Reset Sequence]
212
+ E -->|Same Millisecond| H{Sequence Exhausted?}
207
213
 
208
- H -->|否| I[序列号+1 正常生成]
209
- H -->|是| J[进入漂移状态 时间戳+1]
214
+ H -->|No| I[Increment Sequence]
215
+ H -->|Yes| J[Enter Drift Mode, Timestamp+1]
210
216
 
211
- D --> K{检测时间}
212
- K -->|时间追上| L[退出漂移 恢复正常]
213
- K -->|超过最大漂移| M[等待下一毫秒 退出漂移]
214
- K -->|继续漂移| N{序列号是否溢出?}
217
+ D --> K{Check Time}
218
+ K -->|Time Caught Up| L[Exit Drift, Resume Normal]
219
+ K -->|Exceeded Max Drift| M[Wait for Next ms, Exit Drift]
220
+ K -->|Continue Drift| N{Sequence Exhausted?}
215
221
 
216
- N -->|否| O[使用当前序列号]
217
- N -->|是| P[时间戳+1 重置序列号]
222
+ N -->|No| O[Use Current Sequence]
223
+ N -->|Yes| P[Timestamp+1, Reset Sequence]
218
224
 
219
- F --> Q[计算 ID]
225
+ F --> Q[Assemble ID]
220
226
  G --> Q
221
227
  I --> Q
222
228
  J --> Q
@@ -225,26 +231,146 @@ graph TB
225
231
  O --> Q
226
232
  P --> Q
227
233
 
228
- Q --> R[更新统计]
229
- R --> S[返回 ID]
234
+ Q --> R[Update Statistics]
235
+ R --> S[Return ID]
230
236
  ```
231
237
 
232
- ## 注意事项
238
+ ## Performance
239
+
240
+ Throughput is determined by the number of available sequence slots per millisecond. Increasing `seqBitLength` scales linearly:
241
+
242
+ | seqBitLength | Slots/ms | Throughput |
243
+ | :-------------: | -------: | ------------------: |
244
+ | 3 | 3 | ~3,000 IDs/sec |
245
+ | 4 | 11 | ~11,000 IDs/sec |
246
+ | **6** (default) | **59** | **~58,000 IDs/sec** |
247
+ | 8 | 251 | ~247,000 IDs/sec |
248
+ | 10 | 1,019 | ~1,000,000 IDs/sec |
249
+ | 14 | 16,379 | ~4,500,000 IDs/sec |
250
+
251
+ > Measured on Node.js v22 (x64). Actual results vary by environment.
252
+ > Run `pnpm run benchmark` to probe your own machine.
253
+
254
+ | Metric | Value (default config) |
255
+ | ------------------------- | ---------------------: |
256
+ | Max worker nodes | 64 |
257
+ | Timestamp lifespan | ~139 years |
258
+ | P99 latency (single call) | < 1µs |
259
+
260
+ ## Benchmark
261
+
262
+ A built-in probe script measures the actual capability of the current environment:
263
+
264
+ ```bash
265
+ pnpm run benchmark
266
+ ```
233
267
 
234
- - 每个 Worker/进程必须使用**不同的 workerId**
235
- - 实例**非线程安全**,不要跨线程共享
236
- - `workerIdBitLength + seqBitLength` 不能超过 22
237
- - 序列号 0-4 保留用于时钟回拨处理
238
- - 超出 JavaScript 安全整数范围(2^53-1)时,使用 `nextBigId()` `nextId()`(自动返回 BigInt)
268
+ It reports:
269
+
270
+ 1. **Single-call throughput** peak `nextId()` IDs/sec
271
+ 2. **Batch throughput** — `nextBatch()` across different batch sizes
272
+ 3. **Latency percentiles** P50 / P95 / P99 / P99.9 / Max
273
+ 4. **Algorithm comparison** — DRIFT vs TRADITIONAL side-by-side
274
+ 5. **Throughput by seqBitLength** — how bit allocation affects throughput
275
+ 6. **Memory footprint** — heap delta after 1M generations
276
+ 7. **Recommended thresholds** — suggested safe values for test assertions (60% of peak)
277
+
278
+ <details>
279
+ <summary>Example output</summary>
280
+
281
+ ```bash
282
+ station :: /app/projects/genid ‹main*› » pnpm run benchmark
283
+
284
+ > @cdlab996/genid@1.4.0 benchmark /app/projects/genid
285
+ > npx tsx scripts/benchmark.ts
286
+
287
+ ============================================================
288
+ GenidOptimized — Environment Capability Probe
289
+ Node v22.22.0 | linux x64
290
+ Date: 2026-04-01T08:41:07.669Z
291
+ ============================================================
292
+
293
+ ────────────────────────────────────────────────────────────
294
+ 1. Single-call throughput (nextId)
295
+ ────────────────────────────────────────────────────────────
296
+ Duration: 3001ms
297
+ Generated: 226,073
298
+ Throughput: 75,332 IDs/sec
299
+
300
+ ────────────────────────────────────────────────────────────
301
+ 2. Batch throughput (nextBatch)
302
+ ────────────────────────────────────────────────────────────
303
+ batch= 100 × 5000 => 61,665 IDs/sec
304
+ batch= 1,000 × 500 => 61,577 IDs/sec
305
+ batch= 10,000 × 50 => 61,716 IDs/sec
306
+ batch=100,000 × 5 => 61,644 IDs/sec
307
+
308
+ ────────────────────────────────────────────────────────────
309
+ 3. Single-call latency percentiles
310
+ ────────────────────────────────────────────────────────────
311
+ Samples: 100,000
312
+ Avg: 0µs
313
+ P50: 0µs
314
+ P95: 1µs
315
+ P99: 1µs
316
+ P99.9: 1µs
317
+ Max: 364µs
318
+
319
+ ────────────────────────────────────────────────────────────
320
+ 4. Algorithm comparison (DRIFT vs TRADITIONAL)
321
+ ────────────────────────────────────────────────────────────
322
+ DRIFT 58,296 IDs/sec drift=1
323
+ TRADITIONAL 59,000 IDs/sec drift=0
324
+
325
+ ────────────────────────────────────────────────────────────
326
+ 5. Throughput by sequence bit length
327
+ ────────────────────────────────────────────────────────────
328
+ seqBits= 3 maxSeq= 7 => 2,986 IDs/sec drift=1
329
+ seqBits= 4 maxSeq= 15 => 10,884 IDs/sec drift=1
330
+ seqBits= 6 maxSeq= 63 => 58,240 IDs/sec drift=1
331
+ seqBits= 8 maxSeq= 255 => 247,804 IDs/sec drift=1
332
+ seqBits=10 maxSeq= 1023 => 1,008,930 IDs/sec drift=1
333
+ seqBits=14 maxSeq=16383 => 4,130,701 IDs/sec drift=0
334
+
335
+ ────────────────────────────────────────────────────────────
336
+ 6. Memory footprint
337
+ ────────────────────────────────────────────────────────────
338
+ Generated: 1,000,000 IDs (not stored)
339
+ Heap delta: -6.27 MB
340
+ Note: Run with --expose-gc for accurate GC-forced measurement
341
+
342
+ ────────────────────────────────────────────────────────────
343
+ Summary — Recommended test thresholds
344
+ ────────────────────────────────────────────────────────────
345
+ Peak single-call: 75,332 IDs/sec
346
+ Peak batch: 61,716 IDs/sec
347
+ Suggested min threshold: 45,199 IDs/sec (60% of peak)
348
+ Suggested batch threshold: 37,029 IDs/sec (60% of peak)
349
+ Suggested P99 cap: 2µs (3× measured P99)
350
+ ```
351
+
352
+ </details>
353
+
354
+ ## Development
355
+
356
+ ```bash
357
+ pnpm install # Install dependencies
358
+ pnpm run build # Build (ESM + CJS)
359
+ pnpm run dev # Watch mode
360
+ pnpm run test # Run tests
361
+ pnpm run benchmark # Run environment capability probe
362
+ pnpm run typecheck # Type check
363
+ pnpm run lint # Lint (Biome)
364
+ pnpm run format # Format (Biome)
365
+ ```
239
366
 
240
- ## 性能
367
+ ## Notes
241
368
 
242
- | 指标 | 数值 |
243
- | -------------------------- | ------------- |
244
- | 单实例吞吐量 | > 50,000 ID/s |
245
- | 每毫秒生成量(默认配置) | 59 个 |
246
- | 最大节点数(默认配置) | 64 个 |
247
- | 时间戳可用时长(默认配置) | ~139 年 |
369
+ - Each worker/process must use a **unique workerId**
370
+ - Instances are **not thread-safe** — do not share across threads
371
+ - `workerIdBitLength + seqBitLength` must not exceed 22
372
+ - Sequence values 0-4 are reserved for clock-rollback handling
373
+ - When IDs exceed the JavaScript safe integer range (2^53-1), use `nextBigId()` or `nextId()` (auto-returns BigInt)
248
374
 
249
375
  ## License
250
376
 
@@ -0,0 +1,377 @@
1
+ # @cdlab996/genid
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@cdlab996/genid)](https://www.npmjs.com/package/@cdlab996/genid)
4
+ [![license](https://img.shields.io/npm/l/@cdlab996/genid)](./LICENSE)
5
+
6
+ 基于 Snowflake 算法的高性能分布式唯一 ID 生成器,支持漂移算法和时钟回拨处理。
7
+
8
+ [English](./README.md)
9
+
10
+ ## 特性
11
+
12
+ - **漂移算法** - 高并发场景下突破每毫秒序列号上限,性能更优
13
+ - **时钟回拨处理** - 使用保留序列号优雅降级,不阻塞 ID 生成
14
+ - **灵活配置** - 支持自定义时间戳、节点 ID、序列号的位长度分配
15
+ - **ID 验证** - 支持严格/宽松模式校验,支持 `afterTime` 时间下限约束
16
+ - **运行监控** - 内置统计、解析和二进制格式化调试工具
17
+
18
+ ## 安装
19
+
20
+ ```bash
21
+ # npm
22
+ npm install @cdlab996/genid
23
+
24
+ # pnpm
25
+ pnpm add @cdlab996/genid
26
+ ```
27
+
28
+ ## 快速开始
29
+
30
+ ```typescript
31
+ import { GenidOptimized } from '@cdlab996/genid'
32
+
33
+ // 创建实例(每个 Worker/进程使用不同的 workerId)
34
+ const genid = new GenidOptimized({ workerId: 1 })
35
+
36
+ // 生成 ID
37
+ const id = genid.nextId()
38
+
39
+ // 批量生成
40
+ const ids = genid.nextBatch(1000)
41
+
42
+ // 解析 ID
43
+ const info = genid.parse(id)
44
+ // => { timestamp: Date, timestampMs: 1609459200000, workerId: 1, sequence: 42 }
45
+
46
+ // 验证 ID
47
+ genid.isValid(id) // true
48
+ ```
49
+
50
+ ## API
51
+
52
+ ### `new GenidOptimized(options)`
53
+
54
+ | 参数 | 类型 | 必填 | 默认值 | 说明 |
55
+ | ------------------- | ------------- | :---: | ------------------ | --------------------------------------------- |
56
+ | `workerId` | `number` | Yes | - | 工作节点 ID(0 ~ 2^workerIdBitLength-1) |
57
+ | `method` | `GenidMethod` | | `DRIFT` | 算法:`DRIFT`(漂移)或 `TRADITIONAL`(传统) |
58
+ | `baseTime` | `number` | | `1577836800000` | 起始时间戳,毫秒(默认 2020-01-01) |
59
+ | `workerIdBitLength` | `number` | | `6` | 节点 ID 位数(1-15) |
60
+ | `seqBitLength` | `number` | | `6` | 序列号位数(3-21) |
61
+ | `maxSeqNumber` | `number` | | `2^seqBitLength-1` | 最大序列号 |
62
+ | `minSeqNumber` | `number` | | `5` | 最小序列号(0-4 保留用于时钟回拨) |
63
+ | `topOverCostCount` | `number` | | `2000` | 最大漂移次数 |
64
+
65
+ ### 生成 ID
66
+
67
+ ```typescript
68
+ genid.nextId() // 返回 number | bigint(自动选择)
69
+ genid.nextNumber() // 返回 number(超出安全整数范围抛错)
70
+ genid.nextBigId() // 返回 bigint
71
+ genid.nextBatch(100) // 批量生成 100 个 ID
72
+ genid.nextBatch(100, true) // 批量生成 100 个 BigInt ID
73
+ ```
74
+
75
+ ### 解析与验证
76
+
77
+ ```typescript
78
+ // 解析 ID 的组成部分
79
+ genid.parse(id)
80
+ // => { timestamp: Date, timestampMs: number, workerId: number, sequence: number }
81
+
82
+ // 宽松验证:检查 ID 结构是否有效
83
+ genid.isValid(id) // true
84
+ genid.isValid('invalid') // false
85
+
86
+ // 严格验证:要求 workerId 匹配当前实例
87
+ genid.isValid(id, true) // true(本实例生成的 ID)
88
+ genid.isValid(otherId, true) // false(其他实例生成的 ID)
89
+
90
+ // 时间下限验证:拒绝早于指定时间生成的 ID
91
+ const startupTime = Date.now()
92
+ genid.isValid(id, { afterTime: startupTime }) // true
93
+ genid.isValid(id, { strictWorkerId: true, afterTime: startupTime }) // 组合使用
94
+ ```
95
+
96
+ ### 统计与配置
97
+
98
+ ```typescript
99
+ // 获取运行统计
100
+ genid.getStats()
101
+ // => {
102
+ // totalGenerated: 1000,
103
+ // overCostCount: 10,
104
+ // turnBackCount: 2,
105
+ // uptimeMs: 60000,
106
+ // avgPerSecond: 16,
107
+ // currentState: 'NORMAL' | 'OVER_COST'
108
+ // }
109
+
110
+ // 获取当前配置
111
+ genid.getConfig()
112
+ // => {
113
+ // method: 'DRIFT',
114
+ // workerId: 1,
115
+ // workerIdRange: '0-63',
116
+ // sequenceRange: '5-63',
117
+ // maxSequence: 63,
118
+ // idsPerMillisecond: 59,
119
+ // baseTime: Date,
120
+ // timestampBits: 52,
121
+ // workerIdBits: 6,
122
+ // sequenceBits: 6
123
+ // }
124
+
125
+ // 重置统计
126
+ genid.resetStats()
127
+ ```
128
+
129
+ ### 调试
130
+
131
+ ```typescript
132
+ genid.formatBinary(id)
133
+ // ID: 123456789012345
134
+ // Binary (64-bit):
135
+ // 0000000000011010... - Timestamp (52 bits) = 2025-10-17T...
136
+ // 000001 - Worker ID (6 bits) = 1
137
+ // 101010 - Sequence (6 bits) = 42
138
+ ```
139
+
140
+ ## 使用示例
141
+
142
+ ### 自定义位分配
143
+
144
+ ```typescript
145
+ import { GenidOptimized, GenidMethod } from '@cdlab996/genid'
146
+
147
+ const genid = new GenidOptimized({
148
+ workerId: 1,
149
+ method: GenidMethod.TRADITIONAL,
150
+ baseTime: new Date('2024-01-01').valueOf(),
151
+ workerIdBitLength: 10, // 支持 1024 个节点
152
+ seqBitLength: 12, // 每毫秒 4096 个 ID
153
+ topOverCostCount: 5000,
154
+ })
155
+ ```
156
+
157
+ ### 验证外部 ID
158
+
159
+ ```typescript
160
+ // 验证从数据库或 API 获取的 ID
161
+ const externalId = '123456789012345'
162
+ if (genid.isValid(externalId)) {
163
+ const info = genid.parse(externalId)
164
+ console.log('生成时间:', info.timestamp)
165
+ console.log('来自节点:', info.workerId)
166
+ } else {
167
+ console.error('无效 ID')
168
+ }
169
+ ```
170
+
171
+ ### 性能监控
172
+
173
+ ```typescript
174
+ setInterval(() => {
175
+ const stats = genid.getStats()
176
+ console.log(`速率: ${stats.avgPerSecond} ID/s | 漂移: ${stats.overCostCount} | 回拨: ${stats.turnBackCount}`)
177
+ }, 10000)
178
+ ```
179
+
180
+ ## 算法模式
181
+
182
+ | 模式 | 说明 | 适用场景 |
183
+ | ----------------- | ---------------------------------------- | -------------------- |
184
+ | **DRIFT**(默认) | 序列号耗尽时借用未来时间戳,避免等待 | 高频 ID 生成、高并发 |
185
+ | **TRADITIONAL** | 严格按时间戳递增,序列号耗尽等待下一毫秒 | 对时间顺序严格要求 |
186
+
187
+ ## 架构
188
+
189
+ ### ID 结构(64-bit)
190
+
191
+ ```
192
+ |------------ 时间戳 ------------|-- 工作节点 ID --|-- 序列号 --|
193
+ 42-52 bits 1-15 bits 3-21 bits
194
+ ```
195
+
196
+ 默认配置:时间戳 52 bits(约 139 年)| 节点 ID 6 bits(64 个节点)| 序列号 6 bits(每毫秒 59 个 ID)
197
+
198
+ 序列号 `0-4` 保留用于时钟回拨,正常使用从 `5` 开始。
199
+
200
+ ### 核心流程
201
+
202
+ ```mermaid
203
+ graph TB
204
+ A[开始生成 ID] --> B{是否处于漂移状态?}
205
+
206
+ B -->|否| C[正常路径]
207
+ B -->|是| D[漂移路径]
208
+
209
+ C --> E{检测时钟}
210
+ E -->|时钟回拨| F[使用保留序列号 0-4]
211
+ E -->|时间前进| G[重置序列号]
212
+ E -->|同一毫秒| H{序列号是否溢出?}
213
+
214
+ H -->|否| I[序列号+1 正常生成]
215
+ H -->|是| J[进入漂移状态 时间戳+1]
216
+
217
+ D --> K{检测时间}
218
+ K -->|时间追上| L[退出漂移 恢复正常]
219
+ K -->|超过最大漂移| M[等待下一毫秒 退出漂移]
220
+ K -->|继续漂移| N{序列号是否溢出?}
221
+
222
+ N -->|否| O[使用当前序列号]
223
+ N -->|是| P[时间戳+1 重置序列号]
224
+
225
+ F --> Q[计算 ID]
226
+ G --> Q
227
+ I --> Q
228
+ J --> Q
229
+ L --> Q
230
+ M --> Q
231
+ O --> Q
232
+ P --> Q
233
+
234
+ Q --> R[更新统计]
235
+ R --> S[返回 ID]
236
+ ```
237
+
238
+ ## 性能
239
+
240
+ 吞吐量由每毫秒可用序列号槽位数决定,增大 `seqBitLength` 可线性提升:
241
+
242
+ | seqBitLength | 每毫秒槽位 | 吞吐量 |
243
+ | :-----------: | ---------: | ------------------: |
244
+ | 3 | 3 | ~3,000 IDs/sec |
245
+ | 4 | 11 | ~11,000 IDs/sec |
246
+ | **6**(默认) | **59** | **~58,000 IDs/sec** |
247
+ | 8 | 251 | ~247,000 IDs/sec |
248
+ | 10 | 1,019 | ~1,000,000 IDs/sec |
249
+ | 14 | 16,379 | ~4,500,000 IDs/sec |
250
+
251
+ > 基于 Node.js v22 (x64) 测量,实际结果因环境而异。
252
+ > 运行 `pnpm run benchmark` 可探测当前机器的实际能力。
253
+
254
+ | 指标 | 数值(默认配置) |
255
+ | -------------------- | ---------------: |
256
+ | 最大节点数 | 64 |
257
+ | 时间戳可用时长 | ~139 年 |
258
+ | P99 延迟(单次调用) | < 1µs |
259
+
260
+ ## Benchmark
261
+
262
+ 内置探测脚本,自动测量当前环境的实际性能上限:
263
+
264
+ ```bash
265
+ pnpm run benchmark
266
+ ```
267
+
268
+ 输出内容:
269
+
270
+ 1. **单次调用吞吐量** — `nextId()` 峰值 IDs/sec
271
+ 2. **批量吞吐量** — 不同批次大小的 `nextBatch()` 性能
272
+ 3. **延迟分位数** — P50 / P95 / P99 / P99.9 / Max
273
+ 4. **算法对比** — DRIFT vs TRADITIONAL 并排对比
274
+ 5. **不同 seqBitLength 吞吐量** — 位分配对吞吐量的影响
275
+ 6. **内存占用** — 生成 100 万 ID 后的堆内存变化
276
+ 7. **推荐阈值** — 基于峰值 60% 给出的安全测试断言值
277
+
278
+ <details>
279
+ <summary>输出示例</summary>
280
+
281
+ ```bash
282
+ station :: /app/projects/genid ‹main*› » pnpm run benchmark
283
+
284
+ > @cdlab996/genid@1.4.0 benchmark /app/projects/genid
285
+ > npx tsx scripts/benchmark.ts
286
+
287
+ ============================================================
288
+ GenidOptimized — Environment Capability Probe
289
+ Node v22.22.0 | linux x64
290
+ Date: 2026-04-01T08:41:07.669Z
291
+ ============================================================
292
+
293
+ ────────────────────────────────────────────────────────────
294
+ 1. Single-call throughput (nextId)
295
+ ────────────────────────────────────────────────────────────
296
+ Duration: 3001ms
297
+ Generated: 226,073
298
+ Throughput: 75,332 IDs/sec
299
+
300
+ ────────────────────────────────────────────────────────────
301
+ 2. Batch throughput (nextBatch)
302
+ ────────────────────────────────────────────────────────────
303
+ batch= 100 × 5000 => 61,665 IDs/sec
304
+ batch= 1,000 × 500 => 61,577 IDs/sec
305
+ batch= 10,000 × 50 => 61,716 IDs/sec
306
+ batch=100,000 × 5 => 61,644 IDs/sec
307
+
308
+ ────────────────────────────────────────────────────────────
309
+ 3. Single-call latency percentiles
310
+ ────────────────────────────────────────────────────────────
311
+ Samples: 100,000
312
+ Avg: 0µs
313
+ P50: 0µs
314
+ P95: 1µs
315
+ P99: 1µs
316
+ P99.9: 1µs
317
+ Max: 364µs
318
+
319
+ ────────────────────────────────────────────────────────────
320
+ 4. Algorithm comparison (DRIFT vs TRADITIONAL)
321
+ ────────────────────────────────────────────────────────────
322
+ DRIFT 58,296 IDs/sec drift=1
323
+ TRADITIONAL 59,000 IDs/sec drift=0
324
+
325
+ ────────────────────────────────────────────────────────────
326
+ 5. Throughput by sequence bit length
327
+ ────────────────────────────────────────────────────────────
328
+ seqBits= 3 maxSeq= 7 => 2,986 IDs/sec drift=1
329
+ seqBits= 4 maxSeq= 15 => 10,884 IDs/sec drift=1
330
+ seqBits= 6 maxSeq= 63 => 58,240 IDs/sec drift=1
331
+ seqBits= 8 maxSeq= 255 => 247,804 IDs/sec drift=1
332
+ seqBits=10 maxSeq= 1023 => 1,008,930 IDs/sec drift=1
333
+ seqBits=14 maxSeq=16383 => 4,130,701 IDs/sec drift=0
334
+
335
+ ────────────────────────────────────────────────────────────
336
+ 6. Memory footprint
337
+ ────────────────────────────────────────────────────────────
338
+ Generated: 1,000,000 IDs (not stored)
339
+ Heap delta: -6.27 MB
340
+ Note: Run with --expose-gc for accurate GC-forced measurement
341
+
342
+ ────────────────────────────────────────────────────────────
343
+ Summary — Recommended test thresholds
344
+ ────────────────────────────────────────────────────────────
345
+ Peak single-call: 75,332 IDs/sec
346
+ Peak batch: 61,716 IDs/sec
347
+ Suggested min threshold: 45,199 IDs/sec (60% of peak)
348
+ Suggested batch threshold: 37,029 IDs/sec (60% of peak)
349
+ Suggested P99 cap: 2µs (3× measured P99)
350
+ ```
351
+
352
+ </details>
353
+
354
+ ## 开发
355
+
356
+ ```bash
357
+ pnpm install # 安装依赖
358
+ pnpm run build # 构建(ESM + CJS)
359
+ pnpm run dev # 监听模式
360
+ pnpm run test # 运行测试
361
+ pnpm run benchmark # 运行环境性能探测
362
+ pnpm run typecheck # 类型检查
363
+ pnpm run lint # 代码检查(Biome)
364
+ pnpm run format # 代码格式化(Biome)
365
+ ```
366
+
367
+ ## 注意事项
368
+
369
+ - 每个 Worker/进程必须使用**不同的 workerId**
370
+ - 实例**非线程安全**,不要跨线程共享
371
+ - `workerIdBitLength + seqBitLength` 不能超过 22
372
+ - 序列号 0-4 保留用于时钟回拨处理
373
+ - 超出 JavaScript 安全整数范围(2^53-1)时,使用 `nextBigId()` 或 `nextId()`(自动返回 BigInt)
374
+
375
+ ## License
376
+
377
+ [MIT](./LICENSE) License © 2025-PRESENT [wudi](https://github.com/WuChenDi)
package/dist/index.cjs CHANGED
@@ -15,16 +15,16 @@ function initConfig(options) {
15
15
  const config = {
16
16
  workerId: options.workerId,
17
17
  method: options.method === GenidMethod.TRADITIONAL ? GenidMethod.TRADITIONAL : GenidMethod.DRIFT,
18
- baseTime: options.baseTime && options.baseTime > 0 ? options.baseTime : (/* @__PURE__ */ new Date("2020-01-01")).valueOf(),
19
- workerIdBitLength: options.workerIdBitLength && options.workerIdBitLength > 0 ? options.workerIdBitLength : 6,
20
- seqBitLength: options.seqBitLength && options.seqBitLength > 0 ? options.seqBitLength : 6,
18
+ baseTime: options.baseTime != null && options.baseTime > 0 ? options.baseTime : (/* @__PURE__ */ new Date("2020-01-01")).valueOf(),
19
+ workerIdBitLength: options.workerIdBitLength != null && options.workerIdBitLength > 0 ? options.workerIdBitLength : 6,
20
+ seqBitLength: options.seqBitLength != null && options.seqBitLength > 0 ? options.seqBitLength : 6,
21
21
  maxSeqNumber: 0,
22
22
  minSeqNumber: 0,
23
23
  topOverCostCount: 0
24
24
  };
25
- config.maxSeqNumber = options.maxSeqNumber && options.maxSeqNumber > 0 ? options.maxSeqNumber : (1 << config.seqBitLength) - 1;
26
- config.minSeqNumber = options.minSeqNumber && options.minSeqNumber > 0 ? options.minSeqNumber : 5;
27
- config.topOverCostCount = options.topOverCostCount && options.topOverCostCount > 0 ? options.topOverCostCount : 2e3;
25
+ config.maxSeqNumber = options.maxSeqNumber != null && options.maxSeqNumber > 0 ? options.maxSeqNumber : (1 << config.seqBitLength) - 1;
26
+ config.minSeqNumber = options.minSeqNumber != null && options.minSeqNumber >= 5 ? options.minSeqNumber : 5;
27
+ config.topOverCostCount = options.topOverCostCount != null && options.topOverCostCount > 0 ? options.topOverCostCount : 2e3;
28
28
  return config;
29
29
  }
30
30
  /** 校验配置合法性,不合法则抛出 Error */
@@ -308,10 +308,18 @@ var GenidOptimized = class {
308
308
  }
309
309
  /**
310
310
  * 验证 ID 是否为当前配置下合法的 Snowflake ID
311
- * @param strictWorkerId - 为 true 时要求 workerId 匹配当前实例
311
+ *
312
+ * @param options - 校验选项,或传 boolean 表示 strictWorkerId(向后兼容)
313
+ *
314
+ * @example
315
+ * genid.isValid(id) // 宽松校验
316
+ * genid.isValid(id, true) // 要求 workerId 匹配
317
+ * genid.isValid(id, { strictWorkerId: true }) // 同上
318
+ * genid.isValid(id, { afterTime: startupTime }) // 要求 ID 生成时间晚于 startupTime
312
319
  */
313
- isValid(id, strictWorkerId = false) {
320
+ isValid(id, options = false) {
314
321
  try {
322
+ const opts = typeof options === "boolean" ? { strictWorkerId: options } : options;
315
323
  const idBigInt = BigInt(id);
316
324
  if (idBigInt < 0n) return false;
317
325
  if (idBigInt >= 18446744073709551616n) return false;
@@ -321,11 +329,10 @@ var GenidOptimized = class {
321
329
  const sequence = idBigInt & (1n << this.seqBitLength) - 1n;
322
330
  if (timestamp < this.baseTime) return false;
323
331
  if (timestamp > BigInt(Date.now()) + 1000n) return false;
324
- const maxWorkerId = (1n << this.workerIdBitLength) - 1n;
325
- if (workerId < 0n || workerId > maxWorkerId) return false;
326
- if (strictWorkerId && workerId !== this.workerId) return false;
327
- const maxSeq = (1n << this.seqBitLength) - 1n;
328
- if (sequence < 0n || sequence > maxSeq) return false;
332
+ if (opts.afterTime != null && timestamp < BigInt(opts.afterTime)) return false;
333
+ if (workerId > (1n << this.workerIdBitLength) - 1n) return false;
334
+ if (opts.strictWorkerId && workerId !== this.workerId) return false;
335
+ if (sequence > (1n << this.seqBitLength) - 1n) return false;
329
336
  return true;
330
337
  } catch {
331
338
  return false;
package/dist/index.d.cts CHANGED
@@ -64,6 +64,13 @@ interface StatsResult {
64
64
  avgPerSecond: number;
65
65
  currentState: 'OVER_COST' | 'NORMAL';
66
66
  }
67
+ /** isValid 校验选项 */
68
+ interface ValidateOptions {
69
+ /** 为 true 时要求 workerId 匹配当前实例(默认:false) */
70
+ strictWorkerId?: boolean;
71
+ /** ID 的生成时间不得早于此时间戳/毫秒(默认:baseTime) */
72
+ afterTime?: number;
73
+ }
67
74
  /** 配置信息 */
68
75
  interface ConfigResult {
69
76
  method: 'DRIFT' | 'TRADITIONAL';
@@ -154,11 +161,18 @@ declare class GenidOptimized {
154
161
  getConfig(): ConfigResult;
155
162
  /**
156
163
  * 验证 ID 是否为当前配置下合法的 Snowflake ID
157
- * @param strictWorkerId - 为 true 时要求 workerId 匹配当前实例
164
+ *
165
+ * @param options - 校验选项,或传 boolean 表示 strictWorkerId(向后兼容)
166
+ *
167
+ * @example
168
+ * genid.isValid(id) // 宽松校验
169
+ * genid.isValid(id, true) // 要求 workerId 匹配
170
+ * genid.isValid(id, { strictWorkerId: true }) // 同上
171
+ * genid.isValid(id, { afterTime: startupTime }) // 要求 ID 生成时间晚于 startupTime
158
172
  */
159
- isValid(id: number | bigint | string, strictWorkerId?: boolean): boolean;
173
+ isValid(id: number | bigint | string, options?: boolean | ValidateOptions): boolean;
160
174
  /** 将 ID 格式化为带标注的二进制字符串(调试用) */
161
175
  formatBinary(id: number | bigint | string): string;
162
176
  }
163
177
  //#endregion
164
- export { ConfigResult, GenidConfig, GenidMethod, GenidOptimized, GenidOptions, ParseResult, Stats, StatsResult };
178
+ export { ConfigResult, GenidConfig, GenidMethod, GenidOptimized, GenidOptions, ParseResult, Stats, StatsResult, ValidateOptions };
package/dist/index.d.mts CHANGED
@@ -64,6 +64,13 @@ interface StatsResult {
64
64
  avgPerSecond: number;
65
65
  currentState: 'OVER_COST' | 'NORMAL';
66
66
  }
67
+ /** isValid 校验选项 */
68
+ interface ValidateOptions {
69
+ /** 为 true 时要求 workerId 匹配当前实例(默认:false) */
70
+ strictWorkerId?: boolean;
71
+ /** ID 的生成时间不得早于此时间戳/毫秒(默认:baseTime) */
72
+ afterTime?: number;
73
+ }
67
74
  /** 配置信息 */
68
75
  interface ConfigResult {
69
76
  method: 'DRIFT' | 'TRADITIONAL';
@@ -154,11 +161,18 @@ declare class GenidOptimized {
154
161
  getConfig(): ConfigResult;
155
162
  /**
156
163
  * 验证 ID 是否为当前配置下合法的 Snowflake ID
157
- * @param strictWorkerId - 为 true 时要求 workerId 匹配当前实例
164
+ *
165
+ * @param options - 校验选项,或传 boolean 表示 strictWorkerId(向后兼容)
166
+ *
167
+ * @example
168
+ * genid.isValid(id) // 宽松校验
169
+ * genid.isValid(id, true) // 要求 workerId 匹配
170
+ * genid.isValid(id, { strictWorkerId: true }) // 同上
171
+ * genid.isValid(id, { afterTime: startupTime }) // 要求 ID 生成时间晚于 startupTime
158
172
  */
159
- isValid(id: number | bigint | string, strictWorkerId?: boolean): boolean;
173
+ isValid(id: number | bigint | string, options?: boolean | ValidateOptions): boolean;
160
174
  /** 将 ID 格式化为带标注的二进制字符串(调试用) */
161
175
  formatBinary(id: number | bigint | string): string;
162
176
  }
163
177
  //#endregion
164
- export { ConfigResult, GenidConfig, GenidMethod, GenidOptimized, GenidOptions, ParseResult, Stats, StatsResult };
178
+ export { ConfigResult, GenidConfig, GenidMethod, GenidOptimized, GenidOptions, ParseResult, Stats, StatsResult, ValidateOptions };
package/dist/index.mjs CHANGED
@@ -14,16 +14,16 @@ function initConfig(options) {
14
14
  const config = {
15
15
  workerId: options.workerId,
16
16
  method: options.method === GenidMethod.TRADITIONAL ? GenidMethod.TRADITIONAL : GenidMethod.DRIFT,
17
- baseTime: options.baseTime && options.baseTime > 0 ? options.baseTime : (/* @__PURE__ */ new Date("2020-01-01")).valueOf(),
18
- workerIdBitLength: options.workerIdBitLength && options.workerIdBitLength > 0 ? options.workerIdBitLength : 6,
19
- seqBitLength: options.seqBitLength && options.seqBitLength > 0 ? options.seqBitLength : 6,
17
+ baseTime: options.baseTime != null && options.baseTime > 0 ? options.baseTime : (/* @__PURE__ */ new Date("2020-01-01")).valueOf(),
18
+ workerIdBitLength: options.workerIdBitLength != null && options.workerIdBitLength > 0 ? options.workerIdBitLength : 6,
19
+ seqBitLength: options.seqBitLength != null && options.seqBitLength > 0 ? options.seqBitLength : 6,
20
20
  maxSeqNumber: 0,
21
21
  minSeqNumber: 0,
22
22
  topOverCostCount: 0
23
23
  };
24
- config.maxSeqNumber = options.maxSeqNumber && options.maxSeqNumber > 0 ? options.maxSeqNumber : (1 << config.seqBitLength) - 1;
25
- config.minSeqNumber = options.minSeqNumber && options.minSeqNumber > 0 ? options.minSeqNumber : 5;
26
- config.topOverCostCount = options.topOverCostCount && options.topOverCostCount > 0 ? options.topOverCostCount : 2e3;
24
+ config.maxSeqNumber = options.maxSeqNumber != null && options.maxSeqNumber > 0 ? options.maxSeqNumber : (1 << config.seqBitLength) - 1;
25
+ config.minSeqNumber = options.minSeqNumber != null && options.minSeqNumber >= 5 ? options.minSeqNumber : 5;
26
+ config.topOverCostCount = options.topOverCostCount != null && options.topOverCostCount > 0 ? options.topOverCostCount : 2e3;
27
27
  return config;
28
28
  }
29
29
  /** 校验配置合法性,不合法则抛出 Error */
@@ -307,10 +307,18 @@ var GenidOptimized = class {
307
307
  }
308
308
  /**
309
309
  * 验证 ID 是否为当前配置下合法的 Snowflake ID
310
- * @param strictWorkerId - 为 true 时要求 workerId 匹配当前实例
310
+ *
311
+ * @param options - 校验选项,或传 boolean 表示 strictWorkerId(向后兼容)
312
+ *
313
+ * @example
314
+ * genid.isValid(id) // 宽松校验
315
+ * genid.isValid(id, true) // 要求 workerId 匹配
316
+ * genid.isValid(id, { strictWorkerId: true }) // 同上
317
+ * genid.isValid(id, { afterTime: startupTime }) // 要求 ID 生成时间晚于 startupTime
311
318
  */
312
- isValid(id, strictWorkerId = false) {
319
+ isValid(id, options = false) {
313
320
  try {
321
+ const opts = typeof options === "boolean" ? { strictWorkerId: options } : options;
314
322
  const idBigInt = BigInt(id);
315
323
  if (idBigInt < 0n) return false;
316
324
  if (idBigInt >= 18446744073709551616n) return false;
@@ -320,11 +328,10 @@ var GenidOptimized = class {
320
328
  const sequence = idBigInt & (1n << this.seqBitLength) - 1n;
321
329
  if (timestamp < this.baseTime) return false;
322
330
  if (timestamp > BigInt(Date.now()) + 1000n) return false;
323
- const maxWorkerId = (1n << this.workerIdBitLength) - 1n;
324
- if (workerId < 0n || workerId > maxWorkerId) return false;
325
- if (strictWorkerId && workerId !== this.workerId) return false;
326
- const maxSeq = (1n << this.seqBitLength) - 1n;
327
- if (sequence < 0n || sequence > maxSeq) return false;
331
+ if (opts.afterTime != null && timestamp < BigInt(opts.afterTime)) return false;
332
+ if (workerId > (1n << this.workerIdBitLength) - 1n) return false;
333
+ if (opts.strictWorkerId && workerId !== this.workerId) return false;
334
+ if (sequence > (1n << this.seqBitLength) - 1n) return false;
328
335
  return true;
329
336
  } catch {
330
337
  return false;
package/package.json CHANGED
@@ -1,72 +1,70 @@
1
- {
2
- "name": "@cdlab996/genid",
3
- "type": "module",
4
- "version": "1.3.0",
5
- "description": "基于 Snowflake 算法的高性能分布式唯一 ID 生成器",
6
- "author": "wudi <wuchendi96@gmail.com>",
7
- "license": "MIT",
8
- "homepage": "https://github.com/WuChenDi",
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/WuChenDi/genid.git"
12
- },
13
- "bugs": {
14
- "url": "https://github.com/WuChenDi/genid/issues"
15
- },
16
- "keywords": [
17
- "snowflake",
18
- "id-generator",
19
- "distributed-id",
20
- "unique-id",
21
- "genid",
22
- "uuid",
23
- "uid",
24
- "typescript"
25
- ],
26
- "exports": {
27
- ".": {
28
- "require": {
29
- "types": "./dist/index.d.cts",
30
- "default": "./dist/index.cjs"
31
- },
32
- "import": {
33
- "types": "./dist/index.d.mts",
34
- "default": "./dist/index.mjs"
35
- }
36
- }
37
- },
38
- "main": "./dist/index.cjs",
39
- "module": "./dist/index.mjs",
40
- "types": "./dist/index.d.mts",
41
- "files": [
42
- "dist",
43
- "README.md",
44
- "LICENSE"
45
- ],
46
- "engines": {
47
- "node": ">=20"
48
- },
49
- "devDependencies": {
50
- "@biomejs/biome": "^2.4.7",
51
- "@types/node": "^25",
52
- "tsdown": "^0.21.4",
53
- "typescript": "^5",
54
- "vitest": "^4.1.0"
55
- },
56
- "publishConfig": {
57
- "access": "public",
58
- "registry": "https://registry.npmjs.org/"
59
- },
60
- "scripts": {
61
- "build": "tsdown",
62
- "dev": "tsdown --watch",
63
- "test": "vitest --run",
64
- "test:index": "vitest --run tests/index.test.ts",
65
- "test:performance": "vitest --run tests/performance.test.ts",
66
- "test:stress": "vitest --run tests/stress.test.ts",
67
- "lint": "biome check",
68
- "format": "biome format --write",
69
- "clean": "bash ./scripts/clean.sh",
70
- "typecheck": "tsc --project tsconfig.json"
71
- }
72
- }
1
+ {
2
+ "name": "@cdlab996/genid",
3
+ "type": "module",
4
+ "version": "1.4.1",
5
+ "description": "High-performance distributed unique ID generator based on the Snowflake algorithm",
6
+ "author": "wudi <wuchendi96@gmail.com>",
7
+ "license": "MIT",
8
+ "homepage": "https://github.com/WuChenDi",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/WuChenDi/genid.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/WuChenDi/genid/issues"
15
+ },
16
+ "keywords": [
17
+ "snowflake",
18
+ "id-generator",
19
+ "distributed-id",
20
+ "unique-id",
21
+ "genid",
22
+ "uuid",
23
+ "uid",
24
+ "typescript"
25
+ ],
26
+ "exports": {
27
+ ".": {
28
+ "require": {
29
+ "types": "./dist/index.d.cts",
30
+ "default": "./dist/index.cjs"
31
+ },
32
+ "import": {
33
+ "types": "./dist/index.d.mts",
34
+ "default": "./dist/index.mjs"
35
+ }
36
+ }
37
+ },
38
+ "main": "./dist/index.cjs",
39
+ "module": "./dist/index.mjs",
40
+ "types": "./dist/index.d.mts",
41
+ "files": [
42
+ "dist",
43
+ "README.md",
44
+ "LICENSE"
45
+ ],
46
+ "engines": {
47
+ "node": ">=20"
48
+ },
49
+ "scripts": {
50
+ "build": "tsdown",
51
+ "dev": "tsdown --watch",
52
+ "test": "vitest --run",
53
+ "lint": "biome check",
54
+ "format": "biome format --write",
55
+ "benchmark": "npx tsx scripts/benchmark.ts",
56
+ "clean": "bash ./scripts/clean.sh",
57
+ "typecheck": "tsc --project tsconfig.json"
58
+ },
59
+ "devDependencies": {
60
+ "@biomejs/biome": "^2.4.10",
61
+ "@types/node": "^25",
62
+ "tsdown": "^0.21.7",
63
+ "typescript": "^6",
64
+ "vitest": "^4.1.2"
65
+ },
66
+ "publishConfig": {
67
+ "access": "public",
68
+ "registry": "https://registry.npmjs.org/"
69
+ }
70
+ }