okai 0.0.22 → 0.0.24

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 ADDED
@@ -0,0 +1,352 @@
1
+ declare global {
2
+ export class AuditBase {
3
+ createdDate: Date
4
+ createdBy: string
5
+ modifiedDate: Date
6
+ modifiedBy: string
7
+ deletedDate?: Date
8
+ deletedBy?: string
9
+ }
10
+
11
+ export type TypeOf = `typeof(${string})`
12
+ export type InputAttrOptions = { type?:string, value?:string, placeholder?:string, help?:string, label?:string, title?:string, size?:string,
13
+ pattern?:string, readOnly?:boolean, required?:boolean, disabled?:boolean, autocomplete?:string, autofocus?:string,
14
+ min?:string, max?:string, step?:string, minLength?:number, maxLength?:number, accept?:string, capture?:string, multiple?:boolean,
15
+ allowableValues?:string[], allowableValuesEnum?:TypeOf, options?:string, ignore?:boolean, evalAllowableValues?:string, evalAllowableEntries?:string, }
16
+ export type ScriptValueOptions = { value?: any, expression?: string, eval?: string, noCache?: boolean }
17
+
18
+ type ClassDecoratorDef = (
19
+ target: any,
20
+ context: ClassDecoratorContext
21
+ ) => void
22
+
23
+ type ClassFieldDecoratorDef = (
24
+ target: any,
25
+ context: ClassFieldDecoratorContext
26
+ ) => void
27
+
28
+ type ClassOrFieldDecoratorDef = (
29
+ target: any,
30
+ context: ClassDecoratorContext | ClassFieldDecoratorContext
31
+ ) => void
32
+
33
+ export function validateRequest(validator:string) : ClassDecoratorDef
34
+ export function validateIsAuthenticated() : ClassDecoratorDef
35
+ export function validateIsAdmin() : ClassDecoratorDef
36
+ export function validateAuthSecret() : ClassDecoratorDef
37
+ export function validateHasRole(role:string) : ClassDecoratorDef
38
+ export function validateHasRoles(roles:string[]) : ClassDecoratorDef
39
+ export function validateHasPermission(permission:string) : ClassDecoratorDef
40
+ export function validateHasPermissions(permissions:string[]) : ClassDecoratorDef
41
+ export function validateHasClaim(claim:string) : ClassDecoratorDef
42
+ export function validateHasScope(scope:string) : ClassDecoratorDef
43
+ export function validateApiKey(scope?:string) : ClassDecoratorDef
44
+
45
+ export function schema(schema:string) : ClassDecoratorDef
46
+ export function compositeKey(columns:string[]) : ClassDecoratorDef
47
+ export function compositeIndex(unique:boolean, columns:string[]) : ClassDecoratorDef
48
+ export function preCreateTable(sql:string) : ClassDecoratorDef
49
+ export function preDropTable(sql:string) : ClassDecoratorDef
50
+ export function postCreateTable(sql:string) : ClassDecoratorDef
51
+ export function postDropTable(sql:string) : ClassDecoratorDef
52
+
53
+ export function api(description:string, opt?:{ bodyParameter?:number, requestContentType?:string, isRequired?:boolean }) : ClassDecoratorDef
54
+ export function apiResponse(statusCode:number, description:string, opt?:{ isDefaultResponse?:boolean, responseType?:TypeOf }) : ClassDecoratorDef
55
+
56
+ export function dataContract() : ClassDecoratorDef
57
+ export function route(path:string, opt?:{ summary?:string, notes?:string, verbs?:string, priority?:number, matches?:string, }) : ClassDecoratorDef
58
+ export function icon(opt?:{ svg?:string, uri?:string, alt?:string, cls?:string, }) : ClassDecoratorDef
59
+ export function field(opt?:InputAttrOptions & { name?:string, fieldCss?:string, inputCss?:string, labelCss?:string, }) : ClassDecoratorDef
60
+ export function tag(name:string) : ClassDecoratorDef
61
+ export function worker(name:string) : ClassDecoratorDef
62
+ export function notes(notes:string) : ClassDecoratorDef
63
+ export function namedConnection(name:string) : ClassDecoratorDef
64
+
65
+ export enum QueryTerm { Default = 0, And = 1, Or = 2, Ensure = 3, }
66
+ export function queryDb(defaultTerm:QueryTerm) : ClassDecoratorDef
67
+ export function queryData(defaultTerm:QueryTerm) : ClassDecoratorDef
68
+ export function autoFilter(term:QueryTerm, field?:string, opt?:{ operand?:string, template?:string, valueFormat:string }) : ClassDecoratorDef
69
+ export function autoPopulate(field?:string, opt?:ScriptValueOptions) : ClassDecoratorDef
70
+ export class SqlTemplate {
71
+ static IsNull: string
72
+ static IsNotNull: string
73
+ static GreaterThanOrEqual: string
74
+ static GreaterThan: string
75
+ static LessThan: string
76
+ static LessThanOrEqual: string
77
+ static NotEqual: string
78
+ static CaseSensitiveLike: string
79
+ static CaseInsensitiveLike: string
80
+ }
81
+
82
+ export function alias(table:string) : ClassOrFieldDecoratorDef
83
+ export function meta(name:string,value:string) : ClassOrFieldDecoratorDef
84
+ export function priority(value:string) : ClassOrFieldDecoratorDef
85
+
86
+ // Enum decorators
87
+ export function flags() : ClassDecoratorDef
88
+ export function enumMember(opt:{ value:string }) : ClassFieldDecoratorDef
89
+
90
+ export function validate(validator:string) : ClassFieldDecoratorDef
91
+ export function validateNull() : ClassFieldDecoratorDef
92
+ export function validateEmpty() : ClassFieldDecoratorDef
93
+ export function validateEmail() : ClassFieldDecoratorDef
94
+ export function validateNotNull() : ClassFieldDecoratorDef
95
+ export function validateNotEmpty() : ClassFieldDecoratorDef
96
+ export function validateCreditCard() : ClassFieldDecoratorDef
97
+ export function validateLength(min:number, max:number) : ClassFieldDecoratorDef
98
+ export function validateExactLength(length:number) : ClassFieldDecoratorDef
99
+ export function validateMinimumLength(min:number) : ClassFieldDecoratorDef
100
+ export function validateMaximumLength(max:number) : ClassFieldDecoratorDef
101
+ export function validateLessThan(value:number) : ClassFieldDecoratorDef
102
+ export function validateLessThanOrEqual(value:number) : ClassFieldDecoratorDef
103
+ export function validateGreaterThan(value:number) : ClassFieldDecoratorDef
104
+ export function validateGreaterThanOrEqual(value:number) : ClassFieldDecoratorDef
105
+ export function validateScalePrecision(scale:number, precision:number) : ClassFieldDecoratorDef
106
+ export function validateRegularExpression(pattern:string) : ClassFieldDecoratorDef
107
+ export function validateEqualExpression(value:string|number|boolean) : ClassFieldDecoratorDef
108
+ export function validateNotEqualExpression(value:string|number|boolean) : ClassFieldDecoratorDef
109
+ export function validateInclusiveBetween(from:string|number,to:string|number) : ClassFieldDecoratorDef
110
+ export function validateExclusiveBetween(from:string|number,to:string|number) : ClassFieldDecoratorDef
111
+
112
+ export function allowReset() : ClassFieldDecoratorDef
113
+ export function denyReset() : ClassFieldDecoratorDef
114
+
115
+ export function primaryKey() : ClassFieldDecoratorDef
116
+ export function autoId() : ClassFieldDecoratorDef
117
+ export function autoIncrement() : ClassFieldDecoratorDef
118
+ export function belongTo(BelongToTableType:TypeOf) : ClassFieldDecoratorDef
119
+ export function index(opt?:{ name?:string, unique?:boolean, clustered?:boolean, nonClustered?:boolean, }) : ClassFieldDecoratorDef
120
+ export function compute() : ClassFieldDecoratorDef
121
+ export function computed() : ClassFieldDecoratorDef
122
+ export function persisted() : ClassFieldDecoratorDef
123
+ export function uniqueConstraint(columns:string[]) : ClassFieldDecoratorDef
124
+ export function addColumn() : ClassFieldDecoratorDef
125
+ export function removeColumn() : ClassFieldDecoratorDef
126
+ export function checkConstraint(constraint:string) : ClassFieldDecoratorDef
127
+ export function customField(sql:string, order?:number) : ClassFieldDecoratorDef
128
+ export function customSelect(sql:string) : ClassFieldDecoratorDef
129
+ export function customInsert(sql:string) : ClassFieldDecoratorDef
130
+ export function customUpdate(sql:string) : ClassFieldDecoratorDef
131
+ export function decimalLength(precision:number, scale?:number) : ClassFieldDecoratorDef
132
+ export function Default(value:string|number|boolean) : ClassFieldDecoratorDef
133
+ export function description(description:string) : ClassFieldDecoratorDef
134
+ export function enumAsInt() : ClassFieldDecoratorDef
135
+ export function excludeMetadata() : ClassFieldDecoratorDef
136
+ export function excludeFromDescription() : ClassFieldDecoratorDef
137
+ export function explicitAutoQuery() : ClassFieldDecoratorDef
138
+ export type foreignKeyBehavior = "NO ACTION"|"RESTRICT"|"SET NULL"|"SET DEFAULT"|"CASCADE"
139
+ export function foreignKey(type:string, opt?:{ onDelete?:foreignKeyBehavior, onUpdate?:foreignKeyBehavior, foreignKeyName?:string }) : ClassFieldDecoratorDef
140
+ export function ignore() : ClassFieldDecoratorDef
141
+ export function ignoreOnUpdate() : ClassFieldDecoratorDef
142
+ export function ignoreOnInsert() : ClassFieldDecoratorDef
143
+ export function ignoreDataMember() : ClassFieldDecoratorDef
144
+ export function reference() : ClassFieldDecoratorDef
145
+ export function referenceField(model:TypeOf, id?:string, field?:string) : ClassFieldDecoratorDef
146
+ export function references(type:TypeOf) : ClassFieldDecoratorDef
147
+ export function required() : ClassFieldDecoratorDef
148
+ export function returnOnInsert() : ClassFieldDecoratorDef
149
+ export function rowVersion() : ClassFieldDecoratorDef
150
+ export function unique() : ClassFieldDecoratorDef
151
+
152
+ export enum ValueStyle { Single, Multiple, List, }
153
+ export function queryDbField(opt?:{ term:QueryTerm, operand?:string, template?:string, field?:string, valueFormat?:string, valueStyle?:ValueStyle, valueArity?:number }) : ClassFieldDecoratorDef
154
+ export function queryDataField(opt?:{ term:QueryTerm, condition?:string, field?:string }) : ClassFieldDecoratorDef
155
+
156
+ export enum AutoUpdateStyle { Always, NonDefaults }
157
+ export function autoUpdate(style:AutoUpdateStyle) : ClassFieldDecoratorDef
158
+ export function autoDefault(opt:ScriptValueOptions) : ClassFieldDecoratorDef
159
+ export function autoMap(to:string) : ClassFieldDecoratorDef
160
+ export function autoIgnore() : ClassFieldDecoratorDef
161
+ export function autoApply(name:string, args?:string[]) : ClassFieldDecoratorDef
162
+
163
+ export function apiMember(opt?:{ name?:string, verb?:string, parameterType?:string, description?:string, dataType?:string,
164
+ format?:string, isRequired?:boolean, isOptional?:boolean, allowMultiple?:boolean, route?:string, excludeInSchema?:boolean
165
+ }) : ClassFieldDecoratorDef
166
+ export function apiAllowableValues(name:string, opt?:{ type?:"RANGE"|"LIST", min?:number, max?:number, values?:string[] }) : ClassFieldDecoratorDef
167
+
168
+ export function dataMember(opt?:{ name?:string, order?:number, isRequired?:boolean }) : ClassFieldDecoratorDef
169
+ export function input(opt?:InputAttrOptions) : ClassFieldDecoratorDef
170
+ export function fieldCss(opt?:{ field?:string, input?:string, label?:string, }) : ClassFieldDecoratorDef
171
+ export function explorerCss(opt?:{ form?:string, fieldset?:string, field?:string, }) : ClassFieldDecoratorDef
172
+ export function locodeCss(opt?:{ form?:string, fieldset?:string, field?:string, }) : ClassFieldDecoratorDef
173
+ export function uploadTo(location:string) : ClassFieldDecoratorDef
174
+ export function ref(opt?:{ modelType?:TypeOf, model?:string, refId?:string, refLabel?:string, selfId?:string, queryType?:TypeOf, none?:boolean, }) : ClassFieldDecoratorDef
175
+ export type FormatMethods = "currency"|"bytes"|"icon"|"iconRounded"|"attachment"|"link"|"linkMailTo"|"linkTel"|"enumFlags"|"hidden"
176
+ export function format(method:FormatMethods, opt?:{ options?:string, locale?:string }) : ClassFieldDecoratorDef
177
+
178
+ export enum IntlFormat { Number, DateTime, RelativeTime }
179
+ export enum NumberStyle { Undefined=0, Decimal, Currency, Percent, Unit, }
180
+ export enum DateStyle { Undefined=0, Full, Long, Medium, Short, }
181
+ export enum TimeStyle { Undefined=0, Full, Long, Medium, Short, }
182
+ export enum Numeric { Undefined=0, Always, Auto, }
183
+
184
+ export enum DatePart { Undefined=0, Numeric, Digits2, }
185
+ export enum DateMonth { Undefined=0, Numeric, Digits2, Narrow, Short, Long, }
186
+ export enum DateText { Undefined=0, Narrow, Short, Long }
187
+
188
+ export enum CurrencyDisplay { Undefined=0, Symbol, NarrowSymbol, Code, Name, }
189
+ export enum CurrencySign { Undefined=0, Accounting, Standard, }
190
+ export enum SignDisplay { Undefined=0, Always, Auto, ExceptZero, Negative, Never, }
191
+ export enum RoundingMode { Undefined=0, Ceil, Floor, Expand, Trunc, HalfCeil, HalfFloor, HalfExpand, HalfTrunc, HalfEven, }
192
+
193
+ export enum UnitDisplay { Undefined=0, Long, Short, Narrow }
194
+ export enum Notation { Undefined=0, Standard, Scientific, Engineering, Compact, }
195
+
196
+ export type IntlOptions = { locale?:string, options?:string, number?:NumberStyle, date?:DateStyle, time?:TimeStyle, numeric?:Numeric,
197
+ currency?:string, currencyDisplay?:CurrencyDisplay, currencySign?:CurrencySign, signDisplay?:SignDisplay, roundingMode?:RoundingMode,
198
+ unit?:string, unitDisplay?:UnitDisplay, notation?:Notation,
199
+ minimumIntegerDigits?:number, minimumFractionDigits?:number, maximumFractionDigits?:number, minimumSignificantDigits?:number, maximumSignificantDigits?:number, fractionalSecondDigits?:number,
200
+ weekday?:DateText, era?:DateText, year?:DatePart, month?:DateMonth, day?:DatePart, hour?:DatePart, minute?:DatePart, second?:DatePart,
201
+ timeZoneName?:DateText, timeZone?:string, hour12?:boolean
202
+ }
203
+
204
+ export function intl(type:IntlFormat, opt?:IntlOptions) : ClassFieldDecoratorDef
205
+ export function intlNumber(number?:NumberStyle) : ClassFieldDecoratorDef
206
+ export function intlDateTime(date?:DateStyle, time?:TimeStyle) : ClassFieldDecoratorDef
207
+ export function intlRelativeTime(numeric?:Numeric) : ClassFieldDecoratorDef
208
+
209
+ //RequestAttrs & TypeAttrs & DataModelAttrs & RequestPropAttrs & DataModelPropAttrs
210
+ export class All {
211
+ // RequestAttrs
212
+ api:typeof api
213
+ apiResponse:typeof apiResponse
214
+ validateRequest:typeof validateRequest
215
+ validateIsAuthenticated:typeof validateIsAuthenticated
216
+ validateIsAdmin:typeof validateIsAdmin
217
+ validateAuthSecret:typeof validateAuthSecret
218
+ validateHasRole:typeof validateHasRole
219
+ validateHasRoles:typeof validateHasRoles
220
+ validateHasPermission:typeof validateHasPermission
221
+ validateHasPermissions:typeof validateHasPermissions
222
+ validateHasClaim:typeof validateHasClaim
223
+ validateHasScope:typeof validateHasScope
224
+ validateApiKey:typeof validateApiKey
225
+
226
+ queryDb:typeof queryDb
227
+ queryData:typeof queryData
228
+ autoFilter:typeof autoFilter
229
+ autoPopulate:typeof autoPopulate
230
+
231
+ route:typeof route
232
+ field:typeof field
233
+ tag:typeof tag
234
+ notes:typeof notes
235
+
236
+ // Type and Attrs
237
+ dataContract:typeof dataContract
238
+
239
+ // DataModelAttrs
240
+ schema:typeof schema
241
+ compositeKey:typeof compositeKey
242
+ preCreateTable:typeof preCreateTable
243
+ preDropTable:typeof preDropTable
244
+ postCreateTable:typeof postCreateTable
245
+ postDropTable:typeof postDropTable
246
+
247
+ namedConnection:typeof namedConnection
248
+ icon:typeof icon
249
+
250
+ // Class or Field Attrs
251
+ alias:typeof alias
252
+ meta:typeof meta // Type
253
+ flags:typeof flags // Enum
254
+
255
+ // RequestPropAttrs
256
+ validate:typeof validate
257
+ validateNull:typeof validateNull
258
+ validateEmpty:typeof validateEmpty
259
+ validateEmail:typeof validateEmail
260
+ validateNotNull:typeof validateNotNull
261
+ validateNotEmpty:typeof validateNotEmpty
262
+ validateCreditCard:typeof validateCreditCard
263
+ validateLength:typeof validateLength
264
+ validateExactLength:typeof validateExactLength
265
+ validateMinimumLength:typeof validateMinimumLength
266
+ validateMaximumLength:typeof validateMaximumLength
267
+ validateLessThan:typeof validateLessThan
268
+ validateLessThanOrEqual:typeof validateLessThanOrEqual
269
+ validateGreaterThan:typeof validateGreaterThan
270
+ validateGreaterThanOrEqual:typeof validateGreaterThanOrEqual
271
+ validateScalePrecision:typeof validateScalePrecision
272
+ validateRegularExpression:typeof validateRegularExpression
273
+ validateEqualExpression:typeof validateEqualExpression
274
+ validateNotEqualExpression:typeof validateNotEqualExpression
275
+ validateInclusiveBetween:typeof validateInclusiveBetween
276
+ validateExclusiveBetween:typeof validateExclusiveBetween
277
+ allowReset:typeof allowReset
278
+ denyReset:typeof denyReset
279
+
280
+ queryDbField:typeof queryDbField
281
+ queryDataField:typeof queryDataField
282
+ autoUpdate:typeof autoUpdate
283
+ autoDefault:typeof autoDefault
284
+ autoMap:typeof autoMap
285
+ autoIgnore:typeof autoIgnore
286
+ autoApply:typeof autoApply
287
+
288
+ apiMember:typeof apiMember
289
+ apiAllowableValues:typeof apiAllowableValues
290
+ dataMember:typeof dataMember
291
+ input:typeof input
292
+ fieldCss:typeof fieldCss
293
+ uploadTo:typeof uploadTo
294
+
295
+ enumMember:typeof enumMember
296
+
297
+ // DataModelPropAttrs
298
+ // alias:typeof alias
299
+ // meta:typeof meta
300
+ priority:typeof priority
301
+
302
+ primaryKey:typeof primaryKey
303
+ autoId:typeof autoId
304
+ autoIncrement:typeof autoIncrement
305
+ index:typeof index
306
+ compute:typeof compute
307
+ computed:typeof computed
308
+ persisted:typeof persisted
309
+ uniqueConstraint:typeof uniqueConstraint
310
+ addColumn:typeof addColumn
311
+ removeColumn:typeof removeColumn
312
+ belongTo:typeof belongTo
313
+ checkConstraint:typeof checkConstraint
314
+ customField:typeof customField
315
+ customSelect:typeof customSelect
316
+ customInsert:typeof customInsert
317
+ customUpdate:typeof customUpdate
318
+ decimalLength:typeof decimalLength
319
+ Default:typeof Default
320
+ description:typeof description
321
+ enumAsInt:typeof enumAsInt
322
+ excludeMetadata:typeof excludeMetadata
323
+ excludeFromDescription:typeof excludeFromDescription
324
+ explicitAutoQuery:typeof explicitAutoQuery
325
+ foreignKey:typeof foreignKey
326
+ ignore:typeof ignore
327
+ ignoreOnUpdate:typeof ignoreOnUpdate
328
+ ignoreOnInsert:typeof ignoreOnInsert
329
+ ignoreDataMember:typeof ignoreDataMember
330
+ reference:typeof reference
331
+ referenceField:typeof referenceField
332
+ references:typeof references
333
+ required:typeof required
334
+ returnOnInsert:typeof returnOnInsert
335
+ rowVersion:typeof rowVersion
336
+ unique:typeof unique
337
+
338
+ ref:typeof ref
339
+ format:typeof format
340
+ intl:typeof intl
341
+ intlNumber:typeof intlNumber
342
+ intlDateTime:typeof intlDateTime
343
+ intlRelativeTime:typeof intlRelativeTime
344
+ }
345
+ export const Read : All
346
+ export const Write : All
347
+ export const Create : All
348
+ export const Update : All
349
+ export const Delete : All
350
+ }
351
+
352
+ export {}
package/dist/cs-apis.js CHANGED
@@ -1,4 +1,4 @@
1
- import { getGroupName, splitCase } from "./utils.js";
1
+ import { getGroupName } from "./utils.js";
2
2
  import { CSharpGenerator } from "./cs-gen.js";
3
3
  export class CSharpApiGenerator extends CSharpGenerator {
4
4
  toApiClass(op) {
@@ -78,12 +78,6 @@ export class CSharpApiGenerator extends CSharpGenerator {
78
78
  }
79
79
  generate(ast) {
80
80
  const groupName = getGroupName(ast);
81
- const friendlyGroupName = splitCase(groupName);
82
- ast.operations.forEach(op => {
83
- if (!op.tags)
84
- op.tags = [];
85
- op.tags.push(friendlyGroupName);
86
- });
87
81
  this.namespaces = Array.from(ast.namespaces);
88
82
  this.apis = ast.operations.map(x => this.toApiClass(x));
89
83
  this.classes = ast.types.filter(t => !t.isEnum).map(x => this.toClass(x));
package/dist/cs-ast.js CHANGED
@@ -1,4 +1,4 @@
1
- import { plural, toPascalCase } from "./utils.js";
1
+ import { getGroupName, leftPart, plural, rightPart, splitCase, toPascalCase } from "./utils.js";
2
2
  import { Icons } from "./icons.js";
3
3
  const sys = (name, genericArgs) => ({ name, namespace: "System", genericArgs });
4
4
  const sysObj = sys("object");
@@ -34,6 +34,153 @@ export class CSharpAst {
34
34
  commonValueTypes = [
35
35
  "int", "Int32", "long", "Int64", "string",
36
36
  ];
37
+ requestAttrs = [
38
+ 'api',
39
+ 'apiResponse',
40
+ 'validateRequest',
41
+ 'validateIsAuthenticated',
42
+ 'validateIsAdmin',
43
+ 'validateAuthSecret',
44
+ 'validateHasRole',
45
+ 'validateHasRoles',
46
+ 'validateHasPermission',
47
+ 'validateHasPermissions',
48
+ 'validateHasClaim',
49
+ 'validateHasScope',
50
+ 'validateApiKey',
51
+ 'queryDb',
52
+ 'queryData',
53
+ 'autoFilter',
54
+ 'autoPopulate',
55
+ 'route',
56
+ 'field',
57
+ 'tag',
58
+ 'notes',
59
+ 'meta',
60
+ 'dataContract',
61
+ ].map(x => x.toLowerCase());
62
+ modelAttrs = [
63
+ 'schema',
64
+ 'compositeKey',
65
+ 'preCreateTable',
66
+ 'preDropTable',
67
+ 'postCreateTable',
68
+ 'postDropTable',
69
+ 'namedConnection',
70
+ 'alias',
71
+ 'icon',
72
+ 'meta',
73
+ 'dataContract',
74
+ ].map(x => x.toLowerCase());
75
+ requestPropAttrs = [
76
+ 'validate',
77
+ 'validateNull',
78
+ 'validateEmpty',
79
+ 'validateEmail',
80
+ 'validateNotNull',
81
+ 'validateNotEmpty',
82
+ 'validateCreditCard',
83
+ 'validateLength',
84
+ 'validateExactLength',
85
+ 'validateMinimumLength',
86
+ 'validateMaximumLength',
87
+ 'validateLessThan',
88
+ 'validateLessThanOrEqual',
89
+ 'validateGreaterThan',
90
+ 'validateGreaterThanOrEqual',
91
+ 'validateScalePrecision',
92
+ 'validateRegularExpression',
93
+ 'validateEqualExpression',
94
+ 'validateNotEqualExpression',
95
+ 'validateInclusiveBetween',
96
+ 'validateExclusiveBetween',
97
+ 'allowReset',
98
+ 'denyReset',
99
+ 'queryDbField',
100
+ 'queryDataField',
101
+ 'autoUpdate',
102
+ 'autoDefault',
103
+ 'autoMap',
104
+ 'autoIgnore',
105
+ 'autoApply',
106
+ 'apiMember',
107
+ 'apiAllowableValues',
108
+ 'dataMember',
109
+ 'input',
110
+ 'fieldCss',
111
+ 'uploadTo',
112
+ ].map(x => x.toLowerCase());
113
+ get requestPropAttrsWithoutValidators() {
114
+ return this.requestPropAttrs.filter(x => !x.startsWith('validate'));
115
+ }
116
+ modelPropAttrs = [
117
+ 'alias',
118
+ 'meta',
119
+ 'priority',
120
+ 'primaryKey',
121
+ 'autoId',
122
+ 'autoIncrement',
123
+ 'index',
124
+ 'compute',
125
+ 'computed',
126
+ 'persisted',
127
+ 'uniqueConstraint',
128
+ 'addColumn',
129
+ 'removeColumn',
130
+ 'belongTo',
131
+ 'checkConstraint',
132
+ 'customField',
133
+ 'customSelect',
134
+ 'customInsert',
135
+ 'customUpdate',
136
+ 'decimalLength',
137
+ 'Default',
138
+ 'description',
139
+ 'enumAsInt',
140
+ 'excludeMetadata',
141
+ 'excludeFromDescription',
142
+ 'explicitAutoQuery',
143
+ 'foreignKey',
144
+ 'ignore',
145
+ 'ignoreOnUpdate',
146
+ 'ignoreOnInsert',
147
+ 'ignoreDataMember',
148
+ 'reference',
149
+ 'referenceField',
150
+ 'references',
151
+ 'required',
152
+ 'returnOnInsert',
153
+ 'rowVersion',
154
+ 'unique',
155
+ 'dataMember',
156
+ 'ref',
157
+ 'format',
158
+ 'intl',
159
+ 'intlNumber',
160
+ 'intlDateTime',
161
+ 'intlRelativeTime',
162
+ ].map(x => x.toLowerCase());
163
+ // Ignore properties with these attributes on APIs
164
+ ignoreCreateProps = [
165
+ 'autoIncrement',
166
+ 'references',
167
+ 'compute',
168
+ 'computed',
169
+ ].map(x => x.toLowerCase());
170
+ ignoreUpdateProps = [
171
+ 'references',
172
+ 'compute',
173
+ 'computed',
174
+ ].map(x => x.toLowerCase());
175
+ // Validators that should be on Create but not optional Update APIs
176
+ ignoreUpdateValidators = [
177
+ 'validateNull',
178
+ 'validateNotNull',
179
+ 'validateEmpty',
180
+ 'validateNotEmpty',
181
+ ].map(x => x.toLowerCase());
182
+ ignoreReadValidators = this.requestPropAttrs.filter(x => x.startsWith('validate')).map(x => x.toLowerCase());
183
+ ignoreDeleteValidators = this.requestPropAttrs.filter(x => x.startsWith('validate')).map(x => x.toLowerCase());
37
184
  unwrap(type) {
38
185
  if (type.endsWith("?")) {
39
186
  return type.substring(0, type.length - 1);
@@ -76,19 +223,26 @@ export class CSharpAst {
76
223
  return this.typeMap[type] ?? { name: type, namespace: "MyApp" };
77
224
  }
78
225
  csharpAttribute(attr) {
79
- const to = { name: toPascalCase(attr.name) };
226
+ const attrName = attr.name.includes('.')
227
+ ? rightPart(attr.name, '.')
228
+ : attr.name;
229
+ const to = { name: toPascalCase(attrName) };
80
230
  const attrType = (value) => typeof value == 'string'
81
- ? (`${value}`.startsWith('typeof') ? "Type" : "string")
231
+ ? ((value.startsWith('typeof(') || value.startsWith('nameof(')) && value.endsWith(')')
232
+ ? "constant" :
233
+ value.match(/^[A-Z][A-Za-z0-9_]+\.[A-Z][A-Za-z0-9_]+$/) //Axx.Bxx
234
+ ? "constant"
235
+ : "string")
82
236
  : typeof value == "object"
83
237
  ? (value instanceof Date ? "string" : Array.isArray(value) ? "array" : "object")
84
238
  : typeof value;
85
239
  if (attr.constructorArgs?.length) {
86
240
  to.constructorArgs = attr.constructorArgs.map(x => {
87
- const type = attrType(x.value);
241
+ const type = attrType(x);
88
242
  return {
89
243
  name: 'String',
90
244
  type,
91
- value: `${x.value}`
245
+ value: `${x}`
92
246
  };
93
247
  });
94
248
  }
@@ -102,21 +256,27 @@ export class CSharpAst {
102
256
  };
103
257
  });
104
258
  }
259
+ if (attr.name.includes('.')) {
260
+ to.namespace = leftPart(attr.name, '.');
261
+ }
105
262
  return to;
106
263
  }
107
264
  addMetadataType(cls) {
108
265
  const type = {
109
266
  name: this.toCsName(cls.name),
110
267
  namespace: "MyApp",
111
- description: cls.comment,
112
268
  properties: cls.properties.map(p => {
113
269
  const type = this.csharpType(p.type, p.name);
114
270
  const prop = {
115
271
  name: this.toCsName(p.name),
116
272
  type: p.optional ? this.nullable(type.name) : type.name,
117
- namespace: type.namespace,
118
- description: p.comment,
119
273
  };
274
+ if (type.namespace) {
275
+ prop.namespace = type.namespace;
276
+ }
277
+ if (p.comment) {
278
+ prop.description = p.comment;
279
+ }
120
280
  if (prop.name === 'Id') {
121
281
  prop.isPrimaryKey = true;
122
282
  }
@@ -145,6 +305,12 @@ export class CSharpAst {
145
305
  return prop;
146
306
  }),
147
307
  };
308
+ if (cls.comment) {
309
+ type.description = cls.comment;
310
+ }
311
+ if (cls.extends) {
312
+ type.inherits = { name: this.toCsName(cls.extends) };
313
+ }
148
314
  if (cls.annotations?.length) {
149
315
  type.attributes = cls.annotations.map(x => this.csharpAttribute(x));
150
316
  }
@@ -389,7 +555,86 @@ export class CSharpAst {
389
555
  }
390
556
  }
391
557
  }
558
+ onlyAttrs(attrs, only) {
559
+ if (!attrs)
560
+ return;
561
+ return attrs.filter(x => only.includes(x.name.toLowerCase()));
562
+ }
563
+ attrsFor(dtoType, attrType, attrs) {
564
+ const requestAttrs = this.requestAttrs;
565
+ const requestPropAttrs = this.requestPropAttrs;
566
+ const modelAttrs = this.modelAttrs;
567
+ const modelPropAttrs = this.modelPropAttrs;
568
+ const isRequest = ["Read", "Create", "Update", "Delete"].includes(dtoType);
569
+ const validAttrs = attrType === "Type"
570
+ ? isRequest
571
+ ? requestAttrs
572
+ : modelAttrs
573
+ : isRequest
574
+ ? requestPropAttrs
575
+ : modelPropAttrs;
576
+ const ignoreValidators = attrType === "Prop"
577
+ ? dtoType === "Update"
578
+ ? this.ignoreUpdateValidators
579
+ : dtoType === "Read"
580
+ ? this.ignoreReadValidators
581
+ : dtoType === "Delete"
582
+ ? this.ignoreDeleteValidators
583
+ : []
584
+ : [];
585
+ function shouldInclude(attr, dtoType) {
586
+ if (!validAttrs.includes(attr.name.toLowerCase()))
587
+ return false;
588
+ const ns = attr.namespace;
589
+ if (ns) {
590
+ if (ns == "All")
591
+ return true;
592
+ if (ns == "Read")
593
+ return dtoType == "Read";
594
+ if (ns == "Create")
595
+ return dtoType == "Create";
596
+ if (ns == "Update")
597
+ return dtoType == "Update";
598
+ if (ns == "Delete")
599
+ return dtoType == "Delete";
600
+ if (ns == "Write")
601
+ return ["Create", "Update", "Delete"].includes(dtoType);
602
+ return false;
603
+ }
604
+ else {
605
+ const nameLower = attr.name.toLowerCase();
606
+ if (isRequest) {
607
+ if (attrType === "Type") {
608
+ return requestAttrs.includes(nameLower);
609
+ }
610
+ else if (attrType === "Prop") {
611
+ if (ignoreValidators.length && ignoreValidators.includes(nameLower))
612
+ return false;
613
+ return requestPropAttrs.includes(nameLower);
614
+ }
615
+ }
616
+ else {
617
+ if (attrType === "Type") {
618
+ return modelAttrs.includes(nameLower);
619
+ }
620
+ else if (attrType === "Prop") {
621
+ return modelPropAttrs.includes(nameLower);
622
+ }
623
+ }
624
+ }
625
+ return true;
626
+ }
627
+ const to = [];
628
+ for (const attr of attrs || []) {
629
+ if (shouldInclude(attr, dtoType)) {
630
+ to.push(attr);
631
+ }
632
+ }
633
+ return to;
634
+ }
392
635
  createAutoCrudApis() {
636
+ const groupName = getGroupName(this.result);
637
+ const friendlyGroupName = splitCase(groupName);
393
638
  for (const type of this.classes) {
394
639
  const hasPk = type.properties?.some(x => x.isPrimaryKey);
395
640
  if (!hasPk)
@@ -400,6 +645,12 @@ export class CSharpAst {
400
645
  const pk = type.properties?.find(x => x.isPrimaryKey);
401
646
  const dataModel = { name: type.name, namespace: type.name };
402
647
  const isAuditBase = type.inherits?.name === 'AuditBase';
648
+ const existingTag = type.attributes?.find(x => x.name.toLowerCase() === 'tag');
649
+ const tags = !existingTag ? [friendlyGroupName] : undefined;
650
+ const emptyTag = existingTag?.constructorArgs?.[0]?.value === '';
651
+ if (emptyTag) {
652
+ type.attributes = type.attributes.filter(x => x !== existingTag);
653
+ }
403
654
  const inputTagAttrs = [{
404
655
  name: "Input",
405
656
  args: [{ name: "Type", type: "string", value: "tag" }]
@@ -408,21 +659,14 @@ export class CSharpAst {
408
659
  name: "FieldCss",
409
660
  args: [{ name: "Field", type: "string", value: "col-span-12" }]
410
661
  }];
411
- const ignoreDtoAttrs = ['AutoIncrement'];
412
- const idsProps = pk
413
- ? [
414
- Object.assign({}, pk, {
415
- type: `${this.nullable(pk.type)}`,
416
- attributes: pk.attributes?.filter(a => !ignoreDtoAttrs.includes(a.name)),
417
- }),
418
- {
419
- name: `${pk.name}s`,
420
- type: "List`1?",
421
- namespace: "System.Collections.Generic",
422
- genericArgs: [pk.type]
423
- }
424
- ]
425
- : [];
662
+ const idsProp = pk
663
+ ? {
664
+ name: `${pk.name}s`,
665
+ type: "List`1?",
666
+ namespace: "System.Collections.Generic",
667
+ genericArgs: [pk.type]
668
+ }
669
+ : undefined;
426
670
  if (!queryApi) {
427
671
  queryApi = {
428
672
  method: "GET",
@@ -436,7 +680,16 @@ export class CSharpAst {
436
680
  namespace: "ServiceStack",
437
681
  genericArgs: [type.name]
438
682
  },
439
- properties: idsProps,
683
+ properties: pk
684
+ ? [
685
+ Object.assign({}, pk, {
686
+ type: this.nullable(pk.type),
687
+ attributes: this.attrsFor("Read", "Prop", pk.attributes),
688
+ }),
689
+ idsProp
690
+ ]
691
+ : [],
692
+ attributes: this.attrsFor("Read", "Type", type.attributes),
440
693
  },
441
694
  returnType: {
442
695
  name: "QueryResponse`1",
@@ -446,25 +699,23 @@ export class CSharpAst {
446
699
  dataModel,
447
700
  };
448
701
  if (isAuditBase) {
449
- if (!queryApi.request.attributes)
450
- queryApi.request.attributes = [];
451
- // [AutoApply(Behavior.AuditQuery)]
452
- queryApi.request.attributes.push({
453
- name: "AutoApply",
454
- constructorArgs: [{
455
- name: "name",
456
- type: "constant",
457
- value: "Behavior.AuditQuery"
458
- }]
459
- });
702
+ if (!queryApi.request.attributes?.find(x => x.name === 'AutoApply')) {
703
+ // [AutoApply(Behavior.AuditQuery)]
704
+ queryApi.request.attributes.push({
705
+ name: "AutoApply",
706
+ constructorArgs: [{
707
+ name: "name",
708
+ type: "constant",
709
+ value: "Behavior.AuditQuery"
710
+ }]
711
+ });
712
+ }
460
713
  }
461
714
  this.result.operations.push(queryApi);
462
715
  }
463
716
  let createName = `Create${type.name}`;
464
717
  let createApi = this.result.operations.find(x => x.request.name === createName);
465
718
  if (!createApi) {
466
- const ignorePropsWithAttrs = ['AutoIncrement', 'Reference'];
467
- const ignoreAttrs = [];
468
719
  createApi = {
469
720
  method: "POST",
470
721
  actions: ["ANY"],
@@ -477,12 +728,13 @@ export class CSharpAst {
477
728
  genericArgs: [type.name]
478
729
  }],
479
730
  properties: type.properties
480
- .filter(x => !x.attributes?.some(a => ignorePropsWithAttrs.includes(a.name))).map(x => Object.assign({}, x, {
731
+ .filter(x => !x.attributes?.some(a => this.ignoreCreateProps.includes(a.name.toLowerCase()))).map(x => Object.assign({}, x, {
481
732
  type: x.isPrimaryKey
482
733
  ? x.type
483
734
  : `${x.type}`,
484
- attributes: x.attributes?.filter(a => !ignoreAttrs.includes(a.name)),
735
+ attributes: this.attrsFor("Create", "Prop", x.attributes),
485
736
  })),
737
+ attributes: this.attrsFor("Create", "Type", type.attributes),
486
738
  },
487
739
  returnType: {
488
740
  name: "IdResponse",
@@ -494,42 +746,50 @@ export class CSharpAst {
494
746
  if (prop.isRequired) {
495
747
  if (!prop.attributes)
496
748
  prop.attributes = [];
749
+ var hasAnyValidateAttrs = prop.attributes.some(x => x.name.toLowerCase().startsWith('validate'));
497
750
  if (prop.type === 'string') {
498
- prop.attributes.push({
499
- name: "ValidateNotEmpty",
500
- });
751
+ if (!hasAnyValidateAttrs) {
752
+ prop.attributes.push({
753
+ name: "ValidateNotEmpty",
754
+ });
755
+ }
501
756
  }
502
757
  else if (this.integerTypes.includes(prop.type)) {
503
- prop.attributes.push({
504
- name: "ValidateGreaterThan",
505
- constructorArgs: [{ name: "value", type: "int", value: "0" }]
506
- });
758
+ if (!hasAnyValidateAttrs) {
759
+ prop.attributes.push({
760
+ name: "ValidateGreaterThan",
761
+ constructorArgs: [{ name: "value", type: "int", value: "0" }]
762
+ });
763
+ }
507
764
  }
508
765
  else if (prop.type === 'List`1' && this.commonValueTypes.includes(prop.genericArgs[0])) {
509
766
  prop.attributes.push(...inputTagAttrs);
510
767
  }
768
+ const emptyValidateAttr = prop.attributes.find(x => x.name.toLowerCase() === 'validate');
769
+ if (emptyValidateAttr && emptyValidateAttr.constructorArgs?.[0]?.value === '') {
770
+ prop.attributes = prop.attributes.filter(x => x !== emptyValidateAttr);
771
+ }
511
772
  }
512
773
  }
513
774
  if (isAuditBase) {
514
775
  createApi.requiresAuth = true;
515
- if (!createApi.request.attributes)
516
- createApi.request.attributes = [];
517
- // [AutoApply(Behavior.AuditCreate)]
518
- createApi.request.attributes.push({
519
- name: "AutoApply",
520
- constructorArgs: [{
521
- name: "name",
522
- type: "constant",
523
- value: "Behavior.AuditCreate"
524
- }]
525
- });
776
+ if (!createApi.request.attributes?.find(x => x.name === 'AutoApply')) {
777
+ // [AutoApply(Behavior.AuditCreate)]
778
+ createApi.request.attributes.push({
779
+ name: "AutoApply",
780
+ constructorArgs: [{
781
+ name: "name",
782
+ type: "constant",
783
+ value: "Behavior.AuditCreate"
784
+ }]
785
+ });
786
+ }
526
787
  }
527
788
  this.result.operations.push(createApi);
528
789
  }
529
790
  let updateName = `Update${type.name}`;
530
791
  let updateApi = this.result.operations.find(x => x.request.name === updateName);
531
792
  if (!updateApi) {
532
- const ignoreAttrs = ['AutoIncrement'];
533
793
  updateApi = {
534
794
  method: "PATCH",
535
795
  actions: ["ANY"],
@@ -541,12 +801,13 @@ export class CSharpAst {
541
801
  namespace: "ServiceStack",
542
802
  genericArgs: [type.name]
543
803
  }],
544
- properties: type.properties?.filter(x => !x.attributes?.some(x => x.name === 'References')).map(x => Object.assign({}, x, {
804
+ properties: type.properties?.filter(x => !x.attributes?.some(x => this.ignoreUpdateProps.includes(x.name.toLowerCase()))).map(x => Object.assign({}, x, {
545
805
  type: x.isPrimaryKey
546
806
  ? x.type
547
807
  : `${this.nullable(x.type)}`,
548
- attributes: x.attributes?.filter(a => !ignoreAttrs.includes(a.name)),
808
+ attributes: this.attrsFor("Update", "Prop", x.attributes),
549
809
  })),
810
+ attributes: this.attrsFor("Update", "Type", type.attributes),
550
811
  },
551
812
  returnType: {
552
813
  name: "IdResponse",
@@ -565,17 +826,17 @@ export class CSharpAst {
565
826
  }
566
827
  if (isAuditBase) {
567
828
  updateApi.requiresAuth = true;
568
- if (!updateApi.request.attributes)
569
- updateApi.request.attributes = [];
570
- // [AutoApply(Behavior.AuditModify)]
571
- updateApi.request.attributes.push({
572
- name: "AutoApply",
573
- constructorArgs: [{
574
- name: "name",
575
- type: "constant",
576
- value: "Behavior.AuditModify"
577
- }]
578
- });
829
+ if (!updateApi.request.attributes?.find(x => x.name === 'AutoApply')) {
830
+ // [AutoApply(Behavior.AuditModify)]
831
+ updateApi.request.attributes.push({
832
+ name: "AutoApply",
833
+ constructorArgs: [{
834
+ name: "name",
835
+ type: "constant",
836
+ value: "Behavior.AuditModify"
837
+ }]
838
+ });
839
+ }
579
840
  }
580
841
  this.result.operations.push(updateApi);
581
842
  }
@@ -593,29 +854,50 @@ export class CSharpAst {
593
854
  namespace: "ServiceStack",
594
855
  genericArgs: [type.name]
595
856
  }],
596
- properties: idsProps,
857
+ properties: pk
858
+ ? [
859
+ Object.assign({}, pk, {
860
+ type: this.nullable(pk.type),
861
+ attributes: this.attrsFor("Delete", "Prop", pk.attributes),
862
+ }),
863
+ idsProp
864
+ ]
865
+ : [],
866
+ attributes: this.attrsFor("Delete", "Type", type.attributes),
597
867
  },
598
868
  returnsVoid: true,
599
869
  dataModel,
600
870
  };
601
871
  if (isAuditBase) {
602
872
  deleteApi.requiresAuth = true;
603
- if (!deleteApi.request.attributes)
604
- deleteApi.request.attributes = [];
605
- // [AutoApply(Behavior.AuditSoftDelete)]
606
- deleteApi.request.attributes.push({
607
- name: "AutoApply",
608
- constructorArgs: [{
609
- name: "name",
610
- type: "constant",
611
- value: "Behavior.AuditSoftDelete"
612
- }]
613
- });
873
+ if (!deleteApi.request.attributes?.find(x => x.name === 'AutoApply')) {
874
+ // [AutoApply(Behavior.AuditSoftDelete)]
875
+ deleteApi.request.attributes.push({
876
+ name: "AutoApply",
877
+ constructorArgs: [{
878
+ name: "name",
879
+ type: "constant",
880
+ value: "Behavior.AuditSoftDelete"
881
+ }]
882
+ });
883
+ }
614
884
  }
615
885
  this.result.operations.push(deleteApi);
616
886
  }
617
887
  }
618
888
  }
889
+ filterModelAttributes() {
890
+ for (const type of this.classes) {
891
+ if (type.attributes?.length) {
892
+ type.attributes = this.attrsFor("Model", "Type", type.attributes);
893
+ }
894
+ type.properties?.forEach(p => {
895
+ if (p.attributes?.length) {
896
+ p.attributes = this.attrsFor("Model", "Prop", p.attributes);
897
+ }
898
+ });
899
+ }
900
+ }
619
901
  // Add Icon for BuiltIn UIs and AutoQueryGrid to known type names
620
902
  addIconsToKnownTypes() {
621
903
  for (const type of this.typesWithPrimaryKeys) {
@@ -623,8 +905,14 @@ export class CSharpAst {
623
905
  if (icon) {
624
906
  if (!type.attributes)
625
907
  type.attributes = [];
626
- if (type.attributes.some(x => x.name === 'Icon'))
627
- continue;
908
+ const existingIcon = type.attributes.find(x => x.name === 'Icon');
909
+ if (existingIcon) {
910
+ // remove empty icon
911
+ if (existingIcon.constructorArgs?.[0]?.value === '') {
912
+ type.attributes = type.attributes.filter(x => x !== existingIcon);
913
+ }
914
+ return;
915
+ }
628
916
  type.attributes.push({
629
917
  name: "Icon",
630
918
  args: [{ name: "Svg", type: "string", value: icon }]
@@ -696,6 +984,7 @@ export class CSharpAst {
696
984
  this.hideReferenceProperties();
697
985
  this.replaceUserReferencesWithAuditTables();
698
986
  this.createAutoCrudApis();
987
+ this.filterModelAttributes();
699
988
  }
700
989
  parse(ast) {
701
990
  const classes = ast.classes.concat(ast.interfaces);
package/dist/cs-gen.js CHANGED
@@ -41,7 +41,7 @@ export class CSharpGenerator {
41
41
  body += value;
42
42
  }
43
43
  }
44
- if (!body && attr.args?.length) {
44
+ if (attr.args?.length) {
45
45
  for (const arg of attr.args) {
46
46
  this.addNamespace(arg.namespace);
47
47
  if (body)
package/dist/index.js CHANGED
@@ -57,7 +57,7 @@ function parseArgs(...args) {
57
57
  break;
58
58
  }
59
59
  }
60
- else if (ret.type === "help" && ["help", "info", "init", "ls", "rm", "update", "accept"].includes(arg)) {
60
+ else if (ret.type === "help" && ["help", "info", "init", "ls", "add", "rm", "update", "accept"].includes(arg)) {
61
61
  if (arg == "help")
62
62
  ret.type = "help";
63
63
  else if (arg == "info")
@@ -82,6 +82,10 @@ function parseArgs(...args) {
82
82
  ret.type = "list";
83
83
  ret.list = args[++i];
84
84
  }
85
+ else if (arg == "add") {
86
+ ret.type = "add";
87
+ ret.add = args[++i];
88
+ }
85
89
  else if (arg == "accept") {
86
90
  ret.type = "accept";
87
91
  ret.accept = args[++i];
@@ -208,6 +212,24 @@ Options:
208
212
  process.exit(0);
209
213
  }
210
214
  }
215
+ if (command.type === "add") {
216
+ if (command.add == "types" || command.add?.startsWith("api")) {
217
+ const apiFile = path.join(import.meta.dirname, 'api.d.ts');
218
+ if (!fs.existsSync(apiFile)) {
219
+ console.log(`Could not find: ${apiFile}`);
220
+ process.exit(1);
221
+ }
222
+ const toFile = path.join(info.serviceModelDir, 'api.d.ts');
223
+ fs.copyFileSync(apiFile, toFile);
224
+ console.log(`Added: ${toFile}`);
225
+ process.exit(0);
226
+ }
227
+ else {
228
+ console.log(`Unknown add command: ${command.add}`);
229
+ console.log(`Usage: add types`);
230
+ process.exit(1);
231
+ }
232
+ }
211
233
  function assertTsdPath(tsdFile) {
212
234
  const tryPaths = [
213
235
  path.join(process.cwd(), tsdFile),
@@ -320,6 +342,13 @@ Options:
320
342
  }
321
343
  fs.unlinkSync(tsdPath);
322
344
  console.log(`Removed: ${tsdPath}`);
345
+ const serviceModelDir = path.dirname(tsdPath);
346
+ const tsds = fs.readdirSync(serviceModelDir).filter(x => x.endsWith('.d.ts'));
347
+ if (tsds.length == 1 && tsds[0] == 'api.d.ts') {
348
+ const typesApiPath = path.join(serviceModelDir, 'api.d.ts');
349
+ fs.unlinkSync(typesApiPath);
350
+ console.log(`Removed: ${typesApiPath}`);
351
+ }
323
352
  process.exit(0);
324
353
  }
325
354
  if (command.type == "accept") {
@@ -608,15 +637,18 @@ function chooseFile(ctx, info, gist, comamnd) {
608
637
  const migrationFileName = path.basename(migrationPath);
609
638
  const migrationCls = leftPart(migrationFileName, '.');
610
639
  const migrationContent = replaceMyApp(csMigrationFiles[Object.keys(csMigrationFiles)[0]].replaceAll('Migration1000', migrationCls), info.projectName);
611
- const sb = [];
612
- sb.push(`/*prompt: ${titleBar.content.replaceAll('/*', '').replaceAll('*/', '')}`);
613
- sb.push(`api: ~/${path.join(relativeServiceModelDir, apiFileName)}`);
614
- sb.push(`migration: ~/${path.join(relativeMigrationDir, migrationFileName)}`);
615
- sb.push(`*/`);
616
- sb.push('');
617
- sb.push(tsd);
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
+ ];
618
649
  const tsdContent = sb.join('\n');
619
650
  const tsdFileName = `${groupName}.d.ts`;
651
+ const typesApiPath = path.join(info.slnDir, relativeServiceModelDir, `api.d.ts`);
620
652
  const fullTsdPath = path.join(info.slnDir, relativeServiceModelDir, tsdFileName);
621
653
  const fullApiPath = path.join(info.slnDir, relativeServiceModelDir, apiFileName);
622
654
  const fullMigrationPath = path.join(info.slnDir, relativeMigrationDir, migrationFileName);
@@ -635,6 +667,8 @@ function chooseFile(ctx, info, gist, comamnd) {
635
667
  fs.writeFileSync(fullMigrationPath, migrationContent, { encoding: 'utf-8' });
636
668
  console.log(`Saved: ${fullMigrationPath}`);
637
669
  }
670
+ const apiFile = path.join(import.meta.dirname, 'api.d.ts');
671
+ fs.writeFileSync(typesApiPath, fs.readFileSync(apiFile, 'utf-8'));
638
672
  const script = path.basename(process.argv[1]);
639
673
  console.log(`\nTo regenerate classes, update '${tsdFileName}' then run:`);
640
674
  console.log(`$ ${script} ${tsdFileName}`);
package/dist/ts-parser.js CHANGED
@@ -4,7 +4,8 @@ export class TypeScriptParser {
4
4
  static ENUM_PATTERN = /enum\s+(\w+)\s*{([^}]*)}/g;
5
5
  static PROPERTY_PATTERN = /(?:(?<modifier>private|public|protected|readonly)\s+)*(?<name>\w+)(?<optional>\?)?\s*:\s*(?<type>[\w<>[\],\s]+)(?<union>\|\s*[\w<>[\],|,\s]+)?\s*;?/;
6
6
  static ENUM_MEMBER_PATTERN = /(\w+)\s*(?:=\s*("[^"]*"|'[^']*'|\d+|[^,\n]+))?\s*/;
7
- static ANNOTATION_PATTERN = /@\w+\(.*\)/;
7
+ // private static readonly ANNOTATION_PATTERN = /@(\w+\.?\w*)\s*\((.*)\)/
8
+ static ANNOTATION_PATTERN = /@([A-Za-z_][A-Za-z0-9_]*\.?[A-Za-z_]?[A-Za-z0-9_]*)\s*\((.*)\)/;
8
9
  classes = [];
9
10
  interfaces = [];
10
11
  enums = [];
@@ -260,7 +261,7 @@ export class TypeScriptParser {
260
261
  }
261
262
  export function parseAnnotation(annotation) {
262
263
  // Match @name and everything inside ()
263
- const regex = /@(\w+)\s*\((.*)\)/;
264
+ const regex = /@(\w+\.?\w*)\s*\((.*)\)/;
264
265
  const match = annotation.match(regex);
265
266
  if (!match)
266
267
  return null;
package/dist/tsd-gen.js CHANGED
@@ -32,6 +32,12 @@ export class TsdGenerator {
32
32
  return `${prefix}@${attr.name}(${sbArgs.join(',')})`;
33
33
  }
34
34
  toInterface(ast) {
35
+ return this.toType(ast, 'interface');
36
+ }
37
+ toClass(ast) {
38
+ return this.toType(ast, 'class');
39
+ }
40
+ toType(ast, type) {
35
41
  const sb = [];
36
42
  if (ast.comment) {
37
43
  sb.push(ast.comment.split('\n').map(x => `// ${x}`).join('\n'));
@@ -42,7 +48,7 @@ export class TsdGenerator {
42
48
  }
43
49
  }
44
50
  const extend = ast.extends ? ` extends ${ast.extends}` : '';
45
- sb.push(`export interface ${ast.name}${extend} {`);
51
+ sb.push(`export ${type} ${ast.name}${extend} {`);
46
52
  for (const prop of ast.properties) {
47
53
  if (prop.comment) {
48
54
  sb.push(prop.comment.split('\n').map(x => ` // ${x}`).join('\n'));
@@ -76,7 +82,7 @@ export class TsdGenerator {
76
82
  sb.push('}');
77
83
  return sb.join('\n');
78
84
  }
79
- generate(ast) {
85
+ generate(ast, type = 'interface') {
80
86
  this.ast = ast;
81
87
  const sb = [];
82
88
  for (const cls of ast.enums ?? []) {
@@ -84,12 +90,12 @@ export class TsdGenerator {
84
90
  sb.push('');
85
91
  }
86
92
  for (const cls of ast.interfaces ?? []) {
87
- sb.push(this.toInterface(cls));
93
+ sb.push(this.toType(cls, type));
88
94
  sb.push('');
89
95
  }
90
96
  for (const cls of ast.classes ?? []) {
91
97
  const iface = this.clsToInterface(cls);
92
- sb.push(this.toInterface(iface));
98
+ sb.push(this.toType(iface, type));
93
99
  sb.push('');
94
100
  }
95
101
  const tsd = sb.join('\n');
@@ -154,6 +160,6 @@ export class TsdDataModelGenerator extends TsdGenerator {
154
160
  if (ast.interfaces) {
155
161
  ast.interfaces = ast.interfaces.filter(x => x.name !== 'User');
156
162
  }
157
- return super.generate(ast);
163
+ return super.generate(ast, "class");
158
164
  }
159
165
  }
package/dist/utils.js CHANGED
@@ -113,6 +113,14 @@ export function leftPart(s, needle) {
113
113
  ? s
114
114
  : s.substring(0, pos);
115
115
  }
116
+ export function rightPart(s, needle) {
117
+ if (s == null)
118
+ return null;
119
+ let pos = s.indexOf(needle);
120
+ return pos == -1
121
+ ? s
122
+ : s.substring(pos + needle.length);
123
+ }
116
124
  export function splitCase(t) {
117
125
  return typeof t != 'string' ? t : t.replace(/([A-Z]|[0-9]+)/g, ' $1').replace(/_/g, ' ').trim();
118
126
  }
@@ -126,13 +134,13 @@ export function tsdWithPrompt(tsd, prompt) {
126
134
  return `/*prompt: ${prompt}\n*/\n\n${tsd}`;
127
135
  }
128
136
  export function tsdWithoutPrompt(tsd) {
129
- return tsd.trim().startsWith('/*prompt:')
137
+ return tsd.includes('/*prompt:')
130
138
  ? tsd.substring(tsd.indexOf('*/') + 2).trim()
131
139
  : tsd;
132
140
  }
133
141
  export function parseTsdHeader(tsd) {
134
142
  const header = tsd.includes('/*prompt:')
135
- ? leftPart(tsd, '*/').replace('/*prompt:', '').trim()
143
+ ? leftPart(rightPart(tsd, '/*prompt:'), '*/').trim()
136
144
  : null;
137
145
  if (!header)
138
146
  return null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "okai",
3
3
  "type": "module",
4
- "version": "0.0.22",
4
+ "version": "0.0.24",
5
5
  "bin": "./dist/okai.js",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.js",
@@ -12,7 +12,7 @@
12
12
  "import": "./dist/index.js"
13
13
  },
14
14
  "scripts": {
15
- "build": "bun run clean && tsc && chmod +x ./dist/okai.js",
15
+ "build": "bun run clean && tsc && shx cp ./src/api.d.ts ./dist && chmod +x ./dist/okai.js",
16
16
  "build-bun": "bun run clean && bun build.ts",
17
17
  "clean": "shx rm -rf ./dist",
18
18
  "test": "bun test --",