@redocly/openapi-core 1.15.0 → 1.17.0

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 (45) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/lib/bundle.d.ts +1 -1
  3. package/lib/bundle.js +2 -2
  4. package/lib/config/config.d.ts +0 -6
  5. package/lib/config/config.js +1 -19
  6. package/lib/config/load.d.ts +12 -3
  7. package/lib/config/load.js +34 -14
  8. package/lib/config/utils.d.ts +1 -0
  9. package/lib/config/utils.js +5 -1
  10. package/lib/decorators/common/registry-dependencies.js +2 -2
  11. package/lib/index.d.ts +3 -2
  12. package/lib/index.js +17 -2
  13. package/lib/lint.d.ts +1 -0
  14. package/lib/lint.js +3 -7
  15. package/lib/oas-types.d.ts +1 -1
  16. package/lib/redocly/domains.d.ts +14 -0
  17. package/lib/redocly/domains.js +41 -0
  18. package/lib/redocly/index.d.ts +1 -2
  19. package/lib/redocly/index.js +14 -24
  20. package/lib/redocly/registry-api.d.ts +2 -2
  21. package/lib/redocly/registry-api.js +9 -9
  22. package/lib/types/oas3_1.js +1 -0
  23. package/lib/types/redocly-yaml.d.ts +3 -1
  24. package/lib/types/redocly-yaml.js +34 -36
  25. package/lib/utils.d.ts +2 -0
  26. package/lib/utils.js +7 -1
  27. package/package.json +4 -2
  28. package/src/__tests__/lint.test.ts +22 -12
  29. package/src/bundle.ts +1 -1
  30. package/src/config/__tests__/load.test.ts +86 -61
  31. package/src/config/config.ts +1 -23
  32. package/src/config/load.ts +51 -24
  33. package/src/config/utils.ts +4 -0
  34. package/src/decorators/common/registry-dependencies.ts +1 -1
  35. package/src/index.ts +4 -1
  36. package/src/lint.ts +10 -9
  37. package/src/redocly/__tests__/domains.test.ts +52 -0
  38. package/src/redocly/__tests__/redocly-client.test.ts +5 -3
  39. package/src/redocly/domains.ts +48 -0
  40. package/src/redocly/index.ts +14 -24
  41. package/src/redocly/registry-api.ts +25 -31
  42. package/src/types/oas3_1.ts +1 -0
  43. package/src/types/redocly-yaml.ts +32 -33
  44. package/src/utils.ts +7 -1
  45. package/tsconfig.tsbuildinfo +1 -1
@@ -168,6 +168,7 @@ const Schema = {
168
168
  format: { type: 'string' },
169
169
  contentEncoding: { type: 'string' },
170
170
  contentMediaType: { type: 'string' },
171
+ contentSchema: 'Schema',
171
172
  default: null,
172
173
  readOnly: { type: 'boolean' },
173
174
  writeOnly: { type: 'boolean' },
@@ -1,5 +1,6 @@
1
1
  import type { NodeType } from '.';
2
2
  import type { JSONSchema } from 'json-schema-to-ts';
3
+ import { Config } from '../config';
3
4
  declare const builtInCommonRules: readonly ["spec", "info-contact", "operation-operationId", "tag-description", "tags-alphabetical"];
4
5
  export type BuiltInCommonRuleId = typeof builtInCommonRules[number];
5
6
  declare const builtInCommonOASRules: readonly ["info-license-url", "info-license", "no-ambiguous-paths", "no-enum-type-mismatch", "no-http-verbs-in-paths", "no-identical-paths", "no-invalid-parameter-examples", "no-invalid-schema-examples", "no-path-trailing-slash", "operation-2xx-response", "operation-4xx-response", "operation-description", "operation-operationId-unique", "operation-operationId-url-safe", "operation-parameters-unique", "operation-singular-tag", "operation-summary", "operation-tag-defined", "parameter-description", "path-declaration-must-exist", "path-excludes-patterns", "path-http-verbs-order", "path-not-include-query", "path-params-defined", "path-parameters-defined", "path-segment-plural", "paths-kebab-case", "required-string-property-missing-min-length", "response-contains-header", "scalar-property-missing-example", "security-defined", "spec-strict-refs", "no-unresolved-refs", "no-required-schema-properties-undefined"];
@@ -16,9 +17,10 @@ declare const oas3NodeTypesList: readonly ["Root", "Tag", "TagList", "ExternalDo
16
17
  export type Oas3NodeType = typeof oas3NodeTypesList[number];
17
18
  declare const oas3_1NodeTypesList: readonly ["Root", "Schema", "SchemaProperties", "Info", "License", "Components", "NamedPathItems", "SecurityScheme", "Operation"];
18
19
  export type Oas3_1NodeType = typeof oas3_1NodeTypesList[number];
19
- export declare const createConfigTypes: (extraSchemas: JSONSchema) => {
20
+ export declare function createConfigTypes(extraSchemas: JSONSchema, config?: Config): {
20
21
  ConfigRoot: NodeType;
21
22
  ConfigApisProperties: NodeType;
23
+ AssertionDefinitionSubject: NodeType;
22
24
  };
23
25
  export declare const ConfigTypes: Record<string, NodeType>;
24
26
  export {};
@@ -5,6 +5,7 @@ const config_1 = require("@redocly/config");
5
5
  const _1 = require(".");
6
6
  const utils_1 = require("../utils");
7
7
  const json_schema_adapter_1 = require("./json-schema-adapter");
8
+ const oas_types_1 = require("../oas-types");
8
9
  const builtInCommonRules = [
9
10
  'spec',
10
11
  'info-contact',
@@ -194,7 +195,6 @@ const oas3_1NodeTypesList = [
194
195
  'SecurityScheme',
195
196
  'Operation',
196
197
  ];
197
- const asyncNodeTypesList = ['Message'];
198
198
  const ConfigStyleguide = {
199
199
  properties: {
200
200
  extends: {
@@ -296,37 +296,30 @@ const Schema = {
296
296
  properties: {},
297
297
  additionalProperties: {},
298
298
  };
299
- const AssertionDefinitionSubject = {
300
- properties: {
301
- type: {
302
- enum: [
303
- ...new Set([
304
- 'any',
305
- ...oas2NodeTypesList,
306
- ...oas3NodeTypesList,
307
- ...oas3_1NodeTypesList,
308
- ...asyncNodeTypesList,
309
- 'SpecExtension',
310
- ]),
311
- ],
312
- },
313
- property: (value) => {
314
- if (Array.isArray(value)) {
315
- return { type: 'array', items: { type: 'string' } };
316
- }
317
- else if (value === null) {
318
- return null;
319
- }
320
- else {
321
- return { type: 'string' };
322
- }
299
+ function createAssertionDefinitionSubject(nodeNames) {
300
+ return {
301
+ properties: {
302
+ type: {
303
+ enum: [...new Set(['any', ...nodeNames, 'SpecExtension'])],
304
+ },
305
+ property: (value) => {
306
+ if (Array.isArray(value)) {
307
+ return { type: 'array', items: { type: 'string' } };
308
+ }
309
+ else if (value === null) {
310
+ return null;
311
+ }
312
+ else {
313
+ return { type: 'string' };
314
+ }
315
+ },
316
+ filterInParentKeys: { type: 'array', items: { type: 'string' } },
317
+ filterOutParentKeys: { type: 'array', items: { type: 'string' } },
318
+ matchParentKeys: { type: 'string' },
323
319
  },
324
- filterInParentKeys: { type: 'array', items: { type: 'string' } },
325
- filterOutParentKeys: { type: 'array', items: { type: 'string' } },
326
- matchParentKeys: { type: 'string' },
327
- },
328
- required: ['type'],
329
- };
320
+ required: ['type'],
321
+ };
322
+ }
330
323
  const AssertionDefinitionAssertions = {
331
324
  properties: {
332
325
  enum: { type: 'array', items: { type: 'string' } },
@@ -897,11 +890,17 @@ const ConfigMockServer = {
897
890
  errorIfForcedExampleNotFound: { type: 'boolean' },
898
891
  },
899
892
  };
900
- const createConfigTypes = (extraSchemas) => {
893
+ function createConfigTypes(extraSchemas, config) {
894
+ const nodeNames = Object.values(oas_types_1.SpecVersion).flatMap((version) => {
895
+ const types = (config === null || config === void 0 ? void 0 : config.styleguide)
896
+ ? config.styleguide.extendTypes((0, oas_types_1.getTypes)(version), version)
897
+ : (0, oas_types_1.getTypes)(version);
898
+ return Object.keys(types);
899
+ });
901
900
  // Create types based on external schemas
902
901
  const nodeTypes = (0, json_schema_adapter_1.getNodeTypesFromJSONSchema)('rootRedoclyConfigSchema', extraSchemas);
903
- return Object.assign(Object.assign(Object.assign({}, CoreConfigTypes), { ConfigRoot: createConfigRoot(nodeTypes), ConfigApisProperties: createConfigApisProperties(nodeTypes) }), nodeTypes);
904
- };
902
+ return Object.assign(Object.assign(Object.assign({}, CoreConfigTypes), { ConfigRoot: createConfigRoot(nodeTypes), ConfigApisProperties: createConfigApisProperties(nodeTypes), AssertionDefinitionSubject: createAssertionDefinitionSubject(nodeNames) }), nodeTypes);
903
+ }
905
904
  exports.createConfigTypes = createConfigTypes;
906
905
  const CoreConfigTypes = {
907
906
  Assert,
@@ -964,6 +963,5 @@ const CoreConfigTypes = {
964
963
  Heading,
965
964
  Typography,
966
965
  AssertionDefinitionAssertions,
967
- AssertionDefinitionSubject,
968
966
  };
969
- exports.ConfigTypes = (0, exports.createConfigTypes)(config_1.rootRedoclyConfigSchema);
967
+ exports.ConfigTypes = createConfigTypes(config_1.rootRedoclyConfigSchema);
package/lib/utils.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { UserContext } from './walk';
2
2
  import { HttpResolveConfig } from './config';
3
+ import { HttpsProxyAgent } from 'https-proxy-agent';
3
4
  export { parseYaml, stringifyYaml } from './js-yaml';
4
5
  export type StackFrame<T> = {
5
6
  prev: StackFrame<T> | null;
@@ -51,3 +52,4 @@ export declare function keysOf<T>(obj: T): (keyof T)[];
51
52
  export declare function pickDefined<T extends Record<string, unknown>>(obj?: T): Record<string, unknown> | undefined;
52
53
  export declare function nextTick(): void;
53
54
  export declare function pause(ms: number): Promise<void>;
55
+ export declare function getProxyAgent(): HttpsProxyAgent<string> | undefined;
package/lib/utils.js CHANGED
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.pause = exports.nextTick = exports.pickDefined = exports.keysOf = exports.identity = exports.isTruthy = exports.showErrorForDeprecatedField = exports.showWarningForDeprecatedField = exports.doesYamlFileExist = exports.isCustomRuleId = exports.getMatchingStatusCodeRange = exports.assignExisting = exports.isNotString = exports.isString = exports.isNotEmptyObject = exports.slash = exports.isPathParameter = exports.yamlAndJsonSyncReader = exports.readFileAsStringSync = exports.isSingular = exports.validateMimeTypeOAS3 = exports.validateMimeType = exports.splitCamelCaseIntoWords = exports.omitObjectProps = exports.pickObjectProps = exports.readFileFromUrl = exports.isEmptyArray = exports.isEmptyObject = exports.isPlainObject = exports.isDefined = exports.loadYaml = exports.popStack = exports.pushStack = exports.stringifyYaml = exports.parseYaml = void 0;
12
+ exports.getProxyAgent = exports.pause = exports.nextTick = exports.pickDefined = exports.keysOf = exports.identity = exports.isTruthy = exports.showErrorForDeprecatedField = exports.showWarningForDeprecatedField = exports.doesYamlFileExist = exports.isCustomRuleId = exports.getMatchingStatusCodeRange = exports.assignExisting = exports.isNotString = exports.isString = exports.isNotEmptyObject = exports.slash = exports.isPathParameter = exports.yamlAndJsonSyncReader = exports.readFileAsStringSync = exports.isSingular = exports.validateMimeTypeOAS3 = exports.validateMimeType = exports.splitCamelCaseIntoWords = exports.omitObjectProps = exports.pickObjectProps = exports.readFileFromUrl = exports.isEmptyArray = exports.isEmptyObject = exports.isPlainObject = exports.isDefined = exports.loadYaml = exports.popStack = exports.pushStack = exports.stringifyYaml = exports.parseYaml = void 0;
13
13
  const fs = require("fs");
14
14
  const path_1 = require("path");
15
15
  const minimatch = require("minimatch");
@@ -18,6 +18,7 @@ const pluralize = require("pluralize");
18
18
  const js_yaml_1 = require("./js-yaml");
19
19
  const env_1 = require("./env");
20
20
  const logger_1 = require("./logger");
21
+ const https_proxy_agent_1 = require("https-proxy-agent");
21
22
  var js_yaml_2 = require("./js-yaml");
22
23
  Object.defineProperty(exports, "parseYaml", { enumerable: true, get: function () { return js_yaml_2.parseYaml; } });
23
24
  Object.defineProperty(exports, "stringifyYaml", { enumerable: true, get: function () { return js_yaml_2.stringifyYaml; } });
@@ -246,3 +247,8 @@ exports.pause = pause;
246
247
  function getUpdatedFieldName(updatedField, updatedObject) {
247
248
  return `${typeof updatedObject !== 'undefined' ? `${updatedObject}.` : ''}${updatedField}`;
248
249
  }
250
+ function getProxyAgent() {
251
+ const proxy = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
252
+ return proxy ? new https_proxy_agent_1.HttpsProxyAgent(proxy) : undefined;
253
+ }
254
+ exports.getProxyAgent = getProxyAgent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/openapi-core",
3
- "version": "1.15.0",
3
+ "version": "1.17.0",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "engines": {
@@ -18,7 +18,8 @@
18
18
  "path": "path-browserify",
19
19
  "os": false,
20
20
  "node-fetch": false,
21
- "colorette": false
21
+ "colorette": false,
22
+ "https-proxy-agent": false
22
23
  },
23
24
  "homepage": "https://github.com/Redocly/redocly-cli",
24
25
  "keywords": [
@@ -37,6 +38,7 @@
37
38
  "@redocly/ajv": "^8.11.0",
38
39
  "@redocly/config": "^0.6.0",
39
40
  "colorette": "^1.2.0",
41
+ "https-proxy-agent": "^7.0.4",
40
42
  "js-levenshtein": "^1.1.6",
41
43
  "js-yaml": "^4.1.0",
42
44
  "lodash.isequal": "^4.5.0",
@@ -3,7 +3,7 @@ import { outdent } from 'outdent';
3
3
 
4
4
  import { lintFromString, lintConfig, lintDocument, lint } from '../lint';
5
5
  import { BaseResolver } from '../resolve';
6
- import { loadConfig } from '../config/load';
6
+ import { createConfig, loadConfig } from '../config/load';
7
7
  import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../__tests__/utils';
8
8
  import { detectSpec } from '../oas-types';
9
9
  import { rootRedoclyConfigSchema } from '@redocly/config';
@@ -293,7 +293,7 @@ describe('lint', () => {
293
293
  - url: http://redocly-example.com
294
294
  paths: {}
295
295
  `,
296
- config: await loadConfig(),
296
+ config: await loadConfig({ configPath: path.join(__dirname, 'fixtures/redocly.yaml') }),
297
297
  });
298
298
 
299
299
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
@@ -374,7 +374,8 @@ describe('lint', () => {
374
374
  `,
375
375
  ''
376
376
  );
377
- const results = await lintConfig({ document });
377
+ const config = await createConfig({});
378
+ const results = await lintConfig({ document, config });
378
379
 
379
380
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
380
381
  [
@@ -435,7 +436,8 @@ describe('lint', () => {
435
436
  `,
436
437
  ''
437
438
  );
438
- const results = await lintConfig({ document });
439
+ const config = await createConfig({});
440
+ const results = await lintConfig({ document, config });
439
441
 
440
442
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
441
443
  [
@@ -475,7 +477,8 @@ describe('lint', () => {
475
477
  `,
476
478
  ''
477
479
  );
478
- const results = await lintConfig({ document });
480
+ const config = await createConfig({});
481
+ const results = await lintConfig({ document, config });
479
482
 
480
483
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
481
484
  [
@@ -510,7 +513,8 @@ describe('lint', () => {
510
513
  `,
511
514
  ''
512
515
  );
513
- const results = await lintConfig({ document });
516
+ const config = await createConfig({});
517
+ const results = await lintConfig({ document, config });
514
518
 
515
519
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
516
520
  [
@@ -534,7 +538,8 @@ describe('lint', () => {
534
538
 
535
539
  it('lintConfig should detect wrong fields in the default configuration after merging with the portal config schema', async () => {
536
540
  const document = testPortalConfig;
537
- const results = await lintConfig({ document });
541
+ const config = await createConfig({});
542
+ const results = await lintConfig({ document, config });
538
543
 
539
544
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
540
545
  [
@@ -1165,13 +1170,18 @@ describe('lint', () => {
1165
1170
 
1166
1171
  it('lintConfig should alternate its behavior when supplied externalConfigTypes', async () => {
1167
1172
  const document = testPortalConfig;
1173
+ const config = await createConfig({});
1168
1174
  const results = await lintConfig({
1169
1175
  document,
1170
- externalConfigTypes: createConfigTypes({
1171
- type: 'object',
1172
- properties: { theme: rootRedoclyConfigSchema.properties.theme },
1173
- additionalProperties: false,
1174
- }),
1176
+ externalConfigTypes: createConfigTypes(
1177
+ {
1178
+ type: 'object',
1179
+ properties: { theme: rootRedoclyConfigSchema.properties.theme },
1180
+ additionalProperties: false,
1181
+ },
1182
+ config
1183
+ ),
1184
+ config,
1175
1185
  });
1176
1186
 
1177
1187
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
package/src/bundle.ts CHANGED
@@ -14,7 +14,7 @@ import { isAbsoluteUrl, isRef, Location, refBaseName } from './ref-utils';
14
14
  import { initRules } from './config/rules';
15
15
  import { reportUnresolvedRef } from './rules/no-unresolved-refs';
16
16
  import { isPlainObject, isTruthy } from './utils';
17
- import { isRedoclyRegistryURL } from './redocly';
17
+ import { isRedoclyRegistryURL } from './redocly/domains';
18
18
  import { RemoveUnusedComponents as RemoveUnusedComponentsOas2 } from './decorators/oas2/remove-unused-components';
19
19
  import { RemoveUnusedComponents as RemoveUnusedComponentsOas3 } from './decorators/oas3/remove-unused-components';
20
20
  import { ConfigTypes } from './types/redocly-yaml';
@@ -58,6 +58,88 @@ describe('loadConfig', () => {
58
58
  expect(mockFn).toHaveBeenCalled();
59
59
  });
60
60
 
61
+ it('should resolve config and call processRawConfig', async () => {
62
+ let problems: NormalizedProblem[];
63
+ let doc: any;
64
+
65
+ await loadConfig({
66
+ configPath: path.join(__dirname, './fixtures/resolve-refs-in-config/config-with-refs.yaml'),
67
+ processRawConfig: async ({ document, parsed, resolvedRefMap, config }) => {
68
+ doc = parsed;
69
+ problems = await lintConfig({
70
+ document,
71
+ severity: 'warn',
72
+ resolvedRefMap,
73
+ config,
74
+ });
75
+ },
76
+ });
77
+
78
+ expect(replaceSourceWithRef(problems!, __dirname)).toMatchInlineSnapshot(`
79
+ [
80
+ {
81
+ "from": {
82
+ "pointer": "#/seo",
83
+ "source": "fixtures/resolve-refs-in-config/config-with-refs.yaml",
84
+ },
85
+ "location": [
86
+ {
87
+ "pointer": "#/title",
88
+ "reportOnKey": false,
89
+ "source": "fixtures/resolve-refs-in-config/seo.yaml",
90
+ },
91
+ ],
92
+ "message": "Expected type \`string\` but got \`integer\`.",
93
+ "ruleId": "configuration spec",
94
+ "severity": "warn",
95
+ "suggest": [],
96
+ },
97
+ {
98
+ "from": {
99
+ "pointer": "#/rules",
100
+ "source": "fixtures/resolve-refs-in-config/config-with-refs.yaml",
101
+ },
102
+ "location": [
103
+ {
104
+ "pointer": "#/non-existing-rule",
105
+ "reportOnKey": true,
106
+ "source": "fixtures/resolve-refs-in-config/rules.yaml",
107
+ },
108
+ ],
109
+ "message": "Property \`non-existing-rule\` is not expected here.",
110
+ "ruleId": "configuration spec",
111
+ "severity": "warn",
112
+ "suggest": [],
113
+ },
114
+ {
115
+ "location": [
116
+ {
117
+ "pointer": "#/theme",
118
+ "reportOnKey": false,
119
+ "source": "fixtures/resolve-refs-in-config/config-with-refs.yaml",
120
+ },
121
+ ],
122
+ "message": "Can't resolve $ref: ENOENT: no such file or directory 'fixtures/resolve-refs-in-config/wrong-ref.yaml'",
123
+ "ruleId": "configuration no-unresolved-refs",
124
+ "severity": "warn",
125
+ "suggest": [],
126
+ },
127
+ ]
128
+ `);
129
+ expect(doc).toMatchInlineSnapshot(`
130
+ {
131
+ "rules": {
132
+ "info-license": "error",
133
+ "non-existing-rule": "warn",
134
+ },
135
+ "seo": {
136
+ "title": 1,
137
+ },
138
+ "theme": undefined,
139
+ }
140
+ `);
141
+ });
142
+
61
143
  it('should call externalRefResolver if such passed', async () => {
62
144
  const externalRefResolver = new BaseResolver();
63
145
  const resolverSpy = jest.spyOn(externalRefResolver, 'resolveDocument');
@@ -104,22 +186,16 @@ describe('findConfig', () => {
104
186
  describe('getConfig', () => {
105
187
  jest.spyOn(fs, 'hasOwnProperty').mockImplementation(() => false);
106
188
  it('should return empty object if there is no configPath and config file is not found', () => {
107
- expect(getConfig()).toEqual(Promise.resolve({}));
189
+ expect(getConfig()).toEqual(Promise.resolve({ rawConfig: {} }));
108
190
  });
109
191
 
110
192
  it('should resolve refs in config', async () => {
111
193
  let problems: NormalizedProblem[];
112
- const result = await getConfig({
194
+
195
+ const { rawConfig } = await getConfig({
113
196
  configPath: path.join(__dirname, './fixtures/resolve-refs-in-config/config-with-refs.yaml'),
114
- processRawConfig: async (config, resolvedRefMap) => {
115
- problems = await lintConfig({
116
- document: config,
117
- severity: 'warn',
118
- resolvedRefMap,
119
- });
120
- },
121
197
  });
122
- expect(result).toEqual({
198
+ expect(rawConfig).toEqual({
123
199
  seo: {
124
200
  title: 1,
125
201
  },
@@ -130,57 +206,6 @@ describe('getConfig', () => {
130
206
  },
131
207
  },
132
208
  });
133
- expect(replaceSourceWithRef(problems!, __dirname)).toMatchInlineSnapshot(`
134
- [
135
- {
136
- "from": {
137
- "pointer": "#/seo",
138
- "source": "fixtures/resolve-refs-in-config/config-with-refs.yaml",
139
- },
140
- "location": [
141
- {
142
- "pointer": "#/title",
143
- "reportOnKey": false,
144
- "source": "fixtures/resolve-refs-in-config/seo.yaml",
145
- },
146
- ],
147
- "message": "Expected type \`string\` but got \`integer\`.",
148
- "ruleId": "configuration spec",
149
- "severity": "warn",
150
- "suggest": [],
151
- },
152
- {
153
- "from": {
154
- "pointer": "#/rules",
155
- "source": "fixtures/resolve-refs-in-config/config-with-refs.yaml",
156
- },
157
- "location": [
158
- {
159
- "pointer": "#/non-existing-rule",
160
- "reportOnKey": true,
161
- "source": "fixtures/resolve-refs-in-config/rules.yaml",
162
- },
163
- ],
164
- "message": "Property \`non-existing-rule\` is not expected here.",
165
- "ruleId": "configuration spec",
166
- "severity": "warn",
167
- "suggest": [],
168
- },
169
- {
170
- "location": [
171
- {
172
- "pointer": "#/theme",
173
- "reportOnKey": false,
174
- "source": "fixtures/resolve-refs-in-config/config-with-refs.yaml",
175
- },
176
- ],
177
- "message": "Can't resolve $ref: ENOENT: no such file or directory 'fixtures/resolve-refs-in-config/wrong-ref.yaml'",
178
- "ruleId": "configuration no-unresolved-refs",
179
- "severity": "warn",
180
- "suggest": [],
181
- },
182
- ]
183
- `);
184
209
  });
185
210
  });
186
211
 
@@ -10,7 +10,7 @@ import {
10
10
  Oas3RuleSet,
11
11
  Async2RuleSet,
12
12
  } from '../oas-types';
13
- import { isBrowser, env } from '../env';
13
+ import { isBrowser } from '../env';
14
14
 
15
15
  import type { NodeType } from '../types';
16
16
  import type {
@@ -35,25 +35,6 @@ const IGNORE_BANNER =
35
35
  `# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API.\n` +
36
36
  `# See https://redoc.ly/docs/cli/ for more information.\n`;
37
37
 
38
- export const DEFAULT_REGION = 'us';
39
-
40
- function getDomains() {
41
- const domains: { [region in Region]: string } = {
42
- us: 'redocly.com',
43
- eu: 'eu.redocly.com',
44
- };
45
-
46
- // FIXME: temporary fix for our lab environments
47
- const domain = env.REDOCLY_DOMAIN;
48
- if (domain?.endsWith('.redocly.host')) {
49
- domains[domain.split('.')[0] as Region] = domain;
50
- }
51
- if (domain === 'redoc.online') {
52
- domains[domain as Region] = domain;
53
- }
54
- return domains;
55
- }
56
-
57
38
  function getIgnoreFilePath(configFile?: string): string | undefined {
58
39
  if (configFile) {
59
40
  return doesYamlFileExist(configFile)
@@ -64,9 +45,6 @@ function getIgnoreFilePath(configFile?: string): string | undefined {
64
45
  }
65
46
  }
66
47
 
67
- export const DOMAINS = getDomains();
68
- export const AVAILABLE_REGIONS = Object.keys(DOMAINS) as Region[];
69
-
70
48
  export class StyleguideConfig {
71
49
  plugins: Plugin[];
72
50
  ignore: Record<string, Record<string, Set<string>>> = {};
@@ -3,8 +3,8 @@ import * as path from 'path';
3
3
  import { RedoclyClient } from '../redocly';
4
4
  import { isEmptyObject } from '../utils';
5
5
  import { parseYaml } from '../js-yaml';
6
- import { Config, DOMAINS } from './config';
7
- import { ConfigValidationError, transformConfig } from './utils';
6
+ import { Config } from './config';
7
+ import { ConfigValidationError, transformConfig, deepCloneMapWithJSON } from './utils';
8
8
  import { resolveConfig, resolveConfigFileAndRefs } from './config-resolvers';
9
9
  import { bundleConfig } from '../bundle';
10
10
  import { BaseResolver } from '../resolve';
@@ -14,6 +14,7 @@ import type { Document } from '../resolve';
14
14
  import type { RegionalToken, RegionalTokenWithValidity } from '../redocly/redocly-client-types';
15
15
  import type { RawConfig, RawUniversalConfig, Region } from './types';
16
16
  import type { ResolvedRefMap } from '../resolve';
17
+ import { DOMAINS } from '../redocly/domains';
17
18
 
18
19
  async function addConfigMetadata({
19
20
  rawConfig,
@@ -79,10 +80,12 @@ async function addConfigMetadata({
79
80
  });
80
81
  }
81
82
 
82
- export type RawConfigProcessor = (
83
- rawConfig: Document,
84
- resolvedRefMap: ResolvedRefMap
85
- ) => void | Promise<void>;
83
+ export type RawConfigProcessor = (params: {
84
+ document: Document;
85
+ resolvedRefMap: ResolvedRefMap;
86
+ config: Config;
87
+ parsed: Document['parsed'];
88
+ }) => void | Promise<void>;
86
89
 
87
90
  export async function loadConfig(
88
91
  options: {
@@ -102,12 +105,16 @@ export async function loadConfig(
102
105
  region,
103
106
  externalRefResolver,
104
107
  } = options;
105
- const rawConfig = await getConfig({ configPath, processRawConfig, externalRefResolver });
108
+
109
+ const { rawConfig, document, parsed, resolvedRefMap } = await getConfig({
110
+ configPath,
111
+ externalRefResolver,
112
+ });
106
113
 
107
114
  const redoclyClient = isBrowser ? undefined : new RedoclyClient();
108
115
  const tokens = redoclyClient && redoclyClient.hasTokens() ? redoclyClient.getAllTokens() : [];
109
116
 
110
- return addConfigMetadata({
117
+ const config = await addConfigMetadata({
111
118
  rawConfig,
112
119
  customExtends,
113
120
  configPath,
@@ -116,6 +123,24 @@ export async function loadConfig(
116
123
  region,
117
124
  externalRefResolver,
118
125
  });
126
+
127
+ if (document && parsed && resolvedRefMap && typeof processRawConfig === 'function') {
128
+ try {
129
+ await processRawConfig({
130
+ document,
131
+ resolvedRefMap,
132
+ config,
133
+ parsed,
134
+ });
135
+ } catch (e) {
136
+ if (e instanceof ConfigValidationError) {
137
+ throw e;
138
+ }
139
+ throw new Error(`Error parsing config file at '${configPath}': ${e.message}`);
140
+ }
141
+ }
142
+
143
+ return config;
119
144
  }
120
145
 
121
146
  export const CONFIG_FILE_NAMES = ['redocly.yaml', 'redocly.yml', '.redocly.yaml', '.redocly.yml'];
@@ -138,31 +163,33 @@ export function findConfig(dir?: string): string | undefined {
138
163
  export async function getConfig(
139
164
  options: {
140
165
  configPath?: string;
141
- processRawConfig?: RawConfigProcessor;
142
166
  externalRefResolver?: BaseResolver;
143
167
  } = {}
144
- ): Promise<RawConfig> {
145
- const {
146
- configPath = findConfig(),
147
- processRawConfig,
148
- externalRefResolver = new BaseResolver(),
149
- } = options;
150
- if (!configPath) return {};
168
+ ): Promise<{
169
+ rawConfig: RawConfig;
170
+ document?: Document;
171
+ parsed?: Document['parsed'];
172
+ resolvedRefMap?: ResolvedRefMap;
173
+ }> {
174
+ const { configPath = findConfig(), externalRefResolver = new BaseResolver() } = options;
175
+ if (!configPath) return { rawConfig: {} };
151
176
 
152
177
  try {
153
178
  const { document, resolvedRefMap } = await resolveConfigFileAndRefs({
154
179
  configPath,
155
180
  externalRefResolver,
156
181
  });
157
- if (typeof processRawConfig === 'function') {
158
- await processRawConfig(document, resolvedRefMap);
159
- }
160
- const bundledConfig = await bundleConfig(document, resolvedRefMap);
161
- return transformConfig(bundledConfig);
182
+
183
+ const bundledRefMap = deepCloneMapWithJSON(resolvedRefMap);
184
+ const parsed = await bundleConfig(JSON.parse(JSON.stringify(document)), bundledRefMap);
185
+
186
+ return {
187
+ rawConfig: transformConfig(parsed),
188
+ document,
189
+ parsed,
190
+ resolvedRefMap,
191
+ };
162
192
  } catch (e) {
163
- if (e instanceof ConfigValidationError) {
164
- throw e;
165
- }
166
193
  throw new Error(`Error parsing config file at '${configPath}': ${e.message}`);
167
194
  }
168
195
  }
@@ -364,3 +364,7 @@ export function getUniquePlugins(plugins: Plugin[]): Plugin[] {
364
364
  }
365
365
 
366
366
  export class ConfigValidationError extends Error {}
367
+
368
+ export function deepCloneMapWithJSON<K, V>(originalMap: Map<K, V>): Map<K, V> {
369
+ return new Map(JSON.parse(JSON.stringify([...originalMap])));
370
+ }
@@ -1,5 +1,5 @@
1
1
  import { UserContext } from '../../walk';
2
- import { isRedoclyRegistryURL } from '../../redocly';
2
+ import { isRedoclyRegistryURL } from '../../redocly/domains';
3
3
 
4
4
  import { Oas3Decorator, Oas2Decorator } from '../../visitors';
5
5