@deployport/specular-runtime
Version:
Runtime for Specular API clients with support for Node.js and Browser
156 lines (155 loc) • 4.94 kB
JavaScript
import { CreateContentFromObject } from "../runtime/content.js";
import Struct from "./struct.js";
export function normalizeMapEntry(key) {
return key.toLowerCase();
}
export const moduleSuperType = "application/spec";
export const mediaTypeSeparator = ".";
/**
* PackagePath contains the scope of the package
*/
export class PackagePath {
namespace;
name;
/**
* mediaTypeSubType returns the media type sub type for the package
* as in application/spec.<namespace>.<module>
*/
mediaTypeSubType;
constructor(namespace, name) {
this.namespace = normalizeMapEntry(namespace);
this.name = normalizeMapEntry(name);
this.mediaTypeSubType = `${moduleSuperType}.${this.namespace}.${this.name}`;
}
toString() {
return this.mediaTypeSubType;
}
}
/**
* Package
*/
export default class Package {
path;
resources = [];
types = [];
annotations = [];
/**
* allPackages is a list of all packages including this and all imported
*/
allPackages = [];
structsByFQTN = new Map();
constructor(namespace, module) {
this.path = new PackagePath(namespace, module);
this.allPackages.push(this);
}
importPackage(pkg) {
this.allPackages.push(pkg);
}
_addAnnotation(annotation, config) {
config?.(annotation);
this.annotations = this.annotations || [];
this.annotations.push(annotation);
}
/**
* Resource by name returns the resource with the given name
* or undefined if not found
* @param name name of the resource
* @returns
*/
resourceByName(name) {
name = normalizeMapEntry(name);
return this.resources.find(resource => normalizeMapEntry(resource.name) === name) || null;
}
_addResource(resource) {
if (this.resourceByName(resource.name)) {
throw new Error(`duplicate resource name ${resource.name}`);
}
this.resources.push(resource);
}
typeByName(name) {
name = normalizeMapEntry(name);
return this.types.find(struct => normalizeMapEntry(struct.name) === name) || null;
}
requireTypeByName(name) {
const st = this.typeByName(name);
if (!st) {
throw new Error(`failed to find user defined type ${name}`);
}
return st;
}
_addType(tp) {
if (this.typeByName(tp.name)) {
throw new Error(`duplicate type name ${tp.name}`);
}
this.types.push(tp);
if (tp instanceof Struct) {
this.structsByFQTN.set(normalizeMapEntry(tp.path.mediaType), tp);
}
}
/**
* Returns the Struct with the given FQTN, otherwise throws an error @TypeNotFoundError
* @param fqtn
* @returns
*/
structByFQTN(structPath) {
const mediaType = normalizeMapEntry(structPath.mediaType);
for (const pkg of this.allPackages) {
const struct = pkg.structsByFQTN.get(mediaType);
if (struct) {
return struct;
}
}
throw new TypeNotFoundError(structPath.mediaType);
}
buildByFQTN(structPath, content) {
const structMeta = this.structByFQTN(structPath);
const struct = {
...structMeta.deserialize(content),
__structPath: structPath,
};
return struct;
}
requireBuildFromJSON(structPath, structJSON) {
const responseContent = CreateContentFromObject(structJSON);
if (!responseContent) {
throw new Error("failed to create content from malformed JSON");
}
return this.requireBuildByFQTN(structPath, responseContent);
}
requireBuildByFQTN(structPath, content) {
const st = this.buildByFQTN(structPath, content);
if (!st) {
throw new Error(`failed to build struct ${structPath}`);
}
return st;
}
requireBuildArrayNullableItems(structPath, contentArray) {
const stArray = [];
for (const content of contentArray) {
if (content == null) {
stArray.push(null);
continue;
}
const st = this.requireBuildByFQTN(structPath, content);
stArray.push(st);
}
return stArray;
}
requireBuildArrayNonNullableItems(structPath, contentArray) {
const stArray = [];
for (const [index, value] of contentArray.entries()) {
const content = value;
if (content == null) {
throw new Error(`failed to build struct at index ${index}, unexpected null item`);
}
const st = this.requireBuildByFQTN(structPath, content);
stArray.push(st);
}
return stArray;
}
}
export class TypeNotFoundError extends Error {
constructor(typeName) {
super(`failed to find type ${typeName}`);
}
}