okai 0.0.36 → 0.0.37

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/api.d.ts CHANGED
@@ -19,6 +19,7 @@ declare global {
19
19
  export class TimeSpan {}
20
20
  export class DateOnly {}
21
21
  export class TimeOnly {}
22
+ export class DateTimeOffset {}
22
23
  export class Guid {}
23
24
  export class List<T> {}
24
25
  export class HashSet<T> {}
package/dist/client.js CHANGED
@@ -1,3 +1,142 @@
1
+ import { lastRightPart, leftPart, toCamelCase, toPascalCase } from "./utils.js";
1
2
  export function getFileContent(file) {
2
3
  return file.content;
3
4
  }
5
+ const converter = {
6
+ jsTypeMap: {
7
+ Int64: 'long',
8
+ Int32: 'number',
9
+ Int16: 'short',
10
+ UInt16: 'ushort',
11
+ UInt64: 'ulong',
12
+ Boolean: 'boolean',
13
+ String: 'string',
14
+ 'Byte': 'byte',
15
+ 'Byte[]': 'Uint8Array',
16
+ 'BitArray': 'Uint8Array',
17
+ 'Char': 'string',
18
+ 'Char[]': 'string',
19
+ Single: 'float',
20
+ Double: 'double',
21
+ Decimal: 'decimal',
22
+ DateTime: 'Date',
23
+ TimeSpan: 'TimeSpan',
24
+ DateOnly: 'DateOnly',
25
+ TimeOnly: 'TimeOnly',
26
+ Guid: 'Guid',
27
+ Xml: 'String',
28
+ Object: 'any',
29
+ DateTimeOffset: 'DateTimeOffset',
30
+ 'BigInteger': 'BigInt',
31
+ 'Dictionary<string,string>': 'Record<string,string>',
32
+ 'IDictionary<string,string>': 'Record<string,string>',
33
+ },
34
+ // https://www.npgsql.org/doc/types/basic.html
35
+ jsDateTypeMap: {
36
+ 'boolean': 'boolean',
37
+ 'smallint': 'short',
38
+ 'integer': 'number',
39
+ 'bigint': 'long',
40
+ 'real': 'float',
41
+ 'numeric': 'decimal',
42
+ 'money': 'decimal',
43
+ 'text': 'string',
44
+ 'character varying': 'string',
45
+ 'character': 'string',
46
+ 'citext': 'string',
47
+ 'json': 'string',
48
+ 'jsonb': 'string',
49
+ 'xml': 'string',
50
+ 'uuid': 'Guid',
51
+ 'bytea': 'Uint8Array',
52
+ 'timestamp without time zone': 'Date',
53
+ 'timestamp with time zone': 'Date',
54
+ 'date': 'Date',
55
+ 'time without time zone': 'TimeSpan',
56
+ 'time with time zone': 'DateTimeOffset',
57
+ 'interval': 'TimeSpan',
58
+ 'bit(1)': 'boolean',
59
+ 'bit varying': 'Uint8Array',
60
+ 'oid': 'uint',
61
+ 'xid': 'uint',
62
+ 'cid': 'uint',
63
+ 'oidvector': 'uint[]',
64
+ 'name': 'string',
65
+ 'hstore': 'Record<string,string>',
66
+ 'double precision': 'double',
67
+ },
68
+ rules: {
69
+ equals: {},
70
+ prefix: {
71
+ date: 'Date',
72
+ time: 'TimeSpan',
73
+ },
74
+ suffix: {
75
+ 'Date': 'Date',
76
+ 'Time': 'TimeSpan',
77
+ 'Utc': 'DateTimeOffset',
78
+ 'At': 'Date',
79
+ 'Start': 'Date',
80
+ 'End': 'Date',
81
+ 'Enabled': 'boolean',
82
+ 'Confirmed': 'boolean',
83
+ }
84
+ }
85
+ };
86
+ function toJsType(name, fullType, dataTypeName) {
87
+ const type = lastRightPart(fullType, '.');
88
+ if (type === 'Array') {
89
+ const dataType = leftPart(dataTypeName, '[');
90
+ const jsType = converter.jsDateTypeMap[dataType];
91
+ return jsType ? jsType + '[]' : 'any[]';
92
+ }
93
+ for (const [k, v] of Object.entries(converter.rules.prefix)) {
94
+ if (name.startsWith(k)) {
95
+ return v;
96
+ }
97
+ }
98
+ for (const [k, v] of Object.entries(converter.rules.suffix)) {
99
+ if (name.endsWith(k)) {
100
+ return v;
101
+ }
102
+ }
103
+ let jsType = converter.jsTypeMap[type];
104
+ return jsType ?? type;
105
+ }
106
+ export function convertDefinitionsToAst(definitions) {
107
+ const r = {
108
+ config: {},
109
+ defaultExport: {},
110
+ references: [],
111
+ classes: [],
112
+ interfaces: [],
113
+ enums: []
114
+ };
115
+ for (const def of definitions) {
116
+ if (!def.name) {
117
+ console.log('Table definition:', JSON.stringify(def, null, 2));
118
+ throw new Error('Table definition must have a name');
119
+ }
120
+ const cls = {
121
+ name: toPascalCase(def.name),
122
+ properties: def.columns.map(x => {
123
+ const ret = {
124
+ name: toCamelCase(x.columnName),
125
+ type: toJsType(x.columnName, x.dataType, x.dataTypeName),
126
+ optional: x.allowDBNull,
127
+ };
128
+ if (x.isKey) {
129
+ if (x.isAutoIncrement) {
130
+ ret.annotations = [{ name: 'autoIncrement' }];
131
+ }
132
+ else {
133
+ ret.annotations = [{ name: 'primaryKey' }];
134
+ }
135
+ }
136
+ return ret;
137
+ }),
138
+ };
139
+ r.classes.push(cls);
140
+ }
141
+ return r;
142
+ }
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { leftPart, replaceMyApp, trimStart, toPascalCase, plural } from "./utils
6
6
  import { generateCsAstFromTsd, toAst, astForProject } from "./ts-ast.js";
7
7
  import { CSharpApiGenerator } from "./cs-apis.js";
8
8
  import { CSharpMigrationGenerator } from "./cs-migrations.js";
9
- import { getFileContent } from "./client.js";
9
+ import { convertDefinitionsToAst, getFileContent } from "./client.js";
10
10
  import { toTsd } from "./tsd-gen.js";
11
11
  import { UiMjsGroupGenerator, UiMjsIndexGenerator } from "./ui-mjs.js";
12
12
  function normalizeSwitches(cmd) { return cmd.replace(/^-+/, '/'); }
@@ -60,7 +60,7 @@ function parseArgs(...args) {
60
60
  break;
61
61
  }
62
62
  }
63
- else if (ret.type === "help" && ["help", "info", "init", "ls", "add", "rm", "update", "accept"].includes(arg)) {
63
+ else if (ret.type === "help" && ["help", "info", "init", "ls", "add", "rm", "update", "accept", "convert"].includes(arg)) {
64
64
  if (arg == "help")
65
65
  ret.type = "help";
66
66
  else if (arg == "info")
@@ -97,6 +97,10 @@ function parseArgs(...args) {
97
97
  ret.type = "accept";
98
98
  ret.accept = args[++i];
99
99
  }
100
+ else if (arg == "convert") {
101
+ ret.type = "convert";
102
+ ret.convert = args[++i];
103
+ }
100
104
  }
101
105
  else if (arg == "chat") {
102
106
  ret.type = "chat";
@@ -169,24 +173,25 @@ export async function cli(cmdArgs) {
169
173
  }
170
174
  const bin = script.padStart(7, ' ');
171
175
  console.log(`Usage:
172
- ${bin} <prompt> Generate new TypeScript Data Models, C# APIs and Migrations from prompt
173
- -m, -models <model,> Specify up to 5 LLM models to generate .d.ts Data Models
174
- -l, -license <LC-xxx> Specify valid license certificate or key to use premium models
176
+ ${bin} <prompt> Generate new TypeScript Data Models, C# APIs and Migrations from prompt
177
+ -m, -models <model,> Specify up to 5 LLM models to generate .d.ts Data Models
178
+ -l, -license <LC-xxx> Specify valid license certificate or key to use premium models
175
179
 
176
- ${bin} <models>.d.ts Regenerate C# *.cs files for Data Models defined in the TypeScript .d.ts file
177
- -w, -watch Watch for changes to <models>.d.ts and regenerate *.cs on save
180
+ ${bin} <models>.d.ts Regenerate C# *.cs files for Data Models defined in the TypeScript .d.ts file
181
+ -w, -watch Watch for changes to <models>.d.ts and regenerate *.cs on save
178
182
 
179
- ${bin} rm <models>.d.ts Remove <models>.d.ts and its generated *.cs files
180
- ${bin} ls models Display list of available premium LLM models
181
- ${bin} init Initialize okai.json with project info to override default paths
182
- ${bin} init <model> Create an empty <model>.d.ts file for the specified model
183
- ${bin} info Display current project info
184
- ${bin} chat <prompt> Submit a new OpenAI chat request with the specified prompt
185
- -system <prompt> Specify a system prompt
183
+ ${bin} rm <models>.d.ts Remove <models>.d.ts and its generated *.cs files
184
+ ${bin} ls models Display list of available premium LLM models
185
+ ${bin} init Initialize okai.json with project info to override default paths
186
+ ${bin} init <model> Create an empty <model>.d.ts file for the specified model
187
+ ${bin} convert <schema.json> Convert .NET RDBMS TableDefinitions to TypeScript Data Models
188
+ ${bin} info Display current project info
189
+ ${bin} chat <prompt> Submit a new OpenAI chat request with the specified prompt
190
+ -system <prompt> Specify a system prompt
186
191
 
187
- Options:
188
- -v, -verbose Display verbose logging
189
- --ignore-ssl-errors Ignore SSL Errors`);
192
+ Options:
193
+ -v, -verbose Display verbose logging
194
+ --ignore-ssl-errors Ignore SSL Errors`);
190
195
  process.exit(exitCode);
191
196
  return;
192
197
  }
@@ -387,6 +392,12 @@ Options:
387
392
  fs.writeFileSync(tsdPath, newTsdContent, { encoding: 'utf-8' });
388
393
  return newTsdContent;
389
394
  }
395
+ if (info.serviceModelDir) {
396
+ const relativeServiceModelDir = trimStart(info.serviceModelDir.substring(info.slnDir.length), '~/');
397
+ const apiTypesPath = path.join(info.slnDir, relativeServiceModelDir, `api.d.ts`);
398
+ const apiFile = path.join(import.meta.dirname, 'api.d.ts');
399
+ fs.writeFileSync(apiTypesPath, fs.readFileSync(apiFile, 'utf-8'));
400
+ }
390
401
  if (command.watch) {
391
402
  let lastTsdContent = tsdContent;
392
403
  console.log(`watching ${tsdPath} ...`);
@@ -410,6 +421,25 @@ Options:
410
421
  process.exit(0);
411
422
  }
412
423
  }
424
+ if (command.type == "convert") {
425
+ const dbJson = fs.readFileSync(command.convert, 'utf-8');
426
+ const tableDefs = JSON.parse(dbJson);
427
+ const tsdAst = convertDefinitionsToAst(tableDefs);
428
+ const groupName = path.basename(command.convert).replace('.json', '');
429
+ const apiFileName = `${groupName}.cs`;
430
+ let uiFileName = info.uiMjsDir ? `${groupName}.mjs` : null;
431
+ if (tsdAst.references.length == 0) {
432
+ tsdAst.references.push({ path: './api.d.ts' });
433
+ }
434
+ const tsdContent = createTsdFile(info, {
435
+ prompt: path.basename(command.convert),
436
+ apiFileName,
437
+ tsdAst: tsdAst,
438
+ uiFileName,
439
+ });
440
+ console.log(tsdContent);
441
+ process.exit(0);
442
+ }
413
443
  if (command.type === "remove") {
414
444
  let tsdPath = assertTsdPath(command.tsdFile);
415
445
  if (command.verbose)
package/dist/ts-once.js CHANGED
@@ -1,10 +1,11 @@
1
- import { pick, toCamelCase, toPascalCase } from "./utils.js";
1
+ import { pick, splitCase, toCamelCase, toPascalCase } from "./utils.js";
2
2
  // Tranforms that are only applied once on AI TypeScript AST
3
3
  export function createTdAstFromAIAst(tsAst, gropName) {
4
4
  mergeInterfacesAndClasses(tsAst);
5
5
  rewriteToPascalCase(tsAst);
6
6
  replaceReferences(tsAst);
7
7
  replaceIds(tsAst);
8
+ replaceConflictTypes(tsAst);
8
9
  tsAst.classes.forEach(cls => {
9
10
  // replaceUserRefs(cls)
10
11
  replaceUserBaseClass(cls);
@@ -97,6 +98,34 @@ function replaceUserBaseClass(type) {
97
98
  }
98
99
  }
99
100
  }
101
+ function replaceConflictTypes(gen) {
102
+ const conflictTypeNames = [`Service`, `Task`];
103
+ const conflictTypes = gen.classes.filter(x => conflictTypeNames.includes(x.name));
104
+ for (const conflictType of conflictTypes) {
105
+ const firstFkProp = conflictType.properties?.find(x => x.name.endsWith('Id'));
106
+ if (firstFkProp) {
107
+ const splitWords = splitCase(firstFkProp.name).split(' ');
108
+ const prefix = splitWords.length >= 2 ? splitWords[splitWords.length - 2] : null;
109
+ if (prefix) {
110
+ const newType = `${toPascalCase(prefix)}${conflictType.name}`;
111
+ const conflictTypeName = conflictType.name;
112
+ conflictType.name = newType;
113
+ for (const type of gen.classes) {
114
+ if (type.properties) {
115
+ for (const prop of type.properties) {
116
+ if (prop.type === conflictTypeName) {
117
+ prop.type = newType;
118
+ }
119
+ if (prop.type === `${conflictTypeName}[]`) {
120
+ prop.type = `${newType}[]`;
121
+ }
122
+ }
123
+ }
124
+ }
125
+ }
126
+ }
127
+ }
128
+ }
100
129
  function rewriteDuplicateTypePropNames(type) {
101
130
  const duplicateTypePropMap = {
102
131
  note: 'content',
package/dist/utils.js CHANGED
@@ -196,7 +196,7 @@ export function toPascalCase(s) {
196
196
  return words.map(x => x[0].toUpperCase() + x.substring(1).toLowerCase()).join('');
197
197
  }
198
198
  if (s.includes('_')) {
199
- return s.split('_').map(x => x[0].toUpperCase() + x.substring(1)).join('');
199
+ return s.split('_').filter(x => x[0]).map(x => x[0].toUpperCase() + x.substring(1)).join('');
200
200
  }
201
201
  return s.charAt(0).toUpperCase() + s.substring(1);
202
202
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "okai",
3
3
  "type": "module",
4
- "version": "0.0.36",
4
+ "version": "0.0.37",
5
5
  "bin": "./dist/okai.js",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.js",