@arcjet/stable-hash 1.0.0-beta.8 → 1.0.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 +34 -6
- package/edge-light.js +4 -0
- package/hasher.d.ts +63 -1
- package/hasher.js +56 -0
- package/package.json +27 -18
- package/workerd.d.ts +2 -0
- package/workerd.js +10 -0
package/README.md
CHANGED
|
@@ -18,10 +18,28 @@
|
|
|
18
18
|
|
|
19
19
|
[Arcjet][arcjet] stable hashing utility.
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
- [npm package (`@arcjet/stable-hash`)](https://www.npmjs.com/package/@arcjet/stable-hash)
|
|
22
|
+
- [GitHub source code (`stable-hash/` in `arcjet/arcjet-js`)](https://github.com/arcjet/arcjet-js/tree/main/stable-hash)
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
## What is this?
|
|
25
|
+
|
|
26
|
+
This is an internal utility to help us create stable hashes.
|
|
27
|
+
It’s super minimal and matches similar internal code in other languages.
|
|
28
|
+
This exists to make sure things work the same across languages.
|
|
29
|
+
|
|
30
|
+
## When should I use this?
|
|
31
|
+
|
|
32
|
+
This is an internal Arcjet package not designed for public use.
|
|
33
|
+
See our [_Get started_ guide][arcjet-get-started] for how to use Arcjet in your
|
|
34
|
+
application.
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
This package is ESM only.
|
|
39
|
+
Install with npm in Node.js:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
npm install @arcjet/stable-hash
|
|
25
43
|
```
|
|
26
44
|
|
|
27
45
|
## Example
|
|
@@ -29,18 +47,28 @@ npm install -S @arcjet/stable-hash
|
|
|
29
47
|
```ts
|
|
30
48
|
import * as hasher from "@arcjet/stable-hash";
|
|
31
49
|
|
|
32
|
-
const id = hasher.hash(
|
|
50
|
+
const id = await hasher.hash(
|
|
33
51
|
hasher.string("type", "EMAIL"),
|
|
34
52
|
hasher.uint32("version", 0),
|
|
35
53
|
hasher.string("mode", "LIVE"),
|
|
36
54
|
hasher.stringSliceOrdered("allow", []),
|
|
37
55
|
hasher.stringSliceOrdered("deny", []),
|
|
38
56
|
);
|
|
57
|
+
|
|
58
|
+
console.log(id);
|
|
59
|
+
// => 49573b7df8d854c2cd5d8a755a4c03aff4014493a41b963490861a279ad675b2
|
|
39
60
|
```
|
|
40
61
|
|
|
41
62
|
## License
|
|
42
63
|
|
|
43
|
-
|
|
64
|
+
[Apache License, Version 2.0][apache-license] © [Arcjet Labs, Inc.][arcjet]
|
|
65
|
+
|
|
66
|
+
Derivative work based on [`feross/buffer`][github-buffer] licensed under
|
|
67
|
+
[MIT][github-buffer-license] © Feross Aboukhadijeh and contributors.
|
|
68
|
+
Our work picks its internal hex encoding logic adjusted for our use.
|
|
44
69
|
|
|
45
|
-
[arcjet]: https://arcjet.com
|
|
46
70
|
[apache-license]: http://www.apache.org/licenses/LICENSE-2.0
|
|
71
|
+
[arcjet]: https://arcjet.com
|
|
72
|
+
[arcjet-get-started]: https://docs.arcjet.com/get-started
|
|
73
|
+
[github-buffer-license]: https://github.com/feross/buffer/blob/5857e295f4d37e3ad02c3abcbf7e8e5ef51f3be6/LICENSE
|
|
74
|
+
[github-buffer]: https://github.com/feross/buffer/tree/5857e295f4d37e3ad02c3abcbf7e8e5ef51f3be6
|
package/edge-light.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { makeHasher } from './hasher.js';
|
|
2
2
|
export { bool, string, stringSliceOrdered, uint32 } from './hasher.js';
|
|
3
3
|
|
|
4
|
+
// @ts-ignore: this value exists in Edge Light, as it implements the DOM type for it.
|
|
5
|
+
// See <https://vercel.com/docs/functions/runtimes/edge#crypto-apis>.
|
|
6
|
+
// This can be verified by adding `/// <reference lib="dom" />` above.
|
|
7
|
+
// But we don’t want to load the entire DOM types or edge-light-specific types.
|
|
4
8
|
const hash = makeHasher(crypto.subtle);
|
|
5
9
|
|
|
6
10
|
export { hash, makeHasher };
|
package/hasher.d.ts
CHANGED
|
@@ -1,13 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Writer.
|
|
3
|
+
*/
|
|
1
4
|
export interface StringWriter {
|
|
5
|
+
/**
|
|
6
|
+
* Write data.
|
|
7
|
+
*
|
|
8
|
+
* @param data
|
|
9
|
+
* Value.
|
|
10
|
+
*/
|
|
2
11
|
writeString(data: string): void;
|
|
3
12
|
}
|
|
4
13
|
interface SubtleCryptoLike {
|
|
5
|
-
digest(algorithm:
|
|
14
|
+
digest(algorithm: {
|
|
15
|
+
name: string;
|
|
16
|
+
} | string, data: ArrayBufferView<ArrayBufferLike> | ArrayBufferLike): Promise<ArrayBuffer>;
|
|
6
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Hash a field.
|
|
20
|
+
*/
|
|
7
21
|
export type FieldHasher = (data: StringWriter) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Create a hasher for a boolean.
|
|
24
|
+
*
|
|
25
|
+
* @param key
|
|
26
|
+
* Key.
|
|
27
|
+
* @param value
|
|
28
|
+
* Value.
|
|
29
|
+
* @returns
|
|
30
|
+
* Hasher.
|
|
31
|
+
*/
|
|
8
32
|
export declare function bool(key: string, value: boolean): FieldHasher;
|
|
33
|
+
/**
|
|
34
|
+
* Create a hasher for an unsigned 32-bit integer.
|
|
35
|
+
*
|
|
36
|
+
* @param key
|
|
37
|
+
* Key.
|
|
38
|
+
* @param value
|
|
39
|
+
* Value.
|
|
40
|
+
* @returns
|
|
41
|
+
* Hasher.
|
|
42
|
+
*/
|
|
9
43
|
export declare function uint32(key: string, value: number): FieldHasher;
|
|
44
|
+
/**
|
|
45
|
+
* Create a hasher for a string.
|
|
46
|
+
*
|
|
47
|
+
* @param key
|
|
48
|
+
* Key.
|
|
49
|
+
* @param value
|
|
50
|
+
* Value.
|
|
51
|
+
* @returns
|
|
52
|
+
* Hasher.
|
|
53
|
+
*/
|
|
10
54
|
export declare function string(key: string, value: string): FieldHasher;
|
|
55
|
+
/**
|
|
56
|
+
* Create a hasher for an array of strings.
|
|
57
|
+
*
|
|
58
|
+
* @param key
|
|
59
|
+
* Key.
|
|
60
|
+
* @param values
|
|
61
|
+
* Values.
|
|
62
|
+
* @returns
|
|
63
|
+
* Hasher.
|
|
64
|
+
*/
|
|
11
65
|
export declare function stringSliceOrdered(key: string, values: string[]): FieldHasher;
|
|
66
|
+
/**
|
|
67
|
+
* Create a hasher.
|
|
68
|
+
*
|
|
69
|
+
* @param subtle
|
|
70
|
+
* Subtle crypto.
|
|
71
|
+
* @returns
|
|
72
|
+
* Hasher.
|
|
73
|
+
*/
|
|
12
74
|
export declare function makeHasher(subtle: SubtleCryptoLike): (...hashers: FieldHasher[]) => Promise<string>;
|
|
13
75
|
export {};
|
package/hasher.js
CHANGED
|
@@ -20,6 +20,16 @@ class Sha256 {
|
|
|
20
20
|
const maxUint32 = 4294967295;
|
|
21
21
|
const fieldSeparator = ":";
|
|
22
22
|
const itemSeparator = ",";
|
|
23
|
+
/**
|
|
24
|
+
* Create a hasher for a boolean.
|
|
25
|
+
*
|
|
26
|
+
* @param key
|
|
27
|
+
* Key.
|
|
28
|
+
* @param value
|
|
29
|
+
* Value.
|
|
30
|
+
* @returns
|
|
31
|
+
* Hasher.
|
|
32
|
+
*/
|
|
23
33
|
function bool(key, value) {
|
|
24
34
|
return (data) => {
|
|
25
35
|
data.writeString(key);
|
|
@@ -32,6 +42,16 @@ function bool(key, value) {
|
|
|
32
42
|
}
|
|
33
43
|
};
|
|
34
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Create a hasher for an unsigned 32-bit integer.
|
|
47
|
+
*
|
|
48
|
+
* @param key
|
|
49
|
+
* Key.
|
|
50
|
+
* @param value
|
|
51
|
+
* Value.
|
|
52
|
+
* @returns
|
|
53
|
+
* Hasher.
|
|
54
|
+
*/
|
|
35
55
|
function uint32(key, value) {
|
|
36
56
|
return (data) => {
|
|
37
57
|
data.writeString(key);
|
|
@@ -44,6 +64,16 @@ function uint32(key, value) {
|
|
|
44
64
|
}
|
|
45
65
|
};
|
|
46
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Create a hasher for a string.
|
|
69
|
+
*
|
|
70
|
+
* @param key
|
|
71
|
+
* Key.
|
|
72
|
+
* @param value
|
|
73
|
+
* Value.
|
|
74
|
+
* @returns
|
|
75
|
+
* Hasher.
|
|
76
|
+
*/
|
|
47
77
|
function string(key, value) {
|
|
48
78
|
return (data) => {
|
|
49
79
|
data.writeString(key);
|
|
@@ -53,6 +83,16 @@ function string(key, value) {
|
|
|
53
83
|
data.writeString(`"`);
|
|
54
84
|
};
|
|
55
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Create a hasher for an array of strings.
|
|
88
|
+
*
|
|
89
|
+
* @param key
|
|
90
|
+
* Key.
|
|
91
|
+
* @param values
|
|
92
|
+
* Values.
|
|
93
|
+
* @returns
|
|
94
|
+
* Hasher.
|
|
95
|
+
*/
|
|
56
96
|
function stringSliceOrdered(key, values) {
|
|
57
97
|
return (data) => {
|
|
58
98
|
data.writeString(key);
|
|
@@ -67,7 +107,23 @@ function stringSliceOrdered(key, values) {
|
|
|
67
107
|
data.writeString("]");
|
|
68
108
|
};
|
|
69
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Create a hasher.
|
|
112
|
+
*
|
|
113
|
+
* @param subtle
|
|
114
|
+
* Subtle crypto.
|
|
115
|
+
* @returns
|
|
116
|
+
* Hasher.
|
|
117
|
+
*/
|
|
70
118
|
function makeHasher(subtle) {
|
|
119
|
+
/**
|
|
120
|
+
* Hash fields.
|
|
121
|
+
*
|
|
122
|
+
* @param hashers
|
|
123
|
+
* Hashers.
|
|
124
|
+
* @returns
|
|
125
|
+
* Promise to a hash.
|
|
126
|
+
*/
|
|
71
127
|
return async function hash(...hashers) {
|
|
72
128
|
const h = new Sha256(subtle);
|
|
73
129
|
for (const hasher of hashers) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcjet/stable-hash",
|
|
3
|
-
"version": "1.0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Arcjet stable hashing utility",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"arcjet",
|
|
7
|
+
"hash",
|
|
8
|
+
"utility",
|
|
9
|
+
"util"
|
|
10
|
+
],
|
|
5
11
|
"license": "Apache-2.0",
|
|
6
12
|
"homepage": "https://arcjet.com",
|
|
7
13
|
"repository": {
|
|
@@ -19,39 +25,42 @@
|
|
|
19
25
|
"url": "https://arcjet.com"
|
|
20
26
|
},
|
|
21
27
|
"engines": {
|
|
22
|
-
"node": ">=
|
|
28
|
+
"node": ">=20"
|
|
23
29
|
},
|
|
24
30
|
"type": "module",
|
|
25
31
|
"main": "./index.js",
|
|
26
32
|
"types": "./index.d.ts",
|
|
27
33
|
"exports": {
|
|
28
34
|
"edge-light": "./edge-light.js",
|
|
35
|
+
"workerd": "./workerd.js",
|
|
29
36
|
"default": "./index.js"
|
|
30
37
|
},
|
|
31
38
|
"files": [
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
39
|
+
"edge-light.d.ts",
|
|
40
|
+
"edge-light.js",
|
|
41
|
+
"hasher.d.ts",
|
|
42
|
+
"hasher.js",
|
|
43
|
+
"index.d.ts",
|
|
44
|
+
"index.js",
|
|
45
|
+
"workerd.d.ts",
|
|
46
|
+
"workerd.js"
|
|
37
47
|
],
|
|
38
48
|
"scripts": {
|
|
39
|
-
"prepublishOnly": "npm run build",
|
|
40
49
|
"build": "rollup --config rollup.config.js",
|
|
41
50
|
"lint": "eslint .",
|
|
42
|
-
"
|
|
43
|
-
"test": "node --test --
|
|
51
|
+
"prepublishOnly": "npm run build",
|
|
52
|
+
"test-api": "node --test -- test/*.test.js",
|
|
53
|
+
"test-coverage": "node --experimental-test-coverage --test -- test/*.test.js",
|
|
54
|
+
"test": "npm run build && npm run lint && npm run test-coverage"
|
|
44
55
|
},
|
|
45
56
|
"dependencies": {},
|
|
46
57
|
"devDependencies": {
|
|
47
|
-
"@arcjet/eslint-config": "1.0.0
|
|
48
|
-
"@arcjet/rollup-config": "1.0.0
|
|
49
|
-
"@
|
|
50
|
-
"@
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"expect": "29.7.0",
|
|
54
|
-
"typescript": "5.8.3"
|
|
58
|
+
"@arcjet/eslint-config": "1.0.0",
|
|
59
|
+
"@arcjet/rollup-config": "1.0.0",
|
|
60
|
+
"@rollup/wasm-node": "4.55.1",
|
|
61
|
+
"@types/node": "25.0.8",
|
|
62
|
+
"eslint": "9.39.2",
|
|
63
|
+
"typescript": "5.9.3"
|
|
55
64
|
},
|
|
56
65
|
"publishConfig": {
|
|
57
66
|
"access": "public",
|
package/workerd.d.ts
ADDED
package/workerd.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { makeHasher } from './hasher.js';
|
|
2
|
+
export { bool, string, stringSliceOrdered, uint32 } from './hasher.js';
|
|
3
|
+
|
|
4
|
+
// @ts-ignore: this value exists in Workerd, as it implements the DOM type for it.
|
|
5
|
+
// See <https://developers.cloudflare.com/workers/runtime-apis/web-crypto/>.
|
|
6
|
+
// This can be verified by adding `/// <reference lib="dom" />` above.
|
|
7
|
+
// But we don’t want to load the entire DOM types or workerd-specific types.
|
|
8
|
+
const hash = makeHasher(crypto.subtle);
|
|
9
|
+
|
|
10
|
+
export { hash, makeHasher };
|