Просмотр исходного кода

feat: adds OAComponentsRegistry

e22m4u 5 дней назад
Родитель
Сommit
b6d7da47b9

Разница между файлами не показана из-за своего большого размера
+ 552 - 212
dist/cjs/index.cjs


+ 0 - 26
src/constants.d.ts

@@ -1,26 +0,0 @@
-/**
- * OpenAPI version.
- */
-export declare const OPENAPI_VERSION: '3.1.0';
-
-/**
- * Components segment.
- *
- * "#/components/{segment}/{name}"
- *                  ^^^
- */
-export declare const OAComponentsSegment: {
-  SCHEMAS: 'schemas';
-  RESPONSES: 'responses';
-  PARAMETERS: 'parameters';
-  EXAMPLES: 'examples';
-  REQUEST_BODIES: 'requestBodies';
-  HEADERS: 'headers';
-  SECURITY_SCHEMES: 'securitySchemes';
-  LINKS: 'links';
-  CALLBACKS: 'callbacks';
-  PATH_ITEMS: 'pathItems';
-};
-
-export type OAComponentsSegment =
-  (typeof OAComponentsSegment)[keyof typeof OAComponentsSegment];

+ 0 - 23
src/constants.js

@@ -1,23 +0,0 @@
-/**
- * OpenAPI version.
- */
-export const OPENAPI_VERSION = '3.1.0';
-
-/**
- * Components segment.
- *
- * "#/components/{segment}/{name}"
- *                  ^^^
- */
-export const OAComponentsSegment = {
-  SCHEMAS: 'schemas',
-  RESPONSES: 'responses',
-  PARAMETERS: 'parameters',
-  EXAMPLES: 'examples',
-  REQUEST_BODIES: 'requestBodies',
-  HEADERS: 'headers',
-  SECURITY_SCHEMES: 'securitySchemes',
-  LINKS: 'links',
-  CALLBACKS: 'callbacks',
-  PATH_ITEMS: 'pathItems',
-};

+ 5 - 0
src/document-specification.d.ts

@@ -1,6 +1,11 @@
 // OpenApi version 3.1.0
 // OpenApi version 3.1.0
 // https://spec.openapis.org/oas/v3.1.0
 // https://spec.openapis.org/oas/v3.1.0
 
 
+/**
+ * OpenAPI version.
+ */
+export declare const OPENAPI_VERSION: '3.1.0';
+
 /**
 /**
  * Document object.
  * Document object.
  * https://spec.openapis.org/oas/v3.1.0#openapi-object
  * https://spec.openapis.org/oas/v3.1.0#openapi-object

+ 5 - 0
src/document-specification.js

@@ -1,6 +1,11 @@
 // OpenApi version 3.1.0
 // OpenApi version 3.1.0
 // https://spec.openapis.org/oas/v3.1.0
 // https://spec.openapis.org/oas/v3.1.0
 
 
+/**
+ * OpenAPI version.
+ */
+export const OPENAPI_VERSION = '3.1.0';
+
 /**
 /**
  * Operation Method.
  * Operation Method.
  * https://spec.openapis.org/oas/v3.1.0#path-item-object
  * https://spec.openapis.org/oas/v3.1.0#path-item-object

+ 1 - 1
src/document-validators.spec.js

@@ -1,6 +1,6 @@
 import {expect} from 'chai';
 import {expect} from 'chai';
 import {format} from '@e22m4u/js-format';
 import {format} from '@e22m4u/js-format';
-import {OPENAPI_VERSION} from './constants.js';
+import {OPENAPI_VERSION} from './document-specification.js';
 import {validateShallowOADocumentObject} from './document-validators.js';
 import {validateShallowOADocumentObject} from './document-validators.js';
 
 
 const MINIMAL_DOCUMENT = {
 const MINIMAL_DOCUMENT = {

+ 1 - 1
src/index.d.ts

@@ -1,6 +1,6 @@
-export * from './constants.js';
 export * from './oa-document-scope.js';
 export * from './oa-document-scope.js';
 export * from './oa-document-builder.js';
 export * from './oa-document-builder.js';
+export * from './oa-component-registry.js';
 export * from './document-specification.js';
 export * from './document-specification.js';
 
 
 export * from './utils/oa-ref.js';
 export * from './utils/oa-ref.js';

+ 1 - 1
src/index.js

@@ -1,6 +1,6 @@
-export * from './constants.js';
 export * from './oa-document-scope.js';
 export * from './oa-document-scope.js';
 export * from './oa-document-builder.js';
 export * from './oa-document-builder.js';
+export * from './oa-component-registry.js';
 export * from './document-specification.js';
 export * from './document-specification.js';
 
 
 export * from './utils/oa-ref.js';
 export * from './utils/oa-ref.js';

+ 353 - 0
src/oa-component-registry.d.ts

@@ -0,0 +1,353 @@
+import {Service} from '@e22m4u/js-service';
+
+import {
+  OALinkObject,
+  OASchemaObject,
+  OAHeaderObject,
+  OAExampleObject,
+  OACallbackObject,
+  OAResponseObject,
+  OAPathItemObject,
+  OAReferenceObject,
+  OAParameterObject,
+  OAComponentsObject,
+  OARequestBodyObject,
+  OASecuritySchemeObject,
+} from './document-specification.js';
+
+/**
+ * Component type.
+ */
+export declare const OAComponentType: {
+  SCHEMA: 'schema';
+  RESPONSE: 'response';
+  PARAMETER: 'parameter';
+  EXAMPLE: 'example';
+  REQUEST_BODY: 'requestBody';
+  HEADER: 'header';
+  SECURITY_SCHEME: 'securityScheme';
+  LINK: 'link';
+  CALLBACK: 'callback';
+  PATH_ITEM: 'pathItem';
+};
+
+export type OAComponentType =
+  (typeof OAComponentType)[keyof typeof OAComponentType];
+
+/**
+ * Component type to components key map.
+ */
+export declare const OA_COMPONENT_TYPE_TO_COMPONENTS_KEY_MAP: {
+  [OAComponentType.SCHEMA]: 'schemas';
+  [OAComponentType.RESPONSE]: 'responses';
+  [OAComponentType.PARAMETER]: 'parameters';
+  [OAComponentType.EXAMPLE]: 'examples';
+  [OAComponentType.REQUEST_BODY]: 'requestBodies';
+  [OAComponentType.HEADER]: 'headers';
+  [OAComponentType.SECURITY_SCHEME]: 'securitySchemes';
+  [OAComponentType.LINK]: 'links';
+  [OAComponentType.CALLBACK]: 'callbacks';
+  [OAComponentType.PATH_ITEM]: 'pathItems';
+};
+
+/**
+ * Schema component definition.
+ */
+export type OASchemaComponentDefinition = {
+  name: string;
+  schema: OASchemaObject;
+};
+
+/**
+ * Response component definition.
+ */
+export type OAResponseComponentDefinition = {
+  name: string;
+  response: OAResponseObject | OAReferenceObject;
+};
+
+/**
+ * Parameter component definition.
+ */
+export type OAParameterComponentDefinition = {
+  name: string;
+  parameter: OAParameterObject | OAReferenceObject;
+};
+
+/**
+ * Example component definition.
+ */
+export type OAExampleComponentDefinition = {
+  name: string;
+  example: OAExampleObject | OAReferenceObject;
+};
+
+/**
+ * Request body component definition.
+ */
+export type OARequestBodyComponentDefinition = {
+  name: string;
+  requestBody: OARequestBodyObject | OAReferenceObject;
+};
+
+/**
+ * Header component definition.
+ */
+export type OAHeaderComponentDefinition = {
+  name: string;
+  header: OAHeaderObject | OAReferenceObject;
+};
+
+/**
+ * Security scheme component definition.
+ */
+export type OASecuritySchemeComponentDefinition = {
+  name: string;
+  securityScheme: OASecuritySchemeObject | OAReferenceObject;
+};
+
+/**
+ * Link component definition.
+ */
+export type OALinkComponentDefinition = {
+  name: string;
+  link: OALinkObject | OAReferenceObject;
+};
+
+/**
+ * Callback component definition.
+ */
+export type OACallbackComponentDefinition = {
+  name: string;
+  callback: OACallbackObject | OAReferenceObject;
+};
+
+/**
+ * Path item component definition.
+ */
+export type OAPathItemComponentDefinition = {
+  name: string;
+  pathItem: OAPathItemObject | OAReferenceObject;
+};
+
+/**
+ * Component registry.
+ */
+export class OAComponentRegistry extends Service {
+  /**
+   * Get components object.
+   */
+  getComponentsObject(): Required<OAComponentsObject>;
+
+  /**
+   * Define schema.
+   *
+   * @param schemaDef
+   */
+  defineSchema(schemaDef: OASchemaComponentDefinition): this;
+
+  /**
+   * Has schema.
+   *
+   * @param name
+   */
+  hasSchema(name: string): boolean;
+
+  /**
+   * Get schema.
+   *
+   * @param name
+   */
+  getSchema(name: string): OASchemaObject;
+
+  /**
+   * Define response.
+   *
+   * @param responseDef
+   */
+  defineResponse(responseDef: OAResponseComponentDefinition): this;
+
+  /**
+   * Has response.
+   *
+   * @param name
+   */
+  hasResponse(name: string): boolean;
+
+  /**
+   * Get response.
+   *
+   * @param name
+   */
+  getResponse(name: string): OAResponseObject;
+
+  /**
+   * Define parameter.
+   *
+   * @param parameterDef
+   */
+  defineParameter(parameterDef: OAParameterComponentDefinition): this;
+
+  /**
+   * Has parameter.
+   *
+   * @param name
+   */
+  hasParameter(name: string): boolean;
+
+  /**
+   * Get parameter.
+   *
+   * @param name
+   */
+  getParameter(name: string): OAParameterObject;
+
+  /**
+   * Define example.
+   *
+   * @param exampleDef
+   */
+  defineExample(exampleDef: OAExampleComponentDefinition): this;
+
+  /**
+   * Has example.
+   *
+   * @param name
+   */
+  hasExample(name: string): boolean;
+
+  /**
+   * Get example.
+   *
+   * @param name
+   */
+  getExample(name: string): OAExampleObject;
+
+  /**
+   * Define request body.
+   *
+   * @param requestBodyDef
+   */
+  defineRequestBody(requestBodyDef: OARequestBodyComponentDefinition): this;
+
+  /**
+   * Has request body.
+   *
+   * @param name
+   */
+  hasRequestBody(name: string): boolean;
+
+  /**
+   * Get request body.
+   *
+   * @param name
+   */
+  getRequestBody(name: string): OARequestBodyObject;
+
+  /**
+   * Define header.
+   *
+   * @param headerDef
+   */
+  defineHeader(headerDef: OAHeaderComponentDefinition): this;
+
+  /**
+   * Has header.
+   *
+   * @param name
+   */
+  hasHeader(name: string): boolean;
+
+  /**
+   * Get header.
+   *
+   * @param name
+   */
+  getHeader(name: string): OAHeaderObject;
+
+  /**
+   * Define security scheme.
+   *
+   * @param securitySchemeDef
+   */
+  defineSecurityScheme(
+    securitySchemeDef: OASecuritySchemeComponentDefinition,
+  ): this;
+
+  /**
+   * Has security scheme.
+   *
+   * @param name
+   */
+  hasSecurityScheme(name: string): boolean;
+
+  /**
+   * Get security scheme.
+   *
+   * @param name
+   */
+  getSecurityScheme(name: string): OASecuritySchemeObject;
+
+  /**
+   * Define link.
+   *
+   * @param linkDef
+   */
+  defineLink(linkDef: OALinkComponentDefinition): this;
+
+  /**
+   * Has link.
+   *
+   * @param name
+   */
+  hasLink(name: string): boolean;
+
+  /**
+   * Get link.
+   *
+   * @param name
+   */
+  getLink(name: string): OALinkObject;
+
+  /**
+   * Define callback.
+   *
+   * @param callbackDef
+   */
+  defineCallback(callbackDef: OACallbackComponentDefinition): this;
+
+  /**
+   * Has callback.
+   *
+   * @param name
+   */
+  hasCallback(name: string): boolean;
+
+  /**
+   * Get callback.
+   *
+   * @param name
+   */
+  getCallback(name: string): OACallbackObject;
+
+  /**
+   * Define path item.
+   *
+   * @param pathItemDef
+   */
+  definePathItem(pathItemDef: OAPathItemComponentDefinition): this;
+
+  /**
+   * Has path item.
+   *
+   * @param name
+   */
+  hasPathItem(name: string): boolean;
+
+  /**
+   * Get path item.
+   *
+   * @param name
+   */
+  getPathItem(name: string): OAPathItemObject;
+}

+ 487 - 0
src/oa-component-registry.js

@@ -0,0 +1,487 @@
+import {Service} from '@e22m4u/js-service';
+import {InvalidArgumentError} from '@e22m4u/js-format';
+
+/**
+ * Component type.
+ */
+export const OAComponentType = {
+  SCHEMA: 'schema',
+  RESPONSE: 'response',
+  PARAMETER: 'parameter',
+  EXAMPLE: 'example',
+  REQUEST_BODY: 'requestBody',
+  HEADER: 'header',
+  SECURITY_SCHEME: 'securityScheme',
+  LINK: 'link',
+  CALLBACK: 'callback',
+  PATH_ITEM: 'pathItem',
+};
+
+/**
+ * Component type to components key map.
+ */
+export const OA_COMPONENT_TYPE_TO_COMPONENTS_KEY_MAP = {
+  [OAComponentType.SCHEMA]: 'schemas',
+  [OAComponentType.RESPONSE]: 'responses',
+  [OAComponentType.PARAMETER]: 'parameters',
+  [OAComponentType.EXAMPLE]: 'examples',
+  [OAComponentType.REQUEST_BODY]: 'requestBodies',
+  [OAComponentType.HEADER]: 'headers',
+  [OAComponentType.SECURITY_SCHEME]: 'securitySchemes',
+  [OAComponentType.LINK]: 'links',
+  [OAComponentType.CALLBACK]: 'callbacks',
+  [OAComponentType.PATH_ITEM]: 'pathItems',
+};
+
+/**
+ * Component registry.
+ */
+export class OAComponentRegistry extends Service {
+  /**
+   * Components.
+   */
+  _components = {
+    schemas: {},
+    responses: {},
+    parameters: {},
+    examples: {},
+    requestBodies: {},
+    headers: {},
+    securitySchemes: {},
+    links: {},
+    callbacks: {},
+    pathItems: {},
+  };
+
+  /**
+   * Get components object.
+   *
+   * @returns {object}
+   */
+  getComponentsObject() {
+    return structuredClone(this._components);
+  }
+
+  /**
+   * Define component.
+   *
+   * @protected
+   * @param {string} type
+   * @param {object} definition
+   * @returns {this}
+   */
+  _defineComponent(type, definition) {
+    // componentType
+    if (!Object.values(OAComponentType).includes(type)) {
+      throw new InvalidArgumentError(
+        'Components type %v is not supported.',
+        type,
+      );
+    }
+    // definition
+    if (
+      !definition ||
+      typeof definition !== 'object' ||
+      Array.isArray(definition)
+    ) {
+      throw new InvalidArgumentError(
+        'Component definition must be an Object, but %v was given.',
+        definition,
+      );
+    }
+    // definition.name
+    if (!definition.name || typeof definition.name !== 'string') {
+      throw new InvalidArgumentError(
+        'Property "name" must be a non-empty String, but %v was given.',
+        definition.name,
+      );
+    }
+    // definition[type]
+    const component = definition[type];
+    if (
+      !component ||
+      typeof component !== 'object' ||
+      Array.isArray(component)
+    ) {
+      throw new InvalidArgumentError(
+        'Property %v must be an Object, but %v was given.',
+        type,
+        component,
+      );
+    }
+    // components key
+    const key = OA_COMPONENT_TYPE_TO_COMPONENTS_KEY_MAP[type];
+    if (!key) {
+      throw new InvalidArgumentError(
+        'Component type %v does not have a reference to the components key.',
+        type,
+      );
+    }
+    this._components[key] = structuredClone(component);
+    return this;
+  }
+
+  /**
+   * Has component.
+   *
+   * @protected
+   * @param {string} type
+   * @param {string} name
+   * @returns {boolean}
+   */
+  _hasComponent(type, name) {
+    // name
+    if (!name || typeof name !== 'string') {
+      throw new InvalidArgumentError(
+        'Parameter "name" must be a non-empty String, but %v was given.',
+        name,
+      );
+    }
+    // components key
+    const key = OA_COMPONENT_TYPE_TO_COMPONENTS_KEY_MAP[type];
+    if (!key) {
+      throw new InvalidArgumentError(
+        'Component type %v does not have a reference to the components key.',
+        type,
+      );
+    }
+    return Boolean(this._components[key][name]);
+  }
+
+  /**
+   * Get component.
+   *
+   * @param {string} type
+   * @param {string} name
+   * @returns {object}
+   */
+  _getComponent(type, name) {
+    // name
+    if (!name || typeof name !== 'string') {
+      throw new InvalidArgumentError(
+        'Parameter "name" must be a non-empty String, but %v was given.',
+        name,
+      );
+    }
+    // components key
+    const key = OA_COMPONENT_TYPE_TO_COMPONENTS_KEY_MAP[type];
+    if (!key) {
+      throw new InvalidArgumentError(
+        'Component type %v does not have a reference to the components key.',
+        type,
+      );
+    }
+    const component = this._components[key][name];
+    if (!component) {
+      throw new InvalidArgumentError(
+        'Component "#/components/%s/%s" does not exist.',
+        key,
+        name,
+      );
+    }
+    return structuredClone(component);
+  }
+
+  /**
+   * Define schema.
+   *
+   * @param {object} schemaDef
+   * @returns {this}
+   */
+  defineSchema(schemaDef) {
+    return this._defineComponent(OAComponentType.SCHEMA, schemaDef);
+  }
+
+  /**
+   * Has schema.
+   *
+   * @param {string} name
+   * @returns {boolean}
+   */
+  hasSchema(name) {
+    return this._hasComponent(OAComponentType.SCHEMA, name);
+  }
+
+  /**
+   * Get schema.
+   *
+   * @param {string} name
+   * @returns {object}
+   */
+  getSchema(name) {
+    return this._getComponent(OAComponentType.SCHEMA, name);
+  }
+
+  /**
+   * Define response.
+   *
+   * @param {object} responseDef
+   * @returns {this}
+   */
+  defineResponse(responseDef) {
+    return this._defineComponent(OAComponentType.RESPONSE, responseDef);
+  }
+
+  /**
+   * Has response.
+   *
+   * @param {string} name
+   * @returns {boolean}
+   */
+  hasResponse(name) {
+    return this._hasComponent(OAComponentType.RESPONSE, name);
+  }
+
+  /**
+   * Get response.
+   *
+   * @param {string} name
+   * @returns {object}
+   */
+  getResponse(name) {
+    return this._getComponent(OAComponentType.RESPONSE, name);
+  }
+
+  /**
+   * Define parameter.
+   *
+   * @param {object} parameterDef
+   * @returns {this}
+   */
+  defineParameter(parameterDef) {
+    return this._defineComponent(OAComponentType.PARAMETER, parameterDef);
+  }
+
+  /**
+   * Has parameter.
+   *
+   * @param {string} name
+   * @returns {boolean}
+   */
+  hasParameter(name) {
+    return this._hasComponent(OAComponentType.PARAMETER, name);
+  }
+
+  /**
+   * Get parameter.
+   *
+   * @param {string} name
+   * @returns {object}
+   */
+  getParameter(name) {
+    return this._getComponent(OAComponentType.PARAMETER, name);
+  }
+
+  /**
+   * Define example.
+   *
+   * @param {object} exampleDef
+   * @returns {this}
+   */
+  defineExample(exampleDef) {
+    return this._defineComponent(OAComponentType.EXAMPLE, exampleDef);
+  }
+
+  /**
+   * Has example.
+   *
+   * @param {string} name
+   * @returns {boolean}
+   */
+  hasExample(name) {
+    return this._hasComponent(OAComponentType.EXAMPLE, name);
+  }
+
+  /**
+   * Get example.
+   *
+   * @param {string} name
+   * @returns {object}
+   */
+  getExample(name) {
+    return this._getComponent(OAComponentType.EXAMPLE, name);
+  }
+
+  /**
+   * Define request body.
+   *
+   * @param {object} requestBodyDef
+   * @returns {this}
+   */
+  defineRequestBody(requestBodyDef) {
+    return this._defineComponent(OAComponentType.REQUEST_BODY, requestBodyDef);
+  }
+
+  /**
+   * Has request body.
+   *
+   * @param {string} name
+   * @returns {boolean}
+   */
+  hasRequestBody(name) {
+    return this._hasComponent(OAComponentType.REQUEST_BODY, name);
+  }
+
+  /**
+   * Get request body.
+   *
+   * @param {string} name
+   * @returns {object}
+   */
+  getRequestBody(name) {
+    return this._getComponent(OAComponentType.REQUEST_BODY, name);
+  }
+
+  /**
+   * Define header.
+   *
+   * @param {object} headerDef
+   * @returns {this}
+   */
+  defineHeader(headerDef) {
+    return this._defineComponent(OAComponentType.HEADER, headerDef);
+  }
+
+  /**
+   * Has header.
+   *
+   * @param {string} name
+   * @returns {boolean}
+   */
+  hasHeader(name) {
+    return this._hasComponent(OAComponentType.HEADER, name);
+  }
+
+  /**
+   * Get header.
+   *
+   * @param {string} name
+   * @returns {object}
+   */
+  getHeader(name) {
+    return this._getComponent(OAComponentType.HEADER, name);
+  }
+
+  /**
+   * Define security scheme.
+   *
+   * @param {object} securitySchemeDef
+   * @returns {this}
+   */
+  defineSecurityScheme(securitySchemeDef) {
+    return this._defineComponent(
+      OAComponentType.SECURITY_SCHEME,
+      securitySchemeDef,
+    );
+  }
+
+  /**
+   * Has security scheme.
+   *
+   * @param {string} name
+   * @returns {boolean}
+   */
+  hasSecurityScheme(name) {
+    return this._hasComponent(OAComponentType.SECURITY_SCHEME, name);
+  }
+
+  /**
+   * Get security scheme.
+   *
+   * @param {string} name
+   * @returns {object}
+   */
+  getSecurityScheme(name) {
+    return this._getComponent(OAComponentType.SECURITY_SCHEME, name);
+  }
+
+  /**
+   * Define link.
+   *
+   * @param {object} linkDef
+   * @returns {this}
+   */
+  defineLink(linkDef) {
+    return this._defineComponent(OAComponentType.LINK, linkDef);
+  }
+
+  /**
+   * Has link.
+   *
+   * @param {string} name
+   * @returns {boolean}
+   */
+  hasLink(name) {
+    return this._hasComponent(OAComponentType.LINK, name);
+  }
+
+  /**
+   * Get link.
+   *
+   * @param {string} name
+   * @returns {object}
+   */
+  getLink(name) {
+    return this._getComponent(OAComponentType.LINK, name);
+  }
+
+  /**
+   * Define callback.
+   *
+   * @param {object} callbackDef
+   * @returns {this}
+   */
+  defineCallback(callbackDef) {
+    return this._defineComponent(OAComponentType.CALLBACK, callbackDef);
+  }
+
+  /**
+   * Has callback.
+   *
+   * @param {string} name
+   * @returns {boolean}
+   */
+  hasCallback(name) {
+    return this._hasComponent(OAComponentType.CALLBACK, name);
+  }
+
+  /**
+   * Get callback.
+   *
+   * @param {string} name
+   * @returns {object}
+   */
+  getCallback(name) {
+    return this._getComponent(OAComponentType.CALLBACK, name);
+  }
+
+  /**
+   * Define path item.
+   *
+   * @param {object} pathItemDef
+   * @returns {this}
+   */
+  definePathItem(pathItemDef) {
+    return this._defineComponent(OAComponentType.PATH_ITEM, pathItemDef);
+  }
+
+  /**
+   * Has path item.
+   *
+   * @param {string} name
+   * @returns {boolean}
+   */
+  hasPathItem(name) {
+    return this._hasComponent(OAComponentType.PATH_ITEM, name);
+  }
+
+  /**
+   * Get path item.
+   *
+   * @param {string} name
+   * @returns {object}
+   */
+  getPathItem(name) {
+    return this._getComponent(OAComponentType.PATH_ITEM, name);
+  }
+}

+ 13 - 91
src/oa-document-builder.d.ts

@@ -3,107 +3,29 @@ import {Service, ServiceContainer} from '@e22m4u/js-service';
 import {OADocumentScope, OADocumentScopeOptions} from './oa-document-scope.js';
 import {OADocumentScope, OADocumentScopeOptions} from './oa-document-scope.js';
 
 
 import {
 import {
-  OALinkObject,
-  OASchemaObject,
-  OAHeaderObject,
-  OAExampleObject,
   OADocumentObject,
   OADocumentObject,
-  OACallbackObject,
-  OAResponseObject,
-  OAPathItemObject,
   OAOperationMethod,
   OAOperationMethod,
   OAOperationObject,
   OAOperationObject,
-  OAReferenceObject,
-  OAParameterObject,
-  OARequestBodyObject,
-  OASecuritySchemeObject,
 } from './document-specification.js';
 } from './document-specification.js';
 
 
+import {
+  OALinkComponentDefinition,
+  OASchemaComponentDefinition,
+  OAHeaderComponentDefinition,
+  OAExampleComponentDefinition,
+  OAResponseComponentDefinition,
+  OACallbackComponentDefinition,
+  OAPathItemComponentDefinition,
+  OAParameterComponentDefinition,
+  OARequestBodyComponentDefinition,
+  OASecuritySchemeComponentDefinition,
+} from './oa-component-registry.js';
+
 /**
 /**
  * Document input.
  * Document input.
  */
  */
 export type OADocumentInput = Optional<OADocumentObject, 'openapi' | 'info'>;
 export type OADocumentInput = Optional<OADocumentObject, 'openapi' | 'info'>;
 
 
-/**
- * Schema component definition.
- */
-export type OASchemaComponentDefinition = {
-  name: string;
-  schema: OASchemaObject;
-};
-
-/**
- * Response component definition.
- */
-export type OAResponseComponentDefinition = {
-  name: string;
-  response: OAResponseObject | OAReferenceObject;
-};
-
-/**
- * Parameter component definition.
- */
-export type OAParameterComponentDefinition = {
-  name: string;
-  parameter: OAParameterObject | OAReferenceObject;
-};
-
-/**
- * Example component definition.
- */
-export type OAExampleComponentDefinition = {
-  name: string;
-  example: OAExampleObject | OAReferenceObject;
-};
-
-/**
- * Request body component definition.
- */
-export type OARequestBodyComponentDefinition = {
-  name: string;
-  requestBody: OARequestBodyObject | OAReferenceObject;
-};
-
-/**
- * Header component definition.
- */
-export type OAHeaderComponentDefinition = {
-  name: string;
-  header: OAHeaderObject | OAReferenceObject;
-};
-
-/**
- * Security scheme component definition.
- */
-export type OASecuritySchemeComponentDefinition = {
-  name: string;
-  securityScheme: OASecuritySchemeObject | OAReferenceObject;
-};
-
-/**
- * Link component definition.
- */
-export type OALinkComponentDefinition = {
-  name: string;
-  link: OALinkObject | OAReferenceObject;
-};
-
-/**
- * Callback component definition.
- */
-export type OACallbackComponentDefinition = {
-  name: string;
-  callback: OACallbackObject | OAReferenceObject;
-};
-
-/**
- * Path item component definition.
- */
-export type OAPathItemComponentDefinition = {
-  name: string;
-  pathItem: OAPathItemObject | OAReferenceObject;
-};
-
 /**
 /**
  * Operation definition.
  * Operation definition.
  */
  */

+ 41 - 121
src/oa-document-builder.js

@@ -1,10 +1,10 @@
 import {OADocumentScope} from './oa-document-scope.js';
 import {OADocumentScope} from './oa-document-scope.js';
 import {InvalidArgumentError} from '@e22m4u/js-format';
 import {InvalidArgumentError} from '@e22m4u/js-format';
 import {normalizePath} from './utils/normalize-path.js';
 import {normalizePath} from './utils/normalize-path.js';
-import {OAOperationMethod} from './document-specification.js';
+import {OAComponentRegistry} from './oa-component-registry.js';
 import {isServiceContainer, Service} from '@e22m4u/js-service';
 import {isServiceContainer, Service} from '@e22m4u/js-service';
-import {OAComponentsSegment, OPENAPI_VERSION} from './constants.js';
 import {validateShallowOADocumentObject} from './document-validators.js';
 import {validateShallowOADocumentObject} from './document-validators.js';
+import {OPENAPI_VERSION, OAOperationMethod} from './document-specification.js';
 
 
 /**
 /**
  * OpenAPI Document builder.
  * OpenAPI Document builder.
@@ -92,74 +92,6 @@ export class OADocumentBuilder extends Service {
     }
     }
   }
   }
 
 
-  /**
-   * Define component.
-   *
-   * @param {string} segmentName
-   * @param {string} propertyName
-   * @param {object} definition
-   * @returns {this}
-   */
-  _defineComponent(segmentName, propertyName, definition) {
-    // segmentName
-    if (!Object.values(OAComponentsSegment).includes(segmentName)) {
-      throw new InvalidArgumentError(
-        'Property "segmentName" must be one of values: %l, ' +
-          'but %v was given.',
-        Object.values(OAComponentsSegment),
-        segmentName,
-      );
-    }
-    // propertyName
-    if (!propertyName || typeof propertyName !== 'string') {
-      throw new InvalidArgumentError(
-        'Property "propertyName" must be a non-empty String, ' +
-          'but %v was given.',
-        propertyName,
-      );
-    }
-    // definition
-    if (
-      !definition ||
-      typeof definition !== 'object' ||
-      Array.isArray(definition)
-    ) {
-      throw new InvalidArgumentError(
-        'Component definition must be an Object, but %v was given.',
-        definition,
-      );
-    }
-    // definition.name
-    if (!definition.name || typeof definition.name !== 'string') {
-      throw new InvalidArgumentError(
-        'Property "name" must be a non-empty String, but %v was given.',
-        definition.name,
-      );
-    }
-    // definition[componentKey]
-    const component = definition[propertyName];
-    if (
-      !component ||
-      typeof component !== 'object' ||
-      Array.isArray(component)
-    ) {
-      throw new InvalidArgumentError(
-        'Property %v must be an Object, but %v was given.',
-        propertyName,
-        component,
-      );
-    }
-    if (!this._document.components) {
-      this._document.components = {};
-    }
-    if (!this._document.components[segmentName]) {
-      this._document.components[segmentName] = {};
-    }
-    this._document.components[segmentName][definition.name] =
-      structuredClone(component);
-    return this;
-  }
-
   /**
   /**
    * Define schema component.
    * Define schema component.
    *
    *
@@ -167,11 +99,8 @@ export class OADocumentBuilder extends Service {
    * @returns {this}
    * @returns {this}
    */
    */
   defineSchemaComponent(schemaDef) {
   defineSchemaComponent(schemaDef) {
-    return this._defineComponent(
-      OAComponentsSegment.SCHEMAS,
-      'schema',
-      schemaDef,
-    );
+    this.getService(OAComponentRegistry).defineSchema(schemaDef);
+    return this;
   }
   }
 
 
   /**
   /**
@@ -181,11 +110,8 @@ export class OADocumentBuilder extends Service {
    * @returns {this}
    * @returns {this}
    */
    */
   defineResponseComponent(responseDef) {
   defineResponseComponent(responseDef) {
-    return this._defineComponent(
-      OAComponentsSegment.RESPONSES,
-      'response',
-      responseDef,
-    );
+    this.getService(OAComponentRegistry).defineResponse(responseDef);
+    return this;
   }
   }
 
 
   /**
   /**
@@ -195,11 +121,8 @@ export class OADocumentBuilder extends Service {
    * @returns {this}
    * @returns {this}
    */
    */
   defineParameterComponent(parameterDef) {
   defineParameterComponent(parameterDef) {
-    return this._defineComponent(
-      OAComponentsSegment.PARAMETERS,
-      'parameter',
-      parameterDef,
-    );
+    this.getService(OAComponentRegistry).defineParameter(parameterDef);
+    return this;
   }
   }
 
 
   /**
   /**
@@ -209,11 +132,8 @@ export class OADocumentBuilder extends Service {
    * @returns {this}
    * @returns {this}
    */
    */
   defineExampleComponent(exampleDef) {
   defineExampleComponent(exampleDef) {
-    return this._defineComponent(
-      OAComponentsSegment.EXAMPLES,
-      'example',
-      exampleDef,
-    );
+    this.getService(OAComponentRegistry).defineExample(exampleDef);
+    return this;
   }
   }
 
 
   /**
   /**
@@ -223,11 +143,8 @@ export class OADocumentBuilder extends Service {
    * @returns {this}
    * @returns {this}
    */
    */
   defineRequestBodyComponent(requestBodyDef) {
   defineRequestBodyComponent(requestBodyDef) {
-    return this._defineComponent(
-      OAComponentsSegment.REQUEST_BODIES,
-      'requestBody',
-      requestBodyDef,
-    );
+    this.getService(OAComponentRegistry).defineRequestBody(requestBodyDef);
+    return this;
   }
   }
 
 
   /**
   /**
@@ -237,11 +154,8 @@ export class OADocumentBuilder extends Service {
    * @returns {this}
    * @returns {this}
    */
    */
   defineHeaderComponent(headerDef) {
   defineHeaderComponent(headerDef) {
-    return this._defineComponent(
-      OAComponentsSegment.HEADERS,
-      'header',
-      headerDef,
-    );
+    this.getService(OAComponentRegistry).defineHeader(headerDef);
+    return this;
   }
   }
 
 
   /**
   /**
@@ -251,11 +165,10 @@ export class OADocumentBuilder extends Service {
    * @returns {this}
    * @returns {this}
    */
    */
   defineSecuritySchemeComponent(securitySchemeDef) {
   defineSecuritySchemeComponent(securitySchemeDef) {
-    return this._defineComponent(
-      OAComponentsSegment.SECURITY_SCHEMES,
-      'securityScheme',
+    this.getService(OAComponentRegistry).defineSecurityScheme(
       securitySchemeDef,
       securitySchemeDef,
     );
     );
+    return this;
   }
   }
 
 
   /**
   /**
@@ -265,12 +178,8 @@ export class OADocumentBuilder extends Service {
    * @returns {this}
    * @returns {this}
    */
    */
   defineLinkComponent(linkDef) {
   defineLinkComponent(linkDef) {
-    /* prettier-ignore */
-    return this._defineComponent(
-      OAComponentsSegment.LINKS,
-      'link',
-      linkDef,
-    );
+    this.getService(OAComponentRegistry).defineLink(linkDef);
+    return this;
   }
   }
 
 
   /**
   /**
@@ -280,11 +189,8 @@ export class OADocumentBuilder extends Service {
    * @returns {this}
    * @returns {this}
    */
    */
   defineCallbackComponent(callbackDef) {
   defineCallbackComponent(callbackDef) {
-    return this._defineComponent(
-      OAComponentsSegment.CALLBACKS,
-      'callback',
-      callbackDef,
-    );
+    this.getService(OAComponentRegistry).defineCallback(callbackDef);
+    return this;
   }
   }
 
 
   /**
   /**
@@ -294,11 +200,8 @@ export class OADocumentBuilder extends Service {
    * @returns {this}
    * @returns {this}
    */
    */
   definePathItemComponent(pathItemDef) {
   definePathItemComponent(pathItemDef) {
-    return this._defineComponent(
-      OAComponentsSegment.PATH_ITEMS,
-      'pathItem',
-      pathItemDef,
-    );
+    this.getService(OAComponentRegistry).definePathItem(pathItemDef);
+    return this;
   }
   }
 
 
   /**
   /**
@@ -376,7 +279,24 @@ export class OADocumentBuilder extends Service {
    * @returns {object}
    * @returns {object}
    */
    */
   build() {
   build() {
-    return structuredClone(this._document);
+    const doc = structuredClone(this._document);
+    // внедрение зарегистрированных компонентов
+    // в компоненты текущего документа
+    const compsMap = this.getService(OAComponentRegistry).getComponentsObject();
+    doc.components = doc.components || {};
+    Object.keys(compsMap).forEach(compsKey => {
+      const compsNames = Object.keys(compsMap[compsKey]);
+      if (compsNames.length) {
+        doc.components[compsKey] = doc.components[compsKey] || {};
+        compsNames.forEach(compName => {
+          doc.components[compsKey][compName] = compsMap[compsKey][compName];
+        });
+      }
+    });
+    if (!Object.keys(doc.components).length) {
+      delete doc.components;
+    }
+    return doc;
   }
   }
 
 
   /**
   /**
@@ -386,6 +306,6 @@ export class OADocumentBuilder extends Service {
    * @returns {string}
    * @returns {string}
    */
    */
   buildJson(space = 0) {
   buildJson(space = 0) {
-    return JSON.stringify(this._document, null, space);
+    return JSON.stringify(this.build(), null, space);
   }
   }
 }
 }

+ 0 - 20
src/utils/oa-ref.d.ts

@@ -1,25 +1,5 @@
-import {OAComponentsSegment} from '../constants.js';
 import {OAReferenceObject} from '../document-specification.js';
 import {OAReferenceObject} from '../document-specification.js';
 
 
-/**
- * Create the Reference Object.
- *
- * Example:
- *
- * ```js
- * oaRef('User', OAComponentsSegment.SCHEMAS);
- * // {"$ref": "#/components/schemas/User"}
- * ```
- *
- * @param name
- * @param segment
- */
-export declare function oaRef(
-  name: string,
-  segment: OAComponentsSegment,
-): OAReferenceObject;
-
-/* aliases */
 export declare const oaSchemaRef: (name: string) => OAReferenceObject;
 export declare const oaSchemaRef: (name: string) => OAReferenceObject;
 export declare const oaResponseRef: (name: string) => OAReferenceObject;
 export declare const oaResponseRef: (name: string) => OAReferenceObject;
 export declare const oaParameterRef: (name: string) => OAReferenceObject;
 export declare const oaParameterRef: (name: string) => OAReferenceObject;

+ 19 - 27
src/utils/oa-ref.js

@@ -1,48 +1,40 @@
-import {OAComponentsSegment} from '../constants.js';
 import {InvalidArgumentError} from '@e22m4u/js-format';
 import {InvalidArgumentError} from '@e22m4u/js-format';
 
 
+import {
+  OAComponentType,
+  OA_COMPONENT_TYPE_TO_COMPONENTS_KEY_MAP,
+} from '../oa-component-registry.js';
+
 /**
 /**
  * Create the Reference Object.
  * Create the Reference Object.
  *
  *
- * Example:
- *
- * ```js
- * oaRef('User', OAComponentsSegment.SCHEMAS);
- * // {"$ref": "#/components/schemas/User"}
- * ```
- *
  * @param {string} name
  * @param {string} name
- * @param {string} segment
+ * @param {string} type
  * @returns {object}
  * @returns {object}
  */
  */
-export function oaRef(name, segment) {
+function oaRef(name, type) {
   if (!name || typeof name !== 'string') {
   if (!name || typeof name !== 'string') {
     throw new InvalidArgumentError(
     throw new InvalidArgumentError(
       'Parameter "name" must be a non-empty String, but %v was given.',
       'Parameter "name" must be a non-empty String, but %v was given.',
       name,
       name,
     );
     );
   }
   }
-  if (!Object.values(OAComponentsSegment).includes(segment)) {
-    throw new InvalidArgumentError(
-      'Parameter "segment" must be one of values: %l, but %v was given.',
-      Object.values(OAComponentsSegment),
-      segment,
-    );
+  if (!Object.values(OAComponentType).includes(type)) {
+    throw new InvalidArgumentError('Component type %v is not supported.', type);
   }
   }
+  const segment = OA_COMPONENT_TYPE_TO_COMPONENTS_KEY_MAP[type];
   return {$ref: `#/components/${segment}/${name}`};
   return {$ref: `#/components/${segment}/${name}`};
 }
 }
 
 
 /* aliases */
 /* aliases */
-export const oaSchemaRef = name => oaRef(name, OAComponentsSegment.SCHEMAS);
-export const oaResponseRef = name => oaRef(name, OAComponentsSegment.RESPONSES);
-export const oaParameterRef = name =>
-  oaRef(name, OAComponentsSegment.PARAMETERS);
-export const oaExampleRef = name => oaRef(name, OAComponentsSegment.EXAMPLES);
+export const oaSchemaRef = name => oaRef(name, OAComponentType.SCHEMA);
+export const oaResponseRef = name => oaRef(name, OAComponentType.RESPONSE);
+export const oaParameterRef = name => oaRef(name, OAComponentType.PARAMETER);
+export const oaExampleRef = name => oaRef(name, OAComponentType.EXAMPLE);
 export const oaRequestBodyRef = name =>
 export const oaRequestBodyRef = name =>
-  oaRef(name, OAComponentsSegment.REQUEST_BODIES);
+  oaRef(name, OAComponentType.REQUEST_BODY);
 export const oaSecuritySchemeRef = name =>
 export const oaSecuritySchemeRef = name =>
-  oaRef(name, OAComponentsSegment.SECURITY_SCHEMES);
-export const oaLinkRef = name => oaRef(name, OAComponentsSegment.LINKS);
-export const oaCallbackRef = name => oaRef(name, OAComponentsSegment.CALLBACKS);
-export const oaPathItemRef = name =>
-  oaRef(name, OAComponentsSegment.PATH_ITEMS);
+  oaRef(name, OAComponentType.SECURITY_SCHEME);
+export const oaLinkRef = name => oaRef(name, OAComponentType.LINK);
+export const oaCallbackRef = name => oaRef(name, OAComponentType.CALLBACK);
+export const oaPathItemRef = name => oaRef(name, OAComponentType.PATH_ITEM);

+ 34 - 71
src/utils/oa-ref.spec.js

@@ -1,9 +1,7 @@
 import {expect} from 'chai';
 import {expect} from 'chai';
 import {format} from '@e22m4u/js-format';
 import {format} from '@e22m4u/js-format';
-import {OAComponentsSegment} from '../constants.js';
 
 
 import {
 import {
-  oaRef,
   oaLinkRef,
   oaLinkRef,
   oaSchemaRef,
   oaSchemaRef,
   oaExampleRef,
   oaExampleRef,
@@ -15,78 +13,43 @@ import {
   oaSecuritySchemeRef,
   oaSecuritySchemeRef,
 } from './oa-ref.js';
 } from './oa-ref.js';
 
 
-describe('oaRef', function () {
-  it('should require the "name" parameter to be a non-empty String', function () {
-    const throwable = v => () => oaRef(v, OAComponentsSegment.SCHEMAS);
-    const error = s =>
-      format(
-        'Parameter "name" must be a non-empty String, but %s was given.',
-        s,
-      );
-    expect(throwable('')).to.throw(error('""'));
-    expect(throwable(10)).to.throw(error('10'));
-    expect(throwable(0)).to.throw(error('0'));
-    expect(throwable(true)).to.throw(error('true'));
-    expect(throwable(false)).to.throw(error('false'));
-    expect(throwable([])).to.throw(error('Array'));
-    expect(throwable({})).to.throw(error('Object'));
-    expect(throwable(undefined)).to.throw(error('undefined'));
-    expect(throwable(null)).to.throw(error('null'));
-    throwable('str')();
-  });
-
-  it('should require the "segment" parameter to be a components segment', function () {
-    const throwable = v => () => oaRef('Model', v);
-    const segments = Object.values(OAComponentsSegment);
-    const error = s =>
-      format(
-        'Parameter "segment" must be one of values: %l, but %s was given.',
-        segments,
-        s,
-      );
-    expect(throwable('str')).to.throw(error('"str"'));
-    expect(throwable('')).to.throw(error('""'));
-    expect(throwable(10)).to.throw(error('10'));
-    expect(throwable(0)).to.throw(error('0'));
-    expect(throwable(true)).to.throw(error('true'));
-    expect(throwable(false)).to.throw(error('false'));
-    expect(throwable([])).to.throw(error('Array'));
-    expect(throwable({})).to.throw(error('Object'));
-    expect(throwable(undefined)).to.throw(error('undefined'));
-    expect(throwable(null)).to.throw(error('null'));
-    segments.forEach(segment => throwable(segment)());
-  });
-
-  it('should set the "name" argument to the reference path', function () {
-    expect(oaRef('Model', OAComponentsSegment.SCHEMAS)).to.be.eql({
-      $ref: '#/components/schemas/Model',
-    });
-  });
+const ALIASES_MAP = [
+  [oaSchemaRef, 'schemas'],
+  [oaResponseRef, 'responses'],
+  [oaParameterRef, 'parameters'],
+  [oaExampleRef, 'examples'],
+  [oaRequestBodyRef, 'requestBodies'],
+  [oaSecuritySchemeRef, 'securitySchemes'],
+  [oaLinkRef, 'links'],
+  [oaCallbackRef, 'callbacks'],
+  [oaPathItemRef, 'pathItems'],
+];
 
 
-  it('should set the "segment" argument to the reference path', function () {
-    const segments = Object.values(OAComponentsSegment);
-    segments.forEach(segment => {
-      expect(oaRef('name', segment)).to.be.eql({
-        $ref: `#/components/${segment}/name`,
+describe('oaRef aliases', function () {
+  // eslint-disable-next-line mocha/no-setup-in-describe
+  ALIASES_MAP.forEach(([fn, segment]) => {
+    describe(fn.name, function () {
+      it('should require the "name" parameter to be a none-empty String', function () {
+        const throwable = v => () => fn(v);
+        const error = s =>
+          format(
+            'Parameter "name" must be a non-empty String, but %s was given.',
+            s,
+          );
+        expect(throwable('')).to.throw(error('""'));
+        expect(throwable(10)).to.throw(error('10'));
+        expect(throwable(0)).to.throw(error('0'));
+        expect(throwable(true)).to.throw(error('true'));
+        expect(throwable(false)).to.throw(error('false'));
+        expect(throwable([])).to.throw(error('Array'));
+        expect(throwable({})).to.throw(error('Object'));
+        expect(throwable(undefined)).to.throw(error('undefined'));
+        expect(throwable(null)).to.throw(error('null'));
+        throwable('name')();
       });
       });
-    });
-  });
 
 
-  describe('aliases', function () {
-    it('should use the correct segment', function () {
-      const aliasesToResultMap = [
-        [oaSchemaRef, {$ref: '#/components/schemas/name'}],
-        [oaResponseRef, {$ref: '#/components/responses/name'}],
-        [oaParameterRef, {$ref: '#/components/parameters/name'}],
-        [oaExampleRef, {$ref: '#/components/examples/name'}],
-        [oaRequestBodyRef, {$ref: '#/components/requestBodies/name'}],
-        [oaSecuritySchemeRef, {$ref: '#/components/securitySchemes/name'}],
-        [oaLinkRef, {$ref: '#/components/links/name'}],
-        [oaCallbackRef, {$ref: '#/components/callbacks/name'}],
-        [oaPathItemRef, {$ref: '#/components/pathItems/name'}],
-      ];
-      aliasesToResultMap.forEach(([fn, res]) => {
-        expect(fn('name')).to.be.eql(res);
+      it('should return the correct reference', function () {
+        expect(fn('name')).to.be.eql({$ref: `#/components/${segment}/name`});
       });
       });
     });
     });
   });
   });

Некоторые файлы не были показаны из-за большого количества измененных файлов