New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@stoplight/json-ref-resolver

Package Overview
Dependencies
Maintainers
7
Versions
49
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@stoplight/json-ref-resolver - npm Package Compare versions

Comparing version 1.1.0 to 1.2.0

1

cache.d.ts
import * as Types from './types';
export declare class Cache implements Types.ICache {
debug: boolean;
private _stats;

@@ -5,0 +4,0 @@ private readonly _stdTTL;

@@ -5,3 +5,2 @@ "use strict";

constructor(opts = {}) {
this.debug = false;
this._stats = {

@@ -21,16 +20,7 @@ hits: 0,

this._stats.hits += 1;
if (this.debug) {
console.log('cache.get.hit', { key, val: d });
}
return d.val;
}
this._stats.misses += 1;
if (this.debug) {
console.log('cache.get.miss', { key });
}
}
set(key, val) {
if (this.debug) {
console.log('cache.set', { key, val });
}
this._data[key] = {

@@ -37,0 +27,0 @@ ts: new Date().getTime(),

2

package.json
{
"name": "@stoplight/json-ref-resolver",
"version": "1.1.0",
"version": "1.2.0",
"description": "Recursively resolve JSON pointers and remote authorities.",

@@ -5,0 +5,0 @@ "keywords": [

@@ -14,4 +14,4 @@ # JSON Ref Resolver

- **Caching**: Results from remote authorities are cached.
- **Immutable**: The original object is not changed, and structural sharing is used to only change relevant bits.
- **Reference equality:** Pointers to the same location will resolve to the same object in memory.
- **Immutable**: The original object is not changed, and structural sharing is used to only change relevant bits. [example test](src/__tests__/resolver.spec.ts#L139-L143)
- **Reference equality:** Pointers to the same location will resolve to the same object in memory. [example test](src/__tests__/resolver.spec.ts#L145)
- **Flexible:** Bring your own readers for `http://`, `file://`, `mongo://`, `custom://`... etc.

@@ -33,4 +33,37 @@ - **Reliable:** Well tested to handle all sorts of circular reference edge cases.

#### Basic Local Resolution
```ts
// Import the Resolver class.
import { Resolver } from "@stoplight/json-ref-resolver";
/**
* Create a Resolver instance. Resolve can be called on this instance multiple times to take advantage of caching.
*
* @param globalOpts {IResolverOpts} [{}]
*
* These options are used on every resolve call for this resolver instance.
*
* See `IResolverOpts` interface defined in [src/types.ts](src/types.ts) for available options.
*
* @return IResolver
*/
const resolver = new Resolver(globalOpts);
/**
* Resolve the passed in object, replacing all references.
* @param resolveOpts {any} - The object to resolve.
* @param resolveOpts {IResolveOpts} [{}]
*
* These options override any globalOpts specified on the resolver instance, and only apply during this resolve call.
*
* See `IResolveOpts` interface defined in [src/types.ts](src/types.ts) for available options.
*
* @return IResolveResult - see [src/types.ts](src/types.ts) for interface definition.
*/
const resolved = await resolver.resolve(sourceObj, resolveOpts);
```
#### Example: Basic Local Resolution
```ts

@@ -51,20 +84,69 @@ import { Resolver } from "@stoplight/json-ref-resolver";

console.log(resolved.result);
// ==> result is the original object, with local refs resolved and replaced
expect(resolved.result).toEqual({
user: {
name: "json"
},
models: {
user: {
name: "john"
}
}
});
```
// ==> outputs the original object, with local refs resolved and replaced
//
// {
// user: {
// name: 'json'
// },
// models: {
// user: {
// name: 'john'
// }
// }
// }
#### Example: Resolve a Subset of the Source
This will resolve the minimal number of references needed for the given target, and return the target.
In the example below, the address reference (`https://slow-website.com/definitions#/address`) will NOT be resolved, since
it is not needed to resolve the `#/user` jsonPointer target we have specified. However, `#/models/user/card` IS resolved since
it is needed in order to full resolve the `#/user` property.
```ts
import { Resolver } from "@stoplight/json-ref-resolver";
const resolver = new Resolver();
const resolved = await resolver.resolve(
{
user: {
$ref: "#/models/user"
},
address: {
$ref: "https://slow-website.com/definitions#/address"
},
models: {
user: {
name: "john",
card: {
$ref: "#/models/card"
}
},
card: {
type: "visa"
}
}
},
{
jsonPointer: "#/user"
}
);
// ==> result is the target object, with refs resolved and replaced
expect(resolved.result).toEqual({
name: "json",
card: {
type: "visa"
}
});
```
#### With Authority Readers
#### Example: Resolving Remote References with Readers
By default only local references (those that point to values inside of the original source) are resolved.
In order to resolve remote authorities (file, http, etc) you must provide readers for each authority scheme.
Readers are keyed by scheme, receive the URI to fetch, and must return the fetched data.
```ts

@@ -74,6 +156,6 @@ import { Resolver } from "@stoplight/json-ref-resolver";

// some example http library
const request = require("request");
import * as axios from "axios";
// if we're in node, we create a file reader with fs
const fs = require("fs");
import * as fs from "fs";

@@ -87,3 +169,6 @@ // create our resolver instance

async read(ref: uri.URI) {
return request(ref.toString());
return axios({
method: "get",
url: String(ref)
});
}

@@ -95,3 +180,3 @@ },

async read(ref: uri.URI) {
return fs.read(ref.toString());
return fs.read(String(ref));
}

@@ -113,18 +198,57 @@ }

console.log(resolved.result);
// ==> result is the original object, with refs resolved and replaced
expect(resolved.result).toEqual({
definitions: {
someOASFile: {
// ... the data located in the relative file `./main.oas2.yml` and inner json path `#/definitions/user`
},
someMarkdownFile: {
// ... the data located at the url `https://foo.com/intro.md`
}
}
});
```
// ==> outputs the original object, with refs resolved and replaced
//
// {
// definitions: {
// someOASFile: {
// // ... the data located in the relative file `./main.oas2.yml` and inner json path `#/definitions/user`
// },
// someMarkdownFile: {
// // ... the data located at the url `https://foo.com/intro.md`
// }
// },
#### Example: Resolving Relative Remote References with the Authority Option
If there are relative remote references (for example, a relative file path `../model.json`), then the location of the source
data must be specified via the `authority` resolve option. Relative references will be resolved against this authority.
```ts
import { Resolver } from "@stoplight/json-ref-resolver";
import * as fs from "fs";
import * as URI from "urijs";
const resolver = new Resolver({
readers: {
file: {
async read(ref: uri.URI) {
return fs.read(String(ref));
}
}
}
});
const sourcePath = "/specs/api.json";
const sourceData = fs.readSync(sourcePath);
// sourceData === {
// user: {
// $ref: "../models/user.json"
// }
// }
const resolved = await resolver.resolve(sourceData, {
// Indicate where the `sourceData` being resolved lives, so that relative remote references can be fetched and resolved.
authority: new URI(sourcePath)
});
expect(resolved.result).toEqual({
user: {
// ... the user object defined in `../models/user.json`
}
});
```
In the above example, the user \$ref will resolve to `/models/user.json`, because `../models/user.json` is resolved against the authority of the current document (which was indicated at `/specs/api.json`). Relative references will not work if the source document has no authority set.
### Contributing

@@ -131,0 +255,0 @@

@@ -8,3 +8,2 @@ /// <reference types="urijs" />

protected ctx: any;
protected debug: boolean;
protected readers: {

@@ -11,0 +10,0 @@ [scheme: string]: Types.IReader;

@@ -10,3 +10,2 @@ "use strict";

this.readers = opts.readers || {};
this.debug = opts.debug || false;
this.getRef = opts.getRef;

@@ -23,3 +22,2 @@ this.transformRef = opts.transformRef;

readers: this.readers,
debug: this.debug,
getRef: this.getRef,

@@ -26,0 +24,0 @@ transformRef: this.transformRef,

@@ -13,3 +13,2 @@ /// <reference types="urijs" />

ctx: any;
readonly debug: boolean;
readonly readers: {

@@ -16,0 +15,0 @@ [scheme: string]: Types.IReader;

@@ -15,6 +15,3 @@ "use strict";

exports.defaultGetRef = (key, val) => {
if (key === '$ref') {
return val;
}
else if (val && typeof val === 'object' && val.$ref) {
if (val && typeof val === 'object' && val.$ref) {
return val.$ref;

@@ -68,3 +65,2 @@ }

readers: this.readers,
debug: this.debug,
transformRef: this.transformRef,

@@ -82,2 +78,3 @@ parseAuthorityResult: this.parseAuthorityResult,

const lookupResult = {
uri: ref,
pointerStack,

@@ -89,2 +86,3 @@ targetPath: resolvingPointer === parentPointer ? [] : parentPath,

result: val,
refMap: {},
errors: [],

@@ -121,54 +119,42 @@ runner: this,

if (authorityResolver) {
try {
lookupResult.resolved = yield authorityResolver.resolve(Utils.uriToJSONPointer(ref));
if (lookupResult.resolved.errors.length) {
for (const error of lookupResult.resolved.errors) {
if (error.code === 'POINTER_MISSING' &&
error.path.join('/') === ref.fragment().slice(1)) {
const errorPathInResult = ref.fragment
? json_1.trimStart(error.path, json_1.trimStart(ref.fragment(), '/').split('/'))
: error.path;
if (errorPathInResult && errorPathInResult.length) {
_set(lookupResult.resolved.result, errorPathInResult, val);
}
else if (lookupResult.resolved.result) {
lookupResult.resolved.result = val;
}
lookupResult.resolved = yield authorityResolver.resolve(Utils.uriToJSONPointer(ref));
if (lookupResult.resolved.errors.length) {
for (const error of lookupResult.resolved.errors) {
if (error.code === 'POINTER_MISSING' &&
error.path.join('/') === ref.fragment().slice(1)) {
const errorPathInResult = ref.fragment
? json_1.trimStart(error.path, json_1.trimStart(ref.fragment(), '/').split('/'))
: error.path;
if (errorPathInResult && errorPathInResult.length) {
_set(lookupResult.resolved.result, errorPathInResult, val);
}
else if (lookupResult.resolved.result) {
lookupResult.resolved.result = val;
}
}
}
if (this.parseAuthorityResult) {
try {
const parsed = yield this.parseAuthorityResult({
authorityResult: lookupResult,
result: lookupResult.resolved.result,
targetAuthority: ref,
parentAuthority: this.authority,
parentPath,
});
lookupResult.resolved.result = parsed.result;
}
catch (e) {
lookupResult.resolved.result = val;
lookupResult.error = {
code: 'PARSE_AUTHORITY',
message: `Error parsing lookup result for '${ref.toString()}': ${String(e)}`,
authority: ref,
authorityStack: this.authorityStack,
pointerStack,
path: parentPath,
};
}
}
if (this.parseAuthorityResult) {
try {
const parsed = yield this.parseAuthorityResult({
authorityResult: lookupResult,
result: lookupResult.resolved.result,
targetAuthority: ref,
parentAuthority: this.authority,
parentPath,
});
lookupResult.resolved.result = parsed.result;
}
catch (e) {
lookupResult.resolved.result = val;
lookupResult.error = {
code: 'PARSE_AUTHORITY',
message: `Error parsing lookup result for '${ref.toString()}': ${String(e)}`,
authority: ref,
authorityStack: this.authorityStack,
pointerStack,
path: parentPath,
};
}
}
catch (e) {
lookupResult.error = {
code: 'RESOLVE_POINTER',
message: `Error resolving pointer @ ${Utils.uriToJSONPointer(ref)}: ${String(e)}`,
path: parentPath,
authority: ref,
authorityStack: this.authorityStack,
pointerStack,
};
}
}

@@ -187,5 +173,3 @@ }

}
this.authorityCache = opts.authorityCache || new cache_1.Cache();
this.readers = opts.readers || {};
this.debug = opts.debug || false;
this.getRef = opts.getRef || exports.defaultGetRef;

@@ -213,2 +197,3 @@ this.transformRef = opts.transformRef;

result: this.source,
refMap: {},
errors: [],

@@ -242,2 +227,6 @@ runner: this,

for (const r of authorityResults) {
let resolvedTargetPath = r.targetPath;
if (!resolvedTargetPath.length)
resolvedTargetPath = targetPath || [];
resolved.refMap[String(this.authority.clone().fragment(json_1.pathToPointer(resolvedTargetPath)))] = String(r.uri);
if (r.error) {

@@ -253,5 +242,2 @@ resolved.errors.push(r.error);

continue;
let resolvedTargetPath = r.targetPath;
if (!resolvedTargetPath.length)
resolvedTargetPath = targetPath || [];
this._source = immer_1.default(this._source, draft => {

@@ -296,2 +282,3 @@ if (r.resolved) {

continue;
resolved.refMap[json_1.pathToPointer(dependantPath)] = json_1.pathToPointer(pointerPath);
if (val) {

@@ -298,0 +285,0 @@ _set(draft, dependantPath, val);

@@ -13,3 +13,2 @@ /// <reference types="urijs" />

resolveAuthorities?: boolean;
debug?: boolean;
ctx?: any;

@@ -21,2 +20,10 @@ }

}
export interface IResolveResult {
result: any;
refMap: {
[source: string]: string;
};
errors: IResolveError[];
runner: IResolveRunner;
}
export interface IReader {

@@ -39,2 +46,3 @@ read(ref: uri.URI, ctx: any): Promise<any>;

targetPath: string[];
uri: uri.URI;
resolved?: IResolveResult;

@@ -62,9 +70,3 @@ error?: IResolveError;

}
export interface IResolveResult {
result: any;
errors: IResolveError[];
runner: IResolveRunner;
}
export interface ICache {
debug: boolean;
readonly stats: {

@@ -71,0 +73,0 @@ hits: number;

@@ -21,6 +21,3 @@ "use strict";

const encodeFragmentSegment = (segment) => {
if (typeof segment === 'string') {
return replace(replace(segment, '~', '~0'), '/', '~1');
}
return segment;
return replace(replace(segment, '~', '~0'), '/', '~1');
};

@@ -27,0 +24,0 @@ exports.addToJSONPointer = (pointer, part) => {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc