sdj-esm
Version:
Self Described JSON - ESM
255 lines (254 loc) • 8.88 kB
JavaScript
/*
Copyright (c) 2023-2024 Will Rudolph <@willrudolph.com>
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import { SdjDescription } from "../classes/description.js";
import { DESC_ALT, ESDJ_CLASS, ESDJ_LOG } from "../core/statics.js";
import { SdjLexicons } from "./lexicons.js";
import { SdjEntity } from "../classes/entity.js";
import { SdjItem } from "../classes/item.js";
import { SdjData } from "../classes/data.js";
import { each, isEqual, isFunction } from "lodash-es";
import { SdjSearch } from "./search.js";
import { LogManager } from "../util/log.js";
import { freezeDescription } from "../util/general.js";
class EmptySdjLibrary {
init() { }
getDescList() {
return [];
}
getDescByName() {
return undefined;
}
storeDesc() {
return false;
}
removeDesc() {
return false;
}
}
class IntSingletonLock {
constructor() {
// console.log("IntSingletonLock");
}
}
export class SdjHost {
// eslint-disable-next-line no-use-before-define
static _instance;
gLog;
_lexicons;
_library;
_search;
_logs;
constructor(lockCon, initialSet) {
if (!(lockCon instanceof IntSingletonLock)) {
throw new Error("[SDJ] Illegal attempt create SdjHost");
}
this.initLogs(initialSet?.options);
this.gLog = this._logs.getLogFunc("ISdjHost");
this._lexicons = new SdjLexicons(this, initialSet?.lexicons);
this._search = new SdjSearch(this);
if (initialSet?.options?.logMode && initialSet.options.logMode !== ESDJ_LOG.PROD) {
this.gLog("Set logMode:" + initialSet.options.logMode);
}
if (initialSet?.library) {
this.verifyLibrary(initialSet.library);
this._library = initialSet.library;
this._library.init(this._logs.getLogFunc("ISdjLibrary"), this);
}
else {
this._library = new EmptySdjLibrary();
}
}
get library() {
return this._library;
}
get lexiconMgr() {
return this._lexicons;
}
get searchMgr() {
return this._search;
}
get logs() {
return this._logs;
}
makeDescript(descJI, unlocked = false) {
let rtnDesc = this._library.getDescByName(descJI.sdInfo.name), expDesc, newDesc;
if (rtnDesc) {
if (!isEqual(descJI, rtnDesc.genJI())) {
descJI.sdInfo.name += DESC_ALT;
}
else {
return rtnDesc;
}
}
const thisHost = SdjHost.getHost();
newDesc = new SdjDescription(descJI, thisHost);
if (this._library.storeDesc(newDesc)) {
rtnDesc = this._library.getDescByName(descJI.sdInfo.name);
if (rtnDesc) {
expDesc = rtnDesc;
}
else {
this.gLog("Error on unsuccessful add description to library;", 3);
expDesc = newDesc;
}
}
else {
expDesc = newDesc;
}
if (!unlocked) {
freezeDescription(expDesc);
}
return expDesc;
}
fullDescription(inDescJI) {
if (inDescJI.lexicons && this.lexiconMgr.names.length > 0) {
if (!this.lexiconMgr.validateRequires(inDescJI)) {
throw new Error("[SDJ] There has been an unknown lexicon error");
}
}
inDescJI.items = this.lexiconMgr.fullItemJI(inDescJI);
inDescJI.graph = this.lexiconMgr.fullGraphJI(inDescJI);
if (!this.lexiconMgr.validateGraph(inDescJI)) {
throw new Error("[SDJ] There has been an unknown lexicon error");
}
return inDescJI;
}
getLogFunc(name) {
return this._logs.getLogFunc(name);
}
// Given the validation rules some JIs can look like each other;
// This function is to lock out/check for strange loading of Sdj Class objects that
// are code masquerading themselves or other strangeness.
checkClassInst(ji, jiType, isClass = false) {
let isInst;
switch (jiType) {
case ESDJ_CLASS.DESCRIPTION:
isInst = (ji instanceof SdjDescription);
break;
case ESDJ_CLASS.HOST:
isInst = (ji instanceof SdjHost);
break;
case ESDJ_CLASS.DATA:
isInst = (ji instanceof SdjData);
break;
case ESDJ_CLASS.ITEM:
isInst = (ji instanceof SdjItem);
break;
case ESDJ_CLASS.ENTITY:
isInst = (ji instanceof SdjEntity);
break;
default:
throw new Error(`[SDJ] in checkClassInst jiType must match ESDJ_CLASS types, value is '${jiType}'`);
}
if (isClass && !isInst) {
throw new Error(`[SDJ] input value is required to be class '${jiType}';`);
}
else if (!isClass && isInst) {
throw new Error(`[SDJ] input JI value cannot be of class '${jiType}';`);
}
}
verifyJIbyType(ji, jiType, strict = false) {
let rtnVal = true, ogError;
try {
switch (jiType) {
case ESDJ_CLASS.DESCRIPTION:
SdjDescription.VerifyJI(ji);
break;
case ESDJ_CLASS.DATA:
SdjData.VerifyJI(ji);
break;
case ESDJ_CLASS.ITEM:
SdjItem.VerifyJI(ji);
break;
case ESDJ_CLASS.ENTITY:
SdjEntity.VerifyJI(ji);
break;
default:
// no default
}
}
catch (verifyError) {
ogError = verifyError;
rtnVal = false;
}
if (strict) {
if (!rtnVal) {
throw new Error(`[SDJ] verifyJI '${jiType}' failed: ${ogError}`);
}
}
return rtnVal;
}
createDescription(descJI) {
let rtnDesc = this._library.getDescByName(descJI.sdInfo.name);
if (rtnDesc) {
if (!isEqual(descJI, rtnDesc.genJI())) {
descJI.sdInfo.name += DESC_ALT;
}
else {
return rtnDesc;
}
}
rtnDesc = new SdjDescription(descJI, SdjHost.getHost());
this._library.storeDesc(rtnDesc);
return rtnDesc;
}
initLogs(options) {
if (options?.loggerStore && options?.logManager) {
throw new Error("[SDJ] Create a custom ILogManager or alternate loggerStore, not both;");
}
else if (options?.logManager) {
if (!isFunction(options.logManager.getLogFunc)) {
throw new Error("[SDJ] custom ILogManager missing getLogFunc");
}
else {
this._logs = options.logManager;
}
}
else {
const logMode = (options?.logMode) ? options.logMode : ESDJ_LOG.PROD;
this._logs = new LogManager(logMode, options?.loggerStore);
}
}
verifyLibrary(inLib) {
const vfList = [inLib.init, inLib.getDescByName, inLib.getDescList,
inLib.storeDesc, inLib.removeDesc];
each(vfList, (testFunc) => {
if (!isFunction(testFunc)) {
throw new Error("[SDJ] Illegal ISdjLibrary assignment, does not include all functions;");
}
});
}
// This is for testing purposes commented out on initial release // builds
static setTestingInstance(testInstance) {
this._instance = testInstance;
}
// Primary static external access point
static getISdjHost(options) {
if (this._instance && options) {
throw new Error("[SDJ] Illegal attempt to change SDJ after initialization;");
}
else if (!this._instance) {
this._instance = new SdjHost(new IntSingletonLock(), options);
}
return this._instance;
}
// Sdj Library is built to allow initialization ONCE, and will error on second+ attempts.
//
// Use of either top-level library classes (Description, Json) before an initialization setup
// will lock the system to defaults; This entry point is only meant for those classes.
// Independently created Entities, Items and Data are exempt.
//
// Use of this entry point does allow access to core SdjHost functionality; if needed.
//
static getHost() {
if (!this._instance) {
this._instance = new SdjHost(new IntSingletonLock());
}
return this._instance;
}
}
//# sourceMappingURL=host.js.map