@redocly/openapi-core 1.23.1 → 1.25.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 (43) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/lib/config/all.js +3 -0
  3. package/lib/config/config-resolvers.d.ts +1 -1
  4. package/lib/config/config-resolvers.js +42 -26
  5. package/lib/config/config.js +4 -1
  6. package/lib/config/minimal.js +3 -0
  7. package/lib/config/recommended-strict.js +3 -0
  8. package/lib/config/recommended.js +3 -0
  9. package/lib/config/types.d.ts +10 -0
  10. package/lib/rules/arazzo/criteria-unique.d.ts +2 -0
  11. package/lib/rules/arazzo/criteria-unique.js +65 -0
  12. package/lib/rules/arazzo/index.js +6 -0
  13. package/lib/rules/spot/no-actions-type-end.d.ts +2 -0
  14. package/lib/rules/spot/no-actions-type-end.js +28 -0
  15. package/lib/rules/spot/no-criteria-xpath.d.ts +2 -0
  16. package/lib/rules/spot/no-criteria-xpath.js +21 -0
  17. package/lib/types/arazzo.js +1 -1
  18. package/lib/types/redocly-yaml.d.ts +1 -1
  19. package/lib/types/redocly-yaml.js +3 -0
  20. package/package.json +2 -2
  21. package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +6 -0
  22. package/src/config/__tests__/config-resolvers.test.ts +54 -0
  23. package/src/config/__tests__/fixtures/resolve-config/local-config-with-plugin-init.yaml +2 -0
  24. package/src/config/__tests__/fixtures/resolve-config/local-config-with-realm-plugin.yaml +2 -0
  25. package/src/config/__tests__/fixtures/resolve-config/plugin-with-init-logic.js +9 -0
  26. package/src/config/__tests__/fixtures/resolve-config/realm-plugin.js +12 -0
  27. package/src/config/all.ts +3 -0
  28. package/src/config/config-resolvers.ts +52 -29
  29. package/src/config/config.ts +4 -1
  30. package/src/config/minimal.ts +3 -0
  31. package/src/config/recommended-strict.ts +3 -0
  32. package/src/config/recommended.ts +3 -0
  33. package/src/config/types.ts +16 -0
  34. package/src/rules/arazzo/__tests__/criteria-unique.test.ts +161 -0
  35. package/src/rules/arazzo/__tests__/no-actions-type-end.test.ts +122 -0
  36. package/src/rules/arazzo/__tests__/no-criteria-xpath.test.ts +127 -0
  37. package/src/rules/arazzo/criteria-unique.ts +63 -0
  38. package/src/rules/arazzo/index.ts +6 -0
  39. package/src/rules/spot/no-actions-type-end.ts +27 -0
  40. package/src/rules/spot/no-criteria-xpath.ts +20 -0
  41. package/src/types/arazzo.ts +1 -1
  42. package/src/types/redocly-yaml.ts +3 -0
  43. package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @redocly/openapi-core
2
2
 
3
+ ## 1.25.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added a mechanism that resolves plugin properties specific to the Reunite-hosted product family.
8
+ - Added a cache for resolved plugins to ensure that plugins are only instantiated once during a single execution.
9
+
10
+ ## 1.24.0
11
+
12
+ ### Minor Changes
13
+
14
+ - Added Spot and Arazzo rules: `no-criteria-xpath`, `no-actions-type-end`, `criteria-unique`.
15
+
16
+ ### Patch Changes
17
+
18
+ - Updated @redocly/ajv to v8.11.2.
19
+ - Fixed an issue where custom rules were not applied to Arazzo descriptions.
20
+
3
21
  ## 1.23.1
4
22
 
5
23
  ## 1.23.0
package/lib/config/all.js CHANGED
@@ -139,6 +139,9 @@ const all = {
139
139
  'step-onSuccess-unique': 'error',
140
140
  'step-onFailure-unique': 'error',
141
141
  'requestBody-replacements-unique': 'error',
142
+ 'no-criteria-xpath': 'error',
143
+ 'no-actions-type-end': 'error',
144
+ 'criteria-unique': 'error',
142
145
  },
143
146
  };
144
147
  exports.default = all;
@@ -14,7 +14,7 @@ export declare function resolveConfig({ rawConfig, configPath, externalRefResolv
14
14
  configPath?: string;
15
15
  externalRefResolver?: BaseResolver;
16
16
  }): Promise<Config>;
17
- export declare function resolvePlugins(plugins: (string | Plugin)[] | null, configPath?: string): Promise<Plugin[]>;
17
+ export declare function resolvePlugins(plugins: (string | Plugin)[] | null, configDir?: string): Promise<Plugin[]>;
18
18
  export declare function resolveApis({ rawConfig, configPath, resolver, }: {
19
19
  rawConfig: RawConfig;
20
20
  configPath?: string;
@@ -23,6 +23,8 @@ const redocly_yaml_1 = require("../types/redocly-yaml");
23
23
  const DEFAULT_PROJECT_PLUGIN_PATHS = ['@theme/plugin.js', '@theme/plugin.cjs', '@theme/plugin.mjs'];
24
24
  // Workaround for dynamic imports being transpiled to require by Typescript: https://github.com/microsoft/TypeScript/issues/43329#issuecomment-811606238
25
25
  const _importDynamic = new Function('modulePath', 'return import(modulePath)');
26
+ // Cache instantiated plugins during a single execution
27
+ const pluginsCache = new Map();
26
28
  async function resolveConfigFileAndRefs({ configPath, externalRefResolver = new resolve_1.BaseResolver(), base = null, }) {
27
29
  if (!configPath) {
28
30
  throw new Error('Reference to a config is required.\n');
@@ -60,39 +62,59 @@ async function resolveConfig({ rawConfig, configPath, externalRefResolver, }) {
60
62
  styleguide,
61
63
  }, configPath);
62
64
  }
63
- function getDefaultPluginPath(configPath) {
65
+ function getDefaultPluginPath(configDir) {
64
66
  for (const pluginPath of DEFAULT_PROJECT_PLUGIN_PATHS) {
65
- const absolutePluginPath = path.resolve(path.dirname(configPath), pluginPath);
67
+ const absolutePluginPath = path.resolve(configDir, pluginPath);
66
68
  if ((0, fs_1.existsSync)(absolutePluginPath)) {
67
69
  return pluginPath;
68
70
  }
69
71
  }
70
72
  return;
71
73
  }
72
- async function resolvePlugins(plugins, configPath = '') {
74
+ async function resolvePlugins(plugins, configDir = '') {
73
75
  if (!plugins)
74
76
  return [];
75
77
  // TODO: implement or reuse Resolver approach so it will work in node and browser envs
76
78
  const requireFunc = async (plugin) => {
77
79
  if ((0, utils_1.isString)(plugin)) {
78
80
  try {
79
- const maybeAbsolutePluginPath = path.resolve(path.dirname(configPath), plugin);
81
+ const maybeAbsolutePluginPath = path.resolve(configDir, plugin);
80
82
  const absolutePluginPath = (0, fs_1.existsSync)(maybeAbsolutePluginPath)
81
83
  ? maybeAbsolutePluginPath
82
84
  : // For plugins imported from packages specifically
83
85
  require.resolve(plugin);
84
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
85
- // @ts-ignore
86
- if (typeof __webpack_require__ === 'function') {
86
+ if (!pluginsCache.has(absolutePluginPath)) {
87
+ let requiredPlugin;
87
88
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
88
89
  // @ts-ignore
89
- return __non_webpack_require__(absolutePluginPath);
90
- }
91
- else {
92
- // you can import both cjs and mjs
93
- const mod = await _importDynamic((0, url_1.pathToFileURL)(absolutePluginPath).href);
94
- return mod.default || mod;
90
+ if (typeof __webpack_require__ === 'function') {
91
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
92
+ // @ts-ignore
93
+ requiredPlugin = __non_webpack_require__(absolutePluginPath);
94
+ }
95
+ else {
96
+ // you can import both cjs and mjs
97
+ const mod = await _importDynamic((0, url_1.pathToFileURL)(absolutePluginPath).href);
98
+ requiredPlugin = mod.default || mod;
99
+ }
100
+ const pluginCreatorOptions = { contentDir: configDir };
101
+ const pluginModule = (0, utils_2.isDeprecatedPluginFormat)(requiredPlugin)
102
+ ? requiredPlugin
103
+ : (0, utils_2.isCommonJsPlugin)(requiredPlugin)
104
+ ? await requiredPlugin(pluginCreatorOptions)
105
+ : await requiredPlugin?.default?.(pluginCreatorOptions);
106
+ if (pluginModule?.id && (0, utils_2.isDeprecatedPluginFormat)(requiredPlugin)) {
107
+ logger_1.logger.info(`Deprecated plugin format detected: ${pluginModule.id}\n`);
108
+ }
109
+ if (pluginModule) {
110
+ pluginsCache.set(absolutePluginPath, {
111
+ ...pluginModule,
112
+ path: plugin,
113
+ absolutePath: absolutePluginPath,
114
+ });
115
+ }
95
116
  }
117
+ return pluginsCache.get(absolutePluginPath);
96
118
  }
97
119
  catch (e) {
98
120
  throw new Error(`Failed to load plugin "${plugin}": ${e.message}\n\n${e.stack}`);
@@ -104,7 +126,7 @@ async function resolvePlugins(plugins, configPath = '') {
104
126
  /**
105
127
  * Include the default plugin automatically if it's not in configuration
106
128
  */
107
- const defaultPluginPath = getDefaultPluginPath(configPath);
129
+ const defaultPluginPath = getDefaultPluginPath(configDir);
108
130
  if (defaultPluginPath) {
109
131
  plugins.push(defaultPluginPath);
110
132
  }
@@ -119,19 +141,10 @@ async function resolvePlugins(plugins, configPath = '') {
119
141
  }
120
142
  resolvedPlugins.add(p);
121
143
  }
122
- const requiredPlugin = await requireFunc(p);
123
- const pluginCreatorOptions = { contentDir: path.dirname(configPath) };
124
- const pluginModule = (0, utils_2.isDeprecatedPluginFormat)(requiredPlugin)
125
- ? requiredPlugin
126
- : (0, utils_2.isCommonJsPlugin)(requiredPlugin)
127
- ? await requiredPlugin(pluginCreatorOptions)
128
- : await requiredPlugin?.default?.(pluginCreatorOptions);
144
+ const pluginModule = await requireFunc(p);
129
145
  if (!pluginModule) {
130
146
  return;
131
147
  }
132
- if ((0, utils_1.isString)(p) && pluginModule.id && (0, utils_2.isDeprecatedPluginFormat)(requiredPlugin)) {
133
- logger_1.logger.info(`Deprecated plugin format detected: ${pluginModule.id}\n`);
134
- }
135
148
  const id = pluginModule.id;
136
149
  if (typeof id !== 'string') {
137
150
  throw new Error(logger_1.colorize.red(`Plugin must define \`id\` property in ${logger_1.colorize.blue(p.toString())}.`));
@@ -220,7 +233,10 @@ async function resolvePlugins(plugins, configPath = '') {
220
233
  if (pluginModule.assertions) {
221
234
  plugin.assertions = pluginModule.assertions;
222
235
  }
223
- return plugin;
236
+ return {
237
+ ...pluginModule,
238
+ ...plugin,
239
+ };
224
240
  }));
225
241
  return instances.filter(utils_1.isDefined);
226
242
  }
@@ -248,7 +264,7 @@ async function resolveAndMergeNestedStyleguideConfig({ styleguideConfig, configP
248
264
  const plugins = env_1.isBrowser
249
265
  ? // In browser, we don't support plugins from config file yet
250
266
  [builtIn_1.defaultPlugin]
251
- : (0, utils_2.getUniquePlugins)(await resolvePlugins([...(styleguideConfig?.plugins || []), builtIn_1.defaultPlugin], configPath));
267
+ : (0, utils_2.getUniquePlugins)(await resolvePlugins([...(styleguideConfig?.plugins || []), builtIn_1.defaultPlugin], path.dirname(configPath)));
252
268
  const pluginPaths = styleguideConfig?.plugins
253
269
  ?.filter(utils_1.isString)
254
270
  .map((p) => path.resolve(path.dirname(configPath), p));
@@ -38,7 +38,10 @@ class StyleguideConfig {
38
38
  [oas_types_1.SpecVersion.OAS3_1]: { ...rawConfig.rules, ...rawConfig.oas3_1Rules },
39
39
  [oas_types_1.SpecVersion.Async2]: { ...rawConfig.rules, ...rawConfig.async2Rules },
40
40
  [oas_types_1.SpecVersion.Async3]: { ...rawConfig.rules, ...rawConfig.async3Rules },
41
- [oas_types_1.SpecVersion.Arazzo]: { ...rawConfig.arazzoRules },
41
+ [oas_types_1.SpecVersion.Arazzo]: {
42
+ ...rawConfig.arazzoRules,
43
+ ...(rawConfig.rules?.assertions ? { assertions: rawConfig.rules.assertions } : {}),
44
+ },
42
45
  };
43
46
  this.preprocessors = {
44
47
  [oas_types_1.SpecVersion.OAS2]: { ...rawConfig.preprocessors, ...rawConfig.oas2Preprocessors },
@@ -121,6 +121,9 @@ const minimal = {
121
121
  'step-onSuccess-unique': 'off',
122
122
  'step-onFailure-unique': 'off',
123
123
  'requestBody-replacements-unique': 'off',
124
+ 'no-criteria-xpath': 'off',
125
+ 'no-actions-type-end': 'off',
126
+ 'criteria-unique': 'off',
124
127
  },
125
128
  };
126
129
  exports.default = minimal;
@@ -121,6 +121,9 @@ const recommendedStrict = {
121
121
  'step-onSuccess-unique': 'error',
122
122
  'step-onFailure-unique': 'error',
123
123
  'requestBody-replacements-unique': 'error',
124
+ 'no-criteria-xpath': 'error',
125
+ 'no-actions-type-end': 'error',
126
+ 'criteria-unique': 'error',
124
127
  },
125
128
  };
126
129
  exports.default = recommendedStrict;
@@ -121,6 +121,9 @@ const recommended = {
121
121
  'step-onSuccess-unique': 'warn',
122
122
  'step-onFailure-unique': 'warn',
123
123
  'requestBody-replacements-unique': 'warn',
124
+ 'no-criteria-xpath': 'warn',
125
+ 'no-actions-type-end': 'warn',
126
+ 'criteria-unique': 'warn',
124
127
  },
125
128
  };
126
129
  exports.default = recommended;
@@ -4,6 +4,7 @@ import type { Oas3PreprocessorsSet, SpecMajorVersion, Oas3DecoratorsSet, Oas2Rul
4
4
  import type { NodeType } from '../types';
5
5
  import type { SkipFunctionContext } from '../visitors';
6
6
  import type { BuiltInAsync2RuleId, BuiltInAsync3RuleId, BuiltInCommonOASRuleId, BuiltInOAS2RuleId, BuiltInOAS3RuleId, BuiltInArazzoRuleId } from '../types/redocly-yaml';
7
+ import type { JSONSchema } from 'json-schema-to-ts';
7
8
  export type RuleSeverity = ProblemSeverity | 'off';
8
9
  export type RuleSettings = {
9
10
  severity: RuleSeverity;
@@ -91,6 +92,15 @@ export type Plugin = {
91
92
  decorators?: DecoratorsConfig;
92
93
  typeExtension?: TypeExtensionsConfig;
93
94
  assertions?: AssertionsConfig;
95
+ path?: string;
96
+ absolutePath?: string;
97
+ processContent?: (actions: any, content: any) => Promise<void> | void;
98
+ afterRoutesCreated?: (actions: any, content: any) => Promise<void> | void;
99
+ loaders?: Record<string, (path: string, context: any, reportError: (error: Error) => void) => Promise<unknown>>;
100
+ requiredEntitlements?: string[];
101
+ ssoConfigSchema?: JSONSchema;
102
+ redoclyConfigSchema?: JSONSchema;
103
+ ejectIgnore?: string[];
94
104
  };
95
105
  type PluginCreatorOptions = {
96
106
  contentDir: string;
@@ -0,0 +1,2 @@
1
+ import type { ArazzoRule } from '../../visitors';
2
+ export declare const CriteriaUnique: ArazzoRule;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CriteriaUnique = void 0;
4
+ const CriteriaUnique = () => {
5
+ return {
6
+ FailureActionObject: {
7
+ enter(action, { report, location }) {
8
+ const criterias = action.criteria;
9
+ const seen = new Set();
10
+ for (const criteria of criterias) {
11
+ const key = JSON.stringify(criteria);
12
+ if (seen.has(key)) {
13
+ report({
14
+ message: 'The FailureAction criteria items must be unique.',
15
+ location: location.child(['criteria', criterias.indexOf(criteria)]),
16
+ });
17
+ }
18
+ else {
19
+ seen.add(key);
20
+ }
21
+ }
22
+ },
23
+ },
24
+ SuccessActionObject: {
25
+ enter(action, { report, location }) {
26
+ const criterias = action.criteria;
27
+ const seen = new Set();
28
+ for (const criteria of criterias) {
29
+ const key = JSON.stringify(criteria);
30
+ if (seen.has(key)) {
31
+ report({
32
+ message: 'The SuccessAction criteria items must be unique.',
33
+ location: location.child(['criteria', criterias.indexOf(criteria)]),
34
+ });
35
+ }
36
+ else {
37
+ seen.add(key);
38
+ }
39
+ }
40
+ },
41
+ },
42
+ Step: {
43
+ enter(step, { report, location }) {
44
+ if (!step.successCriteria) {
45
+ return;
46
+ }
47
+ const successCriterias = step.successCriteria;
48
+ const seen = new Set();
49
+ for (const criteria of successCriterias) {
50
+ const key = JSON.stringify(criteria);
51
+ if (seen.has(key)) {
52
+ report({
53
+ message: 'The Step SuccessCriteria items must be unique.',
54
+ location: location.child(['successCriteria', successCriterias.indexOf(criteria)]),
55
+ });
56
+ }
57
+ else {
58
+ seen.add(key);
59
+ }
60
+ }
61
+ },
62
+ },
63
+ };
64
+ };
65
+ exports.CriteriaUnique = CriteriaUnique;
@@ -14,6 +14,9 @@ const parameters_unique_1 = require("./parameters-unique");
14
14
  const step_onSuccess_unique_1 = require("./step-onSuccess-unique");
15
15
  const step_onFailure_unique_1 = require("./step-onFailure-unique");
16
16
  const requestBody_replacements_unique_1 = require("./requestBody-replacements-unique");
17
+ const no_criteria_xpath_1 = require("../spot/no-criteria-xpath");
18
+ const no_actions_type_end_1 = require("../spot/no-actions-type-end");
19
+ const criteria_unique_1 = require("./criteria-unique");
17
20
  exports.rules = {
18
21
  spec: spec_1.Spec,
19
22
  assertions: assertions_1.Assertions,
@@ -28,5 +31,8 @@ exports.rules = {
28
31
  'step-onSuccess-unique': step_onSuccess_unique_1.StepOnSuccessUnique,
29
32
  'step-onFailure-unique': step_onFailure_unique_1.StepOnFailureUnique,
30
33
  'requestBody-replacements-unique': requestBody_replacements_unique_1.RequestBodyReplacementsUnique,
34
+ 'no-criteria-xpath': no_criteria_xpath_1.NoCriteriaXpath,
35
+ 'no-actions-type-end': no_actions_type_end_1.NoActionsTypeEnd,
36
+ 'criteria-unique': criteria_unique_1.CriteriaUnique,
31
37
  };
32
38
  exports.preprocessors = {};
@@ -0,0 +1,2 @@
1
+ import type { ArazzoRule } from '../../visitors';
2
+ export declare const NoActionsTypeEnd: ArazzoRule;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NoActionsTypeEnd = void 0;
4
+ const NoActionsTypeEnd = () => {
5
+ return {
6
+ FailureActionObject: {
7
+ enter(action, { report, location }) {
8
+ if (action.type === 'end') {
9
+ report({
10
+ message: 'The `end` type action is not supported by Spot.',
11
+ location: location.child(['type']),
12
+ });
13
+ }
14
+ },
15
+ },
16
+ SuccessActionObject: {
17
+ enter(action, { report, location }) {
18
+ if (action.type === 'end') {
19
+ report({
20
+ message: 'The `end` type action is not supported by Spot.',
21
+ location: location.child(['type']),
22
+ });
23
+ }
24
+ },
25
+ },
26
+ };
27
+ };
28
+ exports.NoActionsTypeEnd = NoActionsTypeEnd;
@@ -0,0 +1,2 @@
1
+ import type { ArazzoRule } from '../../visitors';
2
+ export declare const NoCriteriaXpath: ArazzoRule;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NoCriteriaXpath = void 0;
4
+ const NoCriteriaXpath = () => {
5
+ return {
6
+ CriterionObject: {
7
+ enter(criteria, { report, location }) {
8
+ if (!criteria.type) {
9
+ return;
10
+ }
11
+ if (criteria?.type?.type === 'xpath' || criteria?.type === 'xpath') {
12
+ report({
13
+ message: 'The `xpath` type criteria is not supported by Spot.',
14
+ location: location.child(['type']),
15
+ });
16
+ }
17
+ },
18
+ },
19
+ };
20
+ };
21
+ exports.NoCriteriaXpath = NoCriteriaXpath;
@@ -212,7 +212,7 @@ const CriterionObject = {
212
212
  else if (typeof value === 'string') {
213
213
  return { enum: ['regex', 'jsonpath', 'simple', 'xpath'] };
214
214
  }
215
- else if (value.type === 'jsonpath') {
215
+ else if (value?.type === 'jsonpath') {
216
216
  return 'JSONPathCriterion';
217
217
  }
218
218
  else {
@@ -11,7 +11,7 @@ declare const builtInAsync2Rules: readonly ["spec", "info-contact", "info-licens
11
11
  declare const builtInAsync3Rules: readonly ["spec", "info-contact", "info-license-strict", "operation-operationId", "tag-description", "tags-alphabetical", "channels-kebab-case", "no-channel-trailing-slash"];
12
12
  export type BuiltInAsync2RuleId = typeof builtInAsync2Rules[number];
13
13
  export type BuiltInAsync3RuleId = typeof builtInAsync3Rules[number];
14
- declare const builtInArazzoRules: readonly ["spec", "parameters-not-in-body", "sourceDescription-type", "version-enum", "workflowId-unique", "stepId-unique", "sourceDescription-name-unique", "workflow-dependsOn", "parameters-unique", "step-onSuccess-unique", "step-onFailure-unique", "requestBody-replacements-unique"];
14
+ declare const builtInArazzoRules: readonly ["spec", "parameters-not-in-body", "sourceDescription-type", "version-enum", "workflowId-unique", "stepId-unique", "sourceDescription-name-unique", "workflow-dependsOn", "parameters-unique", "step-onSuccess-unique", "step-onFailure-unique", "requestBody-replacements-unique", "no-criteria-xpath", "no-actions-type-end", "criteria-unique"];
15
15
  export type BuiltInArazzoRuleId = typeof builtInArazzoRules[number];
16
16
  declare const oas2NodeTypesList: readonly ["Root", "Tag", "TagList", "ExternalDocs", "SecurityRequirement", "SecurityRequirementList", "Info", "Contact", "License", "Paths", "PathItem", "Parameter", "ParameterList", "ParameterItems", "Operation", "Example", "ExamplesMap", "Examples", "Header", "Responses", "Response", "Schema", "Xml", "SchemaProperties", "NamedSchemas", "NamedResponses", "NamedParameters", "NamedSecuritySchemes", "SecurityScheme", "TagGroup", "TagGroups", "EnumDescriptions", "Logo", "XCodeSample", "XCodeSampleList", "XServer", "XServerList"];
17
17
  export type Oas2NodeType = typeof oas2NodeTypesList[number];
@@ -106,6 +106,9 @@ const builtInArazzoRules = [
106
106
  'step-onSuccess-unique',
107
107
  'step-onFailure-unique',
108
108
  'requestBody-replacements-unique',
109
+ 'no-criteria-xpath',
110
+ 'no-actions-type-end',
111
+ 'criteria-unique',
109
112
  ];
110
113
  const builtInRules = [
111
114
  ...builtInCommonOASRules,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/openapi-core",
3
- "version": "1.23.1",
3
+ "version": "1.25.0",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "engines": {
@@ -35,7 +35,7 @@
35
35
  "Roman Hotsiy <roman@redoc.ly> (https://redoc.ly/)"
36
36
  ],
37
37
  "dependencies": {
38
- "@redocly/ajv": "^8.11.0",
38
+ "@redocly/ajv": "^8.11.2",
39
39
  "@redocly/config": "^0.10.1",
40
40
  "colorette": "^1.2.0",
41
41
  "https-proxy-agent": "^7.0.4",
@@ -5,6 +5,9 @@ exports[`resolveConfig should ignore minimal from the root and read local file 1
5
5
  "arazzoDecorators": {},
6
6
  "arazzoPreprocessors": {},
7
7
  "arazzoRules": {
8
+ "criteria-unique": "warn",
9
+ "no-actions-type-end": "warn",
10
+ "no-criteria-xpath": "warn",
8
11
  "parameters-not-in-body": "warn",
9
12
  "parameters-unique": "error",
10
13
  "requestBody-replacements-unique": "warn",
@@ -147,6 +150,9 @@ exports[`resolveStyleguideConfig should resolve extends with local file config w
147
150
  "arazzoDecorators": {},
148
151
  "arazzoPreprocessors": {},
149
152
  "arazzoRules": {
153
+ "criteria-unique": "warn",
154
+ "no-actions-type-end": "warn",
155
+ "no-criteria-xpath": "warn",
150
156
  "parameters-not-in-body": "warn",
151
157
  "parameters-unique": "error",
152
158
  "requestBody-replacements-unique": "warn",
@@ -1,3 +1,4 @@
1
+ import * as util from 'util';
1
2
  import { colorize } from '../../logger';
2
3
  import { Asserts, asserts } from '../../rules/common/assertions/asserts';
3
4
  import { resolveStyleguideConfig, resolveApis, resolveConfig } from '../config-resolvers';
@@ -91,6 +92,59 @@ describe('resolveStyleguideConfig', () => {
91
92
  });
92
93
  });
93
94
 
95
+ it('should instantiate the plugin once', async () => {
96
+ // Called by plugin during init
97
+ const deprecateSpy = jest.spyOn(util, 'deprecate');
98
+
99
+ const config = {
100
+ ...baseStyleguideConfig,
101
+ extends: ['local-config-with-plugin-init.yaml'],
102
+ };
103
+
104
+ await resolveStyleguideConfig({
105
+ styleguideConfig: config,
106
+ configPath,
107
+ });
108
+
109
+ expect(deprecateSpy).toHaveBeenCalledTimes(1);
110
+
111
+ await resolveStyleguideConfig({
112
+ styleguideConfig: config,
113
+ configPath,
114
+ });
115
+
116
+ // Should not execute the init logic again
117
+ expect(deprecateSpy).toHaveBeenCalledTimes(1);
118
+ });
119
+
120
+ it('should resolve realm plugin properties', async () => {
121
+ const config = {
122
+ ...baseStyleguideConfig,
123
+ extends: ['local-config-with-realm-plugin.yaml'],
124
+ };
125
+
126
+ const { plugins } = await resolveStyleguideConfig({
127
+ styleguideConfig: config,
128
+ configPath,
129
+ });
130
+
131
+ const localPlugin = plugins?.find((p) => p.id === 'realm-plugin');
132
+ expect(localPlugin).toBeDefined();
133
+
134
+ expect(localPlugin).toMatchObject({
135
+ id: 'realm-plugin',
136
+ processContent: expect.any(Function),
137
+ afterRoutesCreated: expect.any(Function),
138
+ loaders: {
139
+ 'test-loader': expect.any(Function),
140
+ },
141
+ requiredEntitlements: ['test-entitlement'],
142
+ ssoConfigSchema: { type: 'object', additionalProperties: true },
143
+ redoclyConfigSchema: { type: 'object', additionalProperties: false },
144
+ ejectIgnore: ['Navbar.tsx', 'Footer.tsx'],
145
+ });
146
+ });
147
+
94
148
  it('should resolve local file config with esm plugin', async () => {
95
149
  const config = {
96
150
  ...baseStyleguideConfig,
@@ -0,0 +1,2 @@
1
+ plugins:
2
+ - plugin-with-init-logic.js
@@ -0,0 +1,9 @@
1
+ var util = require('util');
2
+
3
+ module.exports = function pluginWithInitLogic() {
4
+ util.deprecate(() => null);
5
+
6
+ return {
7
+ id: 'test-plugin',
8
+ };
9
+ };
@@ -0,0 +1,12 @@
1
+ module.exports = function realmPlugin() {
2
+ return {
3
+ id: 'realm-plugin',
4
+ processContent: () => {},
5
+ afterRoutesCreated: () => {},
6
+ loaders: { 'test-loader': () => {} },
7
+ requiredEntitlements: ['test-entitlement'],
8
+ ssoConfigSchema: { type: 'object', additionalProperties: true },
9
+ redoclyConfigSchema: { type: 'object', additionalProperties: false },
10
+ ejectIgnore: ['Navbar.tsx', 'Footer.tsx'],
11
+ };
12
+ };
package/src/config/all.ts CHANGED
@@ -139,6 +139,9 @@ const all: PluginStyleguideConfig<'built-in'> = {
139
139
  'step-onSuccess-unique': 'error',
140
140
  'step-onFailure-unique': 'error',
141
141
  'requestBody-replacements-unique': 'error',
142
+ 'no-criteria-xpath': 'error',
143
+ 'no-actions-type-end': 'error',
144
+ 'criteria-unique': 'error',
142
145
  },
143
146
  };
144
147