@cdlab996/genid 1.0.0 → 1.2.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/README.md +66 -7
- package/dist/index.cjs +41 -5
- package/dist/index.d.cts +16 -1
- package/dist/{index.d.ts → index.d.mts} +16 -1
- package/dist/{index.js → index.mjs} +40 -5
- package/package.json +19 -13
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @cdlab996/genid
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
基于 Snowflake 算法的高性能分布式唯一 ID 生成器,支持漂移算法和时钟回拨处理,适用于分布式系统中的唯一标识生成需求。
|
|
4
4
|
|
|
5
5
|
## 特性
|
|
6
6
|
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
- 🔄 **时钟回拨处理**:优雅处理时钟回拨,不阻塞 ID 生成
|
|
9
9
|
- ⚙️ **灵活配置**:支持自定义位长度分配
|
|
10
10
|
- 📊 **性能监控**:内置统计和调试功能
|
|
11
|
+
- ✅ **ID 验证**:验证 ID 的有效性,支持严格/宽松模式
|
|
11
12
|
|
|
12
13
|
## 架构设计
|
|
13
14
|
|
|
@@ -47,12 +48,6 @@ graph TB
|
|
|
47
48
|
|
|
48
49
|
Q --> R[更新统计]
|
|
49
50
|
R --> S[返回 ID]
|
|
50
|
-
|
|
51
|
-
style B fill:#fff4e6
|
|
52
|
-
style E fill:#e8f5e9
|
|
53
|
-
style K fill:#fce4ec
|
|
54
|
-
style Q fill:#f3e5f5
|
|
55
|
-
style S fill:#c8e6c9
|
|
56
51
|
```
|
|
57
52
|
|
|
58
53
|
### ID 结构(64-bit)
|
|
@@ -159,6 +154,46 @@ console.log(info)
|
|
|
159
154
|
// }
|
|
160
155
|
```
|
|
161
156
|
|
|
157
|
+
### 验证 ID
|
|
158
|
+
|
|
159
|
+
#### `isValid(id, strictWorkerId?)`
|
|
160
|
+
|
|
161
|
+
验证 ID 是否为有效的 Snowflake ID
|
|
162
|
+
|
|
163
|
+
**参数**
|
|
164
|
+
- `id` - 要验证的 ID(支持 Number、BigInt、String 类型)
|
|
165
|
+
- `strictWorkerId` - 可选,是否严格验证 workerId 必须匹配当前实例(默认:false)
|
|
166
|
+
|
|
167
|
+
**返回值**
|
|
168
|
+
- `boolean` - ID 是否有效
|
|
169
|
+
|
|
170
|
+
**验证规则**
|
|
171
|
+
- ✅ ID 为正数
|
|
172
|
+
- ✅ ID 在 64 位范围内
|
|
173
|
+
- ✅ 时间戳在合理范围内(>= baseTime,<= 当前时间 + 1秒容差)
|
|
174
|
+
- ✅ workerId 在有效范围内(0 到 2^workerIdBitLength-1)
|
|
175
|
+
- ✅ 序列号在有效范围内(0 到 2^seqBitLength-1)
|
|
176
|
+
- ✅ 严格模式下:workerId 必须匹配当前实例
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
const genid = new GenidOptimized({ workerId: 1 })
|
|
180
|
+
const id = genid.nextId()
|
|
181
|
+
|
|
182
|
+
// 宽松模式:验证 ID 格式是否有效
|
|
183
|
+
genid.isValid(id) // true
|
|
184
|
+
genid.isValid(12345) // false(无效的 ID)
|
|
185
|
+
genid.isValid(-1) // false(负数)
|
|
186
|
+
genid.isValid('invalid') // false(无效格式)
|
|
187
|
+
|
|
188
|
+
// 严格模式:验证 ID 是否由当前实例生成
|
|
189
|
+
const genid2 = new GenidOptimized({ workerId: 2 })
|
|
190
|
+
const id2 = genid2.nextId()
|
|
191
|
+
|
|
192
|
+
genid.isValid(id2) // true(宽松模式,其他实例的 ID 也有效)
|
|
193
|
+
genid.isValid(id2, true) // false(严格模式,workerId 不匹配)
|
|
194
|
+
genid.isValid(id, true) // true(严格模式,workerId 匹配)
|
|
195
|
+
```
|
|
196
|
+
|
|
162
197
|
### 统计与配置
|
|
163
198
|
|
|
164
199
|
#### `getStats()`
|
|
@@ -248,6 +283,30 @@ const genid = new GenidOptimized({
|
|
|
248
283
|
})
|
|
249
284
|
```
|
|
250
285
|
|
|
286
|
+
### 验证 ID
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
const genid = new GenidOptimized({ workerId: 1 })
|
|
290
|
+
|
|
291
|
+
// 生成并验证 ID
|
|
292
|
+
const id = genid.nextId()
|
|
293
|
+
if (genid.isValid(id)) {
|
|
294
|
+
console.log('ID 有效')
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// 验证外部 ID(例如从数据库或 API 获取的 ID)
|
|
298
|
+
const externalId = '123456789012345'
|
|
299
|
+
if (genid.isValid(externalId)) {
|
|
300
|
+
const info = genid.parse(externalId)
|
|
301
|
+
console.log('ID 有效,解析结果:', info)
|
|
302
|
+
} else {
|
|
303
|
+
console.error('ID 无效')
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// 严格验证(只接受当前实例生成的 ID)
|
|
307
|
+
const isMyId = genid.isValid(id, true)
|
|
308
|
+
```
|
|
309
|
+
|
|
251
310
|
### 监控性能
|
|
252
311
|
|
|
253
312
|
```typescript
|
package/dist/index.cjs
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
1
2
|
|
|
2
3
|
//#region src/types/index.ts
|
|
3
4
|
/**
|
|
4
5
|
* ID 生成算法类型
|
|
5
6
|
*/
|
|
6
|
-
let GenidMethod = /* @__PURE__ */ function(GenidMethod
|
|
7
|
+
let GenidMethod = /* @__PURE__ */ function(GenidMethod) {
|
|
7
8
|
/** 漂移算法(推荐用于高性能场景) */
|
|
8
|
-
GenidMethod
|
|
9
|
+
GenidMethod[GenidMethod["DRIFT"] = 1] = "DRIFT";
|
|
9
10
|
/** 传统算法 */
|
|
10
|
-
GenidMethod
|
|
11
|
-
return GenidMethod
|
|
11
|
+
GenidMethod[GenidMethod["TRADITIONAL"] = 2] = "TRADITIONAL";
|
|
12
|
+
return GenidMethod;
|
|
12
13
|
}({});
|
|
13
14
|
|
|
14
15
|
//#endregion
|
|
@@ -183,7 +184,7 @@ var GenidOptimized = class {
|
|
|
183
184
|
*/
|
|
184
185
|
_calcTurnBackId(useTimeTick) {
|
|
185
186
|
const result = (BigInt(useTimeTick) << this._timestampShift) + (this.workerId << this.seqBitLength) + BigInt(this._turnBackIndex);
|
|
186
|
-
this._turnBackTimeTick
|
|
187
|
+
this._turnBackTimeTick++;
|
|
187
188
|
this._stats.totalGenerated++;
|
|
188
189
|
return result;
|
|
189
190
|
}
|
|
@@ -443,6 +444,41 @@ var GenidOptimized = class {
|
|
|
443
444
|
};
|
|
444
445
|
}
|
|
445
446
|
/**
|
|
447
|
+
* 验证 ID 是否为有效的 Snowflake ID
|
|
448
|
+
*
|
|
449
|
+
* @param {number|bigint|string} id - 要验证的 ID
|
|
450
|
+
* @param {boolean} [strictWorkerId=false] - 是否严格验证 workerId 必须匹配当前实例
|
|
451
|
+
* @returns {boolean} ID 是否有效
|
|
452
|
+
*
|
|
453
|
+
* @example
|
|
454
|
+
* const genid = new GenidOptimized({ workerId: 1 });
|
|
455
|
+
* const id = genid.nextId();
|
|
456
|
+
* genid.isValid(id); // true
|
|
457
|
+
* genid.isValid(12345); // false
|
|
458
|
+
* genid.isValid(id, true); // true (workerId 匹配)
|
|
459
|
+
*/
|
|
460
|
+
isValid(id, strictWorkerId = false) {
|
|
461
|
+
try {
|
|
462
|
+
const idBigInt = BigInt(id);
|
|
463
|
+
if (idBigInt < 0n) return false;
|
|
464
|
+
if (idBigInt >= 18446744073709551616n) return false;
|
|
465
|
+
const timestamp = (idBigInt >> this._timestampShift) + this.baseTime;
|
|
466
|
+
const workerIdMask = (1n << this.workerIdBitLength) - 1n;
|
|
467
|
+
const workerId = idBigInt >> this.seqBitLength & workerIdMask;
|
|
468
|
+
const sequence = idBigInt & (1n << this.seqBitLength) - 1n;
|
|
469
|
+
if (timestamp < this.baseTime) return false;
|
|
470
|
+
if (timestamp > BigInt(Date.now()) + 1000n) return false;
|
|
471
|
+
const maxWorkerId = (1n << this.workerIdBitLength) - 1n;
|
|
472
|
+
if (workerId < 0n || workerId > maxWorkerId) return false;
|
|
473
|
+
if (strictWorkerId && workerId !== this.workerId) return false;
|
|
474
|
+
const maxSeq = (1n << this.seqBitLength) - 1n;
|
|
475
|
+
if (sequence < 0n || sequence > maxSeq) return false;
|
|
476
|
+
return true;
|
|
477
|
+
} catch {
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
446
482
|
* 将 ID 格式化为二进制字符串以便调试
|
|
447
483
|
*
|
|
448
484
|
* @param {number|bigint|string} id - 要格式化的 ID
|
package/dist/index.d.cts
CHANGED
|
@@ -6,7 +6,7 @@ declare enum GenidMethod {
|
|
|
6
6
|
/** 漂移算法(推荐用于高性能场景) */
|
|
7
7
|
DRIFT = 1,
|
|
8
8
|
/** 传统算法 */
|
|
9
|
-
TRADITIONAL = 2
|
|
9
|
+
TRADITIONAL = 2
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
12
|
* ID 生成器配置选项
|
|
@@ -304,6 +304,21 @@ declare class GenidOptimized {
|
|
|
304
304
|
* @returns {Object} 配置详情
|
|
305
305
|
*/
|
|
306
306
|
getConfig(): ConfigResult;
|
|
307
|
+
/**
|
|
308
|
+
* 验证 ID 是否为有效的 Snowflake ID
|
|
309
|
+
*
|
|
310
|
+
* @param {number|bigint|string} id - 要验证的 ID
|
|
311
|
+
* @param {boolean} [strictWorkerId=false] - 是否严格验证 workerId 必须匹配当前实例
|
|
312
|
+
* @returns {boolean} ID 是否有效
|
|
313
|
+
*
|
|
314
|
+
* @example
|
|
315
|
+
* const genid = new GenidOptimized({ workerId: 1 });
|
|
316
|
+
* const id = genid.nextId();
|
|
317
|
+
* genid.isValid(id); // true
|
|
318
|
+
* genid.isValid(12345); // false
|
|
319
|
+
* genid.isValid(id, true); // true (workerId 匹配)
|
|
320
|
+
*/
|
|
321
|
+
isValid(id: number | bigint | string, strictWorkerId?: boolean): boolean;
|
|
307
322
|
/**
|
|
308
323
|
* 将 ID 格式化为二进制字符串以便调试
|
|
309
324
|
*
|
|
@@ -6,7 +6,7 @@ declare enum GenidMethod {
|
|
|
6
6
|
/** 漂移算法(推荐用于高性能场景) */
|
|
7
7
|
DRIFT = 1,
|
|
8
8
|
/** 传统算法 */
|
|
9
|
-
TRADITIONAL = 2
|
|
9
|
+
TRADITIONAL = 2
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
12
|
* ID 生成器配置选项
|
|
@@ -304,6 +304,21 @@ declare class GenidOptimized {
|
|
|
304
304
|
* @returns {Object} 配置详情
|
|
305
305
|
*/
|
|
306
306
|
getConfig(): ConfigResult;
|
|
307
|
+
/**
|
|
308
|
+
* 验证 ID 是否为有效的 Snowflake ID
|
|
309
|
+
*
|
|
310
|
+
* @param {number|bigint|string} id - 要验证的 ID
|
|
311
|
+
* @param {boolean} [strictWorkerId=false] - 是否严格验证 workerId 必须匹配当前实例
|
|
312
|
+
* @returns {boolean} ID 是否有效
|
|
313
|
+
*
|
|
314
|
+
* @example
|
|
315
|
+
* const genid = new GenidOptimized({ workerId: 1 });
|
|
316
|
+
* const id = genid.nextId();
|
|
317
|
+
* genid.isValid(id); // true
|
|
318
|
+
* genid.isValid(12345); // false
|
|
319
|
+
* genid.isValid(id, true); // true (workerId 匹配)
|
|
320
|
+
*/
|
|
321
|
+
isValid(id: number | bigint | string, strictWorkerId?: boolean): boolean;
|
|
307
322
|
/**
|
|
308
323
|
* 将 ID 格式化为二进制字符串以便调试
|
|
309
324
|
*
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* ID 生成算法类型
|
|
4
4
|
*/
|
|
5
|
-
let GenidMethod = /* @__PURE__ */ function(GenidMethod
|
|
5
|
+
let GenidMethod = /* @__PURE__ */ function(GenidMethod) {
|
|
6
6
|
/** 漂移算法(推荐用于高性能场景) */
|
|
7
|
-
GenidMethod
|
|
7
|
+
GenidMethod[GenidMethod["DRIFT"] = 1] = "DRIFT";
|
|
8
8
|
/** 传统算法 */
|
|
9
|
-
GenidMethod
|
|
10
|
-
return GenidMethod
|
|
9
|
+
GenidMethod[GenidMethod["TRADITIONAL"] = 2] = "TRADITIONAL";
|
|
10
|
+
return GenidMethod;
|
|
11
11
|
}({});
|
|
12
12
|
|
|
13
13
|
//#endregion
|
|
@@ -182,7 +182,7 @@ var GenidOptimized = class {
|
|
|
182
182
|
*/
|
|
183
183
|
_calcTurnBackId(useTimeTick) {
|
|
184
184
|
const result = (BigInt(useTimeTick) << this._timestampShift) + (this.workerId << this.seqBitLength) + BigInt(this._turnBackIndex);
|
|
185
|
-
this._turnBackTimeTick
|
|
185
|
+
this._turnBackTimeTick++;
|
|
186
186
|
this._stats.totalGenerated++;
|
|
187
187
|
return result;
|
|
188
188
|
}
|
|
@@ -442,6 +442,41 @@ var GenidOptimized = class {
|
|
|
442
442
|
};
|
|
443
443
|
}
|
|
444
444
|
/**
|
|
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 匹配)
|
|
457
|
+
*/
|
|
458
|
+
isValid(id, strictWorkerId = false) {
|
|
459
|
+
try {
|
|
460
|
+
const idBigInt = BigInt(id);
|
|
461
|
+
if (idBigInt < 0n) return false;
|
|
462
|
+
if (idBigInt >= 18446744073709551616n) return false;
|
|
463
|
+
const timestamp = (idBigInt >> this._timestampShift) + this.baseTime;
|
|
464
|
+
const workerIdMask = (1n << this.workerIdBitLength) - 1n;
|
|
465
|
+
const workerId = idBigInt >> this.seqBitLength & workerIdMask;
|
|
466
|
+
const sequence = idBigInt & (1n << this.seqBitLength) - 1n;
|
|
467
|
+
if (timestamp < this.baseTime) return false;
|
|
468
|
+
if (timestamp > BigInt(Date.now()) + 1000n) return false;
|
|
469
|
+
const maxWorkerId = (1n << this.workerIdBitLength) - 1n;
|
|
470
|
+
if (workerId < 0n || workerId > maxWorkerId) return false;
|
|
471
|
+
if (strictWorkerId && workerId !== this.workerId) return false;
|
|
472
|
+
const maxSeq = (1n << this.seqBitLength) - 1n;
|
|
473
|
+
if (sequence < 0n || sequence > maxSeq) return false;
|
|
474
|
+
return true;
|
|
475
|
+
} catch {
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
445
480
|
* 将 ID 格式化为二进制字符串以便调试
|
|
446
481
|
*
|
|
447
482
|
* @param {number|bigint|string} id - 要格式化的 ID
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdlab996/genid",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.2.0",
|
|
5
5
|
"description": "基于 Snowflake 算法的高性能分布式唯一 ID 生成器",
|
|
6
6
|
"author": "wudi <wuchendi96@gmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -46,21 +46,27 @@
|
|
|
46
46
|
"engines": {
|
|
47
47
|
"node": ">=20"
|
|
48
48
|
},
|
|
49
|
-
"scripts": {
|
|
50
|
-
"build": "tsdown",
|
|
51
|
-
"dev": "tsdown --watch",
|
|
52
|
-
"test": "vitest --run",
|
|
53
|
-
"test:watch": "vitest",
|
|
54
|
-
"typecheck": "tsc --project tsconfig.json"
|
|
55
|
-
},
|
|
56
49
|
"devDependencies": {
|
|
57
|
-
"@
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
50
|
+
"@biomejs/biome": "^2.3.14",
|
|
51
|
+
"@types/node": "^25",
|
|
52
|
+
"tsdown": "^0.20.3",
|
|
53
|
+
"typescript": "^5",
|
|
54
|
+
"vitest": "^4.0.18"
|
|
61
55
|
},
|
|
62
56
|
"publishConfig": {
|
|
63
57
|
"access": "public",
|
|
64
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"
|
|
65
71
|
}
|
|
66
|
-
}
|
|
72
|
+
}
|