okai 0.0.24 → 0.0.25

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.
package/dist/cs-gen.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { unwrap } from "./cs-ast";
1
2
  import { leftPart } from "./utils.js";
2
3
  export class CSharpGenerator {
3
4
  namespaces = [];
@@ -25,7 +26,7 @@ export class CSharpGenerator {
25
26
  }
26
27
  const optional = name.endsWith('?');
27
28
  return genericArgs?.length
28
- ? `${leftPart(name, '`')}<${genericArgs.join(',')}>` + (optional ? '?' : '')
29
+ ? `${unwrap(leftPart(name, '`'))}<${genericArgs.join(',')}>` + (optional ? '?' : '')
29
30
  : name;
30
31
  }
31
32
  toAttribtue(attr) {
@@ -36,7 +37,7 @@ export class CSharpGenerator {
36
37
  if (body)
37
38
  body += ',';
38
39
  const value = arg.type.toLowerCase() === 'string'
39
- ? `"${arg.value}"`
40
+ ? this.toQuotedString(arg.value)
40
41
  : arg.value;
41
42
  body += value;
42
43
  }
@@ -47,7 +48,7 @@ export class CSharpGenerator {
47
48
  if (body)
48
49
  body += ',';
49
50
  const value = arg.type.toLowerCase() === 'string'
50
- ? `"${arg.value}"`
51
+ ? this.toQuotedString(arg.value)
51
52
  : arg.value;
52
53
  body += `${arg.name}=${value}`;
53
54
  }
@@ -55,7 +56,7 @@ export class CSharpGenerator {
55
56
  return `[${attr.name}${body ? `(${body})` : ''}]`;
56
57
  }
57
58
  toClass(cls, opt) {
58
- const showDesc = !opt || !opt.hide?.includes('description');
59
+ const showDesc = !opt || !opt.hideAttrs?.includes('description');
59
60
  const sb = [];
60
61
  let clsDef = `public class ${cls.name}`;
61
62
  if (cls.inherits) {
@@ -71,7 +72,7 @@ export class CSharpGenerator {
71
72
  sb.push(`/// </summary>`);
72
73
  }
73
74
  for (const attr of cls.attributes ?? []) {
74
- if (opt?.hide?.includes(attr.name.toLowerCase()))
75
+ if (opt?.hideAttrs?.includes(attr.name.toLowerCase()))
75
76
  continue;
76
77
  const def = this.toAttribtue(attr);
77
78
  sb.push(`${def}`);
@@ -79,6 +80,8 @@ export class CSharpGenerator {
79
80
  sb.push(clsDef);
80
81
  sb.push('{');
81
82
  for (const prop of cls.properties ?? []) {
83
+ if (opt?.ignoreProp?.(prop))
84
+ continue;
82
85
  this.addNamespace(prop.namespace);
83
86
  if (showDesc && prop.description) {
84
87
  sb.push(` /// <summary>`);
@@ -95,11 +98,14 @@ export class CSharpGenerator {
95
98
  sb.push('}');
96
99
  return sb.join('\n');
97
100
  }
101
+ toQuotedString(s, defaultValue = '""') {
102
+ return s ? `"${s.replaceAll('\n', '\\n').replaceAll('"', '\\"')}"` : defaultValue;
103
+ }
98
104
  toEnum(enumType, opt) {
99
- const showDesc = !opt || !opt.hide?.includes('description');
105
+ const showDesc = !opt || !opt.hideAttrs?.includes('description');
100
106
  const sb = [];
101
107
  if (showDesc && enumType.description) {
102
- sb.push(`[Description("${enumType.description}")]`);
108
+ sb.push(`[Description(${this.toQuotedString(enumType.description)})]`);
103
109
  }
104
110
  sb.push(`public enum ${enumType.name}`);
105
111
  sb.push('{');
@@ -112,10 +118,10 @@ export class CSharpGenerator {
112
118
  : value;
113
119
  let desc = enumType.enumDescriptions?.[i];
114
120
  if (desc) {
115
- sb.push(` [Description("${desc}")]`);
121
+ sb.push(` [Description(${this.toQuotedString(desc)})]`);
116
122
  }
117
123
  if (memberValue && memberValue !== value && memberValue !== name) {
118
- sb.push(` [EnumMember(Value = "${memberValue}")]`);
124
+ sb.push(` [EnumMember(Value = ${this.toQuotedString(memberValue)})]`);
119
125
  }
120
126
  sb.push(value
121
127
  ? ` ${name} = ${value},`
@@ -9,9 +9,12 @@ export class CSharpMigrationGenerator extends CSharpGenerator {
9
9
  'ServiceStack.OrmLite',
10
10
  ...ast.namespaces
11
11
  ]));
12
- const hideAttrs = { hide: ['description', 'icon'] };
13
- this.classes = ast.types.filter(t => !t.isEnum && !t.isInterface).map(x => this.toClass(x, hideAttrs));
14
- this.enums = ast.types.filter(t => t.isEnum).map(x => this.toEnum(x, hideAttrs));
12
+ // props with [Reference] attribute don't need to be included in the migration (ignore to avoid missing references e.g. User)
13
+ const ignoreProp = (prop) => prop.attributes?.some(x => x.name === 'Reference');
14
+ const hideAttrs = ['description', 'icon'];
15
+ const opt = { hideAttrs, ignoreProp };
16
+ this.classes = ast.types.filter(t => !t.isEnum && !t.isInterface).map(x => this.toClass(x, opt));
17
+ this.enums = ast.types.filter(t => t.isEnum).map(x => this.toEnum(x, opt));
15
18
  const sb = [];
16
19
  if (this.classes.length) {
17
20
  for (const cls of this.classes) {
@@ -80,16 +83,17 @@ export class CSharpMigrationGenerator extends CSharpGenerator {
80
83
  orderedTypes.push(type.name);
81
84
  }
82
85
  });
86
+ const ignoreTables = ['User'];
83
87
  sb.push(' public override void Up()');
84
88
  sb.push(' {');
85
- for (const typeName of orderedTypes) {
89
+ for (const typeName of orderedTypes.filter(x => !ignoreTables.includes(x))) {
86
90
  sb.push(` Db.CreateTable<${typeName}>();`);
87
91
  }
88
92
  sb.push(' }');
89
93
  sb.push('');
90
94
  sb.push(' public override void Down()');
91
95
  sb.push(' {');
92
- for (const typeName of orderedTypes.reverse()) {
96
+ for (const typeName of orderedTypes.filter(x => !ignoreTables.includes(x)).reverse()) {
93
97
  sb.push(` Db.DropTable<${typeName}>();`);
94
98
  }
95
99
  sb.push(' }');
@@ -100,3 +104,8 @@ export class CSharpMigrationGenerator extends CSharpGenerator {
100
104
  };
101
105
  }
102
106
  }
107
+ export function toCSharpMigration(csAst) {
108
+ const csFiles = new CSharpMigrationGenerator().generate(csAst);
109
+ const cs = csFiles[Object.keys(csFiles)[0]];
110
+ return cs;
111
+ }
package/dist/index.js CHANGED
@@ -2,13 +2,12 @@ import fs from "fs";
2
2
  import path from "path";
3
3
  import blessed from 'blessed';
4
4
  import { projectInfo } from './info.js';
5
- import { parseTsdHeader, toTsdHeader, getGroupName, leftPart, replaceMyApp, trimStart } from "./utils.js";
6
- import { toAst } from "./ts-ast.js";
7
- import { toMetadataTypes } from "./cs-ast.js";
5
+ import { parseTsdHeader, toTsdHeader, leftPart, replaceMyApp, trimStart, toPascalCase, plural } from "./utils.js";
6
+ import { generateCsAstFromTsd, toAst, astForProject } from "./ts-ast.js";
8
7
  import { CSharpApiGenerator } from "./cs-apis.js";
9
8
  import { CSharpMigrationGenerator } from "./cs-migrations.js";
10
- import { TsdDataModelGenerator } from "./tsd-gen.js";
11
9
  import { getFileContent } from "./client.js";
10
+ import { toTsd } from "./tsd-gen.js";
12
11
  function normalizeSwitches(cmd) { return cmd.replace(/^-+/, '/'); }
13
12
  function parseArgs(...args) {
14
13
  const ret = {
@@ -62,8 +61,12 @@ function parseArgs(...args) {
62
61
  ret.type = "help";
63
62
  else if (arg == "info")
64
63
  ret.type = "info";
65
- else if (arg == "init")
64
+ else if (arg == "init") {
66
65
  ret.type = "init";
66
+ if (args[i + 1]) {
67
+ ret.init = args[++i];
68
+ }
69
+ }
67
70
  else if (arg == "update") {
68
71
  ret.type = "update";
69
72
  ret.tsdFile = args[++i];
@@ -136,7 +139,7 @@ export async function cli(cmdArgs) {
136
139
  process.exit(0);
137
140
  return;
138
141
  }
139
- if (command.type === "init") {
142
+ if (command.type === "init" && !command.init) {
140
143
  let info = projectInfo(process.cwd()) ?? {
141
144
  projectName: "<MyApp>",
142
145
  slnDir: "/path/to/.sln/folder",
@@ -146,6 +149,7 @@ export async function cli(cmdArgs) {
146
149
  serviceInterfaceDir: "/path/to/MyApp.ServiceInterfaces",
147
150
  };
148
151
  fs.writeFileSync('okai.json', JSON.stringify(info, undefined, 2));
152
+ console.log(`Added: okai.json`);
149
153
  process.exit(0);
150
154
  return;
151
155
  }
@@ -166,6 +170,7 @@ ${bin} <models>.d.ts Regenerate C# *.cs files for Data Models defined in
166
170
  ${bin} rm <models>.d.ts Remove <models>.d.ts and its generated *.cs files
167
171
  ${bin} ls models Display list of available premium LLM models
168
172
  ${bin} init Initialize okai.json with project info to override default paths
173
+ ${bin} init <model> Create an empty <model>.d.ts file for the specified model
169
174
  ${bin} info Display current project info
170
175
 
171
176
  Options:
@@ -212,6 +217,37 @@ Options:
212
217
  process.exit(0);
213
218
  }
214
219
  }
220
+ if (command.type == "init" && command.info) {
221
+ const toApiFile = path.join(info.serviceModelDir, 'api.d.ts');
222
+ fs.copyFileSync(path.join(import.meta.dirname, 'api.d.ts'), toApiFile);
223
+ console.log(`Added: ${toApiFile}`);
224
+ let model = toPascalCase(leftPart(command.init, '.'));
225
+ let groupName = plural(model);
226
+ if (model == groupName) {
227
+ if (model.endsWith('s')) {
228
+ model = model.substring(0, model.length - 1);
229
+ }
230
+ }
231
+ let tsd = `
232
+ export class ${model} {
233
+ id:number
234
+ name:string
235
+ }
236
+ `;
237
+ const tsdAst = toAst(tsd);
238
+ if (tsdAst.references.length == 0) {
239
+ tsdAst.references.push({ path: './api.d.ts' });
240
+ }
241
+ tsd = toTsd(tsdAst);
242
+ const tsdContent = createTsdFile(info, {
243
+ prompt: `New ${model}`,
244
+ apiFileName: `${groupName}.cs`,
245
+ tsd,
246
+ });
247
+ command.type = "update"; // let update handle the rest
248
+ command.tsdFile = groupName + '.d.ts';
249
+ fs.writeFileSync(path.join(info.serviceModelDir, command.tsdFile), tsdContent, { encoding: 'utf-8' });
250
+ }
215
251
  if (command.type === "add") {
216
252
  if (command.add == "types" || command.add?.startsWith("api")) {
217
253
  const apiFile = path.join(import.meta.dirname, 'api.d.ts');
@@ -263,14 +299,10 @@ Options:
263
299
  if (command.verbose)
264
300
  console.log(JSON.stringify(header, undefined, 2));
265
301
  function regenerate(header, tsdContent, logPrefix = '') {
266
- const tsdAst = toAst(tsdContent);
267
- const tsdGenerator = new TsdDataModelGenerator();
268
- tsdContent = tsdGenerator.generate(tsdAst);
269
- const csAst = toMetadataTypes(tsdAst);
270
- // const groupName = path.basename(command.tsdFile, '.d.ts')
271
- // console.log('groupName', groupName)
302
+ const result = generateCsAstFromTsd(tsdContent);
303
+ tsdContent = result.tsd;
272
304
  const genApis = new CSharpApiGenerator();
273
- const csApiFiles = genApis.generate(csAst);
305
+ const csApiFiles = genApis.generate(result.csAst);
274
306
  const apiContent = replaceMyApp(csApiFiles[Object.keys(csApiFiles)[0]], info.projectName);
275
307
  const apiPath = resolveApiFile(header.api);
276
308
  console.log(`${logPrefix}${apiPath}`);
@@ -278,14 +310,14 @@ Options:
278
310
  if (header?.migration) {
279
311
  const migrationCls = leftPart(path.basename(header.migration), '.');
280
312
  const getMigrations = new CSharpMigrationGenerator();
281
- const csMigrationFiles = getMigrations.generate(csAst);
313
+ const csMigrationFiles = getMigrations.generate(result.csAst);
282
314
  const migrationContent = replaceMyApp(csMigrationFiles[Object.keys(csMigrationFiles)[0]].replaceAll('Migration1000', migrationCls), info.projectName);
283
315
  const migrationPath = resolveApiFile(header.migration);
284
316
  console.log(`${logPrefix}${migrationPath}`);
285
317
  fs.writeFileSync(migrationPath, migrationContent, { encoding: 'utf-8' });
286
318
  }
287
319
  console.log(`${logPrefix}${tsdPath}`);
288
- const newTsdContent = toTsdHeader(header) + '\n\n' + tsdContent;
320
+ const newTsdContent = toTsdHeader(header) + (tsdContent.startsWith('///') ? '' : '\n\n') + tsdContent;
289
321
  fs.writeFileSync(tsdPath, newTsdContent, { encoding: 'utf-8' });
290
322
  return newTsdContent;
291
323
  }
@@ -300,7 +332,7 @@ Options:
300
332
  console.log(`No change detected`);
301
333
  return;
302
334
  }
303
- console.log(`\n${++i}. ${leftPart(new Date().toTimeString(), ' ')} regenerating files:`);
335
+ console.log(`\n${++i}. regenerated files at ${leftPart(new Date().toTimeString(), ' ')}:`);
304
336
  lastTsdContent = regenerate(header, tsdContent);
305
337
  });
306
338
  return;
@@ -620,35 +652,29 @@ function chooseFile(ctx, info, gist, comamnd) {
620
652
  acceptTask = fetch(acceptUrl, { method: 'POST' });
621
653
  }
622
654
  const origTsd = file.content;
623
- const tsdAst = toAst(origTsd);
624
- const generator = new TsdDataModelGenerator();
625
- const tsd = generator.generate(tsdAst);
626
- const csAst = toMetadataTypes(tsdAst);
627
- const groupName = getGroupName(csAst);
655
+ const tsdAst = astForProject(toAst(origTsd), info);
656
+ const res = generateCsAstFromTsd(toTsd(tsdAst));
628
657
  const genApis = new CSharpApiGenerator();
629
- const csApiFiles = genApis.generate(csAst);
658
+ const csApiFiles = genApis.generate(res.csAst);
630
659
  const getMigrations = new CSharpMigrationGenerator();
631
- const csMigrationFiles = getMigrations.generate(csAst);
660
+ const csMigrationFiles = getMigrations.generate(res.csAst);
632
661
  const relativeServiceModelDir = trimStart(info.serviceModelDir.substring(info.slnDir.length), '~/');
633
662
  const relativeMigrationDir = trimStart(info.migrationsDir.substring(info.slnDir.length), '~/');
634
- const apiFileName = `${groupName}.cs`;
663
+ const apiFileName = `${res.groupName}.cs`;
635
664
  const apiContent = replaceMyApp(csApiFiles[Object.keys(csApiFiles)[0]], info.projectName);
636
665
  const migrationPath = resolveMigrationFile(path.join(info.migrationsDir, `Migration1000.cs`));
637
666
  const migrationFileName = path.basename(migrationPath);
638
667
  const migrationCls = leftPart(migrationFileName, '.');
639
668
  const migrationContent = replaceMyApp(csMigrationFiles[Object.keys(csMigrationFiles)[0]].replaceAll('Migration1000', migrationCls), info.projectName);
640
- const sb = [
641
- `/// <reference path="./api.d.ts" />`,
642
- `/*prompt: ${titleBar.content.replaceAll('/*', '').replaceAll('*/', '')}`,
643
- `api: ~/${path.join(relativeServiceModelDir, apiFileName)}`,
644
- `migration: ~/${path.join(relativeMigrationDir, migrationFileName)}`,
645
- `*/`,
646
- '',
647
- tsd,
648
- ];
649
- const tsdContent = sb.join('\n');
650
- const tsdFileName = `${groupName}.d.ts`;
651
- const typesApiPath = path.join(info.slnDir, relativeServiceModelDir, `api.d.ts`);
669
+ const apiTypesPath = path.join(info.slnDir, relativeServiceModelDir, `api.d.ts`);
670
+ const apiFile = path.join(import.meta.dirname, 'api.d.ts');
671
+ fs.writeFileSync(apiTypesPath, fs.readFileSync(apiFile, 'utf-8'));
672
+ const tsdContent = createTsdFile(info, {
673
+ prompt: titleBar.content.replaceAll('/*', '').replaceAll('*/', ''),
674
+ apiFileName,
675
+ tsd: res.tsd,
676
+ });
677
+ const tsdFileName = `${res.groupName}.d.ts`;
652
678
  const fullTsdPath = path.join(info.slnDir, relativeServiceModelDir, tsdFileName);
653
679
  const fullApiPath = path.join(info.slnDir, relativeServiceModelDir, apiFileName);
654
680
  const fullMigrationPath = path.join(info.slnDir, relativeMigrationDir, migrationFileName);
@@ -667,8 +693,6 @@ function chooseFile(ctx, info, gist, comamnd) {
667
693
  fs.writeFileSync(fullMigrationPath, migrationContent, { encoding: 'utf-8' });
668
694
  console.log(`Saved: ${fullMigrationPath}`);
669
695
  }
670
- const apiFile = path.join(import.meta.dirname, 'api.d.ts');
671
- fs.writeFileSync(typesApiPath, fs.readFileSync(apiFile, 'utf-8'));
672
696
  const script = path.basename(process.argv[1]);
673
697
  console.log(`\nTo regenerate classes, update '${tsdFileName}' then run:`);
674
698
  console.log(`$ ${script} ${tsdFileName}`);
@@ -742,6 +766,24 @@ function exit(screen, info, gist) {
742
766
  }
743
767
  process.exit(0);
744
768
  }
769
+ function createTsdFile(info, opt) {
770
+ const migrationPath = resolveMigrationFile(path.join(info.migrationsDir, `Migration1000.cs`));
771
+ const migrationFileName = path.basename(migrationPath);
772
+ const relativeServiceModelDir = trimStart(info.serviceModelDir.substring(info.slnDir.length), '~/');
773
+ const relativeMigrationDir = info.migrationsDir && fs.existsSync(info.migrationsDir)
774
+ ? trimStart(info.migrationsDir.substring(info.slnDir.length), '~/')
775
+ : null;
776
+ const sb = [
777
+ `/*prompt: ${opt.prompt}`,
778
+ `api: ~/${path.join(relativeServiceModelDir, opt.apiFileName)}`,
779
+ ];
780
+ if (relativeMigrationDir) {
781
+ sb.push(`migration: ~/${path.join(relativeMigrationDir, migrationFileName)}`);
782
+ }
783
+ sb.push('*/');
784
+ sb.push('');
785
+ return sb.join('\n') + (opt.tsd.startsWith('///') ? '' : '\n\n') + opt.tsd;
786
+ }
745
787
  function applyCSharpGist(ctx, info, gist, { accept = false, acceptAll = false, discard = false }) {
746
788
  const { screen, titleBar, fileList, preview, statusBar, result } = ctx;
747
789
  function removeSelected() {
package/dist/info.js CHANGED
@@ -53,7 +53,83 @@ export function projectInfo(cwd) {
53
53
  serviceModelDir,
54
54
  serviceInterfaceDir,
55
55
  };
56
+ for (const file of walk(serviceInterfaceDir, [], {
57
+ include: (path) => path.endsWith(".cs"),
58
+ excludeDirs: ["obj", "bin"]
59
+ })) {
60
+ const content = fs.readFileSync(file).toString();
61
+ const userInfo = parseUserType(content);
62
+ if (userInfo) {
63
+ info.userType = userInfo.userType;
64
+ info.userIdType = userInfo.userIdType ?? 'string';
65
+ break;
66
+ }
67
+ }
68
+ for (const file of walk(serviceModelDir, [], {
69
+ include: (path) => path.endsWith(".cs"),
70
+ excludeDirs: ["obj", "bin"]
71
+ })) {
72
+ const content = fs.readFileSync(file).toString();
73
+ const dtoInfo = parseUserDtoType(content);
74
+ if (dtoInfo?.userType) {
75
+ info.userType = dtoInfo.userType;
76
+ if (dtoInfo.userIdType)
77
+ info.userIdType = dtoInfo.userIdType;
78
+ if (dtoInfo.userLabel)
79
+ info.userLabel = dtoInfo.userLabel;
80
+ break;
81
+ }
82
+ }
56
83
  return config
57
84
  ? Object.assign({}, info, config)
58
85
  : info;
59
86
  }
87
+ export function parseUserType(cs) {
88
+ const typePattern = /class\s+(\w+)\s*:\s*IdentityUser(?:<(.+)>)?/;
89
+ const match = cs.match(typePattern);
90
+ if (!match)
91
+ return null;
92
+ return {
93
+ userType: match[1], // Type name
94
+ userIdType: match[2] || null // Generic arguments (content between < >)
95
+ };
96
+ }
97
+ export function parseUserDtoType(cs) {
98
+ const userAliasPos = cs.indexOf('[Alias("AspNetUsers")]');
99
+ if (userAliasPos === -1)
100
+ return null;
101
+ const typeMatch = cs.substring(userAliasPos).match(/class\s+(\w+)\s*/);
102
+ if (!typeMatch)
103
+ return null;
104
+ const idMatch = cs.substring(userAliasPos).match(/\s+(\w+\??)\s+Id\s+/i);
105
+ const nameMatch = Array.from(cs.substring(userAliasPos).matchAll(/\s+(\w+Name)\s+/ig))
106
+ .find(x => x[1].toLowerCase() !== 'username')?.[1];
107
+ const userLabel = cs.includes('DisplayName')
108
+ ? 'DisplayName'
109
+ : nameMatch
110
+ ? nameMatch
111
+ : undefined;
112
+ return {
113
+ userType: typeMatch[1], // DTO Type name
114
+ userIdType: idMatch ? idMatch[1] : null,
115
+ userLabel,
116
+ };
117
+ }
118
+ function walk(dir, fileList = [], opt) {
119
+ const files = fs.readdirSync(dir);
120
+ for (const file of files) {
121
+ const filePath = path.join(dir, file);
122
+ const stat = fs.statSync(filePath);
123
+ if (stat.isDirectory()) {
124
+ if (!opt?.excludeDirs || !opt.excludeDirs.includes(file)) {
125
+ walk(filePath, fileList, opt);
126
+ }
127
+ }
128
+ else {
129
+ if (!opt?.include || opt?.include(filePath)) {
130
+ fileList.push(filePath);
131
+ }
132
+ }
133
+ }
134
+ return fileList;
135
+ }
package/dist/ts-ast.js CHANGED
@@ -1,5 +1,10 @@
1
+ import { toMetadataTypes } from "./cs-ast.js";
2
+ import { createTdAstFromAIAst, transformUserRefs } from "./ts-once.js";
1
3
  import { TypeScriptParser } from "./ts-parser.js";
2
- export function toTypeScriptSrc(msg) {
4
+ import { toTsd } from "./tsd-gen.js";
5
+ import { getGroupName } from "./utils.js";
6
+ // Extract TypeScript source code from a AI Response
7
+ export function extractTypeScriptSrc(msg) {
3
8
  msg = msg.trim();
4
9
  const startPos = msg.indexOf("```typescript");
5
10
  if (startPos >= 0) {
@@ -12,7 +17,33 @@ export function toTypeScriptSrc(msg) {
12
17
  return msg;
13
18
  }
14
19
  }
20
+ // Parse TypeScript source code into AST
15
21
  export function toAst(src) {
16
22
  const parser = new TypeScriptParser();
17
23
  return parser.parse(src);
18
24
  }
25
+ // Tranforms that are only applied once on AI TypeScript AST
26
+ export function createAstFromAITypeScript(ts) {
27
+ const parser = new TypeScriptParser();
28
+ const tdAst = parser.parse(ts);
29
+ return createTdAstFromAIAst(tdAst);
30
+ }
31
+ // Apply Project Info to the AST once after it's downloaded
32
+ export function astForProject(tsAst, info) {
33
+ transformUserRefs(tsAst, info);
34
+ return tsAst;
35
+ }
36
+ // Convert User TypeScript into CS AST and Updated TSD
37
+ export function generateCsAstFromTsd(userTs) {
38
+ const userTsAst = toAst(userTs); // user modified tsd
39
+ const tsd = toTsd(userTsAst);
40
+ const tsdAst = toAst(tsd);
41
+ const csAst = toMetadataTypes(tsdAst);
42
+ const result = {
43
+ tsdAst,
44
+ tsd,
45
+ csAst,
46
+ groupName: getGroupName(csAst)
47
+ };
48
+ return result;
49
+ }