Hello ๐๐ป : FYI @truestamp/truestamp-canonify port
grempe opened this issue ยท 2 comments
Hello, this is not an issue, but just to say thanks for the code which served as part of the base for our Typescript port.
https://github.com/truestamp/truestamp-canonify
You library, along with the reference code, was super helpful.
We ported this to typescript as it is important for us to not only gain the security advantages, but to further flesh out the test suite and allow use of the library in not only Node.js but also in Deno and the browser.
We'd love for you to take a look, and if there are any comments about our friendly fork please do let me know.
Out of curiosity I also copied your most current code over and ran our test suite against it. The following was the output (and will show a couple of the differences in how we're handling certain cases. I went through a number of manual test cases to see how JSON.stringify()
is documented to work and tried to get the output to align closely with that.
Some of the differences are the handling of:
BigInt
values should throw an Error asJSON.stringify()
does. The user would need to call.toString()
on the BigInt.- Serialization of
function
values to in Arrays/Objects tonull
, notundefined
which is not valid JSON - Removal of Object key:value where the value is
undefined
(e.g. Symbol() values)
Here's the test output.
โฏ npm t
> @truestamp/canonify@1.0.0 test
> jest
PASS tests/testdata.spec.ts
FAIL tests/basics.spec.ts
โ serializing โบ should behave like JSON.stringify() for โบ BigInt should throw a TypeError
expect(received).toThrow(expected)
Expected substring: "BigInt value can't be serialized in JSON"
Received message: "Do not know how to serialize a BigInt"
2 | export default function canonify(object: any): string | undefined {
3 | if (object === null || typeof object !== 'object') {
> 4 | return JSON.stringify(object);
| ^
5 | }
6 |
7 | if (object.toJSON instanceof Function) {
at canonify (src/index.ts:4:17)
at t (tests/basics.spec.ts:69:17)
at Object.<anonymous> (node_modules/expect/build/toThrowMatchers.js:83:11)
at Object.throwingMatcher [as toThrow] (node_modules/expect/build/index.js:382:21)
at Object.<anonymous> (tests/basics.spec.ts:72:17)
70 | };
71 | expect(t).toThrow(TypeError);
> 72 | expect(t).toThrow("BigInt value can't be serialized in JSON");
| ^
73 | });
74 |
75 | // JSON.stringify('foo')
at Object.<anonymous> (tests/basics.spec.ts:72:17)
โ serializing โบ should behave like JSON.stringify() for โบ Array
expect(received).toEqual(expected) // deep equality
Expected: "[null,null,true,false,\"foo\",42,\"42\",null,null]"
Received: "[null,null,true,false,\"foo\",42,\"42\",null,undefined]"
93 | test('Array', () => {
94 | const a = [undefined, null, true, false, "foo", 42, BigInt(42).toString(), Symbol('hello'), () => { }]
> 95 | expect(canonify(a)).toEqual('[null,null,true,false,"foo",42,"42",null,null]')
| ^
96 | })
97 |
98 | test('Array with String keys', () => {
at Object.<anonymous> (tests/basics.spec.ts:95:27)
โ serializing โบ should behave like JSON.stringify() for โบ Object
expect(received).toEqual(expected) // deep equality
Expected: "{\"big\":\"42\",\"f\":false,\"n\":null,\"num\":42,\"s\":\"string\",\"t\":true}"
Received: "{\"big\":\"42\",\"f\":false,\"fun\":undefined,\"n\":null,\"num\":42,\"s\":\"string\",\"t\":true}"
107 | test('Object', () => {
108 | const o = { big: BigInt(42).toString(), f: false, fun: () => { }, n: null, num: 42, s: "string", sym: Symbol('hello'), t: true, u: undefined }
> 109 | expect(canonify(o)).toEqual('{"big":"42","f":false,"n":null,"num":42,"s":"string","t":true}')
| ^
110 | })
111 |
112 | // Standard data structures
at Object.<anonymous> (tests/basics.spec.ts:109:27)
โ serializing โบ should behave like JSON.stringify() for โบ Symbols
expect(received).toEqual(expected) // deep equality
Expected: "{}"
Received: "{\"y\":undefined}"
146 | // @ts-ignore-next-line
147 | const e1 = { x: undefined, y: Object, z: Symbol('') }
> 148 | expect(canonify(e1)).toEqual('{}')
| ^
149 |
150 | // @ts-ignore-next-line
151 | const e2 = { [Symbol('foo')]: 'foo' }
at Object.<anonymous> (tests/basics.spec.ts:148:28)
โ serializing โบ arrays should handle โบ a one element function array
expect(received).toEqual(expected) // deep equality
Expected: "[null]"
Received: "[undefined]"
232 | test('a one element function array', () => {
233 | let f = function foo() { }
> 234 | expect(canonify([f])).toEqual('[null]');
| ^
235 | });
236 |
237 | test('a nested array', () => {
at Object.<anonymous> (tests/basics.spec.ts:234:29)
โ serializing โบ objects should handle โบ an object with a function value
expect(received).toEqual(expected) // deep equality
Expected: "{}"
Received: "{\"test\":undefined}"
291 | test('an object with a function value', () => {
292 | let f = function foo() { }
> 293 | expect(canonify({ test: f })).toEqual('{}');
| ^
294 | });
295 |
296 | test('an object with a toJSON serializer function value', () => {
at Object.<anonymous> (tests/basics.spec.ts:293:37)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
index.ts | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 failed, 1 passed, 2 total
Tests: 6 failed, 47 passed, 53 total
Snapshots: 0 total
Time: 4.451 s
Ran all test suites.
Cheers.
Glenn
Thanks for reaching out I will try to make some time to look into this! Happy that the code was of use :)
To any future people who were confused, like me, truestamp's canonify github repo is no longer accessible (I don't know why). Their NPM entry for it is still public: https://www.npmjs.com/package/@truestamp/canonify