smolcanon 0.1.0 → 0.2.1
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/CHANGELOG.md +16 -0
- package/README.md +54 -10
- package/index.cjs +10 -4
- package/index.js +10 -4
- package/package.json +10 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,22 @@ This change log follows the format documented in [Keep a CHANGELOG].
|
|
|
8
8
|
[semantic versioning]: http://semver.org/
|
|
9
9
|
[keep a changelog]: http://keepachangelog.com/
|
|
10
10
|
|
|
11
|
+
## v0.2.1 - 2026-01-29
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Fixed example code in README.md to correctly show usage of the `xxh32` function with the canonicalized string.
|
|
16
|
+
|
|
17
|
+
## v0.2.0 - 2025-08-18
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Significantly improved performance.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- Fixed README initially copied from [`smolxxh`](https://github.com/kossnocorp/smolxxh).
|
|
26
|
+
|
|
11
27
|
## v0.1.0 - 2025-08-17
|
|
12
28
|
|
|
13
29
|
Initial version
|
package/README.md
CHANGED
|
@@ -1,32 +1,76 @@
|
|
|
1
|
-
# Smol
|
|
1
|
+
# Smol Canon
|
|
2
2
|
|
|
3
|
-
Tiny
|
|
3
|
+
Tiny JS values canonicalization for hashing.
|
|
4
4
|
|
|
5
|
-
It
|
|
5
|
+
It uses a simple serialization algorithm, generating a consistent string representation of JS values. It is built to use with [Smol xxHash](https://github.com/kossnocorp/smolxxh).
|
|
6
6
|
|
|
7
|
-
It is
|
|
7
|
+
It is tiny and efficient. It is just `185B` and 30%+ faster than other stable serialization libraries.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Unlike alternatives, it is focused on hashing and doesn't produce valid JSON, making it more efficient, and also supports more value types, i.e., `undefined`.
|
|
10
10
|
|
|
11
|
-
It
|
|
11
|
+
It features dual CJS/ESM support and built-in TypeScript definitions.
|
|
12
12
|
|
|
13
13
|
## Installation
|
|
14
14
|
|
|
15
15
|
The package is available on npm:
|
|
16
16
|
|
|
17
17
|
```sh
|
|
18
|
-
npm install
|
|
18
|
+
npm install smolcanon
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
## Usage
|
|
22
22
|
|
|
23
|
-
Pass
|
|
23
|
+
Pass any JS value to the `canonize` function to get its string representation:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { canonize } from "smolcanon";
|
|
27
|
+
|
|
28
|
+
const canon = canonize({ foo: "bar", baz: "qux" });
|
|
29
|
+
// => '{foo:"bar";baz:"qux"}'
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
You can use it with [Smol xxHash](https://github.com/kossnocorp/smolxxh) to produce consistent hashes for your data:
|
|
24
33
|
|
|
25
34
|
```ts
|
|
35
|
+
import { canonize } from "smolcanon";
|
|
26
36
|
import { xxh32 } from "smolxxh";
|
|
27
37
|
|
|
28
|
-
|
|
29
|
-
|
|
38
|
+
const canon = canonize({ foo: "bar", baz: "qux" });
|
|
39
|
+
const hash = xxh32(Buffer.from(canon, "utf8")).toString(16);
|
|
40
|
+
//=> "ed4e5029"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Benchmark
|
|
44
|
+
|
|
45
|
+
[The benchmark](./benchmark/benchmark.ts) shows that Smol Canon is significantly faster than other popular libraries for canonicalizing JavaScript values:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
canonicalize:
|
|
49
|
+
4 197 ops/s, ±0.28% | 43.59% slower
|
|
50
|
+
|
|
51
|
+
json-canon:
|
|
52
|
+
5 171 ops/s, ±2.11% | 30.5% slower
|
|
53
|
+
|
|
54
|
+
fast-json-stable-stringify:
|
|
55
|
+
4 548 ops/s, ±2.32% | 38.87% slower
|
|
56
|
+
|
|
57
|
+
fast-safe-stringify:
|
|
58
|
+
5 310 ops/s, ±2.30% | 28.63% slower
|
|
59
|
+
|
|
60
|
+
fast-stable-stringify:
|
|
61
|
+
4 973 ops/s, ±1.56% | 33.16% slower
|
|
62
|
+
|
|
63
|
+
json-stable-stringify:
|
|
64
|
+
3 648 ops/s, ±1.93% | 50.97% slower
|
|
65
|
+
|
|
66
|
+
json-stringify-deterministic:
|
|
67
|
+
3 054 ops/s, ±1.80% | slowest, 58.95% slower
|
|
68
|
+
|
|
69
|
+
safe-stable-stringify:
|
|
70
|
+
5 345 ops/s, ±2.17% | 28.16% slower
|
|
71
|
+
|
|
72
|
+
smolcanon:
|
|
73
|
+
7 440 ops/s, ±1.68% | fastest
|
|
30
74
|
```
|
|
31
75
|
|
|
32
76
|
## Changelog
|
package/index.cjs
CHANGED
|
@@ -13,13 +13,19 @@ exports.canonize = canonize;
|
|
|
13
13
|
*/
|
|
14
14
|
function canonize(input) {
|
|
15
15
|
if (typeof input !== "object" || !input) {
|
|
16
|
-
|
|
17
|
-
if (
|
|
16
|
+
// NOTE: Traditional approach is faster than `Object.is(input, -0)`
|
|
17
|
+
if (input === 0 && 1 / input === -Infinity) return "-0";
|
|
18
|
+
// NOTE: Manual replacing is faster than `JSON.stringify`
|
|
19
|
+
if (typeof input === "string") return `"${input.replace(/"/g, '\\"')}"`;
|
|
18
20
|
return String(input);
|
|
19
21
|
}
|
|
20
22
|
let canon = "";
|
|
21
|
-
|
|
23
|
+
const isArray = Array.isArray(input);
|
|
24
|
+
// NOTE: Skipping sorting for arrays improves performance. We also tried
|
|
25
|
+
// using `for...in` loop for arrays but it didn't affect performance at all.
|
|
26
|
+
const keys = isArray ? Object.keys(input) : Object.keys(input).sort();
|
|
27
|
+
for (const key of keys) {
|
|
22
28
|
canon += `${key}:${canonize(input[key])};`;
|
|
23
29
|
}
|
|
24
|
-
return
|
|
30
|
+
return isArray ? `[${canon}]` : `{${canon}}`;
|
|
25
31
|
}
|
package/index.js
CHANGED
|
@@ -10,13 +10,19 @@
|
|
|
10
10
|
*/
|
|
11
11
|
export function canonize(input) {
|
|
12
12
|
if (typeof input !== "object" || !input) {
|
|
13
|
-
|
|
14
|
-
if (
|
|
13
|
+
// NOTE: Traditional approach is faster than `Object.is(input, -0)`
|
|
14
|
+
if (input === 0 && 1 / input === -Infinity) return "-0";
|
|
15
|
+
// NOTE: Manual replacing is faster than `JSON.stringify`
|
|
16
|
+
if (typeof input === "string") return `"${input.replace(/"/g, '\\"')}"`;
|
|
15
17
|
return String(input);
|
|
16
18
|
}
|
|
17
19
|
let canon = "";
|
|
18
|
-
|
|
20
|
+
const isArray = Array.isArray(input);
|
|
21
|
+
// NOTE: Skipping sorting for arrays improves performance. We also tried
|
|
22
|
+
// using `for...in` loop for arrays but it didn't affect performance at all.
|
|
23
|
+
const keys = isArray ? Object.keys(input) : Object.keys(input).sort();
|
|
24
|
+
for (const key of keys) {
|
|
19
25
|
canon += `${key}:${canonize(input[key])};`;
|
|
20
26
|
}
|
|
21
|
-
return
|
|
27
|
+
return isArray ? `[${canon}]` : `{${canon}}`;
|
|
22
28
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "smolcanon",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Tiny JS objects canonicalization for hashing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.cjs",
|
|
@@ -45,10 +45,19 @@
|
|
|
45
45
|
"@swc/core": "^1.13.3",
|
|
46
46
|
"@types/xxhashjs": "^0.2.4",
|
|
47
47
|
"babel-plugin-replace-import-extension": "^1.1.5",
|
|
48
|
+
"benny": "^3.7.1",
|
|
48
49
|
"bytes-iec": "^3.1.1",
|
|
50
|
+
"canonicalize": "^2.1.0",
|
|
51
|
+
"fast-json-stable-stringify": "^2.1.0",
|
|
52
|
+
"fast-safe-stringify": "^2.1.1",
|
|
53
|
+
"fast-stable-stringify": "^1.0.0",
|
|
49
54
|
"glob": "^10.4.5",
|
|
55
|
+
"json-canon": "^1.0.1",
|
|
56
|
+
"json-stable-stringify": "^1.3.0",
|
|
57
|
+
"json-stringify-deterministic": "^1.0.12",
|
|
50
58
|
"minimatch": "^10.0.3",
|
|
51
59
|
"picocolors": "^1.1.1",
|
|
60
|
+
"safe-stable-stringify": "^2.5.0",
|
|
52
61
|
"tsx": "^4.20.4",
|
|
53
62
|
"typescript": "^5.9.2",
|
|
54
63
|
"vitest": "^1.6.1"
|