UNPKG

sdj-esm

Version:
314 lines (308 loc) 12.4 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 { ESDJ_CLASS, ESDJ_LIMIT, GRAPH_ID, SYS_RESERVED } from "../core/statics.js"; import { genSdKeyProps } from "../util/immutables.js"; import { isArrayWithLen } from "../core/validators.js"; import { restrictCoreSD, restrictLimiter, restrictToAllowedKeys } from "../core/restrict.js"; import { each, isBoolean, isEmpty, isEqual, isFunction, isNull, isNumber, isObject, isString, isUndefined, uniq } from "lodash-es"; import { validIntArray, validSDKey } from "../core/sdj-types.js"; /* Entities provide the connective information between the data represented in the SDJ. sdItems* Items that are assigned to entity, unique pos-int values only extendIds* Entities extended by current entity parentIds* Entities that are allowed to contain current entity childIds* Entities that are allowed to be children of current entity limiter 4 Avail: (ESDJ_LIMIT) .REQ Entity is required by parent .REQ_HIDE Entity is required by parent hidden from user (for only one entity) .ONE_NONE Entity is singular on parent or not-present .REQ_ONE At least one Entity is required by parent .KEY_IDX Child Entities of singular type sdProps Optional properties for entities and used by lexicons or manually dataInfo Boolean switch for if Entity stores creation/modification information (sdInfo object) *All pos-int number[] are uniq'd and sorted before operations Extending Rules - - sdProps/limiter and all number[] are considered static during runtime and should be re-validated if changed - auto-inherent all sdItems from lower entities, auto-remove duplicate items - Does not affect childIds or parentIds, which are unique per Entity - sdProps are chained assigned based on entity/order - genJI sdProps will show final values - limiter value matches last extended item if not provided Note: in Sdj "$" functions for deliberate class-only implementations which don't appear on IEntitySdj and should only be used by classes/files that implement "new SdjEntity" in any way. */ export class SdjEntity { _sdId; _sdKey; _parentIds; _sdItems; _extendIds; _childIds; _dataInfo; _limiter; _sdProps; _description; _childRefs; _itemRefs; constructor(inEnt, description) { this._description = this.confirmDescript(description); if (this._description) { this._description.host.checkClassInst(inEnt, ESDJ_CLASS.ENTITY, false); } SdjEntity.VerifyJI(inEnt); this.graphZeroLockout(inEnt); this._sdId = inEnt.sdId; this._sdKey = inEnt.sdKey; if (isArrayWithLen(inEnt.extendIds)) { this._extendIds = uniq([...inEnt.extendIds]); } if (isArrayWithLen(inEnt.parentIds)) { this._parentIds = uniq([...inEnt.parentIds]); } else { this._parentIds = [0]; } if (isArrayWithLen(inEnt.childIds)) { this._childIds = uniq([...inEnt.childIds]); } if (isArrayWithLen(inEnt.sdItems)) { const inEntItems = [...inEnt.sdItems]; this._sdItems = uniq([0, 1].concat(inEntItems)); } else { this._sdItems = [0, 1]; } if (inEnt.dataInfo) { this._dataInfo = inEnt.dataInfo; } if (inEnt.sdProps) { this._sdProps = genSdKeyProps(inEnt.sdProps); } this._limiter = inEnt.limiter || ESDJ_LIMIT.NONE; } get sdKey() { return this._sdKey; } get sdId() { return this._sdId; } get parentIds() { return this._parentIds; } get sdItems() { return this._sdItems; } get extendIds() { return this._extendIds; } get childIds() { return this._childIds; } get childRefs() { return (this._childRefs) ? this._childRefs : {}; } get itemRefs() { return (this._itemRefs) ? this._itemRefs : {}; } get description() { if (!this._description) { throw new ReferenceError("[SDJ] Entity has no description;"); } return this._description; } get sdProps() { return this._sdProps; } get limiter() { return this._limiter; } get dataInfo() { return this._dataInfo; } set description(inDesc) { if (!this._description) { this._description = this.confirmDescript(inDesc); if (this._description) { this.$refreshRefs(); } else { throw new Error("[SDJ] Incorrect or missing description/host;"); } } else { throw new Error("[SDJ] an Entity item is prevented from having its description overwritten;"); } } validStruct(dataSdj, parentRef, strict = false) { if (!this._description) { return false; } return this._description.host.searchMgr.validStruct(this, dataSdj, parentRef, strict); } validData(dataSdj, strict = false) { if (!this._description) { return false; } return this._description.host.searchMgr.validData(this, dataSdj, strict); } genJI() { const rtnEntJI = { sdId: this._sdId, sdKey: this._sdKey, }; // parents will always be at least [0] and should only be eliminated when === [0] if (this._parentIds.length > 1 || this._parentIds[0] !== 0) { rtnEntJI.parentIds = [...this._parentIds]; } each(this._sdItems, (itemId) => { if (itemId !== 0 && itemId !== 1) { if (!rtnEntJI.sdItems) { rtnEntJI.sdItems = []; } rtnEntJI.sdItems.push(itemId); } }); if (isArrayWithLen(this._extendIds)) { rtnEntJI.extendIds = [...this._extendIds]; } if (isArrayWithLen(this._childIds)) { rtnEntJI.childIds = [...this._childIds]; } if (this._dataInfo) { rtnEntJI.dataInfo = true; } if (this.sdProps) { let rtnProps; if (this._description) { rtnProps = this._description.calcSdKeyProps(this); if (!isEmpty(rtnProps)) { rtnEntJI.sdProps = rtnProps; } } else { rtnEntJI.sdProps = genSdKeyProps(this.sdProps); } } if (this._limiter !== ESDJ_LIMIT.NONE) { rtnEntJI.limiter = this._limiter; } return rtnEntJI; } $refreshRefs() { if (this._description) { this._itemRefs = {}; this._childRefs = {}; each(this._childIds, (num) => { const childRef = this._description?.getEntity(num); if (childRef) { this.childRefs[num] = childRef; } else { throw new Error("[SDJ] if this happens something else was missed presence of entity refs should be confirmed;"); } }); each(this._description.getItemsByEntity(this._sdId), (sdItem) => { this.itemRefs[sdItem.sdKey] = sdItem; }); } } confirmDescript(inDesc) { let rtnVal = false; if (!isNull(inDesc) && !isUndefined(inDesc)) { if (inDesc.host?.checkClassInst && isFunction(inDesc.host.checkClassInst)) { inDesc.host.checkClassInst(inDesc, ESDJ_CLASS.DESCRIPTION, true); rtnVal = true; } else if (!inDesc.host && isObject(inDesc)) { throw new Error("[SDJ] Improper Entity init attempt use IDescriptionSdj;"); } } return (rtnVal) ? inDesc : undefined; } graphZeroLockout(inEnt) { if (inEnt.sdId === 0 || inEnt.sdKey === GRAPH_ID) { if (this._description) { // throw nothing for now } else { throw new Error("[SDJ] Entity 0 is reserved for system requires SdjDescription to build;"); } } } static VerifyJI(inEnt) { restrictCoreSD(inEnt); if ((inEnt.sdId === 0 && inEnt?.sdKey !== GRAPH_ID) || inEnt.sdId < 0) { throw new Error(`[SDJ] Entity: '${inEnt.sdKey}' jixId: ${inEnt.sdId} <= 0;`); } else if (inEnt.sdId === 0 && inEnt.sdKey === GRAPH_ID) { return; } SdjEntity.checkArrays(inEnt); if (inEnt.sdProps && !isObject(inEnt.sdProps)) { throw new TypeError(`[SDJ] Entity '${inEnt.sdKey}' invalid keyValues;`); } else if (inEnt.sdProps) { each(inEnt.sdProps, (jValue, key) => { if (SYS_RESERVED.indexOf(key) !== -1 || !validSDKey(key)) { throw new TypeError(`[SDJ] Entity '${inEnt.sdKey}' has an illegal keyName: ${key};`); } else if (!(isString(jValue) || isNumber(jValue) || isBoolean(jValue))) { throw new TypeError(`[SDJ] Entity '${inEnt.sdKey}' key '${key}' has illegal value, not string|number|boolean;`); } }); } if (inEnt.limiter && !restrictLimiter(inEnt.limiter)) { throw new TypeError(`[SDJ] Entity '${inEnt.sdKey}' has unrecognized limiter;`); } else if (inEnt.limiter && inEnt.limiter === ESDJ_LIMIT.KEY_IDX) { if (inEnt.parentIds && !isEqual(inEnt.parentIds, [0])) { throw new TypeError("[SDJ] sdIndexed Entities can only exist on root;"); } if (!inEnt.childIds) { throw new TypeError("[SDJ] sdIndexed Entities are required to have children;"); } } if (inEnt.dataInfo && !isBoolean(inEnt.dataInfo)) { throw new TypeError("[SDJ] Entity dataInfo value boolean only if present;"); } restrictToAllowedKeys("EntityJI: " + inEnt.sdKey, ["sdKey", "sdId", "sdItems", "extendIds", "parentIds", "childIds", "limiter", "sdProps", "dataInfo"], inEnt); } static checkArrays(inEnt) { const nameRefs = ["childIds", "parentIds", "extendIds", "childIds", "sdItems"], valueRefArray = [inEnt.childIds, inEnt.parentIds, inEnt.extendIds, inEnt.childIds, inEnt.sdItems]; each(nameRefs, (keyId, idx) => { const valueRef = valueRefArray[idx]; if (isArrayWithLen(valueRef)) { if (!validIntArray(valueRef)) { throw new TypeError(`[SDJ] Entity '${inEnt.sdKey}' invalid '${keyId}' array;`); } else { switch (keyId) { case "childIds": if (valueRef && valueRef.indexOf(0) !== -1) { throw new Error(`[SDJ] Entity: '${inEnt.sdKey}' GraphZero[0] is not allowed to be a child of an entity;`); } break; case "extendIds": if (valueRef && valueRef.indexOf(0) !== -1) { throw new Error(`[SDJ] Entity '${inEnt.sdKey}' cannot extend graphZero [0];`); } else if (valueRef && valueRef.indexOf(inEnt.sdId) !== -1) { throw new Error(`[SDJ] Entity: '${inEnt.sdKey}' cannot extend itself;`); } break; default: // no default; } } } if (keyId === "sdItems" && !inEnt.sdItems && !inEnt.extendIds && !inEnt.childIds) { throw new Error(`[SDJ] Entity '${inEnt.sdKey}' has no items, does not extend, & has no children;`); } }); } } //# sourceMappingURL=entity.js.map