@cdlab996/genid 1.2.1 → 1.3.0

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/dist/index.mjs CHANGED
@@ -1,29 +1,55 @@
1
- //#region src/types/index.ts
2
- /**
3
- * ID 生成算法类型
4
- */
1
+ //#region src/types.ts
2
+ /** ID 生成算法类型 */
5
3
  let GenidMethod = /* @__PURE__ */ function(GenidMethod) {
6
- /** 漂移算法(推荐用于高性能场景) */
4
+ /** 漂移算法(推荐,高并发下可突破每毫秒序列号上限) */
7
5
  GenidMethod[GenidMethod["DRIFT"] = 1] = "DRIFT";
8
- /** 传统算法 */
6
+ /** 传统算法(序列号耗尽时等待下一毫秒) */
9
7
  GenidMethod[GenidMethod["TRADITIONAL"] = 2] = "TRADITIONAL";
10
8
  return GenidMethod;
11
9
  }({});
12
-
13
10
  //#endregion
14
- //#region src/index.ts
11
+ //#region src/config.ts
12
+ /** 将用户配置与默认值合并,返回完整内部配置 */
13
+ function initConfig(options) {
14
+ const config = {
15
+ workerId: options.workerId,
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,
20
+ maxSeqNumber: 0,
21
+ minSeqNumber: 0,
22
+ topOverCostCount: 0
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;
27
+ return config;
28
+ }
29
+ /** 校验配置合法性,不合法则抛出 Error */
30
+ function validateConfig(config) {
31
+ const { workerId, baseTime, workerIdBitLength, seqBitLength, minSeqNumber, maxSeqNumber } = config;
32
+ if (baseTime > Date.now()) throw new Error("[GenidOptimized] baseTime 不能大于当前时间");
33
+ if (workerIdBitLength < 1 || workerIdBitLength > 15) throw new Error("[GenidOptimized] workerIdBitLength 必须在 1 到 15 之间");
34
+ if (seqBitLength < 3 || seqBitLength > 21) throw new Error("[GenidOptimized] seqBitLength 必须在 3 到 21 之间");
35
+ if (workerIdBitLength + seqBitLength > 22) throw new Error("[GenidOptimized] workerIdBitLength + seqBitLength 不能超过 22");
36
+ const maxWorkerId = (1 << workerIdBitLength) - 1;
37
+ if (workerId < 0 || workerId > maxWorkerId) throw new Error(`[GenidOptimized] workerId 必须在 0 到 ${maxWorkerId} 之间`);
38
+ if (minSeqNumber < 5) throw new Error("[GenidOptimized] minSeqNumber 必须至少为 5(0-4 保留)");
39
+ if (maxSeqNumber < minSeqNumber) throw new Error("[GenidOptimized] maxSeqNumber 必须大于或等于 minSeqNumber");
40
+ }
41
+ //#endregion
42
+ //#region src/genid.ts
15
43
  /**
16
- * 优化版 Snowflake ID 生成器
44
+ * 基于 Snowflake 算法的分布式唯一 ID 生成器
17
45
  *
18
- * 基于 Snowflake 算法的高性能分布式唯一 ID 生成器,支持漂移算法和时钟回拨处理。
46
+ * - 漂移算法:高并发下借用未来时间戳,突破每毫秒序列号上限
47
+ * - 时钟回拨:使用保留序列号(0-4)优雅降级,不阻塞生成
48
+ * - 非线程安全,每个 Worker/进程应使用独立实例和不同 workerId
19
49
  *
20
- * 特性:
21
- * - 漂移算法:提升高并发下的性能
22
- * - 优雅处理时钟回拨,不阻塞生成
23
- * - 可配置的位长度,灵活性高
24
- * - 支持传统和漂移两种生成方法
25
- *
26
- * ⚠️ 注意:此实例不是线程安全的,每个 Worker/进程应该创建独立的实例并使用不同的 workerId
50
+ * @example
51
+ * const genid = new GenidOptimized({ workerId: 1 });
52
+ * const id = genid.nextId();
27
53
  */
28
54
  var GenidOptimized = class {
29
55
  method;
@@ -42,29 +68,10 @@ var GenidOptimized = class {
42
68
  _isOverCost;
43
69
  _overCostCountInOneTerm;
44
70
  _stats;
45
- /**
46
- * 构造函数,初始化 ID 生成器
47
- *
48
- * @param {Object} options - 配置选项
49
- * @param {number} options.workerId - 工作节点/机器 ID(必须,范围 0 到 2^workerIdBitLength-1)
50
- * @param {GenidMethod} [options.method=GenidMethod.DRIFT] - 算法类型
51
- * @param {number} [options.baseTime=1577836800000] - 起始时间戳(毫秒,默认:2020-01-01)
52
- * @param {number} [options.workerIdBitLength=6] - 工作节点 ID 的位数(1-15,默认:6)
53
- * @param {number} [options.seqBitLength=6] - 序列号的位数(3-21,默认:6)
54
- * @param {number} [options.maxSeqNumber] - 最大序列号(默认:2^seqBitLength-1)
55
- * @param {number} [options.minSeqNumber=5] - 最小序列号(默认:5,0-4 保留)
56
- * @param {number} [options.topOverCostCount=2000] - 最大漂移次数(默认:2000)
57
- *
58
- * @throws {Error} 如果缺少 workerId 或配置无效
59
- *
60
- * @example
61
- * const genid = new GenidOptimized({ workerId: 1 });
62
- * const id = genid.nextId();
63
- */
64
71
  constructor(options) {
65
72
  if (options.workerId === void 0 || options.workerId === null) throw new Error("[GenidOptimized] workerId 是必须参数");
66
- const config = this._initConfig(options);
67
- this._validateConfig(config);
73
+ const config = initConfig(options);
74
+ validateConfig(config);
68
75
  this._initVariables(config);
69
76
  this._stats = {
70
77
  totalGenerated: 0n,
@@ -73,49 +80,6 @@ var GenidOptimized = class {
73
80
  startTime: Date.now()
74
81
  };
75
82
  }
76
- /**
77
- * 初始化配置,设置默认值
78
- * @private
79
- * @param {Object} options - 用户提供的配置
80
- * @returns {Object} 合并后的配置对象
81
- */
82
- _initConfig(options) {
83
- const config = {
84
- workerId: options.workerId,
85
- method: options.method === GenidMethod.TRADITIONAL ? GenidMethod.TRADITIONAL : GenidMethod.DRIFT,
86
- baseTime: options.baseTime && options.baseTime > 0 ? options.baseTime : (/* @__PURE__ */ new Date("2020-01-01")).valueOf(),
87
- workerIdBitLength: options.workerIdBitLength && options.workerIdBitLength > 0 ? options.workerIdBitLength : 6,
88
- seqBitLength: options.seqBitLength && options.seqBitLength > 0 ? options.seqBitLength : 6,
89
- maxSeqNumber: 0,
90
- minSeqNumber: 0,
91
- topOverCostCount: 0
92
- };
93
- config.maxSeqNumber = options.maxSeqNumber && options.maxSeqNumber > 0 ? options.maxSeqNumber : (1 << config.seqBitLength) - 1;
94
- config.minSeqNumber = options.minSeqNumber && options.minSeqNumber > 0 ? options.minSeqNumber : 5;
95
- config.topOverCostCount = options.topOverCostCount && options.topOverCostCount > 0 ? options.topOverCostCount : 2e3;
96
- return config;
97
- }
98
- /**
99
- * 验证配置参数的有效性
100
- * @private
101
- * @param {Object} config - 配置对象
102
- * @throws {Error} 如果配置无效
103
- */
104
- _validateConfig(config) {
105
- const { workerId, workerIdBitLength, seqBitLength, minSeqNumber, maxSeqNumber } = config;
106
- if (workerIdBitLength < 1 || workerIdBitLength > 15) throw new Error("[GenidOptimized] workerIdBitLength 必须在 1 到 15 之间");
107
- if (seqBitLength < 3 || seqBitLength > 21) throw new Error("[GenidOptimized] seqBitLength 必须在 3 到 21 之间");
108
- if (workerIdBitLength + seqBitLength > 22) throw new Error("[GenidOptimized] workerIdBitLength + seqBitLength 不能超过 22");
109
- const maxWorkerId = (1 << workerIdBitLength) - 1;
110
- if (workerId < 0 || workerId > maxWorkerId) throw new Error(`[GenidOptimized] workerId 必须在 0 到 ${maxWorkerId} 之间`);
111
- if (minSeqNumber < 5) throw new Error("[GenidOptimized] minSeqNumber 必须至少为 5(0-4 保留)");
112
- if (maxSeqNumber < minSeqNumber) throw new Error("[GenidOptimized] maxSeqNumber 必须大于或等于 minSeqNumber");
113
- }
114
- /**
115
- * 初始化实例变量
116
- * @private
117
- * @param {Object} config - 配置对象
118
- */
119
83
  _initVariables(config) {
120
84
  this.method = BigInt(config.method);
121
85
  this.baseTime = BigInt(config.baseTime);
@@ -133,64 +97,37 @@ var GenidOptimized = class {
133
97
  this._isOverCost = false;
134
98
  this._overCostCountInOneTerm = 0n;
135
99
  }
136
- /**
137
- * 获取当前时间戳(相对于 baseTime 的毫秒数)
138
- * @private
139
- * @returns {bigint} 当前时间戳
140
- */
100
+ /** 获取相对于 baseTime 的当前时间戳 */
141
101
  _getCurrentTimeTick() {
142
102
  return BigInt(Date.now()) - this.baseTime;
143
103
  }
144
- /**
145
- * 等待下一毫秒
146
- * @private
147
- * @returns {bigint} 下一毫秒时间戳
148
- */
104
+ /** 自旋等待直到时间前进到下一毫秒 */
149
105
  _getNextTimeTick() {
150
106
  let timeTick = this._getCurrentTimeTick();
151
107
  let spinCount = 0;
152
108
  const maxSpinCount = 1e6;
153
109
  while (timeTick <= this._lastTimeTick) {
154
110
  spinCount++;
155
- if (spinCount > maxSpinCount)
156
- /**
157
- * 如果自旋太多次,强制返回当前时间 + 1
158
- * 这种情况理论上不应该发生,除非系统时间出现严重问题
159
- */
160
- return this._lastTimeTick + 1n;
111
+ if (spinCount > maxSpinCount) return this._lastTimeTick + 1n;
161
112
  timeTick = this._getCurrentTimeTick();
162
113
  }
163
114
  return timeTick;
164
115
  }
165
- /**
166
- * 根据组件计算 ID
167
- * @private
168
- * @param {bigint} useTimeTick - 使用的时间戳
169
- * @returns {bigint} 计算得到的 ID
170
- */
116
+ /** 组装 ID:timestamp | workerId | sequence,并自增序列号 */
171
117
  _calcId(useTimeTick) {
172
118
  const result = (BigInt(useTimeTick) << this._timestampShift) + (this.workerId << this.seqBitLength) + this._currentSeqNumber;
173
119
  this._currentSeqNumber++;
174
120
  this._stats.totalGenerated++;
175
121
  return result;
176
122
  }
177
- /**
178
- * 计算时钟回拨时的 ID
179
- * @private
180
- * @param {bigint} useTimeTick - 使用的时间戳
181
- * @returns {bigint} 计算得到的 ID
182
- */
123
+ /** 时钟回拨时组装 ID,使用保留序列号(0-4)避免冲突 */
183
124
  _calcTurnBackId(useTimeTick) {
184
125
  const result = (BigInt(useTimeTick) << this._timestampShift) + (this.workerId << this.seqBitLength) + BigInt(this._turnBackIndex);
185
126
  this._turnBackTimeTick++;
186
127
  this._stats.totalGenerated++;
187
128
  return result;
188
129
  }
189
- /**
190
- * 处理漂移情况(漂移算法)
191
- * @private
192
- * @returns {bigint} 生成的 ID
193
- */
130
+ /** 漂移状态下生成 ID */
194
131
  _nextOverCostId() {
195
132
  const currentTimeTick = this._getCurrentTimeTick();
196
133
  if (currentTimeTick > this._lastTimeTick) {
@@ -226,11 +163,7 @@ var GenidOptimized = class {
226
163
  }
227
164
  return this._calcId(this._lastTimeTick);
228
165
  }
229
- /**
230
- * 正常生成 ID
231
- * @private
232
- * @returns {bigint} 生成的 ID
233
- */
166
+ /** 正常状态下生成 ID */
234
167
  _nextNormalId() {
235
168
  const currentTimeTick = this._getCurrentTimeTick();
236
169
  if (currentTimeTick < this._lastTimeTick) {
@@ -247,6 +180,13 @@ var GenidOptimized = class {
247
180
  this._beginTurnBackAction(this._turnBackTimeTick);
248
181
  this._stats.turnBackCount++;
249
182
  }
183
+ if (this._turnBackTimeTick >= this._lastTimeTick) {
184
+ this._turnBackTimeTick = 0n;
185
+ this._turnBackIndex = 0;
186
+ this._lastTimeTick = this._getNextTimeTick();
187
+ this._currentSeqNumber = this.minSeqNumber;
188
+ return this._calcId(this._lastTimeTick);
189
+ }
250
190
  return this._calcTurnBackId(this._turnBackTimeTick);
251
191
  }
252
192
  if (this._turnBackTimeTick > 0) {
@@ -260,6 +200,11 @@ var GenidOptimized = class {
260
200
  return this._calcId(this._lastTimeTick);
261
201
  }
262
202
  if (this._currentSeqNumber > this.maxSeqNumber) {
203
+ if (this.method === BigInt(GenidMethod.TRADITIONAL)) {
204
+ this._lastTimeTick = this._getNextTimeTick();
205
+ this._currentSeqNumber = this.minSeqNumber;
206
+ return this._calcId(this._lastTimeTick);
207
+ }
263
208
  this._beginOverCostAction(currentTimeTick);
264
209
  this._lastTimeTick++;
265
210
  this._currentSeqNumber = this.minSeqNumber;
@@ -270,83 +215,29 @@ var GenidOptimized = class {
270
215
  }
271
216
  return this._calcId(this._lastTimeTick);
272
217
  }
218
+ _beginOverCostAction(_useTimeTick) {}
219
+ _endOverCostAction(_useTimeTick) {}
220
+ _beginTurnBackAction(_useTimeTick) {}
221
+ _endTurnBackAction(_useTimeTick) {}
273
222
  /**
274
- * 钩子函数:开始漂移操作(可被子类重写)
275
- * @protected
276
- * @param {bigint} useTimeTick - 当前时间戳
277
- */
278
- _beginOverCostAction(useTimeTick) {}
279
- /**
280
- * 钩子函数:结束漂移操作(可被子类重写)
281
- * @protected
282
- * @param {bigint} useTimeTick - 当前时间戳
283
- */
284
- _endOverCostAction(useTimeTick) {}
285
- /**
286
- * 钩子函数:开始时钟回拨操作(可被子类重写)
287
- * @protected
288
- * @param {bigint} useTimeTick - 当前时间戳
289
- */
290
- _beginTurnBackAction(useTimeTick) {}
291
- /**
292
- * 钩子函数:结束时钟回拨操作(可被子类重写)
293
- * @protected
294
- * @param {bigint} useTimeTick - 当前时间戳
295
- */
296
- _endTurnBackAction(useTimeTick) {}
297
- /**
298
- * 生成下一个 ID
299
- *
300
- * @returns {number} 唯一 ID(Number 类型)
301
- * @throws {Error} 如果 ID 超出 JavaScript 安全整数范围
302
- *
303
- * @example
304
- * const id = genid.nextNumber();
305
- * console.log(id); // 123456789012345
223
+ * 生成 ID,返回 number。超出安全整数范围时抛错。
224
+ * @throws 当 ID >= Number.MAX_SAFE_INTEGER + 1 时
306
225
  */
307
226
  nextNumber() {
308
227
  const id = this._isOverCost ? this._nextOverCostId() : this._nextNormalId();
309
228
  if (id >= 9007199254740992n) throw new Error(`[GenidOptimized] 生成的 ID ${id.toString()} 超出 JavaScript 安全整数范围 (9007199254740992)。请使用 nextBigId() 方法。`);
310
229
  return Number(id);
311
230
  }
312
- /**
313
- * 生成下一个 ID
314
- *
315
- * 如果 ID 在安全范围内返回 Number,否则返回 BigInt
316
- *
317
- * @returns {number|bigint} 唯一 ID
318
- *
319
- * @example
320
- * const id = genid.nextId();
321
- * console.log(typeof id); // 'number' 或 'bigint'
322
- */
231
+ /** 生成 ID,安全范围内返回 number,否则返回 bigint */
323
232
  nextId() {
324
233
  const id = this._isOverCost ? this._nextOverCostId() : this._nextNormalId();
325
234
  return id >= 9007199254740992n ? id : Number(id);
326
235
  }
327
- /**
328
- * 生成下一个 ID
329
- *
330
- * @returns {bigint} 唯一 ID(BigInt 类型)
331
- *
332
- * @example
333
- * const id = genid.nextBigId();
334
- * console.log(id); // 123456789012345678n
335
- */
236
+ /** 生成 ID,始终返回 bigint */
336
237
  nextBigId() {
337
238
  return this._isOverCost ? this._nextOverCostId() : this._nextNormalId();
338
239
  }
339
- /**
340
- * 批量生成 ID
341
- *
342
- * @param {number} count - 要生成的 ID 数量
343
- * @param {boolean} [asBigInt=false] - 是否返回 BigInt 数组
344
- * @returns {Array<number|bigint>} 唯一 ID 数组
345
- *
346
- * @example
347
- * const ids = genid.nextBatch(100);
348
- * const bigIds = genid.nextBatch(100, true);
349
- */
240
+ /** 批量生成 ID */
350
241
  nextBatch(count, asBigInt = false) {
351
242
  if (count <= 0) throw new Error("[GenidOptimized] 批量生成数量必须大于 0");
352
243
  const ids = [];
@@ -354,24 +245,11 @@ var GenidOptimized = class {
354
245
  return ids;
355
246
  }
356
247
  /**
357
- * 解析 ID,提取其组成部分
358
- *
359
- * @param {number|bigint|string} id - 要解析的 ID
360
- * @returns {Object} 解析结果
361
- * @returns {Date} return.timestamp - 生成时间戳
362
- * @returns {number} return.timestampMs - 时间戳(毫秒)
363
- * @returns {number} return.workerId - 工作节点 ID
364
- * @returns {number} return.sequence - 序列号
248
+ * 解析 ID,提取时间戳、workerId、序列号
365
249
  *
366
250
  * @example
367
- * const info = genid.parse(id);
368
- * console.log(info);
369
- * // {
370
- * // timestamp: Date,
371
- * // timestampMs: 1609459200000,
372
- * // workerId: 1,
373
- * // sequence: 42
374
- * // }
251
+ * genid.parse(id)
252
+ * // { timestamp: Date, timestampMs: 1609459200000, workerId: 1, sequence: 42 }
375
253
  */
376
254
  parse(id) {
377
255
  const idBigInt = BigInt(id);
@@ -387,15 +265,7 @@ var GenidOptimized = class {
387
265
  sequence: Number(sequence)
388
266
  };
389
267
  }
390
- /**
391
- * 获取生成器统计信息
392
- *
393
- * @returns {Object} 统计数据
394
- *
395
- * @example
396
- * const stats = genid.getStats();
397
- * console.log(stats);
398
- */
268
+ /** 获取运行统计信息 */
399
269
  getStats() {
400
270
  const uptime = Date.now() - this._stats.startTime;
401
271
  const totalGenerated = Number(this._stats.totalGenerated);
@@ -408,9 +278,7 @@ var GenidOptimized = class {
408
278
  currentState: this._isOverCost ? "OVER_COST" : "NORMAL"
409
279
  };
410
280
  }
411
- /**
412
- * 重置统计数据
413
- */
281
+ /** 重置统计数据 */
414
282
  resetStats() {
415
283
  this._stats = {
416
284
  totalGenerated: 0n,
@@ -419,11 +287,7 @@ var GenidOptimized = class {
419
287
  startTime: Date.now()
420
288
  };
421
289
  }
422
- /**
423
- * 获取配置信息
424
- *
425
- * @returns {Object} 配置详情
426
- */
290
+ /** 获取当前配置信息 */
427
291
  getConfig() {
428
292
  const maxWorkerId = (1 << Number(this.workerIdBitLength)) - 1;
429
293
  const maxSequence = (1 << Number(this.seqBitLength)) - 1;
@@ -442,18 +306,8 @@ var GenidOptimized = class {
442
306
  };
443
307
  }
444
308
  /**
445
- * 验证 ID 是否为有效的 Snowflake ID
446
- *
447
- * @param {number|bigint|string} id - 要验证的 ID
448
- * @param {boolean} [strictWorkerId=false] - 是否严格验证 workerId 必须匹配当前实例
449
- * @returns {boolean} ID 是否有效
450
- *
451
- * @example
452
- * const genid = new GenidOptimized({ workerId: 1 });
453
- * const id = genid.nextId();
454
- * genid.isValid(id); // true
455
- * genid.isValid(12345); // false
456
- * genid.isValid(id, true); // true (workerId 匹配)
309
+ * 验证 ID 是否为当前配置下合法的 Snowflake ID
310
+ * @param strictWorkerId - 为 true 时要求 workerId 匹配当前实例
457
311
  */
458
312
  isValid(id, strictWorkerId = false) {
459
313
  try {
@@ -476,12 +330,7 @@ var GenidOptimized = class {
476
330
  return false;
477
331
  }
478
332
  }
479
- /**
480
- * 将 ID 格式化为二进制字符串以便调试
481
- *
482
- * @param {number|bigint|string} id - 要格式化的 ID
483
- * @returns {string} 格式化的二进制表示
484
- */
333
+ /** 将 ID 格式化为带标注的二进制字符串(调试用) */
485
334
  formatBinary(id) {
486
335
  const idBigInt = BigInt(id);
487
336
  if (idBigInt < 0n) throw new Error("[GenidOptimized] ID 不能为负数");
@@ -499,6 +348,5 @@ var GenidOptimized = class {
499
348
  ].join("\n");
500
349
  }
501
350
  };
502
-
503
351
  //#endregion
504
- export { GenidOptimized };
352
+ export { GenidMethod, GenidOptimized };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cdlab996/genid",
3
3
  "type": "module",
4
- "version": "1.2.1",
4
+ "version": "1.3.0",
5
5
  "description": "基于 Snowflake 算法的高性能分布式唯一 ID 生成器",
6
6
  "author": "wudi <wuchendi96@gmail.com>",
7
7
  "license": "MIT",
@@ -47,11 +47,11 @@
47
47
  "node": ">=20"
48
48
  },
49
49
  "devDependencies": {
50
- "@biomejs/biome": "^2.3.14",
50
+ "@biomejs/biome": "^2.4.7",
51
51
  "@types/node": "^25",
52
- "tsdown": "^0.20.3",
52
+ "tsdown": "^0.21.4",
53
53
  "typescript": "^5",
54
- "vitest": "^4.0.18"
54
+ "vitest": "^4.1.0"
55
55
  },
56
56
  "publishConfig": {
57
57
  "access": "public",