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

Max call stack if schema already has circular references #23

Closed
domarmstrong opened this issue Jul 23, 2016 · 6 comments
Closed

Max call stack if schema already has circular references #23

domarmstrong opened this issue Jul 23, 2016 · 6 comments

Comments

@domarmstrong
Copy link

If the schema includes circular references already a max call stack is hit.

let x = { y: 1, z: {} };
x.z.x = x;
await jsref.dereference(x);
// Error
``
@JamesMessinger
Copy link
Member

I'm not sure what you're trying to accomplish here. The dereference() method is used to produce a circular object, not consume one.

The idea is that you have an object like this:

{
  "y": 1,
  "z": {
    "x": {
      "$ref": "#/"  // <-- Reference to the document root
    }
  }
}

You could then pass that object to the dereference() method, and you'd end up with the circular object that you showed above. i.e.:

{
  "y": 1,
  "z": {
    "x": {...}  // <--- circular reference
  }
}

obj.z.x === obj;        // true
obj.z.x.z === obj.z;    // true
obj.z.x.z.x === obj;    // true

@domarmstrong
Copy link
Author

So it may be out of scope but I'm using it in .js config files. These files are not guaranteed to be 'JSON' compatible but can have complex objects. I allow json refs to be used in the config and then the paths are resolved with dereference later. This allows the nested config to be really easy to swap some values without having to change multiple places (unless you want to).

Example:

Base config:

{
  foo: 'http:https://foo.com',
  bar: {
    foo: '#/foo',
  },
  baz: {
    foo: '#/foo'
  },
  cache: {
    instance: new Cache()
  }
}

Override config:

{
  foo: 'https://foo.co.uk'
}

Other app override config:

{
  cache: {
    instance: new MyCacheImplementation()
  }
}
let mergedConfig = merge(defaults, config);
let derefConfig = dereference(mergedConfig);

@JamesMessinger
Copy link
Member

So how do you end up with circular references in the config object? Neither the base config, nor the override config, nor the 2nd override config contain any circular references, so they should all merge to produce mergedConfig without any circular references, which then means that dereference(mergedConfig) should work fine. Am I missing something?

@domarmstrong
Copy link
Author

Some implementations are out of my control and contain circular references. For example the Cache implementation used may have circular references, but I can't stop dereference from iterating over it.

@JamesMessinger
Copy link
Member

ah, ok. I see.

I could add circular-reference detection logic to JSON Schema $Ref Parser, but I would prefer to avoid adding that extra overhead, since the intended use-case is to process JSON data, not JavaScript object trees. Supporting JavaScript object trees adds other additional complications too, such as prototype chains, non-enumerable properties, symbols, proxies, and non-JSON data types (Date, RegExp, etc.).

Perhaps an acceptable approach would be for you to convert your mergedConfig object to JSON prior to calling dereference(mergedConfig). You could use a library such as refify to properly handle any circular references when converting to JSON.

@domarmstrong
Copy link
Author

OK i'll try that approach. thanks

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

2 participants