UNPKG

@opra/common

Version:
179 lines (178 loc) 5.9 kB
import { omitUndefined } from '@jsopen/objects'; import { md5 } from 'super-fast-md5'; import { cloneObject, ResponsiveMap } from '../helpers/index.js'; import { OpraSchema } from '../schema/index.js'; import { DataTypeMap } from './common/data-type-map.js'; import { DocumentElement } from './common/document-element.js'; import { BUILTIN, kDataTypeMap, kTypeNSMap, NAMESPACE_PATTERN, } from './constants.js'; import { DataType } from './data-type/data-type.js'; import { HttpApi } from './http/http-api.js'; import { MQApi } from './mq/mq-api.js'; import { WSApi } from './ws/ws-api.js'; /** * * @class ApiDocument */ export class ApiDocument extends DocumentElement { [kTypeNSMap] = new WeakMap(); id = ''; url; info = {}; references = new ResponsiveMap(); types = new DataTypeMap(); api; constructor() { super(null); this.node[kDataTypeMap] = this.types; this.node.findDataType = this._findDataType.bind(this); } /** * Returns NS of datatype. Returns undefined if not found * @param nameOrCtor */ getDataTypeNs(nameOrCtor) { const dt = nameOrCtor instanceof DataType ? this._findDataType(nameOrCtor.name || '') : this._findDataType(nameOrCtor); if (dt) return this[kTypeNSMap].get(dt); } findDocument(id) { if (this.id === id) return this; for (const doc of this.references.values()) { if (doc.id === id) return doc; const d = doc.findDocument(id); if (d) return d; } } get httpApi() { if (this.api && this.api instanceof HttpApi) return this.api; } get mqApi() { if (this.api && this.api instanceof MQApi) return this.api; } get wsApi() { if (this.api && this.api instanceof WSApi) return this.api; } getHttpApi() { if (!(this.api && this.api instanceof HttpApi)) { throw new TypeError('The document do not contains HttpApi instance'); } return this.api; } getMqApi() { if (!(this.api && this.api instanceof MQApi)) { throw new TypeError('The document do not contains MQApi instance'); } return this.api; } getWsApi() { if (!(this.api && this.api instanceof WSApi)) { throw new TypeError('The document do not contains WSApi instance'); } return this.api; } toJSON() { return this.export(); } /** * Export as Opra schema definition object */ export(options) { const out = omitUndefined({ spec: OpraSchema.SpecVersion, id: this.id, url: this.url, info: cloneObject(this.info, true), }); if (this.references.size) { let i = 0; const references = {}; for (const [ns, doc] of this.references.entries()) { if (doc[BUILTIN]) continue; references[ns] = { id: doc.id, url: doc.url, info: cloneObject(doc.info, true), }; i++; } if (i) out.references = references; } if (this.types.size) { out.types = {}; for (const v of this.types.values()) { if (!v.inScope(options?.scope)) continue; out.types[v.name] = v.toJSON(options); } } if (this.api) out.api = this.api.toJSON(options); return out; } invalidate() { /** Generate id */ const x = this.export({}); delete x.id; this.id = md5(JSON.stringify(x)); /** Clear [kTypeNSMap] */ this[kTypeNSMap] = new WeakMap(); } _findDataType(nameOrCtor, scope, visitedRefs) { let result = this.types.get(nameOrCtor); if (result && result.inScope(scope)) return result; if (!this.references.size) return; // Lookup for references if (typeof nameOrCtor === 'string') { // If given string has namespace pattern (ns:type_name) const m = NAMESPACE_PATTERN.exec(nameOrCtor); if (m) { const ns = m[1]; if (ns) { const ref = this.references.get(ns); if (!ref) return; visitedRefs = visitedRefs || new WeakMap(); visitedRefs.set(this, true); visitedRefs.set(ref, true); return ref._findDataType(m[2], scope, visitedRefs); } nameOrCtor = m[2]; } } // if not found, search in references (from last to first) visitedRefs = visitedRefs || new WeakMap(); visitedRefs.set(this, true); const references = Array.from(this.references.keys()).reverse(); /** First step, lookup for own types */ for (const refNs of references) { const ref = this.references.get(refNs); result = ref?.types.get(nameOrCtor); if (result) { this[kTypeNSMap].set(result, ref?.[BUILTIN] ? '' : refNs); return result; } } /** If not found lookup for child references */ for (const refNs of references) { const ref = this.references.get(refNs); visitedRefs.set(ref, true); result = ref._findDataType(nameOrCtor, scope, visitedRefs); if (result) { this[kTypeNSMap].set(result, ref?.[BUILTIN] ? '' : refNs); return result; } } } }