@tozd/identifier 0.6.0 → 0.8.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 +3 -3
- package/package.json +15 -19
- package/src/index.ts +33 -0
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Features:
|
|
|
12
12
|
|
|
13
13
|
- Identifiers have 128 bits of entropy, making them suitable as global identifiers.
|
|
14
14
|
- By default identifiers are random, but you can convert existing
|
|
15
|
-
[UUIDs](https://en.wikipedia.org/wiki/Universally_unique_identifier).
|
|
15
|
+
[UUIDs](https://en.wikipedia.org/wiki/Universally_unique_identifier) or (lists of) strings.
|
|
16
16
|
- They are encoded into readable base 58 strings always of 22 characters in length.
|
|
17
17
|
|
|
18
18
|
## Installation
|
|
@@ -25,7 +25,7 @@ You can add it to your project using `go get`:
|
|
|
25
25
|
go get gitlab.com/tozd/identifier
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
It requires Go 1.
|
|
28
|
+
It requires Go 1.24 or newer.
|
|
29
29
|
|
|
30
30
|
### TypeScript/JavaScript installation
|
|
31
31
|
|
|
@@ -35,7 +35,7 @@ You can add it to your project using `npm`:
|
|
|
35
35
|
npm install --save @tozd/identifer
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
It requires node
|
|
38
|
+
It requires node 22 or newer. It works in browsers, too.
|
|
39
39
|
|
|
40
40
|
## Usage
|
|
41
41
|
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tozd/identifier",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Readable global identifiers.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"engines": {
|
|
8
|
-
"node": ">=
|
|
8
|
+
"node": ">=22"
|
|
9
9
|
},
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
@@ -30,27 +30,23 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"bs58": "^6.0.0",
|
|
33
|
-
"uuid": "^
|
|
33
|
+
"uuid": "^13.0.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@eslint/compat": "^
|
|
37
|
-
"@eslint/js": "^9.
|
|
38
|
-
"@
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"typescript": "^
|
|
47
|
-
"
|
|
48
|
-
"vitest": "^3.0.6"
|
|
36
|
+
"@eslint/compat": "^2.0.0",
|
|
37
|
+
"@eslint/js": "^9.39.1",
|
|
38
|
+
"@vitest/coverage-v8": "^4.0.9",
|
|
39
|
+
"eslint": "^9.39.1",
|
|
40
|
+
"eslint-config-prettier": "^10.1.8",
|
|
41
|
+
"globals": "^16.5.0",
|
|
42
|
+
"npm-check-updates": "^19.1.2",
|
|
43
|
+
"prettier": "^3.6.2",
|
|
44
|
+
"prettier-plugin-organize-imports": "^4.3.0",
|
|
45
|
+
"typescript": "^5.9.3",
|
|
46
|
+
"typescript-eslint": "^8.46.4",
|
|
47
|
+
"vitest": "^4.0.9"
|
|
49
48
|
},
|
|
50
49
|
"publishConfig": {
|
|
51
50
|
"access": "public"
|
|
52
|
-
},
|
|
53
|
-
"overrides": {
|
|
54
|
-
"esbuild": "0.25.0"
|
|
55
51
|
}
|
|
56
52
|
}
|
package/src/index.ts
CHANGED
|
@@ -81,4 +81,37 @@ export class Identifier {
|
|
|
81
81
|
return false
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
+
|
|
85
|
+
// from generates a deterministic identifier from one or more string values using iterative SHA-256 hashing.
|
|
86
|
+
//
|
|
87
|
+
// Each value is normalized using Unicode NFC normalization before hashing. The function computes
|
|
88
|
+
// hash = SHA256(normalize(values[0])), then hash = SHA256(hash + normalize(values[1])), and so on.
|
|
89
|
+
// The final identifier is derived from the first 128 bits of the resulting hash.
|
|
90
|
+
//
|
|
91
|
+
// Different values or different orderings produce different identifiers.
|
|
92
|
+
public static async from(...values: string[]): Promise<Identifier> {
|
|
93
|
+
const encoder = new TextEncoder()
|
|
94
|
+
let hash: Uint8Array | undefined
|
|
95
|
+
for (const value of values) {
|
|
96
|
+
// Normalize the string using NFC.
|
|
97
|
+
const normalized = value.normalize("NFC")
|
|
98
|
+
const normalizedBytes = encoder.encode(normalized)
|
|
99
|
+
|
|
100
|
+
if (hash === undefined) {
|
|
101
|
+
// First iteration: hash just the normalized value.
|
|
102
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", normalizedBytes)
|
|
103
|
+
hash = new Uint8Array(hashBuffer)
|
|
104
|
+
} else {
|
|
105
|
+
// Subsequent iterations: hash = SHA256(hash + normalized).
|
|
106
|
+
const combined = new Uint8Array(hash.length + normalizedBytes.length)
|
|
107
|
+
combined.set(hash)
|
|
108
|
+
combined.set(normalizedBytes, hash.length)
|
|
109
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", combined)
|
|
110
|
+
hash = new Uint8Array(hashBuffer)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Take first 128 bits (16 bytes) of the final hash.
|
|
115
|
+
return new Identifier(hash!.slice(0, 16))
|
|
116
|
+
}
|
|
84
117
|
}
|