Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hello 👋🏻 : FYI @truestamp/truestamp-canonify port #9

Open
grempe opened this issue Mar 3, 2022 · 2 comments
Open

Hello 👋🏻 : FYI @truestamp/truestamp-canonify port #9

grempe opened this issue Mar 3, 2022 · 2 comments

Comments

@grempe
Copy link

grempe commented Mar 3, 2022

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 as JSON.stringify() does. The user would need to call .toString() on the BigInt.
  • Serialization of function values to in Arrays/Objects to null, not undefined 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/[email protected] 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

@erdtman
Copy link
Owner

erdtman commented Apr 21, 2022

Thanks for reaching out I will try to make some time to look into this! Happy that the code was of use :)

@mattjbrill
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants