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 +1 -0
- package/dist/client.js +139 -0
- package/dist/index.js +47 -17
- package/dist/ts-once.js +30 -1
- package/dist/utils.js +1 -1
- package/package.json +1 -1
package/dist/api.d.ts
CHANGED
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>
|
173
|
-
-m, -models <model,>
|
174
|
-
-l, -license <LC-xxx>
|
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
|
177
|
-
-w, -watch
|
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
|
180
|
-
${bin} ls models
|
181
|
-
${bin} init
|
182
|
-
${bin} init <model>
|
183
|
-
${bin}
|
184
|
-
${bin}
|
185
|
-
|
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
|
189
|
-
--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
|
}
|