@redocly/openapi-core 1.0.0-beta.123 → 1.0.0-beta.125

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/lib/bundle.d.ts +7 -12
  2. package/lib/config/config.js +9 -4
  3. package/lib/config/types.d.ts +5 -1
  4. package/lib/format/format.js +16 -7
  5. package/lib/rules/common/assertions/asserts.d.ts +6 -2
  6. package/lib/rules/common/assertions/asserts.js +30 -20
  7. package/lib/rules/common/assertions/utils.d.ts +8 -0
  8. package/lib/rules/common/assertions/utils.js +28 -34
  9. package/lib/rules/common/required-string-property-missing-min-length.d.ts +2 -0
  10. package/lib/rules/common/required-string-property-missing-min-length.js +37 -0
  11. package/lib/rules/oas2/index.d.ts +1 -0
  12. package/lib/rules/oas2/index.js +2 -0
  13. package/lib/rules/oas3/index.js +2 -0
  14. package/lib/types/redocly-yaml.js +1 -0
  15. package/package.json +1 -1
  16. package/src/bundle.ts +11 -2
  17. package/src/config/__tests__/__snapshots__/config.test.ts.snap +144 -0
  18. package/src/config/__tests__/config.test.ts +25 -0
  19. package/src/config/__tests__/fixtures/ingore-file.ts +8 -0
  20. package/src/config/config.ts +14 -4
  21. package/src/config/types.ts +4 -1
  22. package/src/format/format.ts +17 -6
  23. package/src/rules/common/assertions/__tests__/asserts.test.ts +264 -178
  24. package/src/rules/common/assertions/__tests__/index.test.ts +1 -1
  25. package/src/rules/common/assertions/__tests__/utils.test.ts +122 -1
  26. package/src/rules/common/assertions/asserts.ts +59 -28
  27. package/src/rules/common/assertions/utils.ts +46 -44
  28. package/src/rules/common/required-string-property-missing-min-length.ts +44 -0
  29. package/src/rules/oas2/index.ts +2 -0
  30. package/src/rules/oas3/index.ts +2 -0
  31. package/src/types/redocly-yaml.ts +1 -0
  32. package/tsconfig.tsbuildinfo +1 -1
package/lib/bundle.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { BaseResolver, Document } from './resolve';
2
2
  import { Oas3Rule } from './visitors';
3
3
  import { NormalizedNodeType, NodeType } from './types';
4
+ import { NormalizedProblem } from './walk';
4
5
  import { OasMajorVersion } from './oas-types';
5
6
  import type { Config, StyleguideConfig } from './config';
6
7
  export declare type Oas3RuleSet = Record<string, Oas3Rule>;
@@ -19,14 +20,15 @@ export declare function bundle(opts: {
19
20
  skipRedoclyRegistryRefs?: boolean;
20
21
  removeUnusedComponents?: boolean;
21
22
  keepUrlRefs?: boolean;
22
- }): Promise<{
23
+ }): Promise<BundleResult>;
24
+ export declare type BundleResult = {
23
25
  bundle: Document;
24
- problems: import("./walk").NormalizedProblem[];
26
+ problems: NormalizedProblem[];
25
27
  fileDependencies: Set<string>;
26
28
  rootType: NormalizedNodeType;
27
- refTypes: Map<string, NormalizedNodeType> | undefined;
29
+ refTypes?: Map<string, NormalizedNodeType>;
28
30
  visitorsData: Record<string, Record<string, unknown>>;
29
- }>;
31
+ };
30
32
  export declare function bundleDocument(opts: {
31
33
  document: Document;
32
34
  config: StyleguideConfig;
@@ -36,12 +38,5 @@ export declare function bundleDocument(opts: {
36
38
  skipRedoclyRegistryRefs?: boolean;
37
39
  removeUnusedComponents?: boolean;
38
40
  keepUrlRefs?: boolean;
39
- }): Promise<{
40
- bundle: Document;
41
- problems: import("./walk").NormalizedProblem[];
42
- fileDependencies: Set<string>;
43
- rootType: NormalizedNodeType;
44
- refTypes: Map<string, NormalizedNodeType> | undefined;
45
- visitorsData: Record<string, Record<string, unknown>>;
46
- }>;
41
+ }): Promise<BundleResult>;
47
42
  export declare function mapTypeToComponent(typeName: string, version: OasMajorVersion): "headers" | "definitions" | "parameters" | "examples" | "schemas" | "responses" | "requestBodies" | "securitySchemes" | "links" | "callbacks" | null;
@@ -8,6 +8,7 @@ const utils_1 = require("../utils");
8
8
  const oas_types_1 = require("../oas-types");
9
9
  const env_1 = require("../env");
10
10
  const utils_2 = require("./utils");
11
+ const ref_utils_1 = require("../ref-utils");
11
12
  exports.IGNORE_FILE = '.redocly.lint-ignore.yaml';
12
13
  const IGNORE_BANNER = `# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API.\n` +
13
14
  `# See https://redoc.ly/docs/cli/ for more information.\n`;
@@ -75,11 +76,13 @@ class StyleguideConfig {
75
76
  js_yaml_1.parseYaml(fs.readFileSync(ignoreFile, 'utf-8')) || {};
76
77
  // resolve ignore paths
77
78
  for (const fileName of Object.keys(this.ignore)) {
78
- this.ignore[path.resolve(path.dirname(ignoreFile), fileName)] = this.ignore[fileName];
79
+ this.ignore[ref_utils_1.isAbsoluteUrl(fileName) ? fileName : path.resolve(path.dirname(ignoreFile), fileName)] = this.ignore[fileName];
79
80
  for (const ruleId of Object.keys(this.ignore[fileName])) {
80
81
  this.ignore[fileName][ruleId] = new Set(this.ignore[fileName][ruleId]);
81
82
  }
82
- delete this.ignore[fileName];
83
+ if (!ref_utils_1.isAbsoluteUrl(fileName)) {
84
+ delete this.ignore[fileName];
85
+ }
83
86
  }
84
87
  }
85
88
  saveIgnore() {
@@ -87,8 +90,10 @@ class StyleguideConfig {
87
90
  const ignoreFile = path.join(dir, exports.IGNORE_FILE);
88
91
  const mapped = {};
89
92
  for (const absFileName of Object.keys(this.ignore)) {
90
- const ignoredRules = (mapped[utils_1.slash(path.relative(dir, absFileName))] =
91
- this.ignore[absFileName]);
93
+ const mappedDefinitionName = ref_utils_1.isAbsoluteUrl(absFileName)
94
+ ? absFileName
95
+ : utils_1.slash(path.relative(dir, absFileName));
96
+ const ignoredRules = (mapped[mappedDefinitionName] = this.ignore[absFileName]);
92
97
  for (const ruleId of Object.keys(ignoredRules)) {
93
98
  ignoredRules[ruleId] = Array.from(ignoredRules[ruleId]);
94
99
  }
@@ -1,7 +1,8 @@
1
- import type { ProblemSeverity } from '../walk';
1
+ import type { ProblemSeverity, UserContext } from '../walk';
2
2
  import type { Oas3PreprocessorsSet, OasMajorVersion, Oas3DecoratorsSet, Oas2RuleSet, Oas2PreprocessorsSet, Oas2DecoratorsSet, Oas3RuleSet, OasVersion } from '../oas-types';
3
3
  import type { NodeType } from '../types';
4
4
  import { Location } from '../ref-utils';
5
+ import { SkipFunctionContext } from '../visitors';
5
6
  export declare type RuleSeverity = ProblemSeverity | 'off';
6
7
  export declare type RuleSettings = {
7
8
  severity: RuleSeverity;
@@ -54,6 +55,9 @@ export declare type CustomRulesConfig = {
54
55
  oas3?: Oas3RuleSet;
55
56
  oas2?: Oas2RuleSet;
56
57
  };
58
+ export declare type AssertionContext = Partial<UserContext> & SkipFunctionContext & {
59
+ node: any;
60
+ };
57
61
  export declare type AssertResult = {
58
62
  message?: string;
59
63
  location?: Location;
@@ -7,6 +7,7 @@ const output_1 = require("../output");
7
7
  const coreVersion = require('../../package.json').version;
8
8
  const codeframes_1 = require("./codeframes");
9
9
  const env_1 = require("../env");
10
+ const ref_utils_1 = require("../ref-utils");
10
11
  const ERROR_MESSAGE = {
11
12
  INVALID_SEVERITY_LEVEL: 'Invalid severity level; accepted values: error or warn',
12
13
  };
@@ -75,7 +76,7 @@ function formatProblems(problems, opts) {
75
76
  case 'stylish': {
76
77
  const groupedByFile = groupByFiles(problems);
77
78
  for (const [file, { ruleIdPad, locationPad: positionPad, fileProblems }] of Object.entries(groupedByFile)) {
78
- logger_1.logger.info(`${logger_1.colorize.blue(path.relative(cwd, file))}:\n`);
79
+ logger_1.logger.info(`${logger_1.colorize.blue(ref_utils_1.isAbsoluteUrl(file) ? file : path.relative(cwd, file))}:\n`);
79
80
  for (let i = 0; i < fileProblems.length; i++) {
80
81
  const problem = fileProblems[i];
81
82
  logger_1.logger.info(`${formatStylish(problem, positionPad, ruleIdPad)}\n`);
@@ -89,7 +90,7 @@ function formatProblems(problems, opts) {
89
90
  output_1.output.write('<?xml version="1.0" encoding="UTF-8"?>\n');
90
91
  output_1.output.write('<checkstyle version="4.3">\n');
91
92
  for (const [file, { fileProblems }] of Object.entries(groupedByFile)) {
92
- output_1.output.write(`<file name="${xmlEscape(path.relative(cwd, file))}">\n`);
93
+ output_1.output.write(`<file name="${xmlEscape(ref_utils_1.isAbsoluteUrl(file) ? file : path.relative(cwd, file))}">\n`);
93
94
  fileProblems.forEach(formatCheckstyle);
94
95
  output_1.output.write(`</file>\n`);
95
96
  }
@@ -113,7 +114,9 @@ function formatProblems(problems, opts) {
113
114
  return {
114
115
  description: p.message,
115
116
  location: {
116
- path: path.relative(cwd, location.source.absoluteRef),
117
+ path: ref_utils_1.isAbsoluteUrl(location.source.absoluteRef)
118
+ ? location.source.absoluteRef
119
+ : path.relative(cwd, location.source.absoluteRef),
117
120
  lines: {
118
121
  begin: lineCol.start.line,
119
122
  },
@@ -129,12 +132,16 @@ function formatProblems(problems, opts) {
129
132
  totals,
130
133
  version,
131
134
  problems: problems.map((p) => {
132
- var _a;
135
+ var _a, _b, _c;
133
136
  const problem = Object.assign(Object.assign({}, p), { location: p.location.map((location) => (Object.assign(Object.assign({}, location), { source: {
134
- ref: path.relative(cwd, location.source.absoluteRef),
137
+ ref: ref_utils_1.isAbsoluteUrl(location.source.absoluteRef)
138
+ ? location.source.absoluteRef
139
+ : path.relative(cwd, location.source.absoluteRef),
135
140
  } }))), from: p.from
136
141
  ? Object.assign(Object.assign({}, p.from), { source: {
137
- ref: path.relative(cwd, ((_a = p.from) === null || _a === void 0 ? void 0 : _a.source.absoluteRef) || cwd),
142
+ ref: ref_utils_1.isAbsoluteUrl((_a = p.from) === null || _a === void 0 ? void 0 : _a.source.absoluteRef)
143
+ ? (_b = p.from) === null || _b === void 0 ? void 0 : _b.source.absoluteRef
144
+ : path.relative(cwd, ((_c = p.from) === null || _c === void 0 ? void 0 : _c.source.absoluteRef) || cwd),
138
145
  } }) : undefined });
139
146
  if (env_1.env.FORMAT_JSON_WITH_CODEFRAMES) {
140
147
  const location = p.location[0]; // TODO: support multiple locations
@@ -156,7 +163,9 @@ function formatProblems(problems, opts) {
156
163
  function formatCodeframe(problem, idx) {
157
164
  const bgColor = getBgColor(problem);
158
165
  const location = problem.location[0]; // TODO: support multiple locations
159
- const relativePath = path.relative(cwd, location.source.absoluteRef);
166
+ const relativePath = ref_utils_1.isAbsoluteUrl(location.source.absoluteRef)
167
+ ? location.source.absoluteRef
168
+ : path.relative(cwd, location.source.absoluteRef);
160
169
  const loc = codeframes_1.getLineColLocation(location);
161
170
  const atPointer = location.pointer ? logger_1.colorize.gray(`at ${location.pointer}`) : '';
162
171
  const fileWithLoc = `${relativePath}:${loc.start.line}:${loc.start.col}`;
@@ -1,6 +1,10 @@
1
- import { AssertResult, CustomFunction } from 'core/src/config/types';
1
+ import { AssertionContext, AssertResult, CustomFunction } from 'core/src/config/types';
2
2
  import { Location } from '../../../ref-utils';
3
- export declare type AssertionFn = (value: any, condition: any, baseLocation: Location, rawValue?: any) => AssertResult[];
3
+ export declare type AssertionFnContext = AssertionContext & {
4
+ baseLocation: Location;
5
+ rawValue?: any;
6
+ };
7
+ export declare type AssertionFn = (value: any, condition: any, ctx: AssertionFnContext) => AssertResult[];
4
8
  export declare type Asserts = {
5
9
  pattern: AssertionFn;
6
10
  notPattern: AssertionFn;
@@ -35,7 +35,7 @@ exports.runOnValuesSet = new Set([
35
35
  'const',
36
36
  ]);
37
37
  exports.asserts = {
38
- pattern: (value, condition, baseLocation) => {
38
+ pattern: (value, condition, { baseLocation }) => {
39
39
  if (typeof value === 'undefined' || utils_1.isPlainObject(value))
40
40
  return []; // property doesn't exist or is an object, no need to lint it with this assert
41
41
  const values = Array.isArray(value) ? value : [value];
@@ -47,7 +47,7 @@ exports.asserts = {
47
47
  })
48
48
  .filter(utils_1.isTruthy);
49
49
  },
50
- notPattern: (value, condition, baseLocation) => {
50
+ notPattern: (value, condition, { baseLocation }) => {
51
51
  if (typeof value === 'undefined' || utils_1.isPlainObject(value))
52
52
  return []; // property doesn't exist or is an object, no need to lint it with this assert
53
53
  const values = Array.isArray(value) ? value : [value];
@@ -59,7 +59,7 @@ exports.asserts = {
59
59
  })
60
60
  .filter(utils_1.isTruthy);
61
61
  },
62
- enum: (value, condition, baseLocation) => {
62
+ enum: (value, condition, { baseLocation }) => {
63
63
  if (typeof value === 'undefined' || utils_1.isPlainObject(value))
64
64
  return []; // property doesn't exist or is an object, no need to lint it with this assert
65
65
  const values = Array.isArray(value) ? value : [value];
@@ -70,7 +70,7 @@ exports.asserts = {
70
70
  })
71
71
  .filter(utils_1.isTruthy);
72
72
  },
73
- defined: (value, condition = true, baseLocation) => {
73
+ defined: (value, condition = true, { baseLocation }) => {
74
74
  const isDefined = typeof value !== 'undefined';
75
75
  const isValid = condition ? isDefined : !isDefined;
76
76
  return isValid
@@ -82,7 +82,7 @@ exports.asserts = {
82
82
  },
83
83
  ];
84
84
  },
85
- required: (value, keys, baseLocation) => {
85
+ required: (value, keys, { baseLocation }) => {
86
86
  return keys
87
87
  .map((requiredKey) => !value.includes(requiredKey) && {
88
88
  message: `${requiredKey} is required`,
@@ -90,7 +90,7 @@ exports.asserts = {
90
90
  })
91
91
  .filter(utils_1.isTruthy);
92
92
  },
93
- disallowed: (value, condition, baseLocation) => {
93
+ disallowed: (value, condition, { baseLocation }) => {
94
94
  if (typeof value === 'undefined' || utils_1.isPlainObject(value))
95
95
  return []; // property doesn't exist or is an object, no need to lint it with this assert
96
96
  const values = Array.isArray(value) ? value : [value];
@@ -101,7 +101,7 @@ exports.asserts = {
101
101
  })
102
102
  .filter(utils_1.isTruthy);
103
103
  },
104
- const: (value, condition, baseLocation) => {
104
+ const: (value, condition, { baseLocation }) => {
105
105
  if (typeof value === 'undefined')
106
106
  return [];
107
107
  if (Array.isArray(value)) {
@@ -123,7 +123,7 @@ exports.asserts = {
123
123
  : [];
124
124
  }
125
125
  },
126
- undefined: (value, condition = true, baseLocation) => {
126
+ undefined: (value, condition = true, { baseLocation }) => {
127
127
  const isUndefined = typeof value === 'undefined';
128
128
  const isValid = condition ? isUndefined : !isUndefined;
129
129
  return isValid
@@ -135,7 +135,7 @@ exports.asserts = {
135
135
  },
136
136
  ];
137
137
  },
138
- nonEmpty: (value, condition = true, baseLocation) => {
138
+ nonEmpty: (value, condition = true, { baseLocation }) => {
139
139
  const isEmpty = typeof value === 'undefined' || value === null || value === '';
140
140
  const isValid = condition ? !isEmpty : isEmpty;
141
141
  return isValid
@@ -147,17 +147,27 @@ exports.asserts = {
147
147
  },
148
148
  ];
149
149
  },
150
- minLength: (value, condition, baseLocation) => {
150
+ minLength: (value, condition, { baseLocation }) => {
151
151
  if (typeof value === 'undefined' || value.length >= condition)
152
152
  return []; // property doesn't exist, no need to lint it with this assert
153
- return [{ message: `Should have at least ${condition} characters`, location: baseLocation }];
153
+ return [
154
+ {
155
+ message: `Should have at least ${condition} characters`,
156
+ location: baseLocation,
157
+ },
158
+ ];
154
159
  },
155
- maxLength: (value, condition, baseLocation) => {
160
+ maxLength: (value, condition, { baseLocation }) => {
156
161
  if (typeof value === 'undefined' || value.length <= condition)
157
162
  return []; // property doesn't exist, no need to lint it with this assert
158
- return [{ message: `Should have at most ${condition} characters`, location: baseLocation }];
163
+ return [
164
+ {
165
+ message: `Should have at most ${condition} characters`,
166
+ location: baseLocation,
167
+ },
168
+ ];
159
169
  },
160
- casing: (value, condition, baseLocation) => {
170
+ casing: (value, condition, { baseLocation }) => {
161
171
  if (typeof value === 'undefined' || utils_1.isPlainObject(value))
162
172
  return []; // property doesn't exist or is an object, no need to lint it with this assert
163
173
  const values = Array.isArray(value) ? value : [value];
@@ -177,7 +187,7 @@ exports.asserts = {
177
187
  })
178
188
  .filter(utils_1.isTruthy);
179
189
  },
180
- sortOrder: (value, condition, baseLocation) => {
190
+ sortOrder: (value, condition, { baseLocation }) => {
181
191
  const direction = condition.direction || condition;
182
192
  const property = condition.property;
183
193
  if (Array.isArray(value) && value.length > 0 && typeof value[0] === 'object' && !property) {
@@ -197,7 +207,7 @@ exports.asserts = {
197
207
  },
198
208
  ];
199
209
  },
200
- mutuallyExclusive: (value, condition, baseLocation) => {
210
+ mutuallyExclusive: (value, condition, { baseLocation }) => {
201
211
  if (utils_2.getIntersectionLength(value, condition) < 2)
202
212
  return [];
203
213
  return [
@@ -207,7 +217,7 @@ exports.asserts = {
207
217
  },
208
218
  ];
209
219
  },
210
- mutuallyRequired: (value, condition, baseLocation) => {
220
+ mutuallyRequired: (value, condition, { baseLocation }) => {
211
221
  const isValid = utils_2.getIntersectionLength(value, condition) > 0
212
222
  ? utils_2.getIntersectionLength(value, condition) === condition.length
213
223
  : true;
@@ -220,7 +230,7 @@ exports.asserts = {
220
230
  },
221
231
  ];
222
232
  },
223
- requireAny: (value, condition, baseLocation) => {
233
+ requireAny: (value, condition, { baseLocation }) => {
224
234
  return utils_2.getIntersectionLength(value, condition) >= 1
225
235
  ? []
226
236
  : [
@@ -230,7 +240,7 @@ exports.asserts = {
230
240
  },
231
241
  ];
232
242
  },
233
- ref: (_value, condition, baseLocation, rawValue) => {
243
+ ref: (_value, condition, { baseLocation, rawValue }) => {
234
244
  if (typeof rawValue === 'undefined')
235
245
  return []; // property doesn't exist, no need to lint it with this assert
236
246
  const hasRef = rawValue.hasOwnProperty('$ref');
@@ -258,6 +268,6 @@ exports.asserts = {
258
268
  },
259
269
  };
260
270
  function buildAssertCustomFunction(fn) {
261
- return (value, options, baseLocation) => fn.call(null, value, options, baseLocation);
271
+ return (value, options, ctx) => fn.call(null, value, options, ctx);
262
272
  }
263
273
  exports.buildAssertCustomFunction = buildAssertCustomFunction;
@@ -1,4 +1,5 @@
1
1
  import { Asserts } from './asserts';
2
+ import type { AssertionContext, AssertResult } from '../../../config';
2
3
  import type { Assertion, AssertionDefinition } from '.';
3
4
  import type { Oas2Visitor, Oas3Visitor, VisitFunction } from '../../../visitors';
4
5
  export declare type OrderDirection = 'asc' | 'desc';
@@ -12,9 +13,16 @@ export declare type AssertToApply = {
12
13
  runsOnKeys: boolean;
13
14
  runsOnValues: boolean;
14
15
  };
16
+ declare type RunAssertionParams = {
17
+ ctx: AssertionContext;
18
+ assert: AssertToApply;
19
+ assertionProperty?: string;
20
+ };
15
21
  export declare function getAssertsToApply(assertion: AssertionDefinition): AssertToApply[];
16
22
  export declare function buildVisitorObject(assertion: Assertion, subjectVisitor: VisitFunction<any>): Oas2Visitor | Oas3Visitor;
17
23
  export declare function buildSubjectVisitor(assertId: string, assertion: Assertion): VisitFunction<any>;
18
24
  export declare function getIntersectionLength(keys: string[], properties: string[]): number;
19
25
  export declare function isOrdered(value: any[], options: OrderOptions | OrderDirection): boolean;
26
+ export declare function runAssertion({ assert, ctx, assertionProperty, }: RunAssertionParams): AssertResult[];
20
27
  export declare function regexFromString(input: string): RegExp | null;
28
+ export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.regexFromString = exports.isOrdered = exports.getIntersectionLength = exports.buildSubjectVisitor = exports.buildVisitorObject = exports.getAssertsToApply = void 0;
3
+ exports.regexFromString = exports.runAssertion = exports.isOrdered = exports.getIntersectionLength = exports.buildSubjectVisitor = exports.buildVisitorObject = exports.getAssertsToApply = void 0;
4
4
  const asserts_1 = require("./asserts");
5
5
  const logger_1 = require("../../../logger");
6
6
  const ref_utils_1 = require("../../../ref-utils");
@@ -43,31 +43,23 @@ exports.getAssertsToApply = getAssertsToApply;
43
43
  function getAssertionProperties({ subject }) {
44
44
  return (Array.isArray(subject.property) ? subject.property : [subject === null || subject === void 0 ? void 0 : subject.property]).filter(Boolean);
45
45
  }
46
- function applyAssertions(assertionDefinition, asserts, { rawLocation, rawNode, resolve, location, node }) {
47
- var _a;
46
+ function applyAssertions(assertionDefinition, asserts, ctx) {
48
47
  const properties = getAssertionProperties(assertionDefinition);
49
48
  const assertResults = [];
50
49
  for (const assert of asserts) {
51
- const currentLocation = assert.name === 'ref' ? rawLocation : location;
52
50
  if (properties.length) {
53
51
  for (const property of properties) {
54
- // we can have resolvable scalar so need to resolve value here.
55
- const value = ref_utils_1.isRef(node[property]) ? (_a = resolve(node[property])) === null || _a === void 0 ? void 0 : _a.node : node[property];
56
52
  assertResults.push(runAssertion({
57
- values: value,
58
- rawValues: rawNode[property],
59
53
  assert,
60
- location: currentLocation.child(property),
54
+ ctx,
55
+ assertionProperty: property,
61
56
  }));
62
57
  }
63
58
  }
64
59
  else {
65
- const value = Array.isArray(node) ? node : Object.keys(node);
66
60
  assertResults.push(runAssertion({
67
- values: value,
68
- rawValues: rawNode,
69
61
  assert,
70
- location: currentLocation,
62
+ ctx,
71
63
  }));
72
64
  }
73
65
  }
@@ -77,7 +69,7 @@ function buildVisitorObject(assertion, subjectVisitor) {
77
69
  var _a, _b;
78
70
  const targetVisitorLocatorPredicates = getPredicatesFromLocators(assertion.subject);
79
71
  const targetVisitorSkipFunction = targetVisitorLocatorPredicates.length
80
- ? (node, key) => !targetVisitorLocatorPredicates.every((predicate) => predicate(key))
72
+ ? (_, key) => !targetVisitorLocatorPredicates.every((predicate) => predicate(key))
81
73
  : undefined;
82
74
  const targetVisitor = {
83
75
  [assertion.subject.type]: Object.assign({ enter: subjectVisitor }, (targetVisitorSkipFunction && { skip: targetVisitorSkipFunction })),
@@ -95,14 +87,8 @@ function buildVisitorObject(assertion, subjectVisitor) {
95
87
  }
96
88
  const locatorPredicates = getPredicatesFromLocators(assertionDefinitionNode.subject);
97
89
  const assertsToApply = getAssertsToApply(assertionDefinitionNode);
98
- const skipFunction = (node, key, { location, rawLocation, resolve, rawNode }) => !locatorPredicates.every((predicate) => predicate(key)) ||
99
- !!applyAssertions(assertionDefinitionNode, assertsToApply, {
100
- location,
101
- node,
102
- rawLocation,
103
- rawNode,
104
- resolve,
105
- }).length;
90
+ const skipFunction = (node, key, ctx) => !locatorPredicates.every((predicate) => predicate(key)) ||
91
+ !!applyAssertions(assertionDefinitionNode, assertsToApply, Object.assign(Object.assign({}, ctx), { node })).length;
106
92
  const nodeVisitor = Object.assign({}, ((locatorPredicates.length || assertsToApply.length) && { skip: skipFunction }));
107
93
  if (assertionDefinitionNode.subject.type === assertion.subject.type &&
108
94
  index === context.length - 1) {
@@ -123,23 +109,17 @@ function buildVisitorObject(assertion, subjectVisitor) {
123
109
  }
124
110
  exports.buildVisitorObject = buildVisitorObject;
125
111
  function buildSubjectVisitor(assertId, assertion) {
126
- return (node, { report, location, rawLocation, resolve, rawNode }) => {
112
+ return (node, ctx) => {
127
113
  const properties = getAssertionProperties(assertion);
128
114
  const defaultMessage = `${logger_1.colorize.blue(assertId)} failed because the ${logger_1.colorize.blue(assertion.subject.type)} ${logger_1.colorize.blue(properties.join(', '))} didn't meet the assertions: ${assertionMessageTemplates.problems}`.replace(/ +/g, ' ');
129
- const problems = applyAssertions(assertion, getAssertsToApply(assertion), {
130
- rawLocation,
131
- rawNode,
132
- resolve,
133
- location,
134
- node,
135
- });
115
+ const problems = applyAssertions(assertion, getAssertsToApply(assertion), Object.assign(Object.assign({}, ctx), { node }));
136
116
  if (problems.length) {
137
117
  for (const problemGroup of groupProblemsByPointer(problems)) {
138
118
  const message = assertion.message || defaultMessage;
139
119
  const problemMessage = getProblemsMessage(problemGroup);
140
- report({
120
+ ctx.report({
141
121
  message: message.replace(assertionMessageTemplates.problems, problemMessage),
142
- location: getProblemsLocation(problemGroup) || location,
122
+ location: getProblemsLocation(problemGroup) || ctx.location,
143
123
  forceSeverity: assertion.severity || 'error',
144
124
  suggest: assertion.suggest || [],
145
125
  ruleId: assertId,
@@ -207,9 +187,23 @@ function isOrdered(value, options) {
207
187
  return true;
208
188
  }
209
189
  exports.isOrdered = isOrdered;
210
- function runAssertion({ values, rawValues, assert, location }) {
211
- return asserts_1.asserts[assert.name](values, assert.conditions, location, rawValues);
190
+ function runAssertion({ assert, ctx, assertionProperty, }) {
191
+ var _a;
192
+ const currentLocation = assert.name === 'ref' ? ctx.rawLocation : ctx.location;
193
+ if (assertionProperty) {
194
+ const values = ref_utils_1.isRef(ctx.node[assertionProperty])
195
+ ? (_a = ctx.resolve(ctx.node[assertionProperty])) === null || _a === void 0 ? void 0 : _a.node
196
+ : ctx.node[assertionProperty];
197
+ const rawValues = ctx.rawNode[assertionProperty];
198
+ const location = currentLocation.child(assertionProperty);
199
+ return asserts_1.asserts[assert.name](values, assert.conditions, Object.assign(Object.assign({}, ctx), { baseLocation: location, rawValue: rawValues }));
200
+ }
201
+ else {
202
+ const value = Array.isArray(ctx.node) ? ctx.node : Object.keys(ctx.node);
203
+ return asserts_1.asserts[assert.name](value, assert.conditions, Object.assign(Object.assign({}, ctx), { rawValue: ctx.rawNode, baseLocation: currentLocation }));
204
+ }
212
205
  }
206
+ exports.runAssertion = runAssertion;
213
207
  function regexFromString(input) {
214
208
  const matches = input.match(/^\/(.*)\/(.*)|(.*)/);
215
209
  return matches && new RegExp(matches[1] || matches[3], matches[2]);
@@ -0,0 +1,2 @@
1
+ import { Oas3Rule } from 'core/src/visitors';
2
+ export declare const RequiredStringPropertyMissingMunLength: Oas3Rule;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RequiredStringPropertyMissingMunLength = void 0;
4
+ const RequiredStringPropertyMissingMunLength = () => {
5
+ let skipSchemaProperties;
6
+ let requiredPropertiesSet;
7
+ return {
8
+ Schema: {
9
+ enter(schema) {
10
+ if (!(schema === null || schema === void 0 ? void 0 : schema.required)) {
11
+ skipSchemaProperties = true;
12
+ return;
13
+ }
14
+ requiredPropertiesSet = new Set(schema.required);
15
+ skipSchemaProperties = false;
16
+ },
17
+ SchemaProperties: {
18
+ skip() {
19
+ return skipSchemaProperties;
20
+ },
21
+ Schema: {
22
+ enter(schema, { key, location, report }) {
23
+ if (requiredPropertiesSet.has(key) && schema.type === 'string') {
24
+ if (!(schema === null || schema === void 0 ? void 0 : schema.minLength)) {
25
+ report({
26
+ message: 'Property minLength is required.',
27
+ location: location.key(),
28
+ });
29
+ }
30
+ }
31
+ },
32
+ },
33
+ },
34
+ },
35
+ };
36
+ };
37
+ exports.RequiredStringPropertyMissingMunLength = RequiredStringPropertyMissingMunLength;
@@ -41,5 +41,6 @@ export declare const rules: {
41
41
  'response-contains-header': Oas2Rule;
42
42
  'response-contains-property': Oas2Rule;
43
43
  'scalar-property-missing-example': import("../../visitors").Oas3Rule | Oas2Rule;
44
+ 'required-string-property-missing-min-length': import("../../visitors").Oas3Rule;
44
45
  };
45
46
  export declare const preprocessors: {};
@@ -41,6 +41,7 @@ const path_segment_plural_1 = require("../common/path-segment-plural");
41
41
  const response_contains_header_1 = require("../common/response-contains-header");
42
42
  const response_contains_property_1 = require("./response-contains-property");
43
43
  const scalar_property_missing_example_1 = require("../common/scalar-property-missing-example");
44
+ const required_string_property_missing_min_length_1 = require("../common/required-string-property-missing-min-length");
44
45
  exports.rules = {
45
46
  spec: spec_1.OasSpec,
46
47
  'no-invalid-schema-examples': no_invalid_schema_examples_1.NoInvalidSchemaExamples,
@@ -83,5 +84,6 @@ exports.rules = {
83
84
  'response-contains-header': response_contains_header_1.ResponseContainsHeader,
84
85
  'response-contains-property': response_contains_property_1.ResponseContainsProperty,
85
86
  'scalar-property-missing-example': scalar_property_missing_example_1.ScalarPropertyMissingExample,
87
+ 'required-string-property-missing-min-length': required_string_property_missing_min_length_1.RequiredStringPropertyMissingMunLength,
86
88
  };
87
89
  exports.preprocessors = {};
@@ -51,6 +51,7 @@ const response_contains_property_1 = require("./response-contains-property");
51
51
  const scalar_property_missing_example_1 = require("../common/scalar-property-missing-example");
52
52
  const spec_components_invalid_map_name_1 = require("./spec-components-invalid-map-name");
53
53
  const operation_4xx_problem_details_rfc7807_1 = require("./operation-4xx-problem-details-rfc7807");
54
+ const required_string_property_missing_min_length_1 = require("../common/required-string-property-missing-min-length");
54
55
  exports.rules = {
55
56
  spec: spec_1.OasSpec,
56
57
  'info-contact': info_contact_1.InfoContact,
@@ -103,5 +104,6 @@ exports.rules = {
103
104
  'response-contains-property': response_contains_property_1.ResponseContainsProperty,
104
105
  'scalar-property-missing-example': scalar_property_missing_example_1.ScalarPropertyMissingExample,
105
106
  'spec-components-invalid-map-name': spec_components_invalid_map_name_1.SpecComponentsInvalidMapName,
107
+ 'required-string-property-missing-min-length': required_string_property_missing_min_length_1.RequiredStringPropertyMissingMunLength,
106
108
  };
107
109
  exports.preprocessors = {};
@@ -55,6 +55,7 @@ const builtInRulesList = [
55
55
  'response-contains-property',
56
56
  'scalar-property-missing-example',
57
57
  'spec-components-invalid-map-name',
58
+ 'required-string-property-missing-min-length',
58
59
  ];
59
60
  const nodeTypesList = [
60
61
  'any',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/openapi-core",
3
- "version": "1.0.0-beta.123",
3
+ "version": "1.0.0-beta.125",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "engines": {
package/src/bundle.ts CHANGED
@@ -5,7 +5,7 @@ import { Oas3Types } from './types/oas3';
5
5
  import { Oas2Types } from './types/oas2';
6
6
  import { Oas3_1Types } from './types/oas3_1';
7
7
  import { NormalizedNodeType, normalizeTypes, NodeType } from './types';
8
- import { WalkContext, walkDocument, UserContext, ResolveResult } from './walk';
8
+ import { WalkContext, walkDocument, UserContext, ResolveResult, NormalizedProblem } from './walk';
9
9
  import { detectOpenAPI, openAPIMajor, OasMajorVersion } from './oas-types';
10
10
  import { isAbsoluteUrl, isRef, Location, refBaseName } from './ref-utils';
11
11
  import { initRules } from './config/rules';
@@ -64,6 +64,15 @@ export async function bundle(opts: {
64
64
 
65
65
  type BundleContext = WalkContext;
66
66
 
67
+ export type BundleResult = {
68
+ bundle: Document;
69
+ problems: NormalizedProblem[];
70
+ fileDependencies: Set<string>;
71
+ rootType: NormalizedNodeType;
72
+ refTypes?: Map<string, NormalizedNodeType>;
73
+ visitorsData: Record<string, Record<string, unknown>>;
74
+ };
75
+
67
76
  export async function bundleDocument(opts: {
68
77
  document: Document;
69
78
  config: StyleguideConfig;
@@ -73,7 +82,7 @@ export async function bundleDocument(opts: {
73
82
  skipRedoclyRegistryRefs?: boolean;
74
83
  removeUnusedComponents?: boolean;
75
84
  keepUrlRefs?: boolean;
76
- }) {
85
+ }): Promise<BundleResult> {
77
86
  const {
78
87
  document,
79
88
  config,