UNPKG

sdj-esm

Version:
293 lines (292 loc) 12.2 kB
/* 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 { checkResetInfo, verifyUniqKeys, } from "../util/verify.js"; import { genEntityJI, genInfoJI, genItemJI } from "../util/immutables.js"; import { BASE_ITEMS_JI, ESDJ_CLASS, GRAPH_ZERO, SYS_RESERVED } from "../core/statics.js"; import { cloneJI, getFromCoreArray } from "../util/func.std.js"; import { isArrayWithLen, isInfo, validLexiconArray } from "../core/validators.js"; import { restrictToAllowedKeys } from "../core/restrict.js"; import { SdjEntity } from "./entity.js"; import { SdjItem } from "./item.js"; import { clone, cloneDeep, each, find, isArray, isEqual, isFunction, times, uniq } from "lodash-es"; import { validSDKey } from "../core/sdj-types.js"; import { getRegEx } from "../util/regex.js"; // Descriptions created outside of new SdJson() or SdjHost.makeDescription // using 'new SdjDescription(...)' ; will NOT automatically be added to a library. // After initial creation they can be added as needed via library.storeDesc(...); export class SdjDescription { log; _lexicons = []; _graph = []; _sdInfo; _lang = "en"; _items = []; _host; constructor(refDescJI, requiredHost) { this._host = requiredHost; if (requiredHost?.checkClassInst && isFunction(requiredHost.checkClassInst)) { this.host.checkClassInst(requiredHost, ESDJ_CLASS.HOST, true); this.host.checkClassInst(refDescJI, ESDJ_CLASS.DESCRIPTION, false); } else { throw new Error("[SDJ] Improper required Host - use SdjHost.createDescription for auto provided host;"); } SdjDescription.VerifyJI(refDescJI); let inDescJI; inDescJI = cloneJI(refDescJI); inDescJI.sdInfo = checkResetInfo(inDescJI.sdInfo); inDescJI = this._host.fullDescription(inDescJI); this._sdInfo = inDescJI.sdInfo; this.lang = (inDescJI.lang) ? String(inDescJI.lang).toLowerCase() : "en"; this.log = this._host.getLogFunc("Desc:" + this._sdInfo.name); this.log("created"); this.entityItemBuild(inDescJI); } // vvv Below are frozen when description/sdJson locked get graph() { return this._graph; } set graph(inGraph) { this._graph = inGraph; } set items(inItems) { this._items = inItems; } get items() { return this._items; } get name() { return this._sdInfo.name; } set name(name) { this._sdInfo.name = name; } get sdInfo() { return this._sdInfo; } get lang() { return this._lang; } set lang(inStr) { if (getRegEx("lang").test(inStr)) { this._lang = String(inStr).toLowerCase(); } } // ^^ Above Frozen when locked get host() { return this._host; } searchEntities(searchEnt) { return this._host.searchMgr.searchEntities(this, searchEnt); } searchItems(searchItem) { return this._host.searchMgr.searchItems(this, searchItem); } getEntity(entKeyNum) { return (entKeyNum === 0) ? undefined : getFromCoreArray(entKeyNum, this._graph); } getEntityRefs(entArray) { const rtnAry = []; each(entArray, (theValue) => { const entRef = getFromCoreArray(theValue, this._graph); if (entRef && entRef.sdId !== 0) { rtnAry.push(entRef); } else { this.log(`Unable to find item sdId/sdKey '${theValue}'`, 3); } }); return rtnAry; } calcSdKeyProps(entity) { // confirm entity name and sdId, and get from graph instead const localEnt = (entity && this._graph[entity.sdId]) ? this._graph[entity.sdId] : undefined; if (!localEnt || entity.sdKey !== localEnt.sdKey || !entity.sdProps) { this.log(`entity '${entity.sdKey}' does match description '${localEnt?.sdKey}'`); return (entity.sdProps) ? cloneDeep(entity.sdProps) : {}; // calcSdKeyProps shouldn't be called on entities that don't have sdProps } return this._host.lexiconMgr.calcSdKeyProps(entity, this._graph); } getItem(itemKeyNum) { return getFromCoreArray(itemKeyNum, this._items); } getItemRefs(itemArray) { const rtnAry = []; each(itemArray, (theValue) => { const itemRef = getFromCoreArray(theValue, this._items); if (itemRef) { rtnAry.push(itemRef); } else { this.log(`Unable to find item sdId/sdKey '${theValue}'`, 3); } }); return rtnAry; } genJI() { const rtnDescJI = { sdInfo: genInfoJI(this._sdInfo), graph: [], items: [], lang: this.lang }; let lexEntRefs = [], lexItemRefs = []; if (this._lexicons && this._lexicons.length > 0) { rtnDescJI.lexicons = clone(this._lexicons); each(this._lexicons, (lexId) => { const workLex = this._host.lexiconMgr.getByName(lexId); lexEntRefs = (workLex?.entities) ? lexEntRefs.concat(workLex.entities) : lexEntRefs; lexItemRefs = (workLex?.items) ? lexItemRefs.concat(workLex.items) : lexItemRefs; }); } each(this._items, (sdjItem) => { const itemInLex = find(lexItemRefs, { sdId: sdjItem.sdId }), itemInBase = find(BASE_ITEMS_JI, { sdId: sdjItem.sdId }); if (!itemInLex && !itemInBase) { rtnDescJI.items.push(sdjItem.genJI()); } }); each(this._graph, (sdjEnt) => { const entInLex = find(lexEntRefs, { sdId: sdjEnt.sdId }); if (sdjEnt.sdId !== 0 && !entInLex) { rtnDescJI.graph.push(sdjEnt.genJI()); } }); if (this._lang !== "en") { rtnDescJI.lang = this._lang; } return rtnDescJI; } // Use searches for misses; use this function only for Verified Graphs // Internals should not be calling for things that don't exist getEntityRefById(entId) { return (entId > 0) ? this._graph[entId] : undefined; } getItemsByEntity(entKeyNum) { const actualEnt = getFromCoreArray(entKeyNum, this._graph); if (!actualEnt) { return []; } let extendsList = (actualEnt.extendIds) ? clone(actualEnt.extendIds) : [], fullItemList = cloneDeep(actualEnt.sdItems), getEnts = (entRef) => { let rtnSdItems = cloneDeep(entRef.sdItems); if (entRef.extendIds && isArrayWithLen(entRef.extendIds)) { each(entRef.extendIds, (num) => { const subEntRef = getFromCoreArray(num, this._graph); if (subEntRef) { rtnSdItems = rtnSdItems.concat(getEnts(subEntRef)); } }); } return rtnSdItems; }; each(extendsList, (entNum) => { const topExtendRef = getFromCoreArray(entNum, this._graph); if (topExtendRef) { fullItemList = fullItemList.concat(getEnts(topExtendRef)); } }); fullItemList = uniq(fullItemList.sort()); return this.getItemRefs(fullItemList); } verifyJIbyType(ji, jiType, strict = false) { return this._host.verifyJIbyType(ji, jiType, strict); } entityItemBuild(inDescJI) { this._items = this.buildBaseItems(); const descriptRef = this; each(inDescJI.items, (itemJI) => { this.checkBase(itemJI, true); this._items.push(new SdjItem(genItemJI(itemJI), descriptRef)); }); each(inDescJI.graph, (entityJI) => { this.checkBase(entityJI, false); let defItemIds = times(BASE_ITEMS_JI.length); if (entityJI.sdItems && Array.isArray(entityJI.sdItems)) { defItemIds = defItemIds.concat(entityJI.sdItems); } entityJI.sdItems = defItemIds; this._graph.push(new SdjEntity(genEntityJI(entityJI), descriptRef)); }); this._graph.unshift(new SdjEntity(genEntityJI(GRAPH_ZERO), this)); this._lexicons = (inDescJI.lexicons) ? inDescJI.lexicons : []; each(this._items, (itemJI, idx) => { // Has been pre-sorted, final confirmation to confirm sdId === _item index alignment if (idx !== itemJI.sdId) { throw new Error("[SDJ] Description validity error, missing entity or error;"); } }); each(this._graph, (entity, idx) => { // This has been presorted before this, but confirms sdId === _graph index alignment if (idx !== entity.sdId) { throw new Error("[SDJ] Description validity error, missing entity or error;"); } const classRef = entity; classRef.$refreshRefs(); }); } buildBaseItems() { const newBaseItems = this._host.lexiconMgr.newBaseItems(), rtnArray = []; each(newBaseItems, (itemJI) => { rtnArray.push(new SdjItem(itemJI, this)); }); return rtnArray; } checkBase(itemBase, isItem) { const checkArray = (isItem) ? this._items : this._graph, errorType = (isItem) ? "Item" : "Entity"; if (SYS_RESERVED.indexOf(itemBase.sdKey) !== -1) { throw new Error(`[SDJ] Description: ${errorType} sdKey '${itemBase.sdKey}' is reserved;`); } if (find(checkArray, { sdKey: itemBase.sdKey })) { throw new Error(`[SDJ] Description: ${errorType} sdKey '${itemBase.sdKey}' already exists;`); } if (find(checkArray, { sdId: itemBase.sdId })) { throw new Error(`[SDJ] Description:: ${errorType} sdKey '${itemBase.sdKey}' has duplicate sdId [${itemBase.sdId}];`); } } strictLogError(logStr, strict) { if (strict) { throw new Error(logStr); } else { this.log(logStr, 3); } } static VerifyJI(inDesc) { if (!isInfo(inDesc.sdInfo)) { throw new Error("[SDJ] Description - improperly formatted sdInfo;"); } else if (isInfo(inDesc.sdInfo) && !validSDKey(inDesc.sdInfo.name)) { throw new Error(`[SDJ] Description name '${inDesc.sdInfo.name}' must be sdKey format;`); } if (inDesc.lexicons && !validLexiconArray(inDesc.lexicons)) { throw new Error("[SDJ] Description.lexicons improperly formed;"); } if ((!inDesc.items || !isArray(inDesc.items)) && !inDesc.lexicons) { throw new Error("[SDJ] Description - items missing or array error;"); } else if (!inDesc.lexicons && !verifyUniqKeys(inDesc.items)) { throw new Error("[SDJ] Description - items array must contain unique sdKeys and sdIds;"); } if (!inDesc.graph) { inDesc.graph = []; } else if ((inDesc.graph && !isArray(inDesc.graph)) && !inDesc.lexicons) { throw new Error("[SDJ] Description graph is not array;"); } else if (isArray(inDesc.graph) && !verifyUniqKeys(inDesc.graph)) { throw new Error("[SDJ] Description graph does not have unique sdKeys/sdIds;"); } if (inDesc.lang && !getRegEx("lang").test(inDesc.lang)) { throw new Error(`[SDJ] lang value can be 2-3 lower case chars; error with value:'${inDesc.lang}';`); } restrictToAllowedKeys("DescriptionJI:" + inDesc.sdInfo.name, ["sdInfo", "items", "graph", "lexicons", "lang"], inDesc); } static IsEqual(alpha, beta) { const betaJI = (beta instanceof SdjDescription) ? beta.genJI() : beta, alphaJI = (alpha instanceof SdjDescription) ? alpha.genJI() : alpha; return isEqual(alphaJI, betaJI); } } //# sourceMappingURL=description.js.map