okai 0.0.5 → 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.
package/dist/cs-ast.js ADDED
@@ -0,0 +1,685 @@
1
+ import { plural, toPascalCase } from "./utils.js";
2
+ import { Icons } from "./icons.js";
3
+ const sys = (name, genericArgs) => ({ name, namespace: "System", genericArgs });
4
+ const sysObj = sys("object");
5
+ const sysDictObj = { name: "Dictionary", genericArgs: ["string", "object"], namespace: "System.Collections.Generic" };
6
+ export class CSharpAst {
7
+ typeMap = {
8
+ "number": sys("int"),
9
+ "string": sys("string"),
10
+ "boolean": sys("bool"),
11
+ "Date": sys("DateTime"),
12
+ "object": sysDictObj,
13
+ "symbol": sys("string"),
14
+ "null": sys("string"),
15
+ "undefined": sys("string"),
16
+ "bigint": sys("long"),
17
+ "any": sysObj,
18
+ };
19
+ decimalTypeProps = [
20
+ "price", "cost", "amount", "total", "salary", "balance", "rate", "discount", "tax", "fee"
21
+ ];
22
+ currencyTypeProps = [
23
+ "price", "cost", "total", "salary", "balance", "tax", "fee"
24
+ ];
25
+ valueTypes = [
26
+ "int", "long", "short", "ulong", "ushort", "sbyte", "uint", "char", "byte", "float", "double", "decimal", "bool",
27
+ "Int16", "Int32", "Int64", "UInt16", "UInt32", "UInt64", "SByte", "Byte", "Single", "Double", "Decimal", "Boolean",
28
+ "DateTime", "DateTimeOffset", "TimeSpan", "DateOnly", "TimeOnly", "Guid",
29
+ ];
30
+ integerTypes = [
31
+ "int", "long", "short", "ulong", "ushort", "sbyte", "uint",
32
+ "Int16", "Int32", "Int64", "UInt16", "UInt32", "UInt64"
33
+ ];
34
+ commonValueTypes = [
35
+ "int", "Int32", "long", "Int64", "string",
36
+ ];
37
+ unwrap(type) {
38
+ if (type.endsWith("?")) {
39
+ return type.substring(0, type.length - 1);
40
+ }
41
+ return type;
42
+ }
43
+ nullable(type) {
44
+ return type.endsWith('?') ? type : `${type}?`;
45
+ }
46
+ isEnum(type) {
47
+ type = this.unwrap(type);
48
+ return this.ast.enums.some(x => x.name === type) || this.result.types.find(x => x.name === type && x.isEnum);
49
+ }
50
+ isValueType(type) {
51
+ type = this.unwrap(type);
52
+ return this.valueTypes.includes(type) || this.isEnum(type);
53
+ }
54
+ ast = { classes: [], interfaces: [], enums: [] };
55
+ result = {
56
+ config: {},
57
+ namespaces: [],
58
+ operations: [],
59
+ types: []
60
+ };
61
+ toCsName(tsName) {
62
+ return toPascalCase(tsName);
63
+ }
64
+ csharpType(type, propName) {
65
+ if (propName) {
66
+ if (type === "number") {
67
+ if (this.decimalTypeProps.some(x => propName.toLowerCase().includes(x))) {
68
+ return sys("decimal");
69
+ }
70
+ }
71
+ }
72
+ return this.typeMap[type] ?? { name: type, namespace: "MyApp" };
73
+ }
74
+ addMetadataType(cls) {
75
+ const type = {
76
+ name: this.toCsName(cls.name),
77
+ namespace: "MyApp",
78
+ description: cls.comment,
79
+ properties: cls.properties.map(p => {
80
+ const type = this.csharpType(p.type, p.name);
81
+ const prop = {
82
+ name: this.toCsName(p.name),
83
+ type: p.optional ? this.nullable(type.name) : type.name,
84
+ namespace: type.namespace,
85
+ description: p.comment,
86
+ };
87
+ if (prop.name === 'Id') {
88
+ prop.isPrimaryKey = true;
89
+ }
90
+ const valueType = this.isValueType(prop.type);
91
+ if (valueType) {
92
+ prop.isValueType = true;
93
+ }
94
+ const isEnum = this.isEnum(prop.type);
95
+ if (isEnum) {
96
+ prop.isEnum = true;
97
+ }
98
+ if (!prop.isValueType && !prop.type.endsWith('?')) {
99
+ prop.isRequired = true;
100
+ }
101
+ if (this.currencyTypeProps.some(x => prop.name.toLowerCase().includes(x))) {
102
+ if (!prop.attributes)
103
+ prop.attributes = [];
104
+ prop.attributes.push({
105
+ name: "IntlNumber",
106
+ args: [{ name: "Currency", type: "constant", value: "NumberCurrency.USD" }]
107
+ });
108
+ }
109
+ return prop;
110
+ }),
111
+ };
112
+ // Add dependent types first
113
+ type.properties.filter(x => x.namespace === 'MyApp'
114
+ && x.name !== cls.name
115
+ && !this.result.types.some(t => t.name === x.name && t.namespace === 'MyApp')).forEach(x => {
116
+ const refEnum = this.ast.enums.find(e => e.name === x.type);
117
+ if (refEnum) {
118
+ this.addMetadataEnum(refEnum);
119
+ }
120
+ const refType = this.ast.classes.find(c => c.name === x.type);
121
+ if (refType) {
122
+ this.addMetadataType(refType);
123
+ }
124
+ });
125
+ if (!this.result.types.find(x => x.name === cls.name && x.namespace === 'MyApp')) {
126
+ this.result.types.push(type);
127
+ }
128
+ return type;
129
+ }
130
+ addMetadataEnum(e) {
131
+ if (this.result.types.find(x => x.name === e.name && x.isEnum))
132
+ return;
133
+ const type = {
134
+ name: this.toCsName(e.name),
135
+ namespace: "MyApp",
136
+ description: e.comment,
137
+ isEnum: true,
138
+ isEnumInt: typeof e.members[0].value == 'number',
139
+ enumNames: e.members.map(x => this.toCsName(x.name)),
140
+ };
141
+ if (type.isEnumInt) {
142
+ type.enumValues = e.members.map(x => `${x.value}`);
143
+ }
144
+ else {
145
+ type.enumMemberValues = e.members.map(x => `${x.value}`);
146
+ }
147
+ if (e.members.some(x => x.comment)) {
148
+ type.enumDescriptions = e.members.map(x => x.comment);
149
+ }
150
+ this.result.types.push(type);
151
+ return type;
152
+ }
153
+ get classes() {
154
+ return this.result.types.filter(x => !x.isEnum && x.properties);
155
+ }
156
+ // e.g. Table DataModels have Primary Keys
157
+ get typesWithPrimaryKeys() {
158
+ return this.result.types.filter(x => !x.isEnum && !x.isInterface && x.properties?.some(x => x.isPrimaryKey));
159
+ }
160
+ get typesWithReferences() {
161
+ return this.result.types.filter(x => !x.isEnum && !x.isInterface && x.properties?.some(x => x.attributes?.some(x => x.name === 'Reference')));
162
+ }
163
+ replaceReferences(references) {
164
+ // The most important types are the ones with the most references
165
+ const refCount = (t) => t.properties?.filter(p => this.result.types.find(x => x.name === p.type && p.namespace === 'MyApp')).length || 0;
166
+ const importantTypes = this.result.types.sort((x, y) => refCount(y) - refCount(x));
167
+ for (const type of this.result.types) {
168
+ if (references.includes(type.name)) {
169
+ const importantType = importantTypes.find(x => x.properties?.some(p => p.type === type.name));
170
+ if (importantType) {
171
+ const newName = `${importantType.name}${type.name}`;
172
+ this.replaceReference(type.name, newName);
173
+ }
174
+ }
175
+ }
176
+ }
177
+ replaceReference(fromType, toType) {
178
+ for (const type of this.result.types) {
179
+ if (type.name === fromType) {
180
+ type.name = toType;
181
+ }
182
+ if (type.properties) {
183
+ for (const prop of type.properties) {
184
+ if (prop.type === fromType) {
185
+ prop.type = toType;
186
+ }
187
+ if (prop.name === fromType) {
188
+ prop.name = toType;
189
+ }
190
+ if (prop.name === `${fromType}Id`) {
191
+ prop.name = `${toType}Id`;
192
+ }
193
+ }
194
+ }
195
+ }
196
+ }
197
+ replaceIds() {
198
+ for (const type of this.classes) {
199
+ const idProp = type.properties?.find(x => x.name === `${type.name}Id`);
200
+ if (idProp) {
201
+ type.properties?.forEach(x => delete x.isPrimaryKey);
202
+ idProp.name = 'Id';
203
+ idProp.isPrimaryKey = true;
204
+ }
205
+ // If using a shortened id for the type e.g. (PerformanceReview, ReviewId)
206
+ const firstProp = type.properties?.[0];
207
+ if (firstProp?.name.endsWith('Id') && type.name.includes(firstProp.name.substring(0, firstProp.name.length - 2))) {
208
+ firstProp.name = 'Id';
209
+ firstProp.isPrimaryKey = true;
210
+ }
211
+ }
212
+ const anyIntPks = this.classes.some(x => x.properties?.some(p => p.isPrimaryKey && this.integerTypes.includes(p.type)));
213
+ if (!anyIntPks) {
214
+ for (const type of this.classes) {
215
+ const idProp = type.properties?.find(x => x.isPrimaryKey);
216
+ if (idProp) {
217
+ idProp.type = 'int';
218
+ }
219
+ }
220
+ }
221
+ }
222
+ convertReferenceTypes() {
223
+ for (const type of this.classes) {
224
+ for (let i = 0; i < type.properties.length; i++) {
225
+ const p = type.properties[i];
226
+ const refType = this.result.types.find(x => x.name === p.type && x.namespace === 'MyApp' && !x.isEnum);
227
+ if (refType) {
228
+ const fkId = `${p.name}Id`;
229
+ let idProp = refType.properties?.find(x => x.name === 'Id');
230
+ if (!idProp) {
231
+ idProp = { name: 'Id', type: 'int', isPrimaryKey: true, isValueType: true, namespace: 'System' };
232
+ refType.properties?.unshift(idProp);
233
+ }
234
+ const fkProp = {
235
+ name: fkId,
236
+ type: idProp.type,
237
+ namespace: idProp.namespace,
238
+ attributes: [{
239
+ name: "References",
240
+ constructorArgs: [{
241
+ name: "type",
242
+ type: "Type",
243
+ value: `typeof(${p.type})`
244
+ }],
245
+ args: []
246
+ }]
247
+ };
248
+ type.properties.splice(i, 0, fkProp);
249
+ if (!p.attributes)
250
+ p.attributes = [];
251
+ p.attributes.push({ name: "Reference" });
252
+ i++; // Skip over added fk prop
253
+ }
254
+ }
255
+ }
256
+ }
257
+ convertArrayReferenceTypes() {
258
+ for (const type of this.classes) {
259
+ for (const prop of type.properties) {
260
+ if (prop.type.endsWith('[]')) {
261
+ const elType = prop.type.substring(0, prop.type.length - 2);
262
+ const refType = this.result.types.find(x => x.name === elType && x.namespace === 'MyApp' && !x.isEnum);
263
+ if (refType && refType.properties?.find(x => x.name === 'Id' || x.isPrimaryKey)) {
264
+ prop.namespace = 'System.Collections.Generic';
265
+ prop.genericArgs = [elType];
266
+ prop.type = 'List`1';
267
+ if (!prop.attributes)
268
+ prop.attributes = [];
269
+ prop.attributes.push({ name: "Reference" });
270
+ let fkProp = refType.properties.find(x => x.name === `${type.name}Id`);
271
+ if (!fkProp) {
272
+ fkProp = {
273
+ name: `${type.name}Id`,
274
+ type: 'int',
275
+ isValueType: true,
276
+ namespace: 'System',
277
+ attributes: [{
278
+ name: "References",
279
+ constructorArgs: [{
280
+ name: "type",
281
+ type: "Type",
282
+ value: `typeof(${type.name})`
283
+ }]
284
+ }]
285
+ };
286
+ // Insert fk prop after last `*Id` prop
287
+ const lastIdPropIndex = refType.properties.findLastIndex(x => x.name.endsWith('Id'));
288
+ if (lastIdPropIndex >= 0) {
289
+ refType.properties.splice(lastIdPropIndex + 1, 0, fkProp);
290
+ }
291
+ else {
292
+ refType.properties.push(fkProp);
293
+ }
294
+ }
295
+ }
296
+ }
297
+ }
298
+ }
299
+ }
300
+ convertArraysToLists() {
301
+ for (const type of this.classes) {
302
+ for (const prop of type.properties) {
303
+ const optional = prop.type.endsWith('?');
304
+ let propType = this.unwrap(prop.type);
305
+ if (propType.endsWith('[]')) {
306
+ const elType = propType.substring(0, propType.length - 2);
307
+ prop.namespace = 'System.Collections.Generic';
308
+ prop.genericArgs = [elType];
309
+ prop.type = 'List`1' + (optional ? '?' : '');
310
+ }
311
+ }
312
+ }
313
+ }
314
+ addMissingReferencesToForeignKeyProps() {
315
+ for (const type of this.typesWithPrimaryKeys) {
316
+ for (const prop of type.properties) {
317
+ if (prop.name.endsWith('Id') && !prop.isPrimaryKey && !prop.attributes?.some(x => x.name.startsWith('Reference'))) {
318
+ const refTypeName = prop.name.substring(0, prop.name.length - 2);
319
+ const refType = this.result.types.find(x => x.name === refTypeName && x.namespace === 'MyApp' && !x.isEnum);
320
+ if (refType) {
321
+ if (!prop.attributes)
322
+ prop.attributes = [];
323
+ prop.attributes.push({
324
+ name: "References",
325
+ constructorArgs: [{
326
+ name: "type",
327
+ type: "Type",
328
+ value: `typeof(${refTypeName})`
329
+ }]
330
+ });
331
+ }
332
+ }
333
+ }
334
+ }
335
+ }
336
+ addAutoIncrementAttrs() {
337
+ for (const type of this.classes) {
338
+ for (const prop of type.properties) {
339
+ if (prop.isPrimaryKey) {
340
+ if (prop.type === 'int' || prop.type === 'long' || prop.type === 'Int32' || prop.type === 'Int64') {
341
+ if (!prop.attributes)
342
+ prop.attributes = [];
343
+ const attr = { name: "AutoIncrement" };
344
+ prop.attributes.push(attr);
345
+ }
346
+ }
347
+ }
348
+ }
349
+ }
350
+ createAutoCrudApis() {
351
+ for (const type of this.classes) {
352
+ const hasPk = type.properties?.some(x => x.isPrimaryKey);
353
+ if (!hasPk)
354
+ continue;
355
+ const pluralizedType = plural(type.name);
356
+ const queryName = `Query${pluralizedType}`;
357
+ let queryApi = this.result.operations.find(x => x.request.name === queryName);
358
+ const pk = type.properties?.find(x => x.isPrimaryKey);
359
+ const dataModel = { name: type.name, namespace: type.name };
360
+ const isAuditBase = type.inherits?.name === 'AuditBase';
361
+ const inputTagAttrs = [{
362
+ name: "Input",
363
+ args: [{ name: "Type", type: "string", value: "tag" }]
364
+ },
365
+ {
366
+ name: "FieldCss",
367
+ args: [{ name: "Field", type: "string", value: "col-span-12" }]
368
+ }];
369
+ const ignoreDtoAttrs = ['AutoIncrement'];
370
+ const idsProps = pk
371
+ ? [
372
+ Object.assign({}, pk, {
373
+ type: `${this.nullable(pk.type)}`,
374
+ attributes: pk.attributes?.filter(a => !ignoreDtoAttrs.includes(a.name)),
375
+ }),
376
+ {
377
+ name: `${pk.name}s`,
378
+ type: "List`1?",
379
+ namespace: "System.Collections.Generic",
380
+ genericArgs: [pk.type]
381
+ }
382
+ ]
383
+ : [];
384
+ if (!queryApi) {
385
+ queryApi = {
386
+ method: "GET",
387
+ actions: ["ANY"],
388
+ routes: [],
389
+ request: {
390
+ name: queryName,
391
+ namespace: "MyApp",
392
+ inherits: {
393
+ name: "QueryDb`1",
394
+ namespace: "ServiceStack",
395
+ genericArgs: [type.name]
396
+ },
397
+ properties: idsProps,
398
+ },
399
+ returnType: {
400
+ name: "QueryResponse`1",
401
+ namespace: "ServiceStack",
402
+ genericArgs: [type.name]
403
+ },
404
+ dataModel,
405
+ };
406
+ if (isAuditBase) {
407
+ if (!queryApi.request.attributes)
408
+ queryApi.request.attributes = [];
409
+ // [AutoApply(Behavior.AuditQuery)]
410
+ queryApi.request.attributes.push({
411
+ name: "AutoApply",
412
+ constructorArgs: [{
413
+ name: "name",
414
+ type: "constant",
415
+ value: "Behavior.AuditQuery"
416
+ }]
417
+ });
418
+ }
419
+ this.result.operations.push(queryApi);
420
+ }
421
+ let createName = `Create${type.name}`;
422
+ let createApi = this.result.operations.find(x => x.request.name === createName);
423
+ if (!createApi) {
424
+ const ignorePropsWithAttrs = ['AutoIncrement', 'Reference'];
425
+ const ignoreAttrs = [];
426
+ createApi = {
427
+ method: "POST",
428
+ actions: ["ANY"],
429
+ request: {
430
+ name: createName,
431
+ namespace: "MyApp",
432
+ implements: [{
433
+ name: "ICreateDb`1",
434
+ namespace: "ServiceStack",
435
+ genericArgs: [type.name]
436
+ }],
437
+ properties: type.properties
438
+ .filter(x => !x.attributes?.some(a => ignorePropsWithAttrs.includes(a.name))).map(x => Object.assign({}, x, {
439
+ type: x.isPrimaryKey
440
+ ? x.type
441
+ : `${x.type}`,
442
+ attributes: x.attributes?.filter(a => !ignoreAttrs.includes(a.name)),
443
+ })),
444
+ },
445
+ returnType: {
446
+ name: "IdResponse",
447
+ namespace: "ServiceStack",
448
+ },
449
+ dataModel,
450
+ };
451
+ for (const prop of createApi.request.properties) {
452
+ if (prop.isRequired) {
453
+ if (!prop.attributes)
454
+ prop.attributes = [];
455
+ if (prop.type === 'string') {
456
+ prop.attributes.push({
457
+ name: "ValidateNotEmpty",
458
+ });
459
+ }
460
+ else if (this.integerTypes.includes(prop.type)) {
461
+ prop.attributes.push({
462
+ name: "ValidateGreaterThan",
463
+ constructorArgs: [{ name: "value", type: "int", value: "0" }]
464
+ });
465
+ }
466
+ else if (prop.type === 'List`1' && this.commonValueTypes.includes(prop.genericArgs[0])) {
467
+ prop.attributes.push(...inputTagAttrs);
468
+ }
469
+ }
470
+ }
471
+ if (isAuditBase) {
472
+ createApi.requiresAuth = true;
473
+ if (!createApi.request.attributes)
474
+ createApi.request.attributes = [];
475
+ // [AutoApply(Behavior.AuditCreate)]
476
+ createApi.request.attributes.push({
477
+ name: "AutoApply",
478
+ constructorArgs: [{
479
+ name: "name",
480
+ type: "constant",
481
+ value: "Behavior.AuditCreate"
482
+ }]
483
+ });
484
+ }
485
+ this.result.operations.push(createApi);
486
+ }
487
+ let updateName = `Update${type.name}`;
488
+ let updateApi = this.result.operations.find(x => x.request.name === updateName);
489
+ if (!updateApi) {
490
+ const ignoreAttrs = ['AutoIncrement'];
491
+ updateApi = {
492
+ method: "PATCH",
493
+ actions: ["ANY"],
494
+ request: {
495
+ name: updateName,
496
+ namespace: "MyApp",
497
+ implements: [{
498
+ name: "IPatchDb`1",
499
+ namespace: "ServiceStack",
500
+ genericArgs: [type.name]
501
+ }],
502
+ properties: type.properties?.filter(x => !x.attributes?.some(x => x.name === 'References')).map(x => Object.assign({}, x, {
503
+ type: x.isPrimaryKey
504
+ ? x.type
505
+ : `${this.nullable(x.type)}`,
506
+ attributes: x.attributes?.filter(a => !ignoreAttrs.includes(a.name)),
507
+ })),
508
+ },
509
+ returnType: {
510
+ name: "IdResponse",
511
+ namespace: "ServiceStack",
512
+ },
513
+ dataModel,
514
+ };
515
+ for (const prop of updateApi.request.properties) {
516
+ if (prop.isRequired) {
517
+ if (!prop.attributes)
518
+ prop.attributes = [];
519
+ if (prop.type === 'List`1' && this.commonValueTypes.includes(prop.genericArgs[0])) {
520
+ prop.attributes.push(...inputTagAttrs);
521
+ }
522
+ }
523
+ }
524
+ if (isAuditBase) {
525
+ updateApi.requiresAuth = true;
526
+ if (!updateApi.request.attributes)
527
+ updateApi.request.attributes = [];
528
+ // [AutoApply(Behavior.AuditModify)]
529
+ updateApi.request.attributes.push({
530
+ name: "AutoApply",
531
+ constructorArgs: [{
532
+ name: "name",
533
+ type: "constant",
534
+ value: "Behavior.AuditModify"
535
+ }]
536
+ });
537
+ }
538
+ this.result.operations.push(updateApi);
539
+ }
540
+ let deleteName = `Delete${type.name}`;
541
+ let deleteApi = this.result.operations.find(x => x.request.name === deleteName);
542
+ if (!deleteApi) {
543
+ deleteApi = {
544
+ method: "DELETE",
545
+ actions: ["ANY"],
546
+ request: {
547
+ name: deleteName,
548
+ namespace: "MyApp",
549
+ implements: [{
550
+ name: "IDeleteDb`1",
551
+ namespace: "ServiceStack",
552
+ genericArgs: [type.name]
553
+ }],
554
+ properties: idsProps,
555
+ },
556
+ returnsVoid: true,
557
+ dataModel,
558
+ };
559
+ if (isAuditBase) {
560
+ deleteApi.requiresAuth = true;
561
+ if (!deleteApi.request.attributes)
562
+ deleteApi.request.attributes = [];
563
+ // [AutoApply(Behavior.AuditSoftDelete)]
564
+ deleteApi.request.attributes.push({
565
+ name: "AutoApply",
566
+ constructorArgs: [{
567
+ name: "name",
568
+ type: "constant",
569
+ value: "Behavior.AuditSoftDelete"
570
+ }]
571
+ });
572
+ }
573
+ this.result.operations.push(deleteApi);
574
+ }
575
+ }
576
+ }
577
+ // Add Icon for BuiltIn UIs and AutoQueryGrid to known type names
578
+ addIconsToKnownTypes() {
579
+ for (const type of this.typesWithPrimaryKeys) {
580
+ const icon = Icons[type.name];
581
+ if (icon) {
582
+ if (!type.attributes)
583
+ type.attributes = [];
584
+ type.attributes.push({
585
+ name: "Icon",
586
+ args: [{ name: "Svg", type: "string", value: icon }]
587
+ });
588
+ }
589
+ }
590
+ }
591
+ // Hide Reference Properties from AutoQueryGrid Grid View
592
+ hideReferenceProperties() {
593
+ for (const type of this.typesWithReferences) {
594
+ for (const prop of type.properties.filter(x => x.attributes?.some(x => x.name === 'Reference'))) {
595
+ if (!prop.attributes)
596
+ prop.attributes = [];
597
+ //[Format(FormatMethods.Hidden)]
598
+ prop.attributes.push({
599
+ name: "Format",
600
+ constructorArgs: [{ name: "method", type: "constant", value: "FormatMethods.Hidden" }]
601
+ });
602
+ }
603
+ }
604
+ }
605
+ // Replace User Tables and FKs with AuditBase tables and
606
+ replaceUserReferencesWithAuditTables() {
607
+ for (const type of this.typesWithPrimaryKeys) {
608
+ const removeProps = [];
609
+ for (const prop of type.properties) {
610
+ if (prop.name === 'UserId') {
611
+ removeProps.push(prop.name);
612
+ }
613
+ if (prop.attributes?.some(a => a.name === 'Reference' && a.constructorArgs?.some(x => x.value === 'typeof(User)'))) {
614
+ removeProps.push(prop.name);
615
+ }
616
+ if (prop.type === 'User') {
617
+ removeProps.push(prop.name);
618
+ }
619
+ if (prop.genericArgs && prop.genericArgs.includes('User')) {
620
+ removeProps.push(prop.name);
621
+ }
622
+ }
623
+ if (removeProps.length) {
624
+ type.properties = type.properties.filter(x => !removeProps.includes(x.name));
625
+ type.inherits = { name: "AuditBase", namespace: "ServiceStack" };
626
+ }
627
+ }
628
+ // Remove User Table
629
+ this.result.types = this.result.types.filter(x => x.name !== 'User');
630
+ }
631
+ parseTypes() {
632
+ this.ast.classes.forEach(c => {
633
+ const name = toPascalCase(c.name);
634
+ if (this.result.types.find(x => x.name === name && x.namespace === 'MyApp'))
635
+ return;
636
+ this.addMetadataType(c);
637
+ });
638
+ this.ast.enums.forEach(e => {
639
+ const name = toPascalCase(e.name);
640
+ if (this.result.types.find(x => x.name === name && x.isEnum))
641
+ return;
642
+ this.addMetadataEnum(e);
643
+ });
644
+ this.replaceReferences(['Service']);
645
+ this.replaceIds();
646
+ this.convertReferenceTypes();
647
+ this.convertArrayReferenceTypes();
648
+ this.convertArraysToLists();
649
+ this.addMissingReferencesToForeignKeyProps();
650
+ this.addAutoIncrementAttrs();
651
+ this.addIconsToKnownTypes();
652
+ this.hideReferenceProperties();
653
+ this.replaceUserReferencesWithAuditTables();
654
+ this.createAutoCrudApis();
655
+ }
656
+ parse(ast) {
657
+ const classes = ast.classes.concat(ast.interfaces);
658
+ const enums = ast.enums;
659
+ this.ast = {
660
+ classes: classes,
661
+ interfaces: [],
662
+ enums: enums ?? [],
663
+ };
664
+ this.result = {
665
+ config: {},
666
+ namespaces: [
667
+ "System",
668
+ "System.IO",
669
+ "System.Collections",
670
+ "System.Collections.Generic",
671
+ "System.Runtime.Serialization",
672
+ "ServiceStack",
673
+ "ServiceStack.DataAnnotations",
674
+ ],
675
+ operations: [],
676
+ types: [],
677
+ };
678
+ this.parseTypes();
679
+ return this.result;
680
+ }
681
+ }
682
+ export function toMetadataTypes(ast) {
683
+ const generator = new CSharpAst();
684
+ return generator.parse(ast);
685
+ }