Giter Site home page Giter Site logo

Comments (9)

FrederikBolding avatar FrederikBolding commented on June 3, 2024 1

@rekmarks I implemented this initially by simply re-using the existing isValidJson utility function. This function does exactly what you suggested above.

I do however think it might be worth it to consider replacing it with something a bit more performant.

We could do something that looks more like this.

const VALID_JSON_TYPES = ['number', 'boolean', 'string'];

/**
 * {@link Json} type guard.
 *
 * @param value - The value to check.
 * @returns Whether the value is valid JSON.
 */
export function isValidJson(value: unknown): value is Json {
  if (
    value === null ||
    value === undefined ||
    VALID_JSON_TYPES.includes(typeof value)
  ) {
    return true;
  } else if (!isPlainObject(value) && !Array.isArray(value)) {
    return false;
  }

  try {
    const values = Object.values(value);

    for (const nestedValue of values) {
      if (!isValidJson(nestedValue)) {
        return false;
      }
    }
  } catch (_) {
    return false;
  }

  return true;
}

We would then also need to replace current isPlainObject utility function, which also currently seems to accept classes, weirdly?

For that we could do something like this.

export function isPlainObject(value: unknown): value is PlainObject {
  if (typeof value !== 'object' || value === null) return false;

  try {
    let proto = value;
    while (Object.getPrototypeOf(proto) !== null) {
      proto = Object.getPrototypeOf(proto);
    }

    return Object.getPrototypeOf(value) === proto;
  } catch (_) {
    return false;
  }
}

This approach is inspired by the way redux-toolkit does their detection of non-serializable objects.

See the following links:
https://github.com/reduxjs/redux-toolkit/blob/2ff1fa4364ce0dfa82dbbd3187aa0c63e57d5582/packages/toolkit/src/serializableStateInvariantMiddleware.ts#L35

https://github.com/reduxjs/redux-toolkit/blob/2ff1fa4364ce0dfa82dbbd3187aa0c63e57d5582/packages/toolkit/src/isPlainObject.ts#L11

from snaps.

rekmarks avatar rekmarks commented on June 3, 2024 1

That's a great suggestion @FrederikBolding.

Before we pursue a particular implementation, @shanejonas, have you or the OpenRPC folks by chance built anything that could be of help here?

from snaps.

FrederikBolding avatar FrederikBolding commented on June 3, 2024 1

My bad - didn't see you already approved the PR. Will merge now.

from snaps.

FrederikBolding avatar FrederikBolding commented on June 3, 2024 1

@rekmarks Based on our convo about possibly wanting to limit the size allowed for the JSON object I re-wrote my implementation to consider that too.

It's a lot less readable than the way we currently do things, but it is speedier. I compared to a simplified version here: https://jsben.ch/3VY6B - which it beats. And I assume with the deepEqual check we currently do, this would be much faster.

export function getJsonSizing(value: unknown): [boolean, number] {
  if (value === undefined) {
    return [true, 0];
  } else if (value === null) {
    return [true, 4];
  }

  // eslint-disable-next-line default-case
  switch (typeof value) {
    case 'string':
      // +2 for quotes
      return [true, value.length + 2];
    case 'boolean':
      return [true, value ? 4 : 5];
    case 'number':
      return [true, value === 0 ? 1 : Math.floor(Math.log10(value) + 1)];
  }

  if (!isPlainObject(value) && !Array.isArray(value)) {
    return [false, 0];
  }

  try {
    // Starts at 2 because the string will minimally contain {}/[]
    return [
      true,
      Object.entries(value).reduce((sum, [key, nestedValue], idx, arr) => {
        const [valid, size] = getJsonSizing(nestedValue);
        if (!valid) {
          throw new Error();
        }
        // +3 "key":
        const keySize = Array.isArray(value) ? 0 : key.length + 3;
        const seperator = idx < arr.length - 1 ? 1 : 0;
        return sum + keySize + size + seperator;
      }, 2),
    ];
  } catch (_) {
    return [false, 0];
  }
}

from snaps.

rekmarks avatar rekmarks commented on June 3, 2024

Suggested implementation (absent a try/catch):

if (!deepEqual(JSON.parse(JSON.stringify(snapState)), snapState)) throw Error('Invalid state');

On a related note, we may want to enforce some kind of maximum size for snap state, given by the length of the string. The above operation is less than cheap for large objects.

from snaps.

rekmarks avatar rekmarks commented on June 3, 2024

Also @FrederikBolding, if we end up pursuing your implementation, you might find this TypeScript Json type test suite a helpful reference for the unit tests.

from snaps.

FrederikBolding avatar FrederikBolding commented on June 3, 2024

@rekmarks Definitely. Do you want to treat this suggestion as another PR and/or issue? Since the actual issue here should be solved by my already open PR.

from snaps.

rekmarks avatar rekmarks commented on June 3, 2024

That looks awesome. We should thoroughly document what this is doing in its docstring, but this looks pretty damn good.

from snaps.

FrederikBolding avatar FrederikBolding commented on June 3, 2024

@rekmarks I've assumed 1 byte per character for now, but we can make sure of that and/or check byte length of certain characters 👍 But yeah, we should add great documentation for this as well as a bunch of tests 😄

from snaps.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.