UNPKG

agentlang

Version:

The easiest way to build the most reliable AI agents - enterprise-grade teams of AI agents that collaborate with each other and humans

1,553 lines (1,552 loc) 106 kB
import chalk from 'chalk'; import { isRelNodes, isLiteral, } from '../language/generated/ast.js'; import { Path, nameToPath, isString, isNumber, isBoolean, isFqName, makeFqName, DefaultModuleName, DefaultModules, joinStatements, isMinusZero, now, findMetaSchema, findAllPrePostTriggerSchema, asCrudType, isPath, findUqCompositeAttributes, escapeFqName, encryptPassword, splitFqName, splitRefs, forceAsFqName, validateIdFormat, nameContainsSepEscape, registerInitFunction, } from './util.js'; import { parseStatement } from '../language/parser.js'; import { AdminSession } from './auth/defs.js'; import { FetchModuleFn, PathAttributeName } from './defs.js'; import { logger } from './logger.js'; import { CasePattern, FlowStepPattern } from '../language/syntax.js'; import { getAgentDirectives, getAgentDirectivesJson, getAgentGlossary, getAgentGlossaryJson, getAgentResponseSchema, getAgentScenarios, getAgentScenariosJson, registerAgentDirectives, registerAgentGlossary, registerAgentResponseSchema, registerAgentScenarios, removeAgentDirectives, removeAgentGlossary, removeAgentResponseSchema, removeAgentScenarios, } from './agents/common.js'; import { isNode } from '../utils/fs-utils.js'; export class ModuleEntry { constructor(name, moduleName) { this.taggedAsPublic = false; if (nameContainsSepEscape(name)) { throw new Error(`Name cannot contain reserved escape characters - ${name}`); } this.name = name; this.moduleName = moduleName; } getFqName() { return makeFqName(this.moduleName, this.name); } setPublic(flag) { this.taggedAsPublic = flag; return this; } isPublic() { return this.taggedAsPublic; } } function normalizePropertyNames(props) { // Convert iterator to array for compatibility with different Node.js versions const normKs = Array.from(props.keys()).filter((k) => { return k.charAt(0) === '@'; }); normKs.forEach((k) => { const v = props.get(k); props.delete(k); props.set(k.substring(1), v); }); } const SystemAttributeProperty = 'system-attribute'; const SystemDefinedEvent = 'system-event'; function asSystemAttribute(attrSpec) { const props = attrSpec.properties ? attrSpec.properties : new Map(); props.set(SystemAttributeProperty, true); attrSpec.properties = props; return attrSpec; } function isSystemAttribute(attrSpec) { if (attrSpec.properties) { return attrSpec.properties.get(SystemAttributeProperty) == true; } return false; } function recordSchemaToString(scm) { const ss = []; scm.forEach((attrSpec, n) => { if (!isSystemAttribute(attrSpec)) { ss.push(` ${n} ${attributeSpecToString(attrSpec)}`); } }); return `\n${ss.join(',\n')}`; } function attributeSpecToString(attrSpec) { let s = getEnumValues(attrSpec) || getOneOfRef(attrSpec) ? '' : `${attrSpec.type}`; if (isArrayAttribute(attrSpec)) { s = `${s}[]`; } if (attrSpec.properties) { const ps = []; const hasEnum = attrSpec.properties.has('enum'); const hasOneOf = attrSpec.properties.has('oneof'); const needsReordering = hasEnum || hasOneOf; if (needsReordering) { const priorityKeys = ['enum', 'oneof']; const processedKeys = new Set(); priorityKeys.forEach(k => { if (attrSpec.properties.has(k)) { const v = attrSpec.properties.get(k); if (v == true) ps.push(` @${k}`); else ps.push(` @${k}(${attributePropertyValueToString(k, v, attrSpec.type)})`); processedKeys.add(k); } }); attrSpec.properties.forEach((v, k) => { if (k !== 'array' && !processedKeys.has(k)) { if (v == true) ps.push(` @${k}`); else ps.push(` @${k}(${attributePropertyValueToString(k, v, attrSpec.type)})`); } }); } else { attrSpec.properties.forEach((v, k) => { if (k != 'array') { if (v == true) ps.push(` @${k}`); else ps.push(` @${k}(${attributePropertyValueToString(k, v, attrSpec.type)})`); } }); } s = s.concat(ps.join(' ')); } return s; } function attributePropertyValueToString(propName, propValue, attrType) { if (propName == EnumPropertyName) { const v = propValue; const ss = new Array(); v.forEach((s) => { ss.push(`"${s}"`); }); return ss.join(','); } else if (propName == 'default') { if (isTextualType(attrType) && propValue != 'now()' && propValue != 'uuid()') { return `"${propValue}"`; } } else if (propName == 'comment') { return `"${propValue}"`; } return `${propValue}`; } export function newRecordSchema() { return new Map(); } export function newMeta() { return new Map(); } export var RecordType; (function (RecordType) { RecordType[RecordType["RECORD"] = 0] = "RECORD"; RecordType[RecordType["ENTITY"] = 1] = "ENTITY"; RecordType[RecordType["EVENT"] = 2] = "EVENT"; RecordType[RecordType["RELATIONSHIP"] = 3] = "RELATIONSHIP"; RecordType[RecordType["AGENT"] = 4] = "AGENT"; })(RecordType || (RecordType = {})); function normalizeMetaValue(metaValue) { if (!isLiteral(metaValue)) { throw new Error(`Invalid entry ${metaValue} in meta specification - expected a literal`); } const v = metaValue; if (v.array) { return v.array.vals.map((value) => { return normalizeMetaValue(value.pattern.expr); }); } else if (v.bool !== undefined) { return v.bool == 'true' ? true : false; } else if (v.id) { return v.id; } else if (v.map) { const result = new Map(); v.map.entries.forEach((value) => { result.set(value.key, normalizeMetaValue(value.value)); }); return result; } else if (v.ref) { return v.ref; } else if (v.num) { return v.num; } else if (v.str) { return v.str; } else { throw new Error(`Invalid value ${metaValue} passed to meta specification`); } } function asTriggerInfo(te) { return { eventName: te.event, async: te.async ? true : false, }; } const EnumPropertyName = 'enum'; const OneOfPropertyName = 'oneof'; export function enumAttributeSpec(values) { return { type: 'String', properties: new Map().set(EnumPropertyName, values), }; } export function oneOfAttributeSpec(ref) { return { type: 'String', properties: new Map().set(OneOfPropertyName, ref), }; } export class Record extends ModuleEntry { constructor(name, moduleName, scm, parentEntryName) { super(name, moduleName); this.type = RecordType.RECORD; this.parentEntryName = parentEntryName; this.schema = parentEntryName ? cloneParentSchema(parentEntryName, moduleName) : newRecordSchema(); const attributes = scm ? scm.attributes : undefined; if (attributes !== undefined) { attributes.forEach((a) => { var _a, _b; verifyAttribute(a); let props = asPropertiesMap(a.properties); const isArrayType = a.arrayType ? true : false; let t = isArrayType ? a.arrayType : a.type; if (a.refSpec) { let fp = a.refSpec.ref; const rp = nameToPath(fp); if (!rp.hasModule()) { rp.setModuleName(this.moduleName); fp = rp.asFqName(); } if (props === undefined) { props = new Map(); } props.set('ref', escapeFqName(fp)); t = a.refSpec.type === undefined ? 'Any' : a.refSpec.type; } const enumValues = (_a = a.enumSpec) === null || _a === void 0 ? void 0 : _a.values; const oneOfRef = (_b = a.oneOfSpec) === null || _b === void 0 ? void 0 : _b.ref; if (!t) { if (enumValues || oneOfRef) { t = 'String'; } else { throw new Error(`Attribute ${a.name} requires a type`); } } if (a.expr) { if (props === undefined) { props = new Map(); } props.set('expr', a.expr).set('optional', true); } const isObjectType = t == 'Map' || !isBuiltInType(t); if (isArrayType || isObjectType || enumValues || oneOfRef) { if (props === undefined) { props = new Map(); } if (isArrayType) props.set('array', true); if (isObjectType) props.set('object', true); if (enumValues) props.set(EnumPropertyName, new Set(enumValues)); if (oneOfRef) { props.set(OneOfPropertyName, oneOfRef); this.addOneOfRefAttribute(a.name); } } this.schema.set(a.name, { type: t, properties: props }); }); } const meta = findMetaSchema(scm); if (meta) { meta.spec.entries.forEach((entry) => { if (entry.key.str) this.addMeta(entry.key.str, normalizeMetaValue(entry.value)); else throw new Error(`Key must be a string for meta-definition in ${this.moduleName}/${this.name}`); }); } const prepostTrigs = findAllPrePostTriggerSchema(scm); if (prepostTrigs) { prepostTrigs.forEach((ppt) => { if (ppt.after) { if (this.afterTriggers === undefined) { this.afterTriggers = new Map(); } ppt.after.triggers.entries.forEach((te) => { if (this.afterTriggers) this.afterTriggers.set(asCrudType(te.on), asTriggerInfo(te)); }); } else if (ppt.before) { if (this.beforeTriggers === undefined) { this.beforeTriggers = new Map(); } ppt.before.triggers.entries.forEach((te) => { if (this.beforeTriggers) this.beforeTriggers.set(asCrudType(te.on), asTriggerInfo(te)); }); } }); } this.compositeUqAttributes = findUqCompositeAttributes(scm); } addMetaAttributes() { this.schema .set(SysAttr_Created, SysAttr_CreatedSpec) .set(SysAttr_CreatedBy, SysAttr_CreatedBySpec) .set(SysAttr_LastModified, SysAttr_LastModifiedSpec) .set(SysAttr_LastModifiedBy, SysAttr_LastModifiedBySpec); return this; } addOneOfRefAttribute(s) { if (this.oneOfRefAttributes === undefined) { this.oneOfRefAttributes = []; } this.oneOfRefAttributes.push(s); return this; } addAfterTrigger(te) { var _a; if (this.afterTriggers === undefined) { this.afterTriggers = new Map(); } (_a = this.afterTriggers) === null || _a === void 0 ? void 0 : _a.set(asCrudType(te.on), asTriggerInfo(te)); return this; } addBeforeTrigger(te) { var _a; if (this.beforeTriggers === undefined) { this.beforeTriggers = new Map(); } (_a = this.beforeTriggers) === null || _a === void 0 ? void 0 : _a.set(asCrudType(te.on), asTriggerInfo(te)); return this; } getCompositeUniqueAttributes() { return this.compositeUqAttributes; } getPreTriggerInfo(crudType) { if (this.beforeTriggers) { return this.beforeTriggers.get(crudType); } return undefined; } getPostTriggerInfo(crudType) { if (this.afterTriggers) { return this.afterTriggers.get(crudType); } return undefined; } addMeta(k, v) { if (!this.meta) { this.meta = newMeta(); } this.meta.set(k, v); } getMeta(k) { if (this.meta) { return this.meta.get(k); } else { return undefined; } } getFullTextSearchAttributes() { const fts = this.getMeta('fullTextSearch'); if (fts) { if (fts instanceof Array) { return fts; } else if (fts == '*') { return [...this.schema.keys()]; } else { return undefined; } } else { return undefined; } } addAttribute(n, attrSpec) { if (this.schema.has(n)) { throw new Error(`Attribute named ${n} already exists in ${this.moduleName}.${this.name}`); } if (attrSpec.properties !== undefined) { normalizePropertyNames(attrSpec.properties); } this.schema.set(n, attrSpec); return this; } removeAttribute(n) { this.schema.delete(n); return this; } reorderAttributes(desiredOrder) { this.schema = new Map([...this.schema].sort((a, b) => { return desiredOrder.indexOf(a[0]) - desiredOrder.indexOf(b[0]); })); } addSystemAttribute(n, attrSpec) { asSystemAttribute(attrSpec); this.addAttribute(n, attrSpec); return this; } findAttribute(predic) { for (const k of this.schema.keys()) { const attrSpec = this.schema.get(k); if (attrSpec !== undefined) { if (predic(attrSpec)) return { name: k, spec: attrSpec, }; } } return undefined; } hasRefTo(modName, entryName) { if (this.findAttribute((attrSpec) => { if (attrSpec.properties !== undefined) { const ref = attrSpec.properties.get('ref'); if (ref !== undefined) { if (ref.getModuleName() == modName && ref.getEntryName() == entryName) { return true; } } } return false; })) return true; else return false; } getRefOnAttribute(attrName) { const fkspecs = this.getFkAttributeSpecs(); const a = fkspecs.find((v) => { return v.columnName === attrName; }); if (a) { return [makeFqName(a.targetModuleName, a.targetEntityName), a.targetColumnName]; } return undefined; } getIdAttributeName() { const e = this.findAttribute((attrSpec) => { return isIdAttribute(attrSpec); }); if (e !== undefined) { return e.name; } return undefined; } getFkAttributeSpecs() { const result = new Array(); this.schema.forEach((attrSpec, columnName) => { const refSpec = getRefSpec(attrSpec); if (refSpec) { const targetNames = forceAsFqName(refSpec, this.moduleName); const parts = splitFqName(targetNames); const targetModuleName = parts[0]; const refs = splitRefs(parts[1]); const targetEntityName = refs[0]; let targetColumnName = ''; if (refs.length <= 1) { targetColumnName = PathAttributeName; } else { targetColumnName = refs[1]; } result.push({ moduleName: this.moduleName, entityName: this.name, columnName, targetModuleName, targetEntityName, targetColumnName, onDelete: 'SET NULL', onUpdate: 'CASCADE', }); } }); return result; } toString() { return this.toString_(); } toString_(internParentSchema = false) { if (this.type == RecordType.EVENT && this.meta && this.meta.get(SystemDefinedEvent)) { return ''; } let s = `${RecordType[this.type].toLowerCase()} ${this.name}`; let scm = this.schema; if (this.parentEntryName && !internParentSchema) { s = s.concat(` extends ${this.parentEntryName}`); scm = newRecordSchema(); let modName = this.moduleName; let pname = this.parentEntryName; if (isFqName(pname)) { const parts = splitFqName(pname); modName = parts[0]; pname = parts[1]; } const p = fetchModule(modName).getEntry(pname); const pks = new Set(p.schema.keys()); this.schema.forEach((v, n) => { if (!pks.has(n)) { scm.set(n, v); } }); } let scms = recordSchemaToString(scm); if (this.rbac && this.rbac.length > 0) { const rbs = this.rbac.map((rs) => { return rs.toString(); }); scms = `${scms},\n @rbac [${rbs.join(',\n')}]`; } if (this.meta && this.meta.size > 0) { const metaObj = Object.fromEntries(this.meta); const ms = `@meta ${JSON.stringify(metaObj)}`; scms = `${scms},\n ${ms}`; } if (this.isPublic()) { s = `@public ${s}`; } return s.concat('\n{', scms, '\n}\n'); } getUserAttributes() { const recSchema = newRecordSchema(); this.schema.forEach((attrSpec, n) => { if (!isSystemAttribute(attrSpec)) { recSchema.set(n, attrSpec); } }); return recSchema; } getUserAttributeNames() { return [...this.getUserAttributes().keys()]; } } function fetchModuleByEntryName(entryName, suspectModuleName) { if (isFqName(entryName)) { const path = nameToPath(entryName); entryName = path.getEntryName(); suspectModuleName = path.getModuleName(); } return { module: fetchModule(suspectModuleName), entryName: entryName, moduleName: suspectModuleName, }; } function cloneParentSchema(parentName, currentModuleName) { const fr = fetchModuleByEntryName(parentName, currentModuleName); parentName = fr.entryName; currentModuleName = fr.moduleName; const mod = fr.module; const entry = mod.getEntry(parentName); const result = newRecordSchema(); entry.schema.forEach((attrSpec, attrName) => { result.set(attrName, attrSpec); }); return result; } function asPropertiesMap(props) { if (props !== undefined && props.length > 0) { const result = new Map(); props.forEach((p) => { const n = p.name.substring(1); if (p.value !== undefined && p.value.pairs !== undefined && p.value.pairs.length > 0) { if (p.value.pairs.length == 1) { const kvp = p.value.pairs[0]; if (kvp.key === undefined) { result.set(n, normalizeKvPairValue(kvp)); } else { const v = new Map(); v.set(kvp.key, normalizeKvPairValue(kvp)); result.set(n, v); } } else { const v = new Map(); p.value.pairs.forEach((kvp) => { let k = 'null'; if (kvp.key !== undefined) k = kvp.key; v.set(k, normalizeKvPairValue(kvp)); }); result.set(n, v); } } else { result.set(n, true); } }); return maybeProcessRefProperty(result); } return undefined; } function maybeProcessRefProperty(props) { const v = props.get('ref'); if (v !== undefined) { const parts = nameToPath(v); if (!parts.hasModule()) { parts.setModuleName(activeModule); } props.set('ref', parts); } return props; } function normalizeKvPairValue(kvp) { const v = kvp.value; if (v === undefined) return true; if (v.str !== undefined) { return v.str; } else if (v.num !== undefined) { return v.num; } else if (v.bool !== undefined) { return v.bool === 'true' ? true : false; } else if (v.id !== undefined) { return v.id; } else if (v.ref !== undefined) { return v.ref; } else if (v.fnCall !== undefined) { const fncall = v.fnCall; if (fncall.args.length > 0) { throw new Error('Cannot allow arguments in properties function-call'); } return fncall.name + '()'; } else if (v.array !== undefined) { return v.array; } return null; } export const PlaceholderRecordEntry = new Record('--', DefaultModuleName); export var RbacPermissionFlag; (function (RbacPermissionFlag) { RbacPermissionFlag[RbacPermissionFlag["CREATE"] = 0] = "CREATE"; RbacPermissionFlag[RbacPermissionFlag["READ"] = 1] = "READ"; RbacPermissionFlag[RbacPermissionFlag["UPDATE"] = 2] = "UPDATE"; RbacPermissionFlag[RbacPermissionFlag["DELETE"] = 3] = "DELETE"; })(RbacPermissionFlag || (RbacPermissionFlag = {})); export class RbacSpecification { constructor() { this.resource = ''; this.roles = RbacSpecification.EmptyRoles; this.permissions = new Set(); } static from(def) { const result = new RbacSpecification(); def.entries.forEach((se) => { if (se.role) { result.setRoles(se.role.roles); } else if (se.allow) { result.setPermissions(se.allow.oprs.map((opr) => { return opr.value; })); } else if (se.expr) { result.setExpression(se.expr.lhs, se.expr.rhs); } }); return result; } setResource(s) { this.resource = s; return this; } hasResource() { return this.resource.length > 0; } setPermissions(perms) { const ps = new Set(); perms.forEach((v) => { const idx = v.toUpperCase(); const a = RbacPermissionFlag[idx]; if (a === undefined) { throw new Error(`Not a valid RBAC permission - ${v}`); } ps.add(a); }); this.permissions = ps; return this; } hasPermissions() { return this.permissions.size > 0; } hasCreatePermission() { return this.permissions.has(RbacPermissionFlag.CREATE); } hasReadPermission() { return this.permissions.has(RbacPermissionFlag.READ); } hasUpdatePermission() { return this.permissions.has(RbacPermissionFlag.UPDATE); } hasDeletePermission() { return this.permissions.has(RbacPermissionFlag.DELETE); } setRoles(roles) { if (this.expression) { throw new Error('Cannot set roles while `where` expression is set'); } this.roles = new Set(); roles.forEach((r) => { this.roles.add(r); }); return this; } removeRoles() { this.roles = RbacSpecification.EmptyRoles; return this; } setExpression(lhs, rhs) { if (this.roles != RbacSpecification.EmptyRoles) { logger.warn('Cannot set `where` expression along with roles, removing roles'); this.removeRoles(); } this.expression = { lhs: lhs, rhs: rhs, }; return this; } removeExpression() { this.expression = undefined; return this; } toString() { if (this.permissions.size <= 0) { throw new Error(`Cannot emit RbacSpecification, no permissions are set`); } const rs = new Array(); this.roles.forEach((r) => { rs.push(r); }); let cond = ''; if (this.expression) { cond = `where: ${this.expression.lhs} = ${this.expression.rhs}`; } else { cond = `roles: [${rs.join(',')}]`; } const perms = new Array(); this.permissions.forEach((p) => { perms.push(RbacPermissionFlag[p].toLowerCase()); }); return `(${cond}, allow: [${perms.join(',')}])`; } } RbacSpecification.EmptyRoles = new Set(); export class Agent extends Record { constructor(name, moduleName, attrs) { super(Agent.EscapeName(name), moduleName); this.type = RecordType.AGENT; this.attributes = attrs ? attrs : newInstanceAttributes(); } setName(n) { this.name = Agent.EscapeName(n); return this; } setLLM(llm) { this.attributes.set('llm', llm); return this; } getLLM() { return this.attributes.get('llm'); } removeAgentAttribute(n) { this.attributes.delete(n); return this; } removeLLM() { return this.removeAgentAttribute('llm'); } setInstruction(s) { this.attributes.set('instruction', s); return this; } getInstruction() { return this.attributes.get('instruction'); } removeInstruction() { return this.removeAgentAttribute('instruction'); } setType(type) { this.attributes.set('type', type); return this; } getType() { return this.attributes.get('type'); } removeType() { return this.removeAgentAttribute('type'); } setStrings(attrName, v) { this.attributes.set(attrName, v.join(',')); return this; } getStrings(attrName) { const v = this.attributes.get(attrName); if (v) { return v.split(','); } else { return undefined; } } setTools(tools) { return this.setStrings('tools', tools); } getTools() { return this.getStrings('tools'); } removeTools() { return this.removeAgentAttribute('tools'); } setDocuments(docs) { return this.setStrings('documents', docs); } getDocuments() { return this.getStrings('documents'); } removeDocuments() { return this.removeAgentAttribute('documents'); } setFlows(flows) { return this.setStrings('flows', flows); } getFlows() { return this.getStrings('flows'); } getAgentFqName() { return makeFqName(this.moduleName, this.getName()); } setDirectives(conds) { registerAgentDirectives(this.getAgentFqName(), conds); return this; } removeDirectives() { removeAgentDirectives(this.getAgentFqName()); return this; } getDirectives() { return getAgentDirectives(this.getAgentFqName()); } setScenarios(scenarios) { registerAgentScenarios(this.getAgentFqName(), scenarios); return this; } getScenarios() { return getAgentScenarios(this.getAgentFqName()); } removeScenarios() { removeAgentScenarios(this.getAgentFqName()); return this; } setGlossary(glossary) { registerAgentGlossary(this.getAgentFqName(), glossary); return this; } getGlossary() { return getAgentGlossary(this.getAgentFqName()); } removeGlossary() { removeAgentGlossary(this.getAgentFqName()); return this; } setResponseSchema(entryName) { registerAgentResponseSchema(this.getAgentFqName(), entryName); return this; } getResponseSchema() { return getAgentResponseSchema(this.getAgentFqName()); } removeResponseSchema() { removeAgentResponseSchema(this.getAgentFqName()); return this; } toString() { const attrs = new Array(); this.attributes.forEach((value, key) => { const skip = key == 'moduleName' || (key == 'type' && value == 'flow-exec'); if (!skip && value !== null && value !== undefined) { let v = value; const isf = key == 'flows'; if (isf || key == 'tools') { if (isf || v.indexOf(',') > 0 || v.indexOf('/') > 0) v = `[${v}]`; else v = `"${v}"`; } else if (isString(v)) { v = `"${v}"`; } attrs.push(` ${key} ${v}`); } }); const fqName = makeFqName(this.moduleName, this.getName()); const conds = getAgentDirectivesJson(fqName); if (conds) { attrs.push(` directives ${conds}`); } const scns = getAgentScenariosJson(fqName); if (scns) { attrs.push(` scenarios ${scns}`); } const gls = getAgentGlossaryJson(fqName); if (gls) { attrs.push(` glossary ${gls}`); } const rscm = getAgentResponseSchema(fqName); if (rscm) { attrs.push(` responseSchema ${rscm}`); } const s = `agent ${Agent.NormalizeName(this.name)} { ${attrs.join(',\n')} }`; if (this.isPublic()) { return `@public ${s}`; } else { return s; } } static EscapeName(n) { if (n.endsWith(Agent.Suffix)) { return n; } return `${n}${Agent.Suffix}`; } static NormalizeName(n) { if (n.endsWith(Agent.Suffix)) { return n.substring(0, n.lastIndexOf(Agent.Suffix)); } else { return n; } } getName() { return Agent.NormalizeName(this.name); } } Agent.Suffix = '_agent'; const SysAttr_Created = '__created'; const SysAttr_LastModified = '__last_modified'; const SysAttr_CreatedBy = '__created_by'; const SysAttr_LastModifiedBy = '__last_modified_by'; const SysAttr_CreatedSpec = asSystemAttribute({ type: 'DateTime', properties: new Map().set('default', 'now()'), }); const SysAttr_LastModifiedSpec = SysAttr_CreatedSpec; const SysAttr_CreatedBySpec = asSystemAttribute({ type: 'String', properties: new Map().set('optional', true), }); const SysAttr_LastModifiedBySpec = SysAttr_CreatedBySpec; export class Entity extends Record { constructor(name, moduleName, scm, parentEntryName) { super(name, moduleName, scm, parentEntryName); this.type = RecordType.ENTITY; this.addMetaAttributes(); } setRbacSpecifications(rbac) { this.rbac = rbac; return this; } getRbacSpecifications() { return this.rbac; } isConfigEntity() { const v = this.getMeta('configEntity'); return v == true; } } export class Event extends Record { constructor() { super(...arguments); this.type = RecordType.EVENT; } isSystemDefined() { var _a; return ((_a = this.meta) === null || _a === void 0 ? void 0 : _a.get(SystemDefinedEvent)) === 'true'; } } var RelType; (function (RelType) { RelType[RelType["CONTAINS"] = 0] = "CONTAINS"; RelType[RelType["BETWEEN"] = 1] = "BETWEEN"; })(RelType || (RelType = {})); export function newRelNodeEntry(nodeFqName, alias) { const p = nameToPath(nodeFqName); return { path: p, alias: alias ? alias : p.getEntryName(), origName: nodeFqName, origAlias: alias, }; } function relNodeEntryToString(node) { let n = `${node.origName}`; if (node.origAlias) { n = n.concat(` @as ${node.origAlias}`); } return n; } function asRelNodeEntry(n) { const path = nameToPath(n.name); let modName = activeModule; const entryName = path.getEntryName(); if (path.hasModule()) { modName = path.getModuleName(); } let alias = entryName; if (n.alias !== undefined) { alias = n.alias; } return { path: new Path(modName, entryName), alias: alias, origName: n.name, origAlias: n.alias, }; } const OneToOne = 'one_one'; const OneToMany = 'one_many'; const ManyToMany = 'many_many'; export class Relationship extends Record { constructor(name, typ, node1, node2, moduleName, scm, props) { super(name, moduleName, scm); this.type = RecordType.RELATIONSHIP; this.relType = RelType.CONTAINS; if (typ == 'between') { this.relType = RelType.BETWEEN; this.addMetaAttributes(); } this.node1 = node1; this.node2 = node2; this.properties = props; this.updateSchemaWithNodeAttributes(); } updateSchemaWithNodeAttributes() { const attrSpec1 = { type: 'string', }; this.addSystemAttribute(this.node1.alias, attrSpec1); const attrSpec2 = { type: 'string', }; this.addSystemAttribute(this.node2.alias, attrSpec2); if (this.relType == RelType.BETWEEN && this.isOneToMany()) { const attrSpec3 = { type: 'string', properties: new Map().set('unique', true), }; this.addSystemAttribute(this.joinNodesAttributeName(), attrSpec3); } } joinNodesAttributeName() { return this.node1.alias + '_' + this.node2.alias; } setBetweenRef(inst, refPath, isQuery = false) { const refAttrName = `__${this.node1.alias.toLowerCase()}`; if (isQuery) { inst.addQuery(refAttrName, '=', refPath); } else { inst.attributes.set(refAttrName, refPath); } } isContains() { return this.relType == RelType.CONTAINS; } isBetween() { return this.relType == RelType.BETWEEN; } parentNode() { return this.node1; } childNode() { return this.node2; } hasBooleanFlagSet(flag) { if (this.properties !== undefined) { return this.properties.get(flag) == true; } return false; } setProperty(p, v) { if (this.properties === undefined) { this.properties = new Map(); } this.properties.set(p, v); return this; } isOneToOne() { return this.isBetween() && this.hasBooleanFlagSet(OneToOne); } isOneToMany() { return this.isBetween() && this.hasBooleanFlagSet(OneToMany); } isManyToMany() { if (this.isBetween()) { return (this.hasBooleanFlagSet(ManyToMany) || (!this.hasBooleanFlagSet(OneToOne) && !this.hasBooleanFlagSet(OneToMany))); } else { return false; } } setOneToOne(flag = true) { if (flag) { this.setOneToMany(false).setManyToMany(false); } return this.setProperty(OneToOne, flag); } setOneToMany(flag = true) { if (flag) { this.setOneToOne(false).setManyToMany(false); } return this.setProperty(OneToMany, flag); } setManyToMany(flag = true) { this.setOneToOne(false).setOneToMany(false); return this.setProperty(ManyToMany, flag); } isFirstNode(inst) { return this.isFirstNodeName(inst.getFqName()); } getAliasFor(inst) { return this.getAliasForName(inst.getFqName()); } getInverseAliasFor(inst) { return this.getInverseAliasForName(inst.getFqName()); } isFirstNodeName(fqName) { return fqName == this.node1.path.asFqName(); } getAliasForName(fqName) { if (this.isFirstNodeName(fqName)) { return this.node1.alias; } else { return this.node2.alias; } } getInverseAliasForName(fqName) { if (this.isFirstNodeName(fqName)) { return this.node2.alias; } else { return this.node1.alias; } } isParent(inst) { return inst.getFqName() == this.node1.path.asFqName(); } getParentFqName() { return this.node1.path.asFqName(); } getChildFqName() { return this.node2.path.asFqName(); } toString() { const n1 = relNodeEntryToString(this.node1); const n2 = relNodeEntryToString(this.node2); let s = `relationship ${this.name} ${RelType[this.relType].toLowerCase()} (${n1}, ${n2})`; if (this.isBetween()) { if (this.isOneToOne()) { s = `${s} @${OneToOne}`; } else if (this.isOneToMany()) { s = `${s} @${OneToMany}`; } } if (this.getUserAttributes().size > 0) { const attrs = []; this.getUserAttributes().forEach((attrSpec, n) => { attrs.push(`${n} ${attributeSpecToString(attrSpec)}`); }); s = s.concat(`{\n ${attrs.join(',\n')} }`); } return s.concat('\n'); } } export class Workflow extends ModuleEntry { constructor(name, patterns, moduleName, isPrePost = false) { super(name, moduleName); this.statements = patterns; this.isPrePost = isPrePost; } async addStatement(stmtCode) { const result = await parseStatement(stmtCode); this.statements.push(result); return this; } setStatementAtHelper(statements, newStmt, index) { let stmt = statements[index[0]]; const isFe = stmt.pattern.forEach; const isIf = stmt.pattern.if; if (isFe || isIf) { for (let i = 1; i < index.length; ++i) { const found = i == index.length - 1; let idx = index[i]; if (stmt.pattern.forEach) { if (found) { if (!newStmt) { stmt.pattern.forEach.statements.splice(idx, 1); } else { stmt.pattern.forEach.statements[idx] = newStmt; } } else stmt = stmt.pattern.forEach.statements[idx]; } else if (stmt.pattern.if) { if (idx < 0 || isMinusZero(idx)) { if (stmt.pattern.if.else) { idx *= -1; if (found) { if (!newStmt) { stmt.pattern.if.else.statements.splice(idx, 1); } else { stmt.pattern.if.else.statements[idx] = newStmt; } } else stmt = stmt.pattern.if.else.statements[idx]; } else { throw new Error('No else part in if'); } } else { if (found) { if (!newStmt) { stmt.pattern.if.statements.splice(idx, 1); } else { stmt.pattern.if.statements[idx] = newStmt; } } else stmt = stmt.pattern.if.statements[idx]; } } else { throw new Error('Cannot dig further into statements'); } } } return this; } async setStatementAt(stmtCode, index) { const result = await parseStatement(stmtCode); if (index instanceof Array) { if (index.length == 1) { this.statements[index[0]] = result; return this; } else { return this.setStatementAtHelper(this.statements, result, index); } } else { this.statements[index] = result; } return this; } removeStatementAt(index) { if (index instanceof Array) { if (index.length == 1) { this.statements.splice(index[0], 1); return this; } else { return this.setStatementAtHelper(this.statements, undefined, index); } } else { this.statements.splice(index, 1); } return this; } statementsToStringsHelper(statements) { const ss = []; statements.forEach((stmt) => { var _a, _b; if (stmt.pattern.forEach) { ss.push(` for ${stmt.pattern.forEach.var} in ${(_a = stmt.pattern.forEach.src.$cstNode) === null || _a === void 0 ? void 0 : _a.text} { ${joinStatements(this.statementsToStringsHelper(stmt.pattern.forEach.statements))} }`); } else if (stmt.pattern.if) { let s = ` if (${(_b = stmt.pattern.if.cond.$cstNode) === null || _b === void 0 ? void 0 : _b.text}) { ${joinStatements(this.statementsToStringsHelper(stmt.pattern.if.statements))} }`; if (stmt.pattern.if.else) { s = s.concat(` else { ${joinStatements(this.statementsToStringsHelper(stmt.pattern.if.else.statements))} }`); } ss.push(s); } else if (stmt.$cstNode) { ss.push(` ${stmt.$cstNode.text.trimStart()}`); } }); return ss; } statementsToStrings() { return this.statementsToStringsHelper(this.statements); } setPublic(flag) { super.setPublic(flag); if (!this.isPrePost) { const n = normalizeWorkflowName(this.name); const event = getEvent(n, this.moduleName); event.setPublic(flag); } return this; } toString() { const n = this.isPrePost ? untangleWorkflowName(this.name) : this.name; const nn = normalizeWorkflowName(n); let s = `workflow ${nn} {\n`; const ss = this.statementsToStringsHelper(this.statements); s = s.concat(joinStatements(ss)); if (!this.isPrePost) { const event = getEvent(nn, this.moduleName); if ((event.isPublic() && event.isSystemDefined()) || this.isPublic()) { s = `@public ${s}`; } } else if (this.isPublic()) { s = `@public ${s}`; } return s.concat('\n}'); } } export class Flow extends ModuleEntry { constructor(name, moduleName, flow) { super(name, moduleName); this.flowSteps = new Array(); flow === null || flow === void 0 ? void 0 : flow.split('\n').forEach((step) => { const s = step.trim(); if (s.length > 0) { this.flowSteps.push(s); } }); } getFlow() { return this.flowSteps.join('\n'); } removeStep(index) { this.flowSteps.splice(index, 1); return this; } insertStep(index, s) { this.flowSteps.splice(index, 0, s); return this; } appendStep(s) { this.flowSteps.push(s); return this; } stepsCount() { return this.flowSteps.length; } toGraph() { const result = new Array(); this.flowSteps.forEach((s) => { const fp = FlowStepPattern.Parse(s); if (fp.condition) { const orig = result.find((v) => { return v.label == fp.first; }); if (orig) { const nxs = orig.next; nxs === null || nxs === void 0 ? void 0 : nxs.push(fp.next); const conds = orig.on; conds === null || conds === void 0 ? void 0 : conds.push(fp.condition); } else { result.push({ label: fp.first, type: 'condition', on: [fp.condition], next: [fp.next], }); } } else { result.push({ label: fp.first, type: 'action', next: [fp.next], }); } }); return result; } static asFlowName(n) { return `${n}.flow`; } static normaliseFlowName(n) { const i = n.lastIndexOf('.flow'); if (i > 0) { return n.substring(0, i); } else { return n; } } toString() { return `flow ${Flow.normaliseFlowName(this.name)} { ${this.getFlow()} }`; } } export class Scenario extends ModuleEntry { constructor(name, moduleName, scn) { super(name, moduleName); this.def = scn; } toString() { if (this.def.ifPattern) { return `scenario ${this.name} {\n ${this.def.ifPattern.toString()}\n}\n`; } const s = `if ("${this.def.user}") { ${this.def.ai} }`; return `scenario ${this.name} {\n ${s}\n}\n`; } } export class Directive extends ModuleEntry { constructor(name, moduleName, def) { super(name, moduleName); this.def = def; } toString() { if (this.def.ifPattern) { return `directive ${this.name} { ${this.def.if} }`; } else { const obj = { if: this.def.if, then: this.def.then, }; return `directive ${this.name} ${JSON.stringify(obj)}`; } } } export class GlossaryEntry extends ModuleEntry { constructor(name, moduleName, def) { super(name, moduleName); this.def = def; } toString() { const ss = new Array(); ss.push(` name "${this.def.name}"`); ss.push(` meaning "${this.def.meaning}"`); if (this.def.synonyms) { ss.push(` synonyms "${this.def.synonyms}"`); } return `glossaryEntry ${this.name} \n{\n${ss.join(',\n')}\n}`; } } export function flowGraphNext(graph, currentNode, onCondition) { var _a; if (!currentNode) { return graph[0]; } const node = graph.find((n) => { return n.label == currentNode.label; }); if (node) { if (onCondition) { const c = (_a = node.on) === null || _a === void 0 ? void 0 : _a.findIndex((v) => { return v == onCondition; }); if (c !== undefined) { const next = node.next[c]; const r = graph.find((n) => { return n.label == next; }); return r || { label: next, type: 'action', next: [] }; } else { return undefined; } } else { return graph.find((n) => { return n.label == node.next[0]; }); } } return undefined; } export class Retry extends ModuleEntry { constructor(name, moduleName, attempts) { super(name, moduleName); this.attempts = attempts <= 0 ? 0 : attempts; this.backoff = { strategy: undefined, delay: undefined, magnitude: undefined, factor: undefined, }; } setExponentialBackoff() { this.backoff.strategy = 'e'; return this; } isExponentialBackoff() { return this.backoff.strategy === 'e'; } setLinearBackoff() { this.backoff.strategy = 'l'; return this; } isLinearBackoff() { return this.backoff.strategy === 'l'; } setConstantBackoff() { this.backoff.strategy = 'c'; return this; } isConstantBackoff() { return this.backoff.strategy === undefined || this.backoff.strategy === 'c'; } setBackoffDelay(n) { if (n > 0) { this.backoff.delay = n; } return this; } setBackoffMagnitudeAsMilliseconds() { this.backoff.magnitude = 'ms'; return this; } backoffMagnitudeIsMilliseconds() {