@opra/common
Version:
Opra common package
160 lines (159 loc) • 5.4 kB
JavaScript
var _a;
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 { RpcApi } from './rpc/rpc-api.js';
/**
*
* @class ApiDocument
*/
export class ApiDocument extends DocumentElement {
constructor() {
super(null);
this[_a] = new WeakMap();
this.id = '';
this.info = {};
this.references = new ResponsiveMap();
this.types = new DataTypeMap();
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)) {
throw new TypeError('The document do not contains HttpApi instance');
}
return this.api;
}
get rpcApi() {
if (!(this.api && this.api instanceof RpcApi)) {
throw new TypeError('The document do not contains RpcApi 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;
}
}
}
}
_a = kTypeNSMap;