agentlang 0.0.26 → 0.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/out/language/generated/ast.d.ts +16 -18
  2. package/out/language/generated/ast.d.ts.map +1 -1
  3. package/out/language/generated/ast.js +17 -20
  4. package/out/language/generated/ast.js.map +1 -1
  5. package/out/language/generated/grammar.d.ts.map +1 -1
  6. package/out/language/generated/grammar.js +231 -181
  7. package/out/language/generated/grammar.js.map +1 -1
  8. package/out/language/main.cjs +239 -196
  9. package/out/language/main.cjs.map +2 -2
  10. package/out/runtime/interpreter.d.ts +3 -1
  11. package/out/runtime/interpreter.d.ts.map +1 -1
  12. package/out/runtime/interpreter.js +15 -7
  13. package/out/runtime/interpreter.js.map +1 -1
  14. package/out/runtime/loader.js +1 -1
  15. package/out/runtime/loader.js.map +1 -1
  16. package/out/runtime/logger.js +1 -2
  17. package/out/runtime/logger.js.map +1 -1
  18. package/out/runtime/module.d.ts +8 -4
  19. package/out/runtime/module.d.ts.map +1 -1
  20. package/out/runtime/module.js +70 -13
  21. package/out/runtime/module.js.map +1 -1
  22. package/out/runtime/modules/core.js +2 -2
  23. package/out/runtime/modules/core.js.map +1 -1
  24. package/out/runtime/resolvers/interface.d.ts +1 -1
  25. package/out/runtime/resolvers/interface.d.ts.map +1 -1
  26. package/out/runtime/resolvers/interface.js +14 -8
  27. package/out/runtime/resolvers/interface.js.map +1 -1
  28. package/out/runtime/resolvers/sqldb/impl.js +1 -1
  29. package/out/runtime/resolvers/sqldb/impl.js.map +1 -1
  30. package/out/syntaxes/agentlang.monarch.js +1 -1
  31. package/out/syntaxes/agentlang.monarch.js.map +1 -1
  32. package/package.json +1 -1
  33. package/src/language/agentlang.langium +5 -3
  34. package/src/language/generated/ast.ts +31 -37
  35. package/src/language/generated/grammar.ts +231 -181
  36. package/src/runtime/interpreter.ts +24 -7
  37. package/src/runtime/loader.ts +1 -1
  38. package/src/runtime/logger.ts +1 -3
  39. package/src/runtime/module.ts +89 -16
  40. package/src/runtime/modules/core.ts +2 -2
  41. package/src/runtime/resolvers/interface.ts +17 -11
  42. package/src/runtime/resolvers/sqldb/impl.ts +1 -1
  43. package/src/syntaxes/agentlang.monarch.ts +1 -1
@@ -1062,7 +1062,7 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1062
1062
  if (env.isInUpsertMode()) {
1063
1063
  await runPreUpdateEvents(inst, env);
1064
1064
  r = await res.upsertInstance(inst);
1065
- await runPostUpdateEvents(inst, env);
1065
+ await runPostUpdateEvents(inst, undefined, env);
1066
1066
  } else {
1067
1067
  await runPreCreateEvents(inst, env);
1068
1068
  if (isTimer(inst)) triggerTimer(inst);
@@ -1178,7 +1178,7 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1178
1178
  await computeExprAttributes(lastRes[i], env);
1179
1179
  await runPreUpdateEvents(lastRes[i], env);
1180
1180
  const finalInst: Instance = await resolver.updateInstance(lastRes[i], attrs);
1181
- await runPostUpdateEvents(finalInst, env);
1181
+ await runPostUpdateEvents(finalInst, lastRes[i], env);
1182
1182
  res.push(finalInst);
1183
1183
  }
1184
1184
  env.setLastResult(res);
@@ -1190,7 +1190,7 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1190
1190
  await computeExprAttributes(lastRes, env);
1191
1191
  await runPreUpdateEvents(lastRes, env);
1192
1192
  const finalInst: Instance = await res.updateInstance(lastRes, attrs);
1193
- await runPostUpdateEvents(finalInst, env);
1193
+ await runPostUpdateEvents(finalInst, lastRes, env);
1194
1194
  env.setLastResult(finalInst);
1195
1195
  }
1196
1196
  }
@@ -1699,6 +1699,16 @@ async function realizeMap(mapLiteral: MapLiteral, env: Environment): Promise<voi
1699
1699
  env.setLastResult(Object.fromEntries(result.entries()));
1700
1700
  }
1701
1701
 
1702
+ export async function callPostEventOnSubscription(
1703
+ crudType: CrudType,
1704
+ inst: Instance,
1705
+ env?: Environment
1706
+ ): Promise<any> {
1707
+ const newEnv = env ? env : new Environment('onSubs.env');
1708
+ await runPrePostEvents(crudType, false, inst, newEnv);
1709
+ return newEnv.getLastResult();
1710
+ }
1711
+
1702
1712
  async function runPrePostEvents(
1703
1713
  crudType: CrudType,
1704
1714
  pre: boolean,
@@ -1730,8 +1740,11 @@ async function runPrePostEvents(
1730
1740
  }
1731
1741
  };
1732
1742
  if (trigInfo.async) {
1733
- evaluate(eventInst, callback).catch(catchHandler);
1743
+ const newEnv = new Environment('async.prepost.env');
1744
+ newEnv.bind('this', inst);
1745
+ evaluate(eventInst, callback, newEnv).catch(catchHandler);
1734
1746
  } else {
1747
+ env.bind('this', inst);
1735
1748
  await evaluate(eventInst, callback, env).catch(catchHandler);
1736
1749
  }
1737
1750
  }
@@ -1752,9 +1765,13 @@ async function runPreUpdateEvents(inst: Instance, env: Environment) {
1752
1765
  await runPrePostEvents(CrudType.UPDATE, true, inst, env);
1753
1766
  }
1754
1767
 
1755
- export async function runPostUpdateEvents(inst: Instance, env: Environment) {
1768
+ export async function runPostUpdateEvents(
1769
+ inst: Instance,
1770
+ oldInst: Instance | undefined,
1771
+ env: Environment
1772
+ ) {
1756
1773
  if (inst.requireAudit()) {
1757
- await addUpdateAudit(inst.getPath(), undefined, env);
1774
+ await addUpdateAudit(inst.getPath(), oldInst, env);
1758
1775
  }
1759
1776
  await runPrePostEvents(CrudType.UPDATE, false, inst, env);
1760
1777
  }
@@ -1765,7 +1782,7 @@ async function runPreDeleteEvents(inst: Instance, env: Environment) {
1765
1782
 
1766
1783
  export async function runPostDeleteEvents(inst: Instance, env: Environment) {
1767
1784
  if (inst.requireAudit()) {
1768
- await addDeleteAudit(inst.getPath(), undefined, env);
1785
+ await addDeleteAudit(inst.getPath(), inst, env);
1769
1786
  }
1770
1787
  await runPrePostEvents(CrudType.DELETE, false, inst, env);
1771
1788
  }
@@ -413,7 +413,7 @@ export function addRelationshipFromDef(
413
413
  }
414
414
 
415
415
  export function addWorkflowFromDef(def: WorkflowDefinition, moduleName: string): Workflow {
416
- return addWorkflow(def.name, moduleName, def.statements, def.hints);
416
+ return addWorkflow(def.name || '', moduleName, def.statements, def.header);
417
417
  }
418
418
 
419
419
  const StandaloneStatements = new Map<string, Statement[]>();
@@ -25,8 +25,6 @@ if (isNodeEnv) {
25
25
  maxFiles: '7d',
26
26
  });
27
27
 
28
- const consoleTransport = new winston.transports.Console();
29
-
30
28
  logger = winston.createLogger({
31
29
  format: winston.format.combine(
32
30
  winston.format.timestamp(),
@@ -34,7 +32,7 @@ if (isNodeEnv) {
34
32
  return `[${timestamp}] ${level}: ${message}`;
35
33
  })
36
34
  ),
37
- transports: [fileTransport, consoleTransport],
35
+ transports: [fileTransport],
38
36
  });
39
37
  } else {
40
38
  function mkLogger(tag: string): Function {
@@ -19,7 +19,7 @@ import {
19
19
  RbacSpecEntry,
20
20
  RbacSpecEntries,
21
21
  RbacOpr,
22
- WorkflowHint,
22
+ WorkflowHeader,
23
23
  } from '../language/generated/ast.js';
24
24
  import {
25
25
  Path,
@@ -45,7 +45,7 @@ import {
45
45
  } from './util.js';
46
46
  import { parseStatement } from '../language/parser.js';
47
47
  import { ActiveSessionInfo, AdminSession } from './auth/defs.js';
48
- import { FetchModuleFn, PathAttributeName, SetSubscription } from './defs.js';
48
+ import { FetchModuleFn, PathAttributeName } from './defs.js';
49
49
  import { logger } from './logger.js';
50
50
 
51
51
  export class ModuleEntry {
@@ -200,7 +200,7 @@ export type TriggerInfo = {
200
200
  async: boolean;
201
201
  };
202
202
 
203
- function asTriggerInfo(te: TriggerEntry): TriggerInfo {
203
+ function asTriggerInfo(te: any): TriggerInfo {
204
204
  return {
205
205
  eventName: te.event,
206
206
  async: te.async ? true : false,
@@ -338,6 +338,22 @@ export class Record extends ModuleEntry {
338
338
  return this;
339
339
  }
340
340
 
341
+ public addAfterTrigger(te: any): Record {
342
+ if (this.afterTriggers == undefined) {
343
+ this.afterTriggers = new Map();
344
+ }
345
+ this.afterTriggers?.set(asCrudType(te.on), asTriggerInfo(te));
346
+ return this;
347
+ }
348
+
349
+ public addBeforeTrigger(te: any): Record {
350
+ if (this.beforeTriggers == undefined) {
351
+ this.beforeTriggers = new Map();
352
+ }
353
+ this.beforeTriggers?.set(asCrudType(te.on), asTriggerInfo(te));
354
+ return this;
355
+ }
356
+
341
357
  getCompositeUniqueAttributes(): Array<string> | undefined {
342
358
  return this.compositeUqAttributes;
343
359
  }
@@ -1141,10 +1157,17 @@ export class Relationship extends Record {
1141
1157
 
1142
1158
  export class Workflow extends ModuleEntry {
1143
1159
  statements: Statement[];
1160
+ generatedName: boolean;
1144
1161
 
1145
- constructor(name: string, patterns: Statement[], moduleName: string) {
1162
+ constructor(
1163
+ name: string,
1164
+ patterns: Statement[],
1165
+ moduleName: string,
1166
+ generatedName: boolean = false
1167
+ ) {
1146
1168
  super(name, moduleName);
1147
1169
  this.statements = patterns;
1170
+ this.generatedName = generatedName;
1148
1171
  }
1149
1172
 
1150
1173
  async addStatement(stmtCode: string): Promise<Workflow> {
@@ -1262,7 +1285,8 @@ export class Workflow extends ModuleEntry {
1262
1285
  }
1263
1286
 
1264
1287
  override toString() {
1265
- let s: string = `workflow ${normalizeWorkflowName(this.name)} {\n`;
1288
+ const n = this.generatedName ? untangleWorkflowName(this.name) : this.name;
1289
+ let s: string = `workflow ${normalizeWorkflowName(n)} {\n`;
1266
1290
  const ss = this.statementsToStringsHelper(this.statements);
1267
1291
  s = s.concat(joinStatements(ss));
1268
1292
  return s.concat('\n}');
@@ -1871,8 +1895,11 @@ export function addWorkflow(
1871
1895
  name: string,
1872
1896
  moduleName = activeModule,
1873
1897
  statements?: Statement[],
1874
- hints?: WorkflowHint[]
1898
+ hdr?: WorkflowHeader
1875
1899
  ): Workflow {
1900
+ if (hdr) {
1901
+ name = prePostWorkflowName(hdr.tag, hdr.prefix, hdr.name, moduleName);
1902
+ }
1876
1903
  const module: Module = fetchModule(moduleName);
1877
1904
  if (module.hasEntry(name)) {
1878
1905
  const entry: ModuleEntry = module.getEntry(name);
@@ -1884,14 +1911,51 @@ export function addWorkflow(
1884
1911
  event.addMeta(SystemDefinedEvent, 'true');
1885
1912
  }
1886
1913
  if (!statements) statements = new Array<Statement>();
1887
- if (hints && hints.length > 0) {
1888
- hints.forEach((hint: WorkflowHint) => {
1889
- if (hint.subs) {
1890
- SetSubscription(makeFqName(moduleName, name), hint.subs.resolverName);
1891
- }
1892
- });
1914
+ if (hdr) {
1915
+ const eventFqName = makeFqName(moduleName, name);
1916
+ const entityDef = getEntityDef(hdr.name, moduleName);
1917
+ if (hdr.tag == '@after') {
1918
+ entityDef?.addAfterTrigger({
1919
+ on: hdr.prefix,
1920
+ event: eventFqName,
1921
+ async: false,
1922
+ });
1923
+ } else {
1924
+ entityDef?.addBeforeTrigger({
1925
+ on: hdr.prefix,
1926
+ event: eventFqName,
1927
+ async: false,
1928
+ });
1929
+ }
1893
1930
  }
1894
- return module.addEntry(new Workflow(asWorkflowName(name), statements, moduleName)) as Workflow;
1931
+ return module.addEntry(
1932
+ new Workflow(asWorkflowName(name), statements, moduleName, hdr ? true : false)
1933
+ ) as Workflow;
1934
+ }
1935
+
1936
+ function prePostWorkflowName(
1937
+ tag: '@after' | '@before',
1938
+ opr: 'create' | 'update' | 'delete',
1939
+ entityName: string,
1940
+ moduleName?: string
1941
+ ): string {
1942
+ const parts = splitFqName(entityName);
1943
+ const mname = parts.hasModule() ? parts.getModuleName() : moduleName;
1944
+ if (!mname) {
1945
+ throw new Error(`Cannot infer module name for ${entityName}`);
1946
+ }
1947
+ return `${tag.substring(1)}_${opr}_${mname}_${parts.getEntryName()}`;
1948
+ }
1949
+
1950
+ function untangleWorkflowName(name: string): string {
1951
+ const parts = name.split('_');
1952
+ return `@${parts[0]} ${parts[1]}:${parts[2]}/${parts[3]}`;
1953
+ }
1954
+
1955
+ function getEntityDef(entityName: string, moduleName: string): Entity | undefined {
1956
+ const parts = splitFqName(entityName);
1957
+ const mname = parts.hasModule() ? parts.getModuleName() : moduleName;
1958
+ return getEntity(parts.getEntryName(), mname);
1895
1959
  }
1896
1960
 
1897
1961
  export function getWorkflow(eventInstance: Instance): Workflow {
@@ -1905,12 +1969,13 @@ export function getWorkflow(eventInstance: Instance): Workflow {
1905
1969
  return EmptyWorkflow;
1906
1970
  }
1907
1971
 
1908
- export function getEntity(name: string, moduleName: string): Entity {
1972
+ export function getEntity(name: string, moduleName: string): Entity | undefined {
1909
1973
  const fr: FetchModuleByEntryNameResult = fetchModuleByEntryName(name, moduleName);
1910
1974
  if (fr.module.isEntity(fr.entryName)) {
1911
1975
  return fr.module.getEntry(fr.entryName) as Entity;
1912
1976
  }
1913
- throw new Error(`Entity ${fr.entryName} not found in module ${fr.moduleName}`);
1977
+ logger.error(`Entity ${fr.entryName} not found in module ${fr.moduleName}`);
1978
+ return undefined;
1914
1979
  }
1915
1980
 
1916
1981
  function isEntryOfType(t: RecordType, fqName: string): boolean {
@@ -2225,6 +2290,14 @@ export class Instance {
2225
2290
  );
2226
2291
  }
2227
2292
 
2293
+ static clone(inst: Instance): Instance {
2294
+ const attrs = newInstanceAttributes();
2295
+ inst.attributes.forEach((v: any, k: string) => {
2296
+ attrs.set(k, v);
2297
+ });
2298
+ return Instance.newWithAttributes(inst, attrs);
2299
+ }
2300
+
2228
2301
  normalizeAttributes(attrs: InstanceAttributes): InstanceAttributes {
2229
2302
  attrs.forEach((v: any, k: string) => {
2230
2303
  const attrSpec = this.record.schema.get(k);
@@ -2730,7 +2803,7 @@ export function getEntityRbacRules(entityFqName: string): RbacSpecification[] |
2730
2803
  const m = isModule(mn) && fetchModule(mn);
2731
2804
  if (m && m.isEntity(en)) {
2732
2805
  const entity = getEntity(en, mn);
2733
- return entity.getRbacSpecifications()?.filter((spec: RbacSpecification) => {
2806
+ return entity?.getRbacSpecifications()?.filter((spec: RbacSpecification) => {
2734
2807
  return spec.expression != undefined;
2735
2808
  });
2736
2809
  }
@@ -1,6 +1,6 @@
1
1
  import { default as ai } from './ai.js';
2
2
  import { default as auth } from './auth.js';
3
- import { DefaultModuleName, DefaultModules } from '../util.js';
3
+ import { DefaultModuleName, DefaultModules, escapeSpecialChars } from '../util.js';
4
4
  import { Instance, isInstanceOfType, makeInstance, newInstanceAttributes } from '../module.js';
5
5
  import {
6
6
  Environment,
@@ -103,7 +103,7 @@ async function addAudit(
103
103
  `{agentlang/auditlog {
104
104
  action "${action}",
105
105
  resource "${resource}",
106
- previous_value "${previuos_value ? JSON.stringify(previuos_value.asObject()) : ''}",
106
+ previous_value "${previuos_value ? escapeSpecialChars(JSON.stringify(previuos_value.asObject())) : ''}",
107
107
  user "${user}",
108
108
  token "${token ? token : ''}"
109
109
  }}`,
@@ -1,4 +1,5 @@
1
1
  import {
2
+ callPostEventOnSubscription,
2
3
  Environment,
3
4
  evaluate,
4
5
  runPostCreateEvents,
@@ -206,7 +207,7 @@ export class Resolver {
206
207
  case CrudType.CREATE:
207
208
  return await runPostCreateEvents(inst, env);
208
209
  case CrudType.UPDATE:
209
- return await runPostUpdateEvents(inst, env);
210
+ return await runPostUpdateEvents(inst, undefined, env);
210
211
  case CrudType.DELETE:
211
212
  return await runPostDeleteEvents(inst, env);
212
213
  default:
@@ -226,18 +227,23 @@ export class Resolver {
226
227
  return this.onOutOfBandCrud(inst, CrudType.DELETE, env);
227
228
  }
228
229
 
229
- public async onSubscription(result: any): Promise<any> {
230
+ public async onSubscription(result: any, callPostCrudEvent: boolean = false): Promise<any> {
230
231
  if (result != undefined) {
231
232
  try {
232
- const eventName = getSubscriptionEvent(this.name);
233
- if (eventName) {
234
- const path = splitFqName(eventName);
235
- const inst = makeInstance(
236
- path.getModuleName(),
237
- path.getEntryName(),
238
- newInstanceAttributes().set('data', result)
239
- );
240
- return await evaluate(inst);
233
+ if (callPostCrudEvent) {
234
+ const inst = result as Instance;
235
+ return await callPostEventOnSubscription(CrudType.CREATE, inst);
236
+ } else {
237
+ const eventName = getSubscriptionEvent(this.name);
238
+ if (eventName) {
239
+ const path = splitFqName(eventName);
240
+ const inst = makeInstance(
241
+ path.getModuleName(),
242
+ path.getEntryName(),
243
+ newInstanceAttributes().set('data', result)
244
+ );
245
+ return await evaluate(inst);
246
+ }
241
247
  }
242
248
  } catch (err: any) {
243
249
  logger.error(`Resolver ${this.name} raised error in onSubscription handler: ${err}`);
@@ -146,7 +146,7 @@ export class SqlDbResolver extends Resolver {
146
146
  updateObj,
147
147
  this.getDbContext(inst.getFqName())
148
148
  );
149
- return inst.mergeAttributes(newAttrs);
149
+ return Instance.clone(inst).mergeAttributes(newAttrs);
150
150
  }
151
151
 
152
152
  static EmptyResultSet: Array<Instance> = new Array<Instance>();
@@ -1,7 +1,7 @@
1
1
  // Monarch syntax highlighting for the agentlang language.
2
2
  export default {
3
3
  keywords: [
4
- '@actions','@after','@as','@async','@before','@catch','@distinct','@enum','@expr','@from','@into','@meta','@oneof','@rbac','@ref','@subs','@then','@upsert','@with_unique','agent','allow','and','await','between','contains','create','delete','else','entity','error','event','extends','false','for','if','import','in','like','module','not','not_found','onSubscription','or','purge','query','read','record','relationship','resolver','return','roles','subscribe','true','update','upsert','where','workflow'
4
+ '@actions','@after','@as','@async','@before','@catch','@distinct','@enum','@expr','@from','@into','@meta','@oneof','@rbac','@ref','@then','@upsert','@with_unique','agent','allow','and','await','between','contains','create','delete','else','entity','error','event','extends','false','for','if','import','in','like','module','not','not_found','onSubscription','or','purge','query','read','record','relationship','resolver','return','roles','subscribe','true','update','upsert','where','workflow'
5
5
  ],
6
6
  operators: [
7
7
  '!=','*','+',',','-','.','/',':',';','<','<=','<>','=','>','>=','?','@'