Skip to content

Commit

Permalink
Merge pull request #473 from stoplightio/feat/no-ref-siblings
Browse files Browse the repository at this point in the history
feat: $ref cannot have siblings
Phil Sturgeon authored Aug 27, 2019
2 parents b810a5d + d7ff2eb commit 2a85491
Showing 16 changed files with 584 additions and 143 deletions.
18 changes: 18 additions & 0 deletions docs/getting-started/rulesets.md
Original file line number Diff line number Diff line change
@@ -40,6 +40,24 @@ While running it with this object, it will succeed:
}
```

By default, Spectral processes each rule on resolved document with all $refs resolved.
If you would like to have an original input supplied to your rule, you can place `resolved` property as follows:

```yaml
rules:
my-rule-name:
description: Tags must have a description.
given: $.tags[*]
severity: error
resolved: false # note - if not specified or true, a resolved document will be given
then:
field: description
function: truthy
```

In most cases, you will want to operate on resolved document and therefore won't specify that property.
You might find `resolved` useful if your rule requires access to $refs.

## Formats

Formats are an optional way to specify which API description formats a rule, or ruleset, is applicable to. Currently Spectral supports these two formats:
17 changes: 8 additions & 9 deletions rollup.config.js
Original file line number Diff line number Diff line change
@@ -7,22 +7,21 @@ import { terser } from 'rollup-plugin-terser';
const BASE_PATH = process.cwd();

module.exports = [
"oasOp2xxResponse",
"oasOpFormDataConsumeCheck",
"oasOpIdUnique",
"oasOpParams",
"oasOpSecurityDefined",
"oasPathParam"
'oasOp2xxResponse',
'oasOpFormDataConsumeCheck',
'oasOpIdUnique',
'oasOpParams',
'oasOpSecurityDefined',
'oasPathParam',
'refSiblings'
].map(fn => ({
input: path.resolve(BASE_PATH, 'dist/rulesets/oas/functions', `${fn}.js`),
plugins: [
typescript({
tsconfig: path.join(BASE_PATH, './tsconfig.rollup.json'),
include: ['dist/**/*.{ts,tsx}'],
}),
resolve({
only: ['json-schema-merge-allof', /lodash\/?.*/],
}),
resolve(),
commonjs(),
terser(),
],
16 changes: 8 additions & 8 deletions src/__tests__/functions.test.ts
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ const applyRuleToObject = async (r: Rule, o: object): Promise<IRuleResult[]> =>

describe('functions', () => {
describe('xor', () => {
test('returns result if no properties are present', async () => {
test('returns resolved if no properties are present', async () => {
expect(
applyRuleToObject(
{
@@ -35,7 +35,7 @@ describe('functions', () => {
).resolves.toHaveLength(1);
});

test('returns result if both properties are present', async () => {
test('returns resolved if both properties are present', async () => {
expect(
applyRuleToObject(
{
@@ -106,7 +106,7 @@ describe('functions', () => {
).resolves.toHaveLength(1);
});

test('returns result if pattern is not matched (on object keys)', async () => {
test('returns resolved if pattern is not matched (on object keys)', async () => {
expect(
applyRuleToObject(
{
@@ -135,7 +135,7 @@ describe('functions', () => {
).resolves.toHaveLength(1);
});

test('dont return result if pattern is matched (on string)', async () => {
test('dont return resolved if pattern is matched (on string)', async () => {
expect(
applyRuleToObject(
{
@@ -157,7 +157,7 @@ describe('functions', () => {
).resolves.toHaveLength(0);
});

test('dont return result if pattern is matched (on object keys)', async () => {
test('dont return resolved if pattern is matched (on object keys)', async () => {
expect(
applyRuleToObject(
{
@@ -207,7 +207,7 @@ describe('functions', () => {
},
];

test('return result if string, number, array, or object is greater than max', async () => {
test('return resolved if string, number, array, or object is greater than max', async () => {
expect(
applyRuleToObject(
{
@@ -231,7 +231,7 @@ describe('functions', () => {
).resolves.toHaveLength(4);
});

test('return result if string, number, array, or object is less than min', async () => {
test('return resolved if string, number, array, or object is less than min', async () => {
expect(
applyRuleToObject(
{
@@ -255,7 +255,7 @@ describe('functions', () => {
).resolves.toHaveLength(4);
});

test('dont return a result if string, number, array, or object is between min and max', async () => {
test('dont return a resolved if string, number, array, or object is between min and max', async () => {
expect(
applyRuleToObject(
{
75 changes: 75 additions & 0 deletions src/__tests__/linter.test.ts
Original file line number Diff line number Diff line change
@@ -871,6 +871,81 @@ responses:: !!foo
});
});

test('should report ref siblings', async () => {
await spectral.loadRuleset('spectral:oas');

const results = await spectral.run({
$ref: '#/',
responses: {
200: {
description: 'a',
},
201: {
description: 'b',
},
300: {
description: 'c',
abc: 'd',
$ref: '#/d',
},
},
openapi: '3.0.0',
});

expect(results).toEqual(
expect.arrayContaining([
{
code: 'no-$ref-siblings',
message: '$ref cannot be placed next to any other properties',
path: ['responses'],
range: {
end: {
character: 19,
line: 12,
},
start: {
character: 14,
line: 2,
},
},
severity: DiagnosticSeverity.Error,
},
{
code: 'no-$ref-siblings',
message: '$ref cannot be placed next to any other properties',
path: ['responses', '300', 'description'],
range: {
end: {
character: 24,
line: 10,
},
start: {
character: 21,
line: 10,
},
},
severity: DiagnosticSeverity.Error,
},
{
code: 'no-$ref-siblings',
message: '$ref cannot be placed next to any other properties',
path: ['responses', '300', 'abc'],
range: {
end: {
character: 16,
line: 11,
},
start: {
character: 13,
line: 11,
},
},
severity: DiagnosticSeverity.Error,
},
]),
);
});

describe('runWithResolved', () => {
test('should include both resolved and validation results', async () => {
spectral.setRules({
7 changes: 5 additions & 2 deletions src/linter.ts
Original file line number Diff line number Diff line change
@@ -67,7 +67,7 @@ export const lintNode = (
}

if (!targets.length) {
// must call then at least once, with no result
// must call then at least once, with no resolved
targets.push({
path: [],
value: undefined,
@@ -96,7 +96,10 @@ export const lintNode = (
results = results.concat(
targetResults.map<IRuleResult>(result => {
const escapedJsonPath = (result.path || targetPath).map(segment => decodePointerFragment(String(segment)));
const path = getRealJsonPath(resolved.result, escapedJsonPath);
const path = getRealJsonPath(
rule.resolved === false ? resolved.unresolved : resolved.resolved,
escapedJsonPath,
);
const location = resolved.getLocationForJsonPath(path, true);

return {
113 changes: 58 additions & 55 deletions src/meta/rule.schema.json
Original file line number Diff line number Diff line change
@@ -59,69 +59,72 @@
"oneOf": [
{
"properties": {
"description": {
"type": "string"
},
"recommended": {
"type": "boolean"
},
"given": {
"type": "string"
},
"severity": {
"$ref": "#/definitions/Severity"
},
"message": {
"type": "string"
},
"tags": {
"items": {
"type": "string"
},
"type": "array"
},
"formats": {
"type": "array",
"items": {
"type": "string",
"minItems": 1
}
},
"then": {
"anyOf": [
{
"$ref": "#/definitions/IThen<T,O>"
"description": {
"type": "string"
},
{
"recommended": {
"type": "boolean"
},
"given": {
"type": "string"
},
"resolved": {
"type": "boolean"
},
"severity": {
"$ref": "#/definitions/Severity"
},
"message": {
"type": "string"
},
"tags": {
"items": {
"$ref": "#/definitions/IThen<T,O>"
"type": "string"
},
"type": "array"
}
]
},
"type": {
"enum": [
"style",
"validation"
],
"type": "string"
},
"when": {
"properties": {
"field": {
"type": "string"
},
"pattern": {
"formats": {
"type": "array",
"items": {
"type": "string",
"minItems": 1
}
},
"then": {
"anyOf": [
{
"$ref": "#/definitions/IThen<T,O>"
},
{
"items": {
"$ref": "#/definitions/IThen<T,O>"
},
"type": "array"
}
]
},
"type": {
"enum": [
"style",
"validation"
],
"type": "string"
},
"when": {
"properties": {
"field": {
"type": "string"
},
"pattern": {
"type": "string"
}
},
"required": [
"field"
],
"type": "object"
}
},
"required": [
"field"
],
"type": "object"
}
},
"required": [
"given",
"then"
18 changes: 9 additions & 9 deletions src/resolved.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import { IResolveError, IResolveResult, IResolveRunner } from '@stoplight/json-ref-resolver/types';
import { IResolveError, IResolveResult } from '@stoplight/json-ref-resolver/types';
import { Dictionary, ILocation, JsonPath } from '@stoplight/types';
import { Segment } from '@stoplight/types/dist';
import { get } from 'lodash';
import { IParseMap, REF_METADATA } from './spectral';
import { IParsedResult } from './types';

export class Resolved implements IResolveResult {
export class Resolved {
public refMap: Dictionary<string>;
public result: IResolveResult;
public resolved: unknown;
public unresolved: unknown;
public errors: IResolveError[];
public runner: IResolveRunner;
public format?: string | null;

constructor(public spec: IParsedResult, result: IResolveResult, public parsedMap: IParseMap) {
this.refMap = result.refMap;
this.result = result.result;
this.errors = result.errors;
this.runner = result.runner;
constructor(public spec: IParsedResult, resolveResult: IResolveResult, public parsedMap: IParseMap) {
this.refMap = resolveResult.refMap;
this.resolved = resolveResult.result;
this.unresolved = spec.parsed.data;
this.errors = resolveResult.errors;
this.format = spec.format;
}

267 changes: 267 additions & 0 deletions src/rulesets/oas/functions/__tests__/refSiblings.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
import { getLocationForJsonPath, parseWithPointers } from '@stoplight/json';
import { DiagnosticSeverity } from '@stoplight/types';
import { RuleType, Spectral } from '../../../../index';
import { rules as oasRules } from '../../../oas/index.json';
import refSiblings from '../refSiblings';

describe('refSiblings', () => {
const s = new Spectral();
s.setFunctions({ refSiblings });
s.setRules({
'no-$ref-siblings': Object.assign(oasRules['no-$ref-siblings'], {
recommended: true,
type: RuleType[oasRules['no-$ref-siblings'].type],
}),
});

test('does not report anything for valid object', async () => {
const results = await s.run({
swagger: '2.0',
securityDefinitions: {
apikey: {},
},
paths: {
'/path': {
get: {
security: [
{
apikey: [],
},
],
},
},
},
});

expect(results.length).toEqual(0);
});

test('reports $ref siblings for oas2 document', async () => {
const doc = `{
"swagger": "2.0",
"securityDefinitions": {
"apikey": {},
"$ref": "#/securityDefinitions/apikey"
},
"paths": {
"$ref": "#/securityDefinitions/apikey",
"/path": {
"post": {},
"$ref": "#/foo/bar",
"get": {
"$ref": "#/da",
"security": [
{
"apikey": []
}
]
}
}
}
}`;

const results = await s.run({
parsed: parseWithPointers(doc),
getLocationForJsonPath,
});

expect(results).toEqual([
{
code: 'no-$ref-siblings',
message: '$ref cannot be placed next to any other properties',
path: ['securityDefinitions', 'apikey'],
range: {
end: {
character: 16,
line: 3,
},
start: {
character: 14,
line: 3,
},
},
severity: DiagnosticSeverity.Error,
},
{
code: 'no-$ref-siblings',
message: '$ref cannot be placed next to any other properties',
path: ['paths', '/path'],
range: {
end: {
character: 5,
line: 19,
},
start: {
character: 13,
line: 8,
},
},
severity: DiagnosticSeverity.Error,
},
{
code: 'no-$ref-siblings',
message: '$ref cannot be placed next to any other properties',
path: ['paths', '/path', 'post'],
range: {
end: {
character: 16,
line: 9,
},
start: {
character: 14,
line: 9,
},
},
severity: DiagnosticSeverity.Error,
},
{
code: 'no-$ref-siblings',
message: '$ref cannot be placed next to any other properties',
path: ['paths', '/path', 'get'],
range: {
end: {
character: 7,
line: 18,
},
start: {
character: 13,
line: 11,
},
},
severity: DiagnosticSeverity.Error,
},
{
code: 'no-$ref-siblings',
message: '$ref cannot be placed next to any other properties',
path: ['paths', '/path', 'get', 'security'],
range: {
end: {
character: 9,
line: 17,
},
start: {
character: 20,
line: 13,
},
},
severity: DiagnosticSeverity.Error,
},
]);
});

test('reports $ref siblings for oas3 document', async () => {
const doc = `{
"openapi": "3.0.0",
"components": {
"securityDefinitions": {
"apikey": {},
"$ref": "#/components/securityDefinitions/apikey"
}
},
"paths": {
"$ref": "#/components/securityDefinitions/apikey",
"/path": {
"post": {},
"$ref": "#/foo/bar",
"get": {
"$ref": "#/da",
"security": [
{
"apikey": []
}
]
}
}
}
}`;

const results = await s.run({
parsed: parseWithPointers(doc),
getLocationForJsonPath,
});

expect(results).toEqual([
{
code: 'no-$ref-siblings',

message: '$ref cannot be placed next to any other properties',
path: ['components', 'securityDefinitions', 'apikey'],
range: {
end: {
character: 18,
line: 4,
},
start: {
character: 16,
line: 4,
},
},
severity: DiagnosticSeverity.Error,
},
{
code: 'no-$ref-siblings',
message: '$ref cannot be placed next to any other properties',
path: ['paths', '/path'],
range: {
end: {
character: 5,
line: 21,
},
start: {
character: 13,
line: 10,
},
},
severity: DiagnosticSeverity.Error,
},
{
code: 'no-$ref-siblings',
message: '$ref cannot be placed next to any other properties',
path: ['paths', '/path', 'post'],
range: {
end: {
character: 16,
line: 11,
},
start: {
character: 14,
line: 11,
},
},
severity: DiagnosticSeverity.Error,
},
{
code: 'no-$ref-siblings',
message: '$ref cannot be placed next to any other properties',
path: ['paths', '/path', 'get'],
range: {
end: {
character: 7,
line: 20,
},
start: {
character: 13,
line: 13,
},
},
severity: DiagnosticSeverity.Error,
},
{
code: 'no-$ref-siblings',
message: '$ref cannot be placed next to any other properties',
path: ['paths', '/path', 'get', 'security'],
range: {
end: {
character: 9,
line: 19,
},
start: {
character: 20,
line: 15,
},
},
severity: DiagnosticSeverity.Error,
},
]);
});
});
35 changes: 35 additions & 0 deletions src/rulesets/oas/functions/refSiblings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { JsonPath } from '@stoplight/types';
import { IFunction, IFunctionResult } from '../../../types';

// function is needed because `$..$ref` or `$..[?(@.$ref)]` are not parsed correctly
// and therefore lead to infinite recursion due to the dollar sign ('$' in '$ref')
function* siblingIterator(obj: object, path: JsonPath): IterableIterator<JsonPath> {
const hasRef = '$ref' in obj;
for (const key in obj) {
if (!Object.hasOwnProperty.call(obj, key)) continue;
const scopedPath = [...path, key];
if (hasRef && key !== '$ref') {
yield scopedPath;
}

if (key !== '$ref' && typeof obj[key] === 'object' && obj[key] !== null) {
yield* siblingIterator(obj[key], scopedPath);
}
}
}

const refSiblings: IFunction = (data: unknown) => {
const results: IFunctionResult[] = [];
if (typeof data !== 'object' || data === null) return results;

for (const path of siblingIterator(data, [])) {
results.push({
message: '$ref cannot be placed next to any other properties',
path,
});
}

return results;
};

export default refSiblings;
15 changes: 14 additions & 1 deletion src/rulesets/oas/index.json
Original file line number Diff line number Diff line change
@@ -7,7 +7,8 @@
"oasOpIdUnique",
"oasOpParams",
"oasOpSecurityDefined",
"oasPathParam"
"oasPathParam",
"refSiblings"
],
"rules": {
"operation-2xx-response": {
@@ -428,6 +429,18 @@
"schemaPath": "$"
}
}
},
"no-$ref-siblings": {
"description": "Property cannot be placed among $ref",
"message": "{{error}}",
"type": "validation",
"severity": 0,
"recommended": true,
"resolved": false,
"given": "$",
"then": {
"function": "refSiblings"
}
}
}
}
1 change: 1 addition & 0 deletions src/rulesets/oas/index.ts
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@ export const commonOasFunctions = (): FunctionCollection => {
oasOpIdUnique: require('./functions/oasOpIdUnique').oasOpIdUnique,
oasOpFormDataConsumeCheck: require('./functions/oasOpFormDataConsumeCheck').oasOpFormDataConsumeCheck,
oasOpParams: require('./functions/oasOpParams').oasOpParams,
refSiblings: require('./functions/refSiblings').refSiblings,
};
};

6 changes: 3 additions & 3 deletions src/runner.ts
Original file line number Diff line number Diff line change
@@ -40,9 +40,9 @@ export const runRules = (
};

const runRule = (resolved: Resolved, rule: IRunRule, functions: FunctionCollection): IRuleResult[] => {
const { result: target } = resolved;
const target = rule.resolved === false ? resolved.unresolved : resolved.resolved;

let results: IRuleResult[] = [];
const results: IRuleResult[] = [];
const nodes: IGivenNode[] = [];

// don't have to spend time running jsonpath if given is $ - can just use the root object
@@ -79,7 +79,7 @@ const runRule = (resolved: Resolved, rule: IRunRule, functions: FunctionCollecti
continue;
}

results = results.concat(lintNode(node, rule, then, func, resolved));
results.push(...lintNode(node, rule, then, func, resolved));
}
} catch (e) {
console.warn(`Encountered error when running rule '${rule.name}' on node at path '${node.path}':\n${e}`);
80 changes: 41 additions & 39 deletions src/spectral.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { JsonParserResult, safeStringify } from '@stoplight/json';
import {
getLocationForJsonPath as getLocationForJsonPathJSON,
JsonParserResult,
parseWithPointers as parseJSONWithPointers,
safeStringify,
} from '@stoplight/json';
import { Cache, Resolver } from '@stoplight/json-ref-resolver';
import { IUriParser } from '@stoplight/json-ref-resolver/types';
@@ -14,6 +15,7 @@ import {
} from '@stoplight/yaml';
import { merge, set } from 'lodash';

import { IDiagnostic } from '@stoplight/types/dist';
import deprecated from 'deprecated-decorator';
import { formatParserDiagnostics, formatResolverErrors } from './error-messages';
import { functions as defaultFunctions } from './functions';
@@ -83,48 +85,13 @@ export class Spectral {
await this._resolver.resolve(parsedResult.parsed.data, {
uriCache: this._uriCache,
baseUri: documentUri,
parseResolveResult: async resolveOpts => {
const ref = resolveOpts.targetAuthority.toString();
const ext = extname(ref);

const content = String(resolveOpts.result);
let parsedRefResult:
| IParsedResult<YamlParserResult<unknown>>
| IParsedResult<JsonParserResult<unknown>>
| undefined;
if (ext === '.yml' || ext === '.yaml') {
parsedRefResult = {
parsed: parseYAMLWithPointers(content, { ignoreDuplicateKeys: false }),
source: ref,
getLocationForJsonPath: getLocationForJsonPathYAML,
};
} else if (ext === '.json') {
parsedRefResult = {
parsed: parseJSONWithPointers(content, { ignoreDuplicateKeys: false }),
source: ref,
getLocationForJsonPath: getLocationForJsonPathJSON,
};
}

if (parsedRefResult !== undefined) {
resolveOpts.result = parsedRefResult.parsed.data;
if (parsedRefResult.parsed.diagnostics.length > 0) {
refDiagnostics.push(
...formatParserDiagnostics(parsedRefResult.parsed.diagnostics, parsedRefResult.source),
);
}

this._processExternalRef(parsedRefResult, resolveOpts);
}

return resolveOpts;
},
parseResolveResult: this._parseResolveResult(refDiagnostics),
}),
this._parsedMap,
);

if (resolved.format === void 0) {
const foundFormat = Object.keys(this.formats).find(format => this.formats[format](resolved.result));
const foundFormat = Object.keys(this.formats).find(format => this.formats[format](resolved.resolved));
resolved.format = foundFormat === void 0 ? null : foundFormat;
}

@@ -136,7 +103,7 @@ export class Spectral {
];

return {
resolved: resolved.result,
resolved: resolved.resolved,
results: validationResults,
};
}
@@ -260,6 +227,41 @@ export class Spectral {
}),
);
}

private _parseResolveResult = (refDiagnostics: IDiagnostic[]) => async (resolveOpts: IUriParser) => {
const ref = resolveOpts.targetAuthority.toString();
const ext = extname(ref);

const content = String(resolveOpts.result);
let parsedRefResult:
| IParsedResult<YamlParserResult<unknown>>
| IParsedResult<JsonParserResult<unknown>>
| undefined;
if (ext === '.yml' || ext === '.yaml') {
parsedRefResult = {
parsed: parseYAMLWithPointers(content, { ignoreDuplicateKeys: false }),
source: ref,
getLocationForJsonPath: getLocationForJsonPathYAML,
};
} else if (ext === '.json') {
parsedRefResult = {
parsed: parseJSONWithPointers(content, { ignoreDuplicateKeys: false }),
source: ref,
getLocationForJsonPath: getLocationForJsonPathJSON,
};
}

if (parsedRefResult !== undefined) {
resolveOpts.result = parsedRefResult.parsed.data;
if (parsedRefResult.parsed.diagnostics.length > 0) {
refDiagnostics.push(...formatParserDiagnostics(parsedRefResult.parsed.diagnostics, parsedRefResult.source));
}

this._processExternalRef(parsedRefResult, resolveOpts);
}

return resolveOpts;
};
}

export const REF_METADATA = Symbol('external_ref_metadata');
4 changes: 4 additions & 0 deletions src/types/rule.ts
Original file line number Diff line number Diff line change
@@ -26,6 +26,10 @@ export interface IRule<T = string, O = any> {
// Filter the target down to a subset[] with a JSON path
given: string;

// If false, rule will operate on original (unresolved) data
// If undefined or true, resolved data will be supplied
resolved?: boolean;

when?: {
// the `path.to.prop` to field, or special `@key` value to target keys for matched `given` object
// EXAMPLE: if the target object is an oas object and given = `$..responses[*]`, then `@key` would be the response code (200, 400, etc)
3 changes: 1 addition & 2 deletions src/types/spectral.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Resolver } from '@stoplight/json-ref-resolver';
import { IResolveResult } from '@stoplight/json-ref-resolver/types';
import {
DiagnosticSeverity,
Dictionary,
@@ -44,7 +43,7 @@ export interface IRuleResult extends IDiagnostic {
}

export interface ISpectralFullResult {
resolved: IResolveResult;
resolved: unknown;
results: IRuleResult[];
}

52 changes: 37 additions & 15 deletions yarn.lock
Original file line number Diff line number Diff line change
@@ -604,21 +604,36 @@
dependencies:
any-observable "^0.3.0"

"@stoplight/fast-safe-stringify@^2.1.2":
version "2.1.2"
resolved "https://registry.yarnpkg.com/@stoplight/fast-safe-stringify/-/fast-safe-stringify-2.1.2.tgz#2811eff4989c3d4c5c9a3789f057e25358db0899"
integrity sha512-ayw3qQ9KNn2K5q6ggrLclh+rdZsSLwYIUz3uAer1NzaCpalJLR4g32G845ylgq7Xy2AunMHnNfD3FC3s0rUcsA==

"@stoplight/json-ref-resolver@^2.1.2":
version "2.2.0"
resolved "https://registry.yarnpkg.com/@stoplight/json-ref-resolver/-/json-ref-resolver-2.2.0.tgz#f12fed6ca618d9cd6031f5d0405a3ae5ffd54fad"
integrity sha512-42z5MNkLKF7Ynq2rM8YSd6SoJvEX5k0ugQujpB8Co7K5m3vhq1LK+/faFhuMfuFy6As9BxVUnDbGchYERXK17Q==
version "2.1.2"
resolved "https://registry.yarnpkg.com/@stoplight/json-ref-resolver/-/json-ref-resolver-2.1.2.tgz#56993d3245dba1264ff87052ee960fbe8d259c98"
integrity sha512-DaHxK2KI7HmnsI2BTMwdnsdmj65VNFFPTkvhg2FukszxUpcPlgYVbHjtzEigj4Jn4XryTexlWx7Id4fuRUM7Ug==
dependencies:
"@stoplight/json" "^3.1.0"
"@stoplight/json" "^2.2.2"
"@stoplight/types" "^11.0.0"
dependency-graph "~0.8.0"
fast-memoize "^2.5.1"
immer "^3.2.0"
lodash "^4.17.15"
immer "^3.1.3"
lodash "^4.17"
urijs "~1.19.1"
vscode-uri "^2.0.3"
vscode-uri "^2.0.2"

"@stoplight/json@^3.0.4", "@stoplight/json@^3.1.0":
"@stoplight/json@^2.2.2":
version "2.3.3"
resolved "https://registry.yarnpkg.com/@stoplight/json/-/json-2.3.3.tgz#7f461f7e9234a244af7d7e3c0d37fd66fc944eac"
integrity sha512-iGRT6N/uUnoXbaGAGlcJJNbI7zZity+vLb1GBUezLeuW1ZHUevxfqK1kvrxK3iZD/2slOPeH23mDHIk56nswSA==
dependencies:
"@stoplight/fast-safe-stringify" "^2.1.2"
"@stoplight/types" "^9.1.2"
jsonc-parser "~2.1.0"
lodash "^4.10"

"@stoplight/json@^3.0.4":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@stoplight/json/-/json-3.1.0.tgz#a03bd8d065a62eb00cd42cf003df431f23d7a5dd"
integrity sha512-8Fev+RW+UJNYtcpDgvQPH6MuHEf5cr95KnAyMBNNXlVIeRAIJqYK3r5FWZv4/KFH08ozQy4QpMM9MdsR8J3cZQ==
@@ -640,6 +655,13 @@
dependencies:
"@types/json-schema" "^7.0.3"

"@stoplight/types@^9.1.2":
version "9.1.2"
resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-9.1.2.tgz#0b2ed2032ae901491543b9dd19309ff3a0acf12a"
integrity sha512-eNeWwTZ1E47mrprEdgtOutovVKxYTbm8k/B7Lh4JAusGoiHbMmzVT+IDdayBpOCzq+qKLzuP0z3IZo74IXaaEg==
dependencies:
"@types/json-schema" "^7.0.3"

"@stoplight/yaml@^3.0.0":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@stoplight/yaml/-/yaml-3.0.2.tgz#acd567d580b35c7b2b8c9802a448ffcc70698507"
@@ -3544,10 +3566,10 @@ ignore@^5.1.1:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.2.tgz#e28e584d43ad7e92f96995019cc43b9e1ac49558"
integrity sha512-vdqWBp7MyzdmHkkRWV5nY+PfGRbYbahfuvsBCh277tq+w9zyNi7h5CYJCK0kmzti9kU+O/cB7sE8HvKv6aXAKQ==

immer@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/immer/-/immer-3.2.0.tgz#53686471e9dd2b070e0fb5500c6fdecd3a99375f"
integrity sha512-+a2R8z9eELHst6aht++nzVzJ8LJ+Hsg49qttfg9Kc/vmoxEdPXw5/rV6+4DYWGgnq+B36KbLr4OTaGtS9mDjtg==
immer@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/immer/-/immer-3.1.3.tgz#59bc742b2aab6e2c676445edb653e588a23c70fc"
integrity sha512-HG5SXTXTTVy9lGNwS075cNhQoV375jHsIJO3UtMjuUWJOuwlMr0u42FlsKTJcppt5AzsFAsmj9r4kHvsSHh3hQ==

import-fresh@^2.0.0:
version "2.0.0"
@@ -4565,7 +4587,7 @@ json5@2.x, json5@^2.1.0:
dependencies:
minimist "^1.2.0"

jsonc-parser@~2.1.1:
jsonc-parser@~2.1.0, jsonc-parser@~2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.1.1.tgz#83dc3d7a6e7186346b889b1280eefa04446c6d3e"
integrity sha512-VC0CjnWJylKB1iov4u76/W/5Ef0ydDkjtYWxoZ9t3HdWlSnZQwZL5MgFikaB/EtQ4RmMEw3tmQzuYnZA2/Ja1g==
@@ -4923,7 +4945,7 @@ lodash@4.17.14:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba"
integrity sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==

lodash@>=4.17.5, lodash@^4.17.0, lodash@^4.17.11, lodash@^4.17.15, lodash@^4.17.5, lodash@^4.2.1:
lodash@>=4.17.5, lodash@^4.10, lodash@^4.17, lodash@^4.17.0, lodash@^4.17.11, lodash@^4.17.15, lodash@^4.17.5, lodash@^4.2.1:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
@@ -7830,7 +7852,7 @@ void-elements@^2.0.0:
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=

vscode-uri@^2.0.3:
vscode-uri@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.3.tgz#25e5f37f552fbee3cec7e5f80cef8469cefc6543"
integrity sha512-4D3DI3F4uRy09WNtDGD93H9q034OHImxiIcSq664Hq1Y1AScehlP3qqZyTkX/RWxeu0MRMHGkrxYqm2qlDF/aw==

0 comments on commit 2a85491

Please sign in to comment.