1
- // WebCrypto (crypto global) not available in Node 18.
2
- function createHashNodeCrypto ( content ) {
3
- // require only when necessary
4
- const { createHash } = require ( "node:crypto" ) ;
5
1
6
- let hash = createHash ( "sha256" ) ;
7
- hash . update ( content ) ;
8
-
9
- // Note that Node does include a `digest("base64url")` that is supposedly Node 14+ but curiously failed on Stackblitz’s Node 16.
10
- let base64 = hash . digest ( "base64" ) ;
11
- return urlSafe ( base64 ) ;
12
- }
13
-
14
- function urlSafe ( hashString = "" ) {
15
- return hashString . replace ( / [ = \+ \/ ] / g, function ( match ) {
16
- if ( match === "=" ) {
17
- return "" ;
18
- }
19
- if ( match === "+" ) {
20
- return "-" ;
21
- }
22
- return "_" ;
23
- } ) ;
24
- }
2
+ const { base64UrlSafe } = require ( "./Url.js" ) ;
25
3
26
4
function toBase64 ( bytes ) {
27
5
let str = Array . from ( bytes , ( b ) => String . fromCodePoint ( b ) ) . join ( "" ) ;
28
- // `btoa` is Node 16+
6
+
7
+ // `btoa` Node 16+
29
8
return btoa ( str ) ;
30
9
}
31
10
11
+ // Thanks https://evanhahn.com/the-best-way-to-concatenate-uint8arrays/ (Public domain)
12
+ function mergeUint8Array ( ...arrays ) {
13
+ let totalLength = arrays . reduce (
14
+ ( total , uint8array ) => total + uint8array . byteLength ,
15
+ 0
16
+ ) ;
17
+
18
+ let result = new Uint8Array ( totalLength ) ;
19
+ let offset = 0 ;
20
+ arrays . forEach ( ( uint8array ) => {
21
+ result . set ( uint8array , offset ) ;
22
+ offset += uint8array . byteLength ;
23
+ } ) ;
24
+
25
+ return result ;
26
+ }
27
+
32
28
// same output as node:crypto above (though now async).
33
- module . exports = async function createHash ( content ) {
29
+ module . exports = async function createHash ( ... content ) {
34
30
if ( typeof globalThis . crypto === "undefined" ) {
35
31
// Backwards compat with Node Crypto, since WebCrypto (crypto global) is Node 20+
36
- return createHashNodeCrypto ( content ) ;
32
+ const createHashNodeCrypto = require ( "./CreateHash-Node.js" ) ;
33
+ return createHashNodeCrypto ( ...content ) ;
37
34
}
38
35
39
36
let encoder = new TextEncoder ( ) ;
40
- let data = encoder . encode ( content ) ;
37
+ let input = mergeUint8Array ( ... content . map ( c => encoder . encode ( c ) ) )
41
38
42
39
// `crypto` is Node 20+
43
- return crypto . subtle . digest ( "SHA-256" , data ) . then ( hashBuffer => {
44
- return urlSafe ( toBase64 ( new Uint8Array ( hashBuffer ) ) ) ;
40
+ return crypto . subtle . digest ( "SHA-256" , input ) . then ( hashBuffer => {
41
+ return base64UrlSafe ( toBase64 ( new Uint8Array ( hashBuffer ) ) ) ;
45
42
} ) ;
46
43
}
0 commit comments