agentlang 0.7.9 → 0.7.11

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 (131) hide show
  1. package/out/api/http.d.ts.map +1 -1
  2. package/out/api/http.js +8 -1
  3. package/out/api/http.js.map +1 -1
  4. package/out/cli/main.d.ts.map +1 -1
  5. package/out/cli/main.js +33 -2
  6. package/out/cli/main.js.map +1 -1
  7. package/out/extension/main.cjs +250 -250
  8. package/out/extension/main.cjs.map +2 -2
  9. package/out/language/agentlang-validator.d.ts +1 -2
  10. package/out/language/agentlang-validator.d.ts.map +1 -1
  11. package/out/language/agentlang-validator.js +0 -39
  12. package/out/language/agentlang-validator.js.map +1 -1
  13. package/out/language/generated/ast.d.ts +62 -11
  14. package/out/language/generated/ast.d.ts.map +1 -1
  15. package/out/language/generated/ast.js +75 -3
  16. package/out/language/generated/ast.js.map +1 -1
  17. package/out/language/generated/grammar.d.ts.map +1 -1
  18. package/out/language/generated/grammar.js +758 -239
  19. package/out/language/generated/grammar.js.map +1 -1
  20. package/out/language/main.cjs +1370 -824
  21. package/out/language/main.cjs.map +4 -4
  22. package/out/language/parser.d.ts +1 -0
  23. package/out/language/parser.d.ts.map +1 -1
  24. package/out/language/parser.js +44 -7
  25. package/out/language/parser.js.map +1 -1
  26. package/out/language/syntax.d.ts +1 -1
  27. package/out/language/syntax.d.ts.map +1 -1
  28. package/out/language/syntax.js +22 -13
  29. package/out/language/syntax.js.map +1 -1
  30. package/out/runtime/api.d.ts +2 -0
  31. package/out/runtime/api.d.ts.map +1 -1
  32. package/out/runtime/api.js +5 -0
  33. package/out/runtime/api.js.map +1 -1
  34. package/out/runtime/auth/cognito.d.ts.map +1 -1
  35. package/out/runtime/auth/cognito.js +4 -4
  36. package/out/runtime/auth/cognito.js.map +1 -1
  37. package/out/runtime/defs.d.ts +5 -0
  38. package/out/runtime/defs.d.ts.map +1 -1
  39. package/out/runtime/defs.js +16 -0
  40. package/out/runtime/defs.js.map +1 -1
  41. package/out/runtime/exec-graph.js +1 -1
  42. package/out/runtime/exec-graph.js.map +1 -1
  43. package/out/runtime/interpreter.d.ts +6 -1
  44. package/out/runtime/interpreter.d.ts.map +1 -1
  45. package/out/runtime/interpreter.js +144 -112
  46. package/out/runtime/interpreter.js.map +1 -1
  47. package/out/runtime/loader.d.ts +1 -1
  48. package/out/runtime/loader.d.ts.map +1 -1
  49. package/out/runtime/loader.js +74 -27
  50. package/out/runtime/loader.js.map +1 -1
  51. package/out/runtime/module.d.ts +41 -11
  52. package/out/runtime/module.d.ts.map +1 -1
  53. package/out/runtime/module.js +238 -50
  54. package/out/runtime/module.js.map +1 -1
  55. package/out/runtime/modules/ai.d.ts.map +1 -1
  56. package/out/runtime/modules/ai.js +13 -6
  57. package/out/runtime/modules/ai.js.map +1 -1
  58. package/out/runtime/modules/auth.d.ts +2 -1
  59. package/out/runtime/modules/auth.d.ts.map +1 -1
  60. package/out/runtime/modules/auth.js +93 -3
  61. package/out/runtime/modules/auth.js.map +1 -1
  62. package/out/runtime/modules/core.d.ts +7 -5
  63. package/out/runtime/modules/core.d.ts.map +1 -1
  64. package/out/runtime/modules/core.js +93 -16
  65. package/out/runtime/modules/core.js.map +1 -1
  66. package/out/runtime/monitor.d.ts.map +1 -1
  67. package/out/runtime/monitor.js +1 -0
  68. package/out/runtime/monitor.js.map +1 -1
  69. package/out/runtime/resolvers/interface.d.ts +6 -1
  70. package/out/runtime/resolvers/interface.d.ts.map +1 -1
  71. package/out/runtime/resolvers/interface.js +2 -2
  72. package/out/runtime/resolvers/interface.js.map +1 -1
  73. package/out/runtime/resolvers/sqldb/database.d.ts +19 -2
  74. package/out/runtime/resolvers/sqldb/database.d.ts.map +1 -1
  75. package/out/runtime/resolvers/sqldb/database.js +107 -21
  76. package/out/runtime/resolvers/sqldb/database.js.map +1 -1
  77. package/out/runtime/resolvers/sqldb/dbutil.d.ts +1 -0
  78. package/out/runtime/resolvers/sqldb/dbutil.d.ts.map +1 -1
  79. package/out/runtime/resolvers/sqldb/dbutil.js +25 -3
  80. package/out/runtime/resolvers/sqldb/dbutil.js.map +1 -1
  81. package/out/runtime/resolvers/sqldb/impl.d.ts +3 -2
  82. package/out/runtime/resolvers/sqldb/impl.d.ts.map +1 -1
  83. package/out/runtime/resolvers/sqldb/impl.js +80 -6
  84. package/out/runtime/resolvers/sqldb/impl.js.map +1 -1
  85. package/out/runtime/state.d.ts +58 -0
  86. package/out/runtime/state.d.ts.map +1 -1
  87. package/out/runtime/state.js +12 -0
  88. package/out/runtime/state.js.map +1 -1
  89. package/out/runtime/util.d.ts +1 -1
  90. package/out/runtime/util.d.ts.map +1 -1
  91. package/out/runtime/util.js +27 -13
  92. package/out/runtime/util.js.map +1 -1
  93. package/out/setupClassic.d.ts +98 -0
  94. package/out/setupClassic.d.ts.map +1 -0
  95. package/out/setupClassic.js +38 -0
  96. package/out/setupClassic.js.map +1 -0
  97. package/out/setupCommon.d.ts +2 -0
  98. package/out/setupCommon.d.ts.map +1 -0
  99. package/out/setupCommon.js +33 -0
  100. package/out/setupCommon.js.map +1 -0
  101. package/out/setupExtended.d.ts +40 -0
  102. package/out/setupExtended.d.ts.map +1 -0
  103. package/out/setupExtended.js +67 -0
  104. package/out/setupExtended.js.map +1 -0
  105. package/out/syntaxes/agentlang.monarch.js +1 -1
  106. package/out/syntaxes/agentlang.monarch.js.map +1 -1
  107. package/package.json +187 -185
  108. package/src/api/http.ts +8 -0
  109. package/src/cli/main.ts +38 -2
  110. package/src/language/agentlang-validator.ts +1 -51
  111. package/src/language/agentlang.langium +17 -4
  112. package/src/language/generated/ast.ts +147 -13
  113. package/src/language/generated/grammar.ts +758 -239
  114. package/src/language/parser.ts +43 -8
  115. package/src/language/syntax.ts +25 -12
  116. package/src/runtime/api.ts +8 -0
  117. package/src/runtime/defs.ts +8 -0
  118. package/src/runtime/interpreter.ts +104 -76
  119. package/src/runtime/loader.ts +75 -25
  120. package/src/runtime/module.ts +194 -32
  121. package/src/runtime/modules/ai.ts +10 -4
  122. package/src/runtime/modules/auth.ts +1 -0
  123. package/src/runtime/modules/core.ts +99 -23
  124. package/src/runtime/monitor.ts +1 -0
  125. package/src/runtime/resolvers/interface.ts +9 -2
  126. package/src/runtime/resolvers/sqldb/database.ts +68 -17
  127. package/src/runtime/resolvers/sqldb/dbutil.ts +28 -6
  128. package/src/runtime/resolvers/sqldb/impl.ts +86 -14
  129. package/src/runtime/state.ts +14 -0
  130. package/src/runtime/util.ts +25 -12
  131. package/src/syntaxes/agentlang.monarch.ts +1 -1
@@ -33,7 +33,7 @@ import {
33
33
  Statement,
34
34
  WorkflowDefinition,
35
35
  } from './generated/ast.js';
36
- import { firstAliasSpec, firstCatchSpec, QuerySuffix } from '../runtime/util.js';
36
+ import { firstAliasSpec, firstCatchSpec, isString, QuerySuffix } from '../runtime/util.js';
37
37
  import {
38
38
  BasePattern,
39
39
  CrudPattern,
@@ -291,7 +291,7 @@ function introspectPattern(pat: Pattern): BasePattern {
291
291
 
292
292
  function introspectInto(intoSpec: SelectIntoSpec, p: CrudPattern): CrudPattern {
293
293
  intoSpec.entries.forEach((se: SelectIntoEntry) => {
294
- p.addInto(se.alias, se.attribute);
294
+ if (se.attribute) p.addInto(se.alias, se.attribute);
295
295
  });
296
296
  return p;
297
297
  }
@@ -340,12 +340,14 @@ function introspectPrimExpr(expr: PrimExpr): BasePattern {
340
340
  }
341
341
  }
342
342
 
343
- function introspectExpression(expr: Expr | Expr): BasePattern {
344
- if (isPrimExpr(expr)) {
345
- return introspectPrimExpr(expr);
346
- }
347
- if (expr.$cstNode) {
348
- return new ExpressionPattern(expr.$cstNode.text);
343
+ function introspectExpression(expr: Expr | undefined): BasePattern {
344
+ if (expr !== undefined) {
345
+ if (isPrimExpr(expr)) {
346
+ return introspectPrimExpr(expr);
347
+ }
348
+ if (expr.$cstNode) {
349
+ return new ExpressionPattern(expr.$cstNode.text);
350
+ }
349
351
  }
350
352
  throw new Error('Failed to introspect expression - ' + expr);
351
353
  }
@@ -507,3 +509,36 @@ export function canParse(s: string): boolean {
507
509
  }
508
510
  return false;
509
511
  }
512
+
513
+ export function objectToQueryPattern(obj: any): string {
514
+ const strs = new Array<string>();
515
+ Object.keys(obj).forEach((k: string) => {
516
+ if (k.startsWith('@')) {
517
+ const xs: any = obj[k];
518
+ if (k.endsWith('join')) {
519
+ xs.forEach((x: any[]) => {
520
+ strs.push(`${k} ${x[0]} ${objectToQuerySpecPattern(x[1], true)}`);
521
+ });
522
+ } else if (k === '@groupBy' || k === '@orderBy') {
523
+ strs.push(`${k} ( ${xs.join(', ')} )`);
524
+ } else {
525
+ strs.push(`${k} ${objectToQuerySpecPattern(xs, true)}`);
526
+ }
527
+ } else {
528
+ strs.push(`${k} ${objectToQuerySpecPattern(obj[k])}`);
529
+ }
530
+ });
531
+ return `{${strs.join(',\n')}}`;
532
+ }
533
+
534
+ function objectToQuerySpecPattern(obj: any, refMode: boolean = false): string {
535
+ const strs = new Array<string>();
536
+ Object.keys(obj).forEach((k: string) => {
537
+ let v = obj[k];
538
+ if (!refMode && isString(v) && !v.startsWith('@')) {
539
+ v = `"${v}"`;
540
+ }
541
+ strs.push(`${k} ${v}`);
542
+ });
543
+ return `{${strs.join(', ')}}`;
544
+ }
@@ -1,6 +1,11 @@
1
1
  import { parseHelper } from 'langium/test';
2
2
  import { escapeQueryName, trimQuotes } from '../runtime/util.js';
3
- import { isDecisionDefinition, ModuleDefinition } from './generated/ast.js';
3
+ import {
4
+ FlowEntry,
5
+ isDecisionDefinition,
6
+ isFlowDefinition,
7
+ ModuleDefinition,
8
+ } from './generated/ast.js';
4
9
  import { createAgentlangServices } from './agentlang-module.js';
5
10
  import { EmptyFileSystem } from 'langium';
6
11
  import { introspect, parseModule } from './parser.js';
@@ -811,18 +816,26 @@ export class FlowStepPattern extends BasePattern {
811
816
  this.condition = condition ? trimQuotes(condition) : undefined;
812
817
  }
813
818
 
814
- static Parse(s: string): FlowStepPattern {
815
- const parts = s.trim().split(' ');
816
- const first = parts[0];
817
- if (parts[1] == '-->') {
818
- if (parts.length == 3) {
819
- return new FlowStepPattern(first, parts[2]);
820
- } else {
821
- return new FlowStepPattern(first, parts[3], parts[2]);
822
- }
823
- } else {
824
- throw new Error(`Invalid flow-step format in ${s}`);
819
+ static async Parse(s: string): Promise<FlowStepPattern> {
820
+ const p = await parseModule(`module Temp
821
+ flow tempFlow {
822
+ ${s}
823
+ }`);
824
+ let result: FlowStepPattern | undefined;
825
+ const d = p.defs[0];
826
+ if (isFlowDefinition(d)) {
827
+ d.body?.entries.forEach((fe: FlowEntry) => {
828
+ if (fe.cond) {
829
+ result = new FlowStepPattern(fe.root, fe.cond.next.$cstNode?.text || '', fe.cond.expr);
830
+ } else {
831
+ result = new FlowStepPattern(fe.root, fe.next?.$cstNode?.text || '');
832
+ }
833
+ });
834
+ }
835
+ if (result !== undefined) {
836
+ return result;
825
837
  }
838
+ throw new Error(`Failed to parse flow-step - ${s}`);
826
839
  }
827
840
 
828
841
  override toString(): string {
@@ -4,11 +4,14 @@ import {
4
4
  isInstanceOfType as al_isInstanceOfType,
5
5
  } from './module.js';
6
6
  import { getLocalEnv as al_getLocalEnv, setLocalEnv as al_setLocalEnv } from './auth/defs.js';
7
+ import { now } from './util.js';
7
8
 
8
9
  declare global {
9
10
  var agentlang: any | undefined;
10
11
  function getLocalEnv(k: string, defaultValue?: string): string | undefined;
11
12
  function setLocalEnv(k: string, v: string): string;
13
+ function uuid(): string;
14
+ function now(): string;
12
15
  }
13
16
 
14
17
  let ApiInited = false;
@@ -20,6 +23,11 @@ export function initGlobalApi() {
20
23
  globalThis.agentlang.isInstanceOfType = al_isInstanceOfType;
21
24
  globalThis.agentlang.fetchConfig = al_fetchConfig;
22
25
 
26
+ globalThis.uuid = () => {
27
+ return crypto.randomUUID();
28
+ };
29
+ globalThis.now = now;
30
+
23
31
  // Expose environment variable functions globally (like readSecret pattern)
24
32
  globalThis.getLocalEnv = al_getLocalEnv;
25
33
  globalThis.setLocalEnv = al_setLocalEnv;
@@ -419,6 +419,7 @@ export function isRuntimeMode_undo_migration(): boolean {
419
419
 
420
420
  let UpdateEventEndpoints: Function | undefined;
421
421
  let UpdateEntityEndpoints: Function | undefined;
422
+ let UpdateRelationshipEndpoints: Function | undefined;
422
423
 
423
424
  export function setEventEndpointsUpdater(f: Function) {
424
425
  UpdateEventEndpoints = f;
@@ -428,6 +429,10 @@ export function setEntityEndpointsUpdater(f: Function) {
428
429
  UpdateEntityEndpoints = f;
429
430
  }
430
431
 
432
+ export function setRelationshipEndpointsUpdater(f: Function) {
433
+ UpdateRelationshipEndpoints = f;
434
+ }
435
+
431
436
  export function updateEndpoints(moduleName: string) {
432
437
  if (UpdateEventEndpoints !== undefined) {
433
438
  UpdateEventEndpoints(moduleName);
@@ -435,6 +440,9 @@ export function updateEndpoints(moduleName: string) {
435
440
  if (UpdateEntityEndpoints !== undefined) {
436
441
  UpdateEntityEndpoints(moduleName);
437
442
  }
443
+ if (UpdateRelationshipEndpoints !== undefined) {
444
+ UpdateRelationshipEndpoints(moduleName);
445
+ }
438
446
  }
439
447
 
440
448
  export let InternDynamicModule: Function | undefined;
@@ -28,6 +28,7 @@ import {
28
28
  SetAttribute,
29
29
  Statement,
30
30
  ThrowError,
31
+ WhereSpec,
31
32
  } from '../language/generated/ast.js';
32
33
  import {
33
34
  maybeInstanceAsString,
@@ -52,7 +53,7 @@ import {
52
53
  Workflow,
53
54
  setMetaAttributes,
54
55
  } from './module.js';
55
- import { JoinInfo, Resolver } from './resolvers/interface.js';
56
+ import { JoinInfo, Resolver, WhereClause } from './resolvers/interface.js';
56
57
  import { ResolverAuthInfo } from './resolvers/authinfo.js';
57
58
  import { SqlDbResolver } from './resolvers/sqldb/impl.js';
58
59
  import {
@@ -97,8 +98,7 @@ import {
97
98
  addUpdateAudit,
98
99
  createSuspension,
99
100
  flushMonitoringData,
100
- maybeCancelTimer,
101
- setTimerRunning,
101
+ triggerTimer,
102
102
  } from './modules/core.js';
103
103
  import { invokeModuleFn } from './jsmodules.js';
104
104
  import { invokeOpenApiEvent, isOpenApiEventInstance } from './openapi.js';
@@ -106,6 +106,7 @@ import { fetchDoc } from './docs.js';
106
106
  import { FlowSpec, FlowStep, getAgentFlow } from './agents/flows.js';
107
107
  import { isMonitoringEnabled } from './state.js';
108
108
  import { Monitor, MonitorEntry } from './monitor.js';
109
+ import { detailedDiff } from 'deep-object-diff';
109
110
 
110
111
  export type Result = any;
111
112
 
@@ -1341,6 +1342,11 @@ async function lookupOneOfVals(fqName: string, env: Environment): Promise<Instan
1341
1342
  return await parseAndEvaluateStatement(`{${fqName}? {}}`, undefined, env);
1342
1343
  }
1343
1344
 
1345
+ export type AggregateFunctionCall = {
1346
+ name: string;
1347
+ args: string[];
1348
+ };
1349
+
1344
1350
  async function patternToInstance(
1345
1351
  entryName: string,
1346
1352
  attributes: SetAttribute[] | undefined,
@@ -1349,6 +1355,7 @@ async function patternToInstance(
1349
1355
  const attrs: InstanceAttributes = newInstanceAttributes();
1350
1356
  let qattrs: InstanceAttributes | undefined;
1351
1357
  let qattrVals: InstanceAttributes | undefined;
1358
+ let aggregates: Map<string, AggregateFunctionCall> | undefined;
1352
1359
  const isQueryAll: boolean = entryName.endsWith(QuerySuffix);
1353
1360
  if (isQueryAll) {
1354
1361
  entryName = entryName.slice(0, entryName.length - 1);
@@ -1356,20 +1363,25 @@ async function patternToInstance(
1356
1363
  if (attributes) {
1357
1364
  for (let i = 0; i < attributes.length; ++i) {
1358
1365
  const a: SetAttribute = attributes[i];
1359
- await evaluateExpression(a.value, env);
1360
- const v: Result = env.getLastResult();
1361
- let aname: string = a.name;
1362
- if (aname.endsWith(QuerySuffix)) {
1363
- if (isQueryAll) {
1364
- throw new Error(`Cannot specifiy query attribute ${aname} here`);
1366
+ if (a.value !== undefined) {
1367
+ await evaluateExpression(a.value, env);
1368
+ const v: Result = env.getLastResult();
1369
+ let aname: string = a.name;
1370
+ if (aname.endsWith(QuerySuffix)) {
1371
+ if (isQueryAll) {
1372
+ throw new Error(`Cannot specifiy query attribute ${aname} here`);
1373
+ }
1374
+ if (qattrs === undefined) qattrs = newInstanceAttributes();
1375
+ if (qattrVals === undefined) qattrVals = newInstanceAttributes();
1376
+ aname = aname.slice(0, aname.length - 1);
1377
+ qattrs.set(aname, a.op === undefined ? '=' : a.op);
1378
+ qattrVals.set(aname, v);
1379
+ } else {
1380
+ attrs.set(aname, v);
1365
1381
  }
1366
- if (qattrs === undefined) qattrs = newInstanceAttributes();
1367
- if (qattrVals === undefined) qattrVals = newInstanceAttributes();
1368
- aname = aname.slice(0, aname.length - 1);
1369
- qattrs.set(aname, a.op === undefined ? '=' : a.op);
1370
- qattrVals.set(aname, v);
1371
- } else {
1372
- attrs.set(aname, v);
1382
+ } else if (a.aggregate !== undefined) {
1383
+ if (aggregates === undefined) aggregates = new Map<string, AggregateFunctionCall>();
1384
+ aggregates.set(escapeQueryName(a.name), { name: a.aggregate.name, args: a.aggregate.args });
1373
1385
  }
1374
1386
  }
1375
1387
  }
@@ -1379,7 +1391,11 @@ async function patternToInstance(
1379
1391
  if (p.hasModule()) moduleName = p.getModuleName();
1380
1392
  if (p.hasEntry()) entryName = p.getEntryName();
1381
1393
  }
1382
- return makeInstance(moduleName, entryName, attrs, qattrs, qattrVals, isQueryAll);
1394
+ const inst = makeInstance(moduleName, entryName, attrs, qattrs, qattrVals, isQueryAll);
1395
+ if (aggregates !== undefined) {
1396
+ return inst.setAggregates(aggregates);
1397
+ }
1398
+ return inst;
1383
1399
  }
1384
1400
 
1385
1401
  async function instanceFromSource(crud: CrudMap, env: Environment): Promise<Instance> {
@@ -1431,6 +1447,15 @@ async function maybeValidateOneOfRefs(inst: Instance, env: Environment) {
1431
1447
  }
1432
1448
  }
1433
1449
 
1450
+ function maybeSetQueryClauses(inst: Instance, crud: CrudMap) {
1451
+ if (crud.groupByClause) {
1452
+ inst.setGroupBy(crud.groupByClause.colNames);
1453
+ }
1454
+ if (crud.orderByClause) {
1455
+ inst.setOrderBy(crud.orderByClause.colNames, crud.orderByClause.order === '@desc');
1456
+ }
1457
+ }
1458
+
1434
1459
  async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1435
1460
  if (!env.isInUpsertMode() && crud.upsert.length > 0) {
1436
1461
  return await evaluateUpsert(crud, env);
@@ -1442,8 +1467,10 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1442
1467
  const moduleName = inst.moduleName;
1443
1468
  const attrs = inst.attributes;
1444
1469
  const qattrs = inst.queryAttributes;
1445
- const isQueryAll = crud.name.endsWith(QuerySuffix);
1470
+ const onlyAggregates = inst.aggregates !== undefined && qattrs === undefined;
1471
+ const isQueryAll = onlyAggregates || crud.name.endsWith(QuerySuffix);
1446
1472
  const distinct: boolean = crud.distinct.length > 0;
1473
+ maybeSetQueryClauses(inst, crud);
1447
1474
  if (attrs.size > 0) {
1448
1475
  await maybeValidateOneOfRefs(inst, env);
1449
1476
  }
@@ -1456,8 +1483,8 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1456
1483
  if (qattrs === undefined && !isQueryAll) {
1457
1484
  throw new Error(`Pattern for ${entryName} with 'into' clause must be a query`);
1458
1485
  }
1459
- if (crud.join) {
1460
- await evaluateJoinQuery(crud.join, crud.into, inst, distinct, env);
1486
+ if (crud.joins.length > 0) {
1487
+ await evaluateJoinQuery(crud.joins, crud.into, crud.where, inst, distinct, env);
1461
1488
  } else {
1462
1489
  await evaluateJoinQueryWithRelationships(crud.into, inst, crud.relationships, distinct, env);
1463
1490
  }
@@ -1671,51 +1698,6 @@ async function handleDocEvent(inst: Instance, env: Environment): Promise<void> {
1671
1698
  }
1672
1699
  }
1673
1700
 
1674
- function triggerTimer(timerInst: Instance): Instance {
1675
- const dur = timerInst.lookup('duration');
1676
- const unit = timerInst.lookup('unit');
1677
- let millisecs = 0;
1678
- switch (unit) {
1679
- case 'millisecond': {
1680
- millisecs = dur;
1681
- break;
1682
- }
1683
- case 'second': {
1684
- millisecs = dur * 1000;
1685
- break;
1686
- }
1687
- case 'minute': {
1688
- millisecs = dur * 60 * 1000;
1689
- break;
1690
- }
1691
- case 'hour': {
1692
- millisecs = dur * 60 * 60 * 1000;
1693
- break;
1694
- }
1695
- }
1696
- const eventName = nameToPath(timerInst.lookup('trigger'));
1697
- const m = eventName.hasModule() ? eventName.getModuleName() : timerInst.moduleName;
1698
- const n = eventName.getEntryName();
1699
- const inst = makeInstance(m, n, newInstanceAttributes());
1700
- const name = timerInst.lookup('name');
1701
- const timer = setInterval(async () => {
1702
- const env = new Environment();
1703
- try {
1704
- await evaluate(
1705
- inst,
1706
- (result: Result) => logger.debug(`Timer ${name} ran with result ${result}`),
1707
- env
1708
- );
1709
- await env.commitAllTransactions();
1710
- await maybeCancelTimer(name, timer, env);
1711
- } catch (reason: any) {
1712
- logger.error(`Timer ${name} raised error: ${reason}`);
1713
- }
1714
- }, millisecs);
1715
- setTimerRunning(timerInst);
1716
- return timerInst;
1717
- }
1718
-
1719
1701
  async function computeExprAttributes(
1720
1702
  inst: Instance,
1721
1703
  origAttrs: SetAttribute[] | undefined,
@@ -1749,7 +1731,7 @@ async function computeExprAttributes(
1749
1731
  for (let i = 0; i < origAttrs.length; ++i) {
1750
1732
  const a: SetAttribute = origAttrs[i];
1751
1733
  const n = a.name;
1752
- if (!n.endsWith(QuerySuffix) && updatedAttrs.has(n)) {
1734
+ if (!n.endsWith(QuerySuffix) && updatedAttrs.has(n) && a.value !== undefined) {
1753
1735
  await evaluateExpression(a.value, newEnv);
1754
1736
  const v: Result = newEnv.getLastResult();
1755
1737
  updatedAttrs.set(n, v);
@@ -1760,19 +1742,51 @@ async function computeExprAttributes(
1760
1742
  }
1761
1743
  }
1762
1744
 
1745
+ async function evalWhereClauses(whereSpec: WhereSpec, env: Environment): Promise<WhereClause[]> {
1746
+ const result = new Array<WhereClause>();
1747
+ const e = new Environment(undefined, env);
1748
+ for (let i = 0; i < whereSpec.clauses.length; ++i) {
1749
+ const c = whereSpec.clauses[i];
1750
+ await evaluateExpression(c.rhs, e);
1751
+ result.push({
1752
+ attrName: escapeQueryName(c.lhs),
1753
+ op: c.op === undefined ? '=' : c.op,
1754
+ qval: e.getLastResult(),
1755
+ });
1756
+ }
1757
+ return result;
1758
+ }
1759
+
1763
1760
  async function evaluateJoinQuery(
1764
- joinSpec: JoinSpec,
1761
+ joinSpec: JoinSpec[],
1765
1762
  intoSpec: SelectIntoSpec,
1763
+ whereSpec: WhereSpec | undefined,
1766
1764
  inst: Instance,
1767
1765
  distinct: boolean,
1768
1766
  env: Environment
1769
1767
  ): Promise<void> {
1770
1768
  const normIntoSpec = new Map<string, string>();
1769
+ let aggregates: Map<string, AggregateFunctionCall> | undefined;
1771
1770
  intoSpec.entries.forEach((entry: SelectIntoEntry) => {
1772
- normIntoSpec.set(entry.alias, entry.attribute);
1771
+ if (entry.attribute !== undefined) normIntoSpec.set(entry.alias, entry.attribute);
1772
+ else {
1773
+ if (aggregates === undefined) aggregates = new Map<string, AggregateFunctionCall>();
1774
+ if (entry.aggregate !== undefined) aggregates?.set(entry.alias, entry.aggregate);
1775
+ }
1773
1776
  });
1777
+ if (aggregates !== undefined) {
1778
+ inst.setAggregates(aggregates);
1779
+ }
1780
+ const clauses = whereSpec ? await evalWhereClauses(whereSpec, env) : undefined;
1774
1781
  const resolver = await getResolverForPath(inst.name, inst.moduleName, env);
1775
- const result: Result = await resolver.queryByJoin(inst, [], normIntoSpec, distinct, joinSpec);
1782
+ const result: Result = await resolver.queryByJoin(
1783
+ inst,
1784
+ [],
1785
+ normIntoSpec,
1786
+ distinct,
1787
+ joinSpec,
1788
+ clauses
1789
+ );
1776
1790
 
1777
1791
  const transformedResult = transformDateFieldsInJoinResult(result);
1778
1792
 
@@ -1787,9 +1801,17 @@ async function evaluateJoinQueryWithRelationships(
1787
1801
  env: Environment
1788
1802
  ): Promise<void> {
1789
1803
  const normIntoSpec = new Map<string, string>();
1804
+ let aggregates: Map<string, AggregateFunctionCall> | undefined;
1790
1805
  intoSpec.entries.forEach((entry: SelectIntoEntry) => {
1791
- normIntoSpec.set(entry.alias, entry.attribute);
1806
+ if (entry.attribute !== undefined) normIntoSpec.set(entry.alias, entry.attribute);
1807
+ else {
1808
+ if (aggregates === undefined) aggregates = new Map<string, AggregateFunctionCall>();
1809
+ if (entry.aggregate !== undefined) aggregates?.set(entry.alias, entry.aggregate);
1810
+ }
1792
1811
  });
1812
+ if (aggregates !== undefined) {
1813
+ inst.setAggregates(aggregates);
1814
+ }
1793
1815
  const moduleName = inst.moduleName;
1794
1816
  let joinsSpec = new Array<JoinInfo>();
1795
1817
  for (let i = 0; i < relationships.length; ++i) {
@@ -2126,7 +2148,7 @@ async function preprocessStep(spec: string, env: Environment): Promise<string> {
2126
2148
  const step = crudMap.name;
2127
2149
  if (crudMap.body) {
2128
2150
  crudMap.body.attributes.forEach((sa: SetAttribute) => {
2129
- const v = sa.value.$cstNode?.text;
2151
+ const v = sa.value?.$cstNode?.text;
2130
2152
  if (v) env.setTemplateMapping(sa.name, v);
2131
2153
  });
2132
2154
  }
@@ -2202,7 +2224,7 @@ export async function maybeDeleteQueriedInstances(
2202
2224
  const inst: Instance[] | Instance = queryEnv.getLastResult();
2203
2225
  let resolver: Resolver = Resolver.Default;
2204
2226
  if (inst instanceof Array) {
2205
- if (inst.length > 0 && isEntityInstance(inst[0])) {
2227
+ if (inst.length > 0) {
2206
2228
  resolver = await getResolverForPath(inst[0].name, inst[0].moduleName, queryEnv);
2207
2229
  const finalResult: Array<any> = new Array<any>();
2208
2230
  for (let i = 0; i < inst.length; ++i) {
@@ -2215,7 +2237,7 @@ export async function maybeDeleteQueriedInstances(
2215
2237
  } else {
2216
2238
  queryEnv.setLastResult(inst);
2217
2239
  }
2218
- } else if (isEntityInstance(inst)) {
2240
+ } else {
2219
2241
  resolver = await getResolverForPath(inst.name, inst.moduleName, queryEnv);
2220
2242
  await runPreDeleteEvents(inst, env);
2221
2243
  const r: Instance | null = await resolver.deleteInstance(inst, purge);
@@ -2479,7 +2501,7 @@ async function runPreCreateEvents(inst: Instance, env: Environment) {
2479
2501
 
2480
2502
  export async function runPostCreateEvents(inst: Instance, env: Environment) {
2481
2503
  if (inst.requireAudit()) {
2482
- await addCreateAudit(inst.getPath(), env);
2504
+ await addCreateAudit(inst.getPath(), env, { original: inst.userAttributesAsObject() });
2483
2505
  }
2484
2506
  await runPrePostEvents(CrudType.CREATE, false, inst, env);
2485
2507
  }
@@ -2494,7 +2516,13 @@ export async function runPostUpdateEvents(
2494
2516
  env: Environment
2495
2517
  ) {
2496
2518
  if (inst.requireAudit()) {
2497
- await addUpdateAudit(inst.getPath(), oldInst, env);
2519
+ let diff: object | undefined;
2520
+ if (oldInst !== undefined) {
2521
+ const oldAttrs = oldInst.userAttributesAsObject();
2522
+ const d = detailedDiff(oldAttrs, inst.userAttributesAsObject());
2523
+ diff = { original: oldAttrs, updated: d.updated };
2524
+ }
2525
+ await addUpdateAudit(inst.getPath(), diff, env);
2498
2526
  }
2499
2527
  await runPrePostEvents(CrudType.UPDATE, false, inst, env);
2500
2528
  }
@@ -2505,7 +2533,7 @@ async function runPreDeleteEvents(inst: Instance, env: Environment) {
2505
2533
 
2506
2534
  export async function runPostDeleteEvents(inst: Instance, env: Environment) {
2507
2535
  if (inst.requireAudit()) {
2508
- await addDeleteAudit(inst.getPath(), inst, env);
2536
+ await addDeleteAudit(inst.getPath(), { deleted: inst.userAttributesAsObject() }, env);
2509
2537
  }
2510
2538
  await runPrePostEvents(CrudType.DELETE, false, inst, env);
2511
2539
  }
@@ -69,6 +69,7 @@ import {
69
69
  addAgent,
70
70
  fetchModule,
71
71
  Retry,
72
+ addGlobalRetry,
72
73
  } from './module.js';
73
74
  import {
74
75
  asStringLiteralsMap,
@@ -81,6 +82,7 @@ import {
81
82
  preprocessRawConfig,
82
83
  registerInitFunction,
83
84
  rootRef,
85
+ ScratchModuleName,
84
86
  } from './util.js';
85
87
  import { getFileSystem, toFsPath, readFile, readdir, exists } from '../utils/fs-utils.js';
86
88
  import { URI } from 'vscode-uri';
@@ -348,10 +350,14 @@ async function evaluateConfigPatterns(cfgPats: string): Promise<any> {
348
350
  const wf = await parseWorkflow(cfgWf);
349
351
  const cfgStmts = new Array<Statement>();
350
352
  const initInsts = new Array<Statement>();
353
+ const deleted = new Set<string>();
351
354
  for (let i = 0; i < wf.statements.length; ++i) {
352
355
  const stmt: Statement = wf.statements[i];
353
356
  if (stmt.pattern.crudMap) {
354
- initInsts.push(await makeDeleteAllConfigStatement(stmt.pattern.crudMap));
357
+ if (!deleted.has(stmt.pattern.crudMap?.name)) {
358
+ initInsts.push(await makeDeleteAllConfigStatement(stmt.pattern.crudMap));
359
+ deleted.add(stmt.pattern.crudMap.name);
360
+ }
355
361
  initInsts.push(stmt);
356
362
  } else {
357
363
  cfgStmts.push(stmt);
@@ -377,20 +383,37 @@ async function evaluateConfigPatterns(cfgPats: string): Promise<any> {
377
383
  return undefined;
378
384
  }
379
385
 
380
- export async function loadAppConfig(configDir: string): Promise<Config> {
386
+ function isStringContent(content: string): boolean {
387
+ return content.includes('{');
388
+ }
389
+
390
+ export async function loadAppConfig(configDirOrContent: string): Promise<Config> {
391
+ const stringContent = isStringContent(configDirOrContent);
392
+
381
393
  let cfgObj: any = undefined;
382
- const fs = await getFileSystem();
383
- const alCfgFile = `${configDir}${path.sep}config.al`;
384
- if (await fs.exists(alCfgFile)) {
385
- const cfgPats = await fs.readFile(alCfgFile);
386
- if (canParse(cfgPats)) {
387
- cfgObj = await evaluateConfigPatterns(cfgPats);
394
+
395
+ if (!stringContent) {
396
+ const fs = await getFileSystem();
397
+ const alCfgFile = `${configDirOrContent}${path.sep}config.al`;
398
+ if (await fs.exists(alCfgFile)) {
399
+ configDirOrContent = await fs.readFile(alCfgFile);
388
400
  }
389
401
  }
402
+
403
+ if (canParse(configDirOrContent)) {
404
+ cfgObj = await evaluateConfigPatterns(configDirOrContent);
405
+ }
406
+
390
407
  try {
391
- const cfg = cfgObj
408
+ let cfg = cfgObj
392
409
  ? await configFromObject(cfgObj)
393
- : await loadRawConfig(`${configDir}${path.sep}app.config.json`);
410
+ : await loadRawConfig(`${configDirOrContent}${path.sep}app.config.json`);
411
+
412
+ const envAppConfig = typeof process !== 'undefined' ? process.env.APP_CONFIG : undefined;
413
+ if (envAppConfig) {
414
+ const envConfig = JSON.parse(envAppConfig);
415
+ cfg = { ...cfg, ...envConfig };
416
+ }
394
417
  return setAppConfig(cfg);
395
418
  } catch (err: any) {
396
419
  if (err instanceof z.ZodError) {
@@ -891,7 +914,7 @@ function addFlowDefinition(def: FlowDefinition, moduleName: string) {
891
914
  f = sdef;
892
915
  }
893
916
  }
894
- m.addFlow(def.name, f);
917
+ m.addFlow(def.name, def);
895
918
  registerFlow(`${moduleName}/${def.name}`, f);
896
919
  }
897
920
 
@@ -1161,6 +1184,7 @@ export async function internModule(
1161
1184
  // Process imports sequentially to ensure all JS modules are loaded before definitions
1162
1185
  for (const imp of module.imports as Import[]) {
1163
1186
  await importModule(imp.path, imp.name, moduleFileName);
1187
+ r.addImport({ name: imp.name, path: imp.path });
1164
1188
  }
1165
1189
  for (let i = 0; i < module.defs.length; ++i) {
1166
1190
  const def = module.defs[i];
@@ -1186,31 +1210,57 @@ export async function loadRawConfig(
1186
1210
  }
1187
1211
  }
1188
1212
 
1189
- function filterConfigEntityInstances(rawConfig: any): [any, Map<string, any>] {
1190
- const cfg = new Map<string, any>();
1191
- const insts = new Map<string, any>();
1192
- Object.entries(rawConfig).forEach(([key, value]: [string, any]) => {
1193
- if (isFqName(key)) {
1194
- insts.set(key, value);
1195
- } else {
1196
- cfg.set(key, value);
1197
- }
1213
+ function filterConfigEntityInstances(rawConfig: any): [any, Array<any>] {
1214
+ let cfg: any = undefined;
1215
+ const insts = new Array<any>();
1216
+ const oldFormat = Object.keys(rawConfig).some((k: string) => {
1217
+ return k === 'store' || k === 'service';
1198
1218
  });
1199
- return [Object.fromEntries(cfg), insts];
1219
+ const newFormat = !oldFormat;
1220
+ if (newFormat) {
1221
+ Object.entries(rawConfig).forEach(([key, value]: [string, any]) => {
1222
+ if (key === 'agentlang') {
1223
+ cfg = value;
1224
+ } else {
1225
+ if (value instanceof Array) {
1226
+ value.forEach((v: any) => {
1227
+ insts.push(v);
1228
+ });
1229
+ } else {
1230
+ insts.push(value);
1231
+ }
1232
+ }
1233
+ });
1234
+ if (cfg === undefined) cfg = {};
1235
+ return [cfg, insts];
1236
+ } else {
1237
+ return [rawConfig, insts];
1238
+ }
1200
1239
  }
1201
1240
 
1202
1241
  async function configFromObject(cfgObj: any, validate: boolean = true): Promise<any> {
1203
1242
  const rawConfig = preprocessRawConfig(cfgObj);
1204
- if (validate) {
1243
+ if (validate && rawConfig) {
1205
1244
  const [cfg, insts] = filterConfigEntityInstances(rawConfig);
1206
1245
  const pats = new Array<string>();
1207
- insts.forEach((v: any, k: string) => {
1208
- pats.push(`{${k} ${objectAsString(v)}}`);
1246
+ insts.forEach((v: any) => {
1247
+ const n = Object.keys(v)[0];
1248
+ const attrs = v[n];
1249
+ pats.push(`{${n} ${objectAsString(attrs)}}`);
1209
1250
  });
1210
1251
  if (pats.length > 0) {
1211
1252
  await evaluateConfigPatterns(pats.join('\n'));
1212
1253
  }
1213
- return ConfigSchema.parse(cfg);
1254
+ const result = ConfigSchema.parse(cfg);
1255
+ cfg.retry?.forEach((r: any) => {
1256
+ const retry: Retry = new Retry(r.name, ScratchModuleName, r.attempts)
1257
+ .setBackoffDelay(r.backoff.delay)
1258
+ .setBackoffFactor(r.backoff.factor)
1259
+ .setBackoffStrategy(r.backoff.strategy[0])
1260
+ .setBackoffMagnitude(r.backoff.magnitude);
1261
+ addGlobalRetry(retry);
1262
+ });
1263
+ return result;
1214
1264
  }
1215
1265
  return rawConfig;
1216
1266
  }