@redocly/openapi-core 1.0.0-beta.95 → 1.0.0-beta.98

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 (40) hide show
  1. package/README.md +1 -1
  2. package/lib/config/config-resolvers.d.ts +1 -1
  3. package/lib/config/config-resolvers.js +31 -2
  4. package/lib/config/config.d.ts +4 -1
  5. package/lib/config/config.js +18 -12
  6. package/lib/format/format.js +2 -1
  7. package/lib/redocly/index.js +10 -26
  8. package/lib/redocly/registry-api-types.d.ts +1 -0
  9. package/lib/redocly/registry-api.d.ts +1 -1
  10. package/lib/redocly/registry-api.js +2 -1
  11. package/lib/rules/common/assertions/index.js +1 -1
  12. package/lib/types/oas2.js +3 -1
  13. package/lib/types/oas3.js +4 -2
  14. package/lib/types/oas3_1.js +3 -1
  15. package/lib/types/redocly-yaml.js +2 -0
  16. package/lib/typings/openapi.d.ts +2 -0
  17. package/lib/typings/swagger.d.ts +2 -0
  18. package/lib/utils.d.ts +0 -1
  19. package/lib/utils.js +3 -3
  20. package/package.json +5 -5
  21. package/src/benchmark/benchmark.js +1 -1
  22. package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +18 -1
  23. package/src/config/__tests__/config-resolvers.test.ts +31 -0
  24. package/src/config/__tests__/fixtures/resolve-config/api/nested-config.yaml +5 -0
  25. package/src/config/__tests__/fixtures/resolve-config/local-config-with-file.yaml +7 -0
  26. package/src/config/config-resolvers.ts +58 -3
  27. package/src/config/config.ts +21 -12
  28. package/src/format/format.ts +2 -1
  29. package/src/redocly/index.ts +12 -41
  30. package/src/redocly/registry-api-types.ts +1 -0
  31. package/src/redocly/registry-api.ts +2 -0
  32. package/src/rules/common/assertions/index.ts +1 -1
  33. package/src/types/oas2.ts +2 -0
  34. package/src/types/oas3.ts +3 -1
  35. package/src/types/oas3_1.ts +2 -0
  36. package/src/types/redocly-yaml.ts +2 -0
  37. package/src/typings/openapi.ts +2 -0
  38. package/src/typings/swagger.ts +2 -0
  39. package/src/utils.ts +3 -2
  40. package/tsconfig.tsbuildinfo +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # openapi-core
2
2
 
3
- See https://github.com/Redocly/openapi-cli
3
+ See https://github.com/Redocly/redocly-cli
4
4
 
5
5
  ## Basic usage
6
6
 
@@ -8,7 +8,7 @@ export declare function resolveApis({ rawConfig, configPath, resolver, }: {
8
8
  configPath?: string;
9
9
  resolver?: BaseResolver;
10
10
  }): Promise<Record<string, ResolvedApi>>;
11
- export declare function resolveLint({ lintConfig, configPath, resolver, }: {
11
+ export declare function resolveLint(lintOpts: {
12
12
  lintConfig?: LintRawConfig;
13
13
  configPath?: string;
14
14
  resolver?: BaseResolver;
@@ -141,7 +141,7 @@ function resolveApis({ rawConfig, configPath = '', resolver, }) {
141
141
  });
142
142
  }
143
143
  exports.resolveApis = resolveApis;
144
- function resolveLint({ lintConfig, configPath = '', resolver = new resolve_1.BaseResolver(), }, parentConfigPaths = [], extendPaths = []) {
144
+ function resolveAndMergeNestedLint({ lintConfig, configPath = '', resolver = new resolve_1.BaseResolver(), }, parentConfigPaths = [], extendPaths = []) {
145
145
  var _a, _b, _c;
146
146
  return __awaiter(this, void 0, void 0, function* () {
147
147
  if (parentConfigPaths.includes(configPath)) {
@@ -162,7 +162,7 @@ function resolveLint({ lintConfig, configPath = '', resolver = new resolve_1.Bas
162
162
  ? new URL(presetItem, configPath).href
163
163
  : path.resolve(path.dirname(configPath), presetItem);
164
164
  const extendedLintConfig = yield loadExtendLintConfig(pathItem, resolver);
165
- return yield resolveLint({
165
+ return yield resolveAndMergeNestedLint({
166
166
  lintConfig: extendedLintConfig,
167
167
  configPath: pathItem,
168
168
  resolver: resolver,
@@ -175,6 +175,12 @@ function resolveLint({ lintConfig, configPath = '', resolver = new resolve_1.Bas
175
175
  return Object.assign(Object.assign({}, lint), { extendPaths: (_c = lint.extendPaths) === null || _c === void 0 ? void 0 : _c.filter((path) => path && !ref_utils_1.isAbsoluteUrl(path)), plugins: utils_1.getUniquePlugins(mergedPlugins), recommendedFallback: lintConfig === null || lintConfig === void 0 ? void 0 : lintConfig.recommendedFallback, doNotResolveExamples: lintConfig === null || lintConfig === void 0 ? void 0 : lintConfig.doNotResolveExamples });
176
176
  });
177
177
  }
178
+ function resolveLint(lintOpts, parentConfigPaths = [], extendPaths = []) {
179
+ return __awaiter(this, void 0, void 0, function* () {
180
+ const resolvedLint = yield resolveAndMergeNestedLint(lintOpts, parentConfigPaths, extendPaths);
181
+ return Object.assign(Object.assign({}, resolvedLint), { rules: resolvedLint.rules && groupLintAssertionRules(resolvedLint.rules) });
182
+ });
183
+ }
178
184
  exports.resolveLint = resolveLint;
179
185
  function resolvePreset(presetName, plugins) {
180
186
  var _a;
@@ -211,3 +217,26 @@ function getMergedLintRawConfig(configLint, apiLint) {
211
217
  const resultLint = Object.assign(Object.assign(Object.assign({}, configLint), apiLint), { rules: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.rules), apiLint === null || apiLint === void 0 ? void 0 : apiLint.rules), oas2Rules: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.oas2Rules), apiLint === null || apiLint === void 0 ? void 0 : apiLint.oas2Rules), oas3_0Rules: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.oas3_0Rules), apiLint === null || apiLint === void 0 ? void 0 : apiLint.oas3_0Rules), oas3_1Rules: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.oas3_1Rules), apiLint === null || apiLint === void 0 ? void 0 : apiLint.oas3_1Rules), preprocessors: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.preprocessors), apiLint === null || apiLint === void 0 ? void 0 : apiLint.preprocessors), oas2Preprocessors: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.oas2Preprocessors), apiLint === null || apiLint === void 0 ? void 0 : apiLint.oas2Preprocessors), oas3_0Preprocessors: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.oas3_0Preprocessors), apiLint === null || apiLint === void 0 ? void 0 : apiLint.oas3_0Preprocessors), oas3_1Preprocessors: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.oas3_1Preprocessors), apiLint === null || apiLint === void 0 ? void 0 : apiLint.oas3_1Preprocessors), decorators: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.decorators), apiLint === null || apiLint === void 0 ? void 0 : apiLint.decorators), oas2Decorators: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.oas2Decorators), apiLint === null || apiLint === void 0 ? void 0 : apiLint.oas2Decorators), oas3_0Decorators: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.oas3_0Decorators), apiLint === null || apiLint === void 0 ? void 0 : apiLint.oas3_0Decorators), oas3_1Decorators: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.oas3_1Decorators), apiLint === null || apiLint === void 0 ? void 0 : apiLint.oas3_1Decorators), recommendedFallback: (apiLint === null || apiLint === void 0 ? void 0 : apiLint.extends) ? false : configLint.recommendedFallback });
212
218
  return resultLint;
213
219
  }
220
+ function groupLintAssertionRules(rules) {
221
+ if (!rules) {
222
+ return rules;
223
+ }
224
+ // Create a new record to avoid mutating original
225
+ const transformedRules = {};
226
+ // Collect assertion rules
227
+ const assertions = [];
228
+ for (const [ruleKey, rule] of Object.entries(rules)) {
229
+ if (ruleKey.startsWith('assert/') && typeof rule === 'object' && rule !== null) {
230
+ const assertion = rule;
231
+ assertions.push(Object.assign(Object.assign({}, assertion), { assertionId: ruleKey.replace('assert/', '') }));
232
+ }
233
+ else {
234
+ // If it's not an assertion, keep it as is
235
+ transformedRules[ruleKey] = rule;
236
+ }
237
+ }
238
+ if (assertions.length > 0) {
239
+ transformedRules.assertions = assertions;
240
+ }
241
+ return transformedRules;
242
+ }
@@ -1,11 +1,14 @@
1
+ /// <reference types="node" />
1
2
  import { NormalizedProblem } from '../walk';
2
3
  import { OasVersion, OasMajorVersion, Oas2RuleSet, Oas3RuleSet } from '../oas-types';
3
4
  import type { NodeType } from '../types';
4
5
  import type { DecoratorConfig, Plugin, PreprocessorConfig, Region, ResolveConfig, ResolvedApi, ResolvedConfig, ResolvedLintConfig, RuleConfig } from './types';
6
+ export declare const env: NodeJS.ProcessEnv;
5
7
  export declare const IGNORE_FILE = ".redocly.lint-ignore.yaml";
6
8
  export declare const DEFAULT_REGION = "us";
7
9
  export declare const DOMAINS: {
8
- [region in Region]: string;
10
+ us: string;
11
+ eu: string;
9
12
  };
10
13
  export declare const AVAILABLE_REGIONS: Region[];
11
14
  export declare class LintConfig {
@@ -1,28 +1,34 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Config = exports.LintConfig = exports.AVAILABLE_REGIONS = exports.DOMAINS = exports.DEFAULT_REGION = exports.IGNORE_FILE = void 0;
3
+ exports.Config = exports.LintConfig = exports.AVAILABLE_REGIONS = exports.DOMAINS = exports.DEFAULT_REGION = exports.IGNORE_FILE = exports.env = void 0;
4
4
  const fs = require("fs");
5
5
  const path = require("path");
6
6
  const js_yaml_1 = require("../js-yaml");
7
7
  const utils_1 = require("../utils");
8
8
  const oas_types_1 = require("../oas-types");
9
9
  const utils_2 = require("./utils");
10
+ // Alias environment here so this file can work in browser environments too.
11
+ exports.env = typeof process !== 'undefined' ? process.env || {} : {};
10
12
  exports.IGNORE_FILE = '.redocly.lint-ignore.yaml';
11
13
  const IGNORE_BANNER = `# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API.\n` +
12
14
  `# See https://redoc.ly/docs/cli/ for more information.\n`;
13
15
  exports.DEFAULT_REGION = 'us';
14
- const REDOCLY_DOMAIN = process.env.REDOCLY_DOMAIN;
15
- exports.DOMAINS = {
16
- us: 'redocly.com',
17
- eu: 'eu.redocly.com',
18
- };
19
- // FIXME: temporary fix for our lab environments
20
- if (REDOCLY_DOMAIN === null || REDOCLY_DOMAIN === void 0 ? void 0 : REDOCLY_DOMAIN.endsWith('.redocly.host')) {
21
- exports.DOMAINS[REDOCLY_DOMAIN.split('.')[0]] = REDOCLY_DOMAIN;
22
- }
23
- if (REDOCLY_DOMAIN === 'redoc.online') {
24
- exports.DOMAINS[REDOCLY_DOMAIN] = REDOCLY_DOMAIN;
16
+ function getDomains() {
17
+ const domains = {
18
+ us: 'redocly.com',
19
+ eu: 'eu.redocly.com',
20
+ };
21
+ // FIXME: temporary fix for our lab environments
22
+ const domain = exports.env.REDOCLY_DOMAIN;
23
+ if (domain === null || domain === void 0 ? void 0 : domain.endsWith('.redocly.host')) {
24
+ domains[domain.split('.')[0]] = domain;
25
+ }
26
+ if (domain === 'redoc.online') {
27
+ domains[domain] = domain;
28
+ }
29
+ return domains;
25
30
  }
31
+ exports.DOMAINS = getDomains();
26
32
  exports.AVAILABLE_REGIONS = Object.keys(exports.DOMAINS);
27
33
  class LintConfig {
28
34
  constructor(rawConfig, configFile) {
@@ -5,6 +5,7 @@ const path = require("path");
5
5
  const colorette_1 = require("colorette");
6
6
  const coreVersion = require('../../package.json').version;
7
7
  const codeframes_1 = require("./codeframes");
8
+ const config_1 = require("../config");
8
9
  const ERROR_MESSAGE = {
9
10
  INVALID_SEVERITY_LEVEL: 'Invalid severity level; accepted values: error or warn',
10
11
  };
@@ -131,7 +132,7 @@ function formatProblems(problems, opts) {
131
132
  ? Object.assign(Object.assign({}, p.from), { source: {
132
133
  ref: path.relative(cwd, ((_a = p.from) === null || _a === void 0 ? void 0 : _a.source.absoluteRef) || cwd),
133
134
  } }) : undefined });
134
- if (process.env.FORMAT_JSON_WITH_CODEFRAMES) {
135
+ if (config_1.env.FORMAT_JSON_WITH_CODEFRAMES) {
135
136
  const location = p.location[0]; // TODO: support multiple locations
136
137
  const loc = codeframes_1.getLineColLocation(location);
137
138
  problem.codeframe = codeframes_1.getCodeframe(loc, color);
@@ -18,27 +18,21 @@ const registry_api_1 = require("./registry-api");
18
18
  const config_1 = require("../config/config");
19
19
  const utils_1 = require("../utils");
20
20
  const TOKEN_FILENAME = '.redocly-config.json';
21
- let REDOCLY_DOMAIN; // workaround for the isRedoclyRegistryURL, see more below
22
21
  class RedoclyClient {
23
22
  constructor(region) {
24
23
  this.accessTokens = {};
25
24
  this.region = this.loadRegion(region);
26
25
  this.loadTokens();
27
- this.domain = region ? config_1.DOMAINS[region] : process.env.REDOCLY_DOMAIN || config_1.DOMAINS[config_1.DEFAULT_REGION];
28
- /*
29
- * We can't use process.env here because it is replaced by a const in some client-side bundles,
30
- * which breaks assignment.
31
- */
32
- REDOCLY_DOMAIN = this.domain; // isRedoclyRegistryURL depends on the value to be set
26
+ this.domain = region ? config_1.DOMAINS[region] : config_1.env.REDOCLY_DOMAIN || config_1.DOMAINS[config_1.DEFAULT_REGION];
27
+ config_1.env.REDOCLY_DOMAIN = this.domain; // isRedoclyRegistryURL depends on the value to be set
33
28
  this.registryApi = new registry_api_1.RegistryApi(this.accessTokens, this.region);
34
29
  }
35
30
  loadRegion(region) {
36
31
  if (region && !config_1.DOMAINS[region]) {
37
- process.stdout.write(colorette_1.red(`Invalid argument: region in config file.\nGiven: ${colorette_1.green(region)}, choices: "us", "eu".\n`));
38
- process.exit(1);
32
+ throw new Error(`Invalid argument: region in config file.\nGiven: ${colorette_1.green(region)}, choices: "us", "eu".`);
39
33
  }
40
- if (process.env.REDOCLY_DOMAIN) {
41
- return (config_1.AVAILABLE_REGIONS.find((region) => config_1.DOMAINS[region] === process.env.REDOCLY_DOMAIN) || config_1.DEFAULT_REGION);
34
+ if (config_1.env.REDOCLY_DOMAIN) {
35
+ return (config_1.AVAILABLE_REGIONS.find((region) => config_1.DOMAINS[region] === config_1.env.REDOCLY_DOMAIN) || config_1.DEFAULT_REGION);
42
36
  }
43
37
  return region || config_1.DEFAULT_REGION;
44
38
  }
@@ -54,13 +48,7 @@ class RedoclyClient {
54
48
  }
55
49
  getAuthorizationHeader() {
56
50
  return __awaiter(this, void 0, void 0, function* () {
57
- const token = this.accessTokens[this.region];
58
- // print this only if there is token but invalid
59
- if (token && !this.isAuthorizedWithRedoclyByRegion()) {
60
- process.stderr.write(`${colorette_1.yellow('Warning:')} invalid Redocly API key. Use "npx @redocly/openapi-cli login" to provide your API key\n`);
61
- return undefined;
62
- }
63
- return token;
51
+ return this.accessTokens[this.region];
64
52
  });
65
53
  }
66
54
  // </backward compatibility: portal>
@@ -76,8 +64,8 @@ class RedoclyClient {
76
64
  [this.region]: credentials.token,
77
65
  })));
78
66
  }
79
- if (process.env.REDOCLY_AUTHORIZATION) {
80
- this.setAccessTokens(Object.assign(Object.assign({}, this.accessTokens), { [this.region]: process.env.REDOCLY_AUTHORIZATION }));
67
+ if (config_1.env.REDOCLY_AUTHORIZATION) {
68
+ this.setAccessTokens(Object.assign(Object.assign({}, this.accessTokens), { [this.region]: config_1.env.REDOCLY_AUTHORIZATION }));
81
69
  }
82
70
  }
83
71
  getAllTokens() {
@@ -133,19 +121,16 @@ class RedoclyClient {
133
121
  login(accessToken, verbose = false) {
134
122
  return __awaiter(this, void 0, void 0, function* () {
135
123
  const credentialsPath = path_1.resolve(os_1.homedir(), TOKEN_FILENAME);
136
- process.stdout.write(colorette_1.gray('\n Logging in...\n'));
137
124
  try {
138
125
  yield this.verifyToken(accessToken, this.region, verbose);
139
126
  }
140
127
  catch (err) {
141
- process.stdout.write(colorette_1.red('Authorization failed. Please check if you entered a valid API key.\n'));
142
- process.exit(1);
128
+ throw new Error('Authorization failed. Please check if you entered a valid API key.');
143
129
  }
144
130
  const credentials = Object.assign(Object.assign({}, this.readCredentialsFile(credentialsPath)), { [this.region]: accessToken, token: accessToken });
145
131
  this.accessTokens = credentials;
146
132
  this.registryApi.setAccessTokens(credentials);
147
133
  fs_1.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2));
148
- process.stdout.write(colorette_1.green(' Authorization confirmed. ✅\n\n'));
149
134
  });
150
135
  }
151
136
  logout() {
@@ -153,12 +138,11 @@ class RedoclyClient {
153
138
  if (fs_1.existsSync(credentialsPath)) {
154
139
  fs_1.unlinkSync(credentialsPath);
155
140
  }
156
- process.stdout.write('Logged out from the Redocly account. ✋\n');
157
141
  }
158
142
  }
159
143
  exports.RedoclyClient = RedoclyClient;
160
144
  function isRedoclyRegistryURL(link) {
161
- const domain = REDOCLY_DOMAIN || process.env.REDOCLY_DOMAIN || config_1.DOMAINS[config_1.DEFAULT_REGION];
145
+ const domain = config_1.env.REDOCLY_DOMAIN || config_1.DOMAINS[config_1.DEFAULT_REGION];
162
146
  const legacyDomain = domain === 'redocly.com' ? 'redoc.ly' : domain;
163
147
  if (!link.startsWith(`https://api.${domain}/registry/`) &&
164
148
  !link.startsWith(`https://api.${legacyDomain}/registry/`)) {
@@ -14,6 +14,7 @@ export declare namespace RegistryApiTypes {
14
14
  filePaths: string[];
15
15
  branch?: string;
16
16
  isUpsert?: boolean;
17
+ isPublic?: boolean;
17
18
  }
18
19
  export interface PrepareFileuploadOKResponse {
19
20
  filePath: string;
@@ -13,5 +13,5 @@ export declare class RegistryApi {
13
13
  organizations: string[];
14
14
  }>;
15
15
  prepareFileUpload({ organizationId, name, version, filesHash, filename, isUpsert, }: RegistryApiTypes.PrepareFileuploadParams): Promise<RegistryApiTypes.PrepareFileuploadOKResponse>;
16
- pushApi({ organizationId, name, version, rootFilePath, filePaths, branch, isUpsert, }: RegistryApiTypes.PushApiParams): Promise<void>;
16
+ pushApi({ organizationId, name, version, rootFilePath, filePaths, branch, isUpsert, isPublic, }: RegistryApiTypes.PushApiParams): Promise<void>;
17
17
  }
@@ -80,7 +80,7 @@ class RegistryApi {
80
80
  throw new Error('Could not prepare file upload');
81
81
  });
82
82
  }
83
- pushApi({ organizationId, name, version, rootFilePath, filePaths, branch, isUpsert, }) {
83
+ pushApi({ organizationId, name, version, rootFilePath, filePaths, branch, isUpsert, isPublic, }) {
84
84
  return __awaiter(this, void 0, void 0, function* () {
85
85
  const response = yield this.request(`/${organizationId}/${name}/${version}`, {
86
86
  method: 'PUT',
@@ -93,6 +93,7 @@ class RegistryApi {
93
93
  filePaths,
94
94
  branch,
95
95
  isUpsert,
96
+ isPublic,
96
97
  }),
97
98
  }, this.region);
98
99
  if (response.ok) {
@@ -7,7 +7,7 @@ const Assertions = (opts) => {
7
7
  let visitors = [];
8
8
  // As 'Assertions' has an array of asserts,
9
9
  // that array spreads into an 'opts' object on init rules phase here
10
- // https://github.com/Redocly/openapi-cli/blob/master/packages/core/src/config/config.ts#L311
10
+ // https://github.com/Redocly/redocly-cli/blob/master/packages/core/src/config/config.ts#L311
11
11
  // that is why we need to iterate through 'opts' values;
12
12
  // before - filter only object 'opts' values
13
13
  const assertions = Object.values(opts).filter((opt) => typeof opt === 'object' && opt !== null);
package/lib/types/oas2.js CHANGED
@@ -80,7 +80,8 @@ const Operation = {
80
80
  deprecated: { type: 'boolean' },
81
81
  security: _1.listOf('SecurityRequirement'),
82
82
  'x-codeSamples': _1.listOf('XCodeSample'),
83
- 'x-code-samples': _1.listOf('XCodeSample'), // deprecated
83
+ 'x-code-samples': _1.listOf('XCodeSample'),
84
+ 'x-hideTryItPanel': { type: 'boolean' },
84
85
  },
85
86
  required: ['responses'],
86
87
  };
@@ -275,6 +276,7 @@ const Schema = {
275
276
  xml: 'Xml',
276
277
  externalDocs: 'ExternalDocs',
277
278
  example: { isExample: true },
279
+ 'x-tags': { type: 'array', items: { type: 'string' } },
278
280
  },
279
281
  };
280
282
  const SchemaProperties = {
package/lib/types/oas3.js CHANGED
@@ -149,7 +149,8 @@ const Operation = {
149
149
  deprecated: { type: 'boolean' },
150
150
  callbacks: _1.mapOf('Callback'),
151
151
  'x-codeSamples': _1.listOf('XCodeSample'),
152
- 'x-code-samples': _1.listOf('XCodeSample'), // deprecated
152
+ 'x-code-samples': _1.listOf('XCodeSample'),
153
+ 'x-hideTryItPanel': { type: 'boolean' },
153
154
  },
154
155
  required: ['responses'],
155
156
  };
@@ -292,6 +293,7 @@ const Schema = {
292
293
  xml: 'Xml',
293
294
  example: { isExample: true },
294
295
  deprecated: { type: 'boolean' },
296
+ 'x-tags': { type: 'array', items: { type: 'string' } },
295
297
  },
296
298
  };
297
299
  const Xml = {
@@ -344,7 +346,7 @@ const ImplicitFlow = {
344
346
  scopes: { type: 'object', additionalProperties: { type: 'string' } },
345
347
  authorizationUrl: { type: 'string' },
346
348
  },
347
- required: ['authorizationUrl', 'scopes']
349
+ required: ['authorizationUrl', 'scopes'],
348
350
  };
349
351
  const PasswordFlow = {
350
352
  properties: {
@@ -71,7 +71,8 @@ const Operation = {
71
71
  deprecated: { type: 'boolean' },
72
72
  callbacks: _1.mapOf('Callback'),
73
73
  'x-codeSamples': _1.listOf('XCodeSample'),
74
- 'x-code-samples': _1.listOf('XCodeSample'), // deprecated
74
+ 'x-code-samples': _1.listOf('XCodeSample'),
75
+ 'x-hideTryItPanel': { type: 'boolean' },
75
76
  },
76
77
  };
77
78
  const Schema = {
@@ -164,6 +165,7 @@ const Schema = {
164
165
  deprecated: { type: 'boolean' },
165
166
  const: null,
166
167
  $comment: { type: 'string' },
168
+ 'x-tags': { type: 'array', items: { type: 'string' } },
167
169
  },
168
170
  };
169
171
  const SecurityScheme = {
@@ -414,6 +414,7 @@ const ConfigReferenceDocs = {
414
414
  hideSchemaPattern: { type: 'boolean' },
415
415
  hideSchemaTitles: { type: 'boolean' },
416
416
  hideSingleRequestSampleTab: { type: 'boolean' },
417
+ hideTryItPanel: { type: 'boolean' },
417
418
  htmlTemplate: { type: 'string' },
418
419
  jsonSampleExpandLevel: { type: 'string' },
419
420
  labels: 'ConfigLabels',
@@ -433,6 +434,7 @@ const ConfigReferenceDocs = {
433
434
  routingBasePath: { type: 'string' },
434
435
  samplesTabsMaxCount: { type: 'number' },
435
436
  schemaExpansionLevel: { type: 'string' },
437
+ schemaDefinitionsTagName: { type: 'string' },
436
438
  scrollYOffset: { type: 'string' },
437
439
  searchAutoExpand: { type: 'boolean' },
438
440
  searchFieldLevelBoost: { type: 'number' },
@@ -72,6 +72,7 @@ export interface Oas3Operation {
72
72
  servers?: Oas3Server[];
73
73
  'x-codeSamples'?: Oas3XCodeSample[];
74
74
  'x-code-samples'?: Oas3XCodeSample[];
75
+ 'x-hideTryItPanel'?: boolean;
75
76
  }
76
77
  export interface Oas3Parameter {
77
78
  name: string;
@@ -144,6 +145,7 @@ export interface Oas3Schema {
144
145
  enum?: any[];
145
146
  example?: any;
146
147
  xml?: Oas3Xml;
148
+ 'x-tags'?: string[];
147
149
  }
148
150
  export interface Oas3_1Schema extends Oas3Schema {
149
151
  examples?: any[];
@@ -71,6 +71,7 @@ export interface Oas2Operation {
71
71
  security?: Oas2SecurityRequirement[];
72
72
  'x-codeSamples'?: Oas2XCodeSample[];
73
73
  'x-code-samples'?: Oas2XCodeSample[];
74
+ 'x-hideTryItPanel'?: boolean;
74
75
  }
75
76
  export declare type Oas2Parameter = Oas2BodyParameter | Oas2SimpleParameter;
76
77
  export interface Oas2BodyParameter {
@@ -166,6 +167,7 @@ export interface Oas2Schema {
166
167
  enum?: any[];
167
168
  example?: any;
168
169
  xml?: Oas2Xml;
170
+ 'x-tags'?: string[];
169
171
  }
170
172
  export declare type Oas2ParameterLocation = 'query' | 'header' | 'path' | 'formData';
171
173
  export interface Oas2Responses {
package/lib/utils.d.ts CHANGED
@@ -22,7 +22,6 @@ export declare function readFileFromUrl(url: string, config: HttpResolveConfig):
22
22
  body: any;
23
23
  mimeType: any;
24
24
  }>;
25
- export declare function match(url: string, pattern: string): boolean;
26
25
  export declare function pickObjectProps<T extends Record<string, unknown>>(object: T, keys: Array<string>): T;
27
26
  export declare function omitObjectProps<T extends Record<string, unknown>>(object: T, keys: Array<string>): T;
28
27
  export declare function splitCamelCaseIntoWords(str: string): Set<string>;
package/lib/utils.js CHANGED
@@ -9,12 +9,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.assignExisting = exports.isNotString = exports.isString = exports.isNotEmptyObject = exports.slash = exports.isPathParameter = exports.readFileAsStringSync = exports.isSingular = exports.validateMimeTypeOAS3 = exports.validateMimeType = exports.splitCamelCaseIntoWords = exports.omitObjectProps = exports.pickObjectProps = exports.match = exports.readFileFromUrl = exports.isEmptyArray = exports.isEmptyObject = exports.isPlainObject = exports.notUndefined = exports.loadYaml = exports.popStack = exports.pushStack = exports.stringifyYaml = exports.parseYaml = void 0;
12
+ exports.assignExisting = exports.isNotString = exports.isString = exports.isNotEmptyObject = exports.slash = exports.isPathParameter = exports.readFileAsStringSync = exports.isSingular = exports.validateMimeTypeOAS3 = exports.validateMimeType = exports.splitCamelCaseIntoWords = exports.omitObjectProps = exports.pickObjectProps = exports.readFileFromUrl = exports.isEmptyArray = exports.isEmptyObject = exports.isPlainObject = exports.notUndefined = exports.loadYaml = exports.popStack = exports.pushStack = exports.stringifyYaml = exports.parseYaml = void 0;
13
13
  const fs = require("fs");
14
14
  const minimatch = require("minimatch");
15
15
  const node_fetch_1 = require("node-fetch");
16
16
  const pluralize = require("pluralize");
17
17
  const js_yaml_1 = require("./js-yaml");
18
+ const config_1 = require("./config");
18
19
  var js_yaml_2 = require("./js-yaml");
19
20
  Object.defineProperty(exports, "parseYaml", { enumerable: true, get: function () { return js_yaml_2.parseYaml; } });
20
21
  Object.defineProperty(exports, "stringifyYaml", { enumerable: true, get: function () { return js_yaml_2.stringifyYaml; } });
@@ -56,7 +57,7 @@ function readFileFromUrl(url, config) {
56
57
  for (const header of config.headers) {
57
58
  if (match(url, header.matches)) {
58
59
  headers[header.name] =
59
- header.envVariable !== undefined ? process.env[header.envVariable] || '' : header.value;
60
+ header.envVariable !== undefined ? config_1.env[header.envVariable] || '' : header.value;
60
61
  }
61
62
  }
62
63
  const req = yield (config.customFetch || node_fetch_1.default)(url, {
@@ -76,7 +77,6 @@ function match(url, pattern) {
76
77
  }
77
78
  return minimatch(url, pattern);
78
79
  }
79
- exports.match = match;
80
80
  function pickObjectProps(object, keys) {
81
81
  return Object.fromEntries(keys.filter((key) => key in object).map((key) => [key, object[key]]));
82
82
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/openapi-core",
3
- "version": "1.0.0-beta.95",
3
+ "version": "1.0.0-beta.98",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "engines": {
@@ -10,7 +10,7 @@
10
10
  "license": "MIT",
11
11
  "repository": {
12
12
  "type": "git",
13
- "url": "https://github.com/Redocly/openapi-cli.git"
13
+ "url": "https://github.com/Redocly/redocly-cli.git"
14
14
  },
15
15
  "browser": {
16
16
  "fs": false,
@@ -18,7 +18,7 @@
18
18
  "os": false,
19
19
  "node-fetch": false
20
20
  },
21
- "homepage": "https://github.com/Redocly/openapi-cli",
21
+ "homepage": "https://github.com/Redocly/redocly-cli",
22
22
  "keywords": [
23
23
  "linter",
24
24
  "OpenAPI",
@@ -39,7 +39,7 @@
39
39
  "js-levenshtein": "^1.1.6",
40
40
  "js-yaml": "^4.1.0",
41
41
  "lodash.isequal": "^4.5.0",
42
- "minimatch": "^3.0.4",
42
+ "minimatch": "^5.0.1",
43
43
  "node-fetch": "^2.6.1",
44
44
  "pluralize": "^8.0.0",
45
45
  "yaml-ast-parser": "0.0.43"
@@ -48,7 +48,7 @@
48
48
  "@types/js-levenshtein": "^1.1.0",
49
49
  "@types/js-yaml": "^4.0.3",
50
50
  "@types/lodash.isequal": "^4.5.5",
51
- "@types/minimatch": "^3.0.3",
51
+ "@types/minimatch": "^3.0.5",
52
52
  "@types/node-fetch": "^2.5.7",
53
53
  "@types/pluralize": "^0.0.29",
54
54
  "typescript": "^4.0.5"
@@ -29,7 +29,7 @@ function prepareRevision(revision) {
29
29
 
30
30
  // Returns the complete git hash for a given git revision reference.
31
31
  const hash = exec(`git rev-parse "${revision}"`);
32
- const dir = path.join(os.tmpdir(), 'openapi-cli-benchmark', hash);
32
+ const dir = path.join(os.tmpdir(), 'redocly-cli-benchmark', hash);
33
33
  if (fs.existsSync(dir)) {
34
34
  fs.rmdirSync(dir, { recursive: true});
35
35
  }
@@ -103,7 +103,24 @@ Object {
103
103
  "preprocessors": Object {},
104
104
  "recommendedFallback": undefined,
105
105
  "rules": Object {
106
- "assertions": "warn",
106
+ "assertions": Array [
107
+ Object {
108
+ "assertionId": "path-item-get-defined",
109
+ "defined": true,
110
+ "message": "Every path item must have a GET operation.",
111
+ "property": "get",
112
+ "subject": "PathItem",
113
+ },
114
+ Object {
115
+ "assertionId": "tag-description",
116
+ "message": "Tag description must be at least 13 characters and end with a full stop.",
117
+ "minLength": 13,
118
+ "pattern": "/\\\\.$/",
119
+ "property": "description",
120
+ "severity": "error",
121
+ "subject": "Tag",
122
+ },
123
+ ],
107
124
  "boolean-parameter-prefixes": "error",
108
125
  "info-contact": "off",
109
126
  "info-description": "warn",
@@ -130,6 +130,37 @@ describe('resolveLint', () => {
130
130
  expect(lint).toMatchSnapshot();
131
131
  });
132
132
 
133
+ it('should correctly merge assertions from nested config', async () => {
134
+ const lintConfig = {
135
+ extends: ['local-config-with-file.yaml'],
136
+ };
137
+
138
+ const lint = await resolveLint({
139
+ lintConfig,
140
+ configPath,
141
+ });
142
+
143
+ expect(Array.isArray(lint.rules?.assertions)).toEqual(true);
144
+ expect(lint.rules?.assertions).toMatchObject( [
145
+ {
146
+ subject: 'PathItem',
147
+ property: 'get',
148
+ message: 'Every path item must have a GET operation.',
149
+ defined: true,
150
+ assertionId: 'path-item-get-defined'
151
+ },
152
+ {
153
+ subject: 'Tag',
154
+ property: 'description',
155
+ message: 'Tag description must be at least 13 characters and end with a full stop.',
156
+ severity: 'error',
157
+ minLength: 13,
158
+ pattern: '/\\.$/',
159
+ assertionId: 'tag-description'
160
+ }
161
+ ])
162
+ });
163
+
133
164
  it('should resolve extends with url file config witch contains path to nested config', async () => {
134
165
  const lintConfig = {
135
166
  // This points to ./fixtures/resolve-remote-configs/remote-config.yaml
@@ -3,5 +3,10 @@ lint:
3
3
  - plugin.js
4
4
  rules:
5
5
  operation-2xx-response: error
6
+ assert/path-item-get-defined:
7
+ subject: PathItem
8
+ property: get
9
+ message: Every path item must have a GET operation.
10
+ defined: true
6
11
  extends:
7
12
  - test-plugin-nested/all
@@ -2,6 +2,13 @@ lint:
2
2
  rules:
3
3
  no-invalid-media-type-examples: warn
4
4
  operation-4xx-response: off
5
+ assert/tag-description:
6
+ subject: Tag
7
+ property: description
8
+ message: Tag description must be at least 13 characters and end with a full stop.
9
+ severity: error
10
+ minLength: 13
11
+ pattern: /\.$/
5
12
  plugins:
6
13
  - plugin.js
7
14
  - api/plugin.js