agentlang 0.0.6 → 0.0.8

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.
@@ -731,6 +731,26 @@ async function patternToInstance(
731
731
  return makeInstance(moduleName, entryName, attrs, qattrs, qattrVals, isQueryAll);
732
732
  }
733
733
 
734
+ async function instanceFromSource(crud: CrudMap, env: Environment): Promise<Instance> {
735
+ if (crud.source) {
736
+ await evaluateLiteral(crud.source, env);
737
+ const attrsSrc = env.getLastResult();
738
+ if (attrsSrc && attrsSrc instanceof Object) {
739
+ const attrs: InstanceAttributes = new Map(Object.entries(attrsSrc));
740
+ const nparts = splitFqName(crud.name);
741
+ const n = nparts.getEntryName();
742
+ const m = nparts.hasModule() ? nparts.getModuleName() : env.getActiveModuleName();
743
+ return makeInstance(m, n, attrs);
744
+ } else {
745
+ throw new Error(`Failed to initialize instance of ${crud.name}, expected a map after @from.`);
746
+ }
747
+ } else {
748
+ throw new Error(
749
+ `Cannot create instance of ${crud.name}, CRUD pattern does not specify a source map.`
750
+ );
751
+ }
752
+ }
753
+
734
754
  async function maybeValidateOneOfRefs(inst: Instance, env: Environment) {
735
755
  const attrs = inst.record.oneOfRefAttributes;
736
756
  if (!attrs) return;
@@ -761,10 +781,12 @@ async function maybeValidateOneOfRefs(inst: Instance, env: Environment) {
761
781
  }
762
782
 
763
783
  async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
764
- if (!env.isInUpsertMode() && crud.upsert) {
784
+ if (!env.isInUpsertMode() && crud.upsert.length > 0) {
765
785
  return await evaluateUpsert(crud, env);
766
786
  }
767
- const inst: Instance = await patternToInstance(crud.name, crud.body?.attributes, env);
787
+ const inst: Instance = crud.source
788
+ ? await instanceFromSource(crud, env)
789
+ : await patternToInstance(crud.name, crud.body?.attributes, env);
768
790
  const entryName = inst.name;
769
791
  const moduleName = inst.moduleName;
770
792
  const attrs = inst.attributes;
@@ -6,6 +6,9 @@ const importedModules = new Map<string, any>();
6
6
 
7
7
  // Usage: importModule("./mymodels/acme.js")
8
8
  export async function importModule(path: string, name: string) {
9
+ if (importedModules.has(name)) {
10
+ logger.warn(`Alias '${name}' will overwrite a previously imported module`);
11
+ }
9
12
  if (!(path.startsWith('/') || path.startsWith('.'))) {
10
13
  path = process.cwd() + '/' + path;
11
14
  }
@@ -50,7 +50,6 @@ import {
50
50
  makeFqName,
51
51
  maybeExtends,
52
52
  registerInitFunction,
53
- runShellCommand,
54
53
  } from './util.js';
55
54
  import { getFileSystem, toFsPath, readFile, readdir, exists } from '../utils/fs-utils.js';
56
55
  import { URI } from 'vscode-uri';
@@ -172,7 +171,7 @@ async function loadApp(appDir: string, fsOptions?: any, callback?: Function): Pr
172
171
  return;
173
172
  }
174
173
  directoryContents.forEach(file => {
175
- if (path.extname(file) == '.al') {
174
+ if (path.extname(file).toLowerCase() == '.al') {
176
175
  alFiles.push(appDir + path.sep + file);
177
176
  }
178
177
  });
@@ -182,19 +181,19 @@ async function loadApp(appDir: string, fsOptions?: any, callback?: Function): Pr
182
181
  if (callback) await callback(appSpec);
183
182
  }
184
183
  if (appSpec.dependencies != undefined) {
185
- if (isNodeEnv) {
186
- // Only run shell commands in Node.js environment
187
- for (const [depName, depVer] of Object.entries(appSpec.dependencies)) {
188
- runShellCommand(`npm install ${depName}@${depVer}`, undefined, cont2);
184
+ for (const [depName, _] of Object.entries(appSpec.dependencies)) {
185
+ const depDirName = `./node_modules/${depName}`;
186
+ const files = await fs.readdir(depDirName);
187
+ if (
188
+ files.find(file => {
189
+ return path.extname(file).toLowerCase() == '.al';
190
+ })
191
+ ) {
192
+ await loadApp(depDirName, fsOptions);
189
193
  }
190
- } else {
191
- // In non-Node environments, log a warning and continue
192
- console.warn('Dependencies cannot be installed in non-Node.js environments');
193
- await cont2();
194
194
  }
195
- } else {
196
- await cont2();
197
195
  }
196
+ await cont2();
198
197
  return appSpec.name || lastModuleLoaded;
199
198
  }
200
199
 
@@ -106,18 +106,43 @@ function recordSchemaToString(scm: RecordSchema): string {
106
106
  }
107
107
 
108
108
  function attributeSpecToString(attrSpec: AttributeSpec): string {
109
- let s: string = `${attrSpec.type}`;
109
+ let s: string = getEnumValues(attrSpec) || getOneOfRef(attrSpec) ? '' : `${attrSpec.type}`;
110
+ if (isArrayAttribute(attrSpec)) {
111
+ s = `${s}[]`;
112
+ }
110
113
  if (attrSpec.properties) {
111
114
  const ps: Array<string> = [];
112
115
  attrSpec.properties.forEach((v: any, k: string) => {
113
- if (v == true) ps.push(` @${k}`);
114
- else ps.push(` @${k}(${v})`);
116
+ if (k != 'array') {
117
+ if (v == true) ps.push(` @${k}`);
118
+ else ps.push(` @${k}(${attributePropertyValueToString(k, v, attrSpec.type)})`);
119
+ }
115
120
  });
116
121
  s = s.concat(ps.join(' '));
117
122
  }
118
123
  return s;
119
124
  }
120
125
 
126
+ function attributePropertyValueToString(
127
+ propName: string,
128
+ propValue: any,
129
+ attrType: string
130
+ ): string {
131
+ if (propName == EnumPropertyName) {
132
+ const v = propValue as Set<string>;
133
+ const ss = new Array<string>();
134
+ v.forEach((s: string) => {
135
+ ss.push(`"${s}"`);
136
+ });
137
+ return ss.join(',');
138
+ } else if (propName == 'default') {
139
+ if (isTextualType(attrType) && propValue != 'now()' && propValue != 'uuid()') {
140
+ return `"${propValue}"`;
141
+ }
142
+ }
143
+ return `${propValue}`;
144
+ }
145
+
121
146
  export function newRecordSchema(): RecordSchema {
122
147
  return new Map<string, AttributeSpec>();
123
148
  }
@@ -178,8 +203,8 @@ function asTriggerInfo(te: TriggerEntry): TriggerInfo {
178
203
  };
179
204
  }
180
205
 
181
- const EnumPropertyName = 'one-of';
182
- const OneOfPropertyName = 'one-of-ref';
206
+ const EnumPropertyName = 'enum';
207
+ const OneOfPropertyName = 'oneof';
183
208
 
184
209
  export function enumAttributeSpec(values: Set<string>): AttributeSpec {
185
210
  return {
@@ -1085,6 +1110,13 @@ export class Relationship extends Record {
1085
1110
  const n1 = relNodeEntryToString(this.node1);
1086
1111
  const n2 = relNodeEntryToString(this.node2);
1087
1112
  let s = `relationship ${this.name} ${RelType[this.relType].toLowerCase()} (${n1}, ${n2})`;
1113
+ if (this.isBetween()) {
1114
+ if (this.isOneToOne()) {
1115
+ s = `${s} @${OneToOne}`;
1116
+ } else if (this.isOneToMany()) {
1117
+ s = `${s} @${OneToMany}`;
1118
+ }
1119
+ }
1088
1120
  if (this.getUserAttributes().size > 0) {
1089
1121
  const attrs: Array<string> = [];
1090
1122
  this.getUserAttributes().forEach((attrSpec: AttributeSpec, n: string) => {
@@ -1551,8 +1583,16 @@ export const propertyNames = new Set([
1551
1583
  '@fk',
1552
1584
  '@ref',
1553
1585
  '@readonly',
1586
+ '@enum',
1587
+ '@oneof',
1554
1588
  ]);
1555
1589
 
1590
+ const TextualTypes = new Set(['String', 'Email', 'UUID', 'DateTime', 'Date', 'Time', 'Path']);
1591
+
1592
+ function isTextualType(type: string): boolean {
1593
+ return TextualTypes.has(type);
1594
+ }
1595
+
1556
1596
  export function isBuiltInType(type: string): boolean {
1557
1597
  return builtInTypes.has(type);
1558
1598
  }
@@ -1678,12 +1718,12 @@ export function getAttributeExpr(attrSpec: AttributeSpec): Expr | undefined {
1678
1718
  return getAnyProperty('expr', attrSpec);
1679
1719
  }
1680
1720
 
1681
- export function getOneOfValues(attrSpec: AttributeSpec): Set<string> | undefined {
1682
- return getAnyProperty('one-of', attrSpec);
1721
+ export function getEnumValues(attrSpec: AttributeSpec): Set<string> | undefined {
1722
+ return getAnyProperty(EnumPropertyName, attrSpec);
1683
1723
  }
1684
1724
 
1685
1725
  export function getOneOfRef(attrSpec: AttributeSpec): string | undefined {
1686
- return getAnyProperty('one-of-ref', attrSpec);
1726
+ return getAnyProperty(OneOfPropertyName, attrSpec);
1687
1727
  }
1688
1728
 
1689
1729
  export function getAttributeDefaultValue(attrSpec: AttributeSpec): any | undefined {
@@ -2034,10 +2074,10 @@ function checkOneOfValue(attrSpec: AttributeSpec, attrName: string, attrValue: a
2034
2074
  if (getOneOfRef(attrSpec)) {
2035
2075
  return true;
2036
2076
  }
2037
- const vals: Set<string> | undefined = getOneOfValues(attrSpec);
2077
+ const vals: Set<string> | undefined = getEnumValues(attrSpec);
2038
2078
  if (vals) {
2039
2079
  if (!vals.has(attrValue as string)) {
2040
- throw new Error(`Value of ${attrName} must be one-of ${vals}`);
2080
+ throw new Error(`Value of ${attrName} must be one of ${vals}`);
2041
2081
  }
2042
2082
  return true;
2043
2083
  }
@@ -2341,7 +2381,8 @@ function maybeSetDefaultAttributeValues(
2341
2381
  ): InstanceAttributes {
2342
2382
  const defAttrs = defaultAttributes(schema);
2343
2383
  defAttrs.forEach((v: any, k: string) => {
2344
- if (!attributes.has(k)) {
2384
+ const cv = attributes.get(k);
2385
+ if (cv == undefined || cv == null) {
2345
2386
  if (isString(v)) {
2346
2387
  if (v == 'uuid()') {
2347
2388
  v = crypto.randomUUID();
@@ -1,7 +1,7 @@
1
1
  // Monarch syntax highlighting for the agentlang language.
2
2
  export default {
3
3
  keywords: [
4
- '@actions','@after','@async','@before','@enum','@expr','@meta','@oneof','@rbac','@ref','@upsert','@with_unique','agent','allow','and','as','await','between','catch','contains','create','delete','else','entity','error','event','extends','false','for','if','import','in','into','like','module','not','not_found','onSubscription','or','purge','query','read','record','relationship','resolver','roles','subscribe','true','update','upsert','where','workflow'
4
+ '@actions','@after','@async','@before','@enum','@expr','@from','@meta','@oneof','@rbac','@ref','@upsert','@with_unique','agent','allow','and','as','await','between','catch','contains','create','delete','else','entity','error','event','extends','false','for','if','import','in','into','like','module','not','not_found','onSubscription','or','purge','query','read','record','relationship','resolver','roles','subscribe','true','update','upsert','where','workflow'
5
5
  ],
6
6
  operators: [
7
7
  '*','+',',','-','.','/',':',';','<','<=','<>','=','>','>=','?','@'