@voxgig/apidef 0.1.4 → 0.3.0

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.
@@ -0,0 +1,178 @@
1
+ #!/usr/bin/env node
2
+
3
+ const Path = require('node:path')
4
+ const { statSync } = require('node:fs')
5
+ const { parseArgs } = require('node:util')
6
+
7
+ const { Gubu, Fault, One } = require('gubu')
8
+ const { Aontu, Context } = require('aontu')
9
+
10
+
11
+ const Pkg = require('../package.json')
12
+
13
+ const { ApiDef } = require('../dist/apidef.js')
14
+
15
+
16
+ let CONSOLE = console
17
+
18
+
19
+ try {
20
+ let options = resolveOptions()
21
+
22
+ if(options.version) {
23
+ version()
24
+ }
25
+
26
+ if(options.help) {
27
+ help()
28
+ }
29
+
30
+ if(options.version || options.help) {
31
+ exit()
32
+ }
33
+
34
+ options = validateOptions(options)
35
+
36
+ generate(options)
37
+
38
+ }
39
+ catch(err) {
40
+ handleError(err)
41
+ }
42
+
43
+
44
+
45
+ function exit(err) {
46
+ let code = 0
47
+ if(err) {
48
+ code = 1
49
+ }
50
+ process.exit(code)
51
+ }
52
+
53
+
54
+ function generate(options) {
55
+ const apidef = new ApiDef({
56
+ debug: options.debug
57
+ })
58
+
59
+ const spec = {
60
+ def: options.def,
61
+ kind: 'openapi-3',
62
+ model: Path.join(options.folder,'model/api.jsonic'),
63
+ meta: { name: options.name },
64
+ }
65
+
66
+ if(options.watch) {
67
+ apidef.watch(spec)
68
+ }
69
+ else {
70
+ apidef.generate(spec)
71
+ }
72
+
73
+ }
74
+
75
+
76
+
77
+ function resolveOptions() {
78
+
79
+ const args = parseArgs({
80
+ allowPositionals: true,
81
+ options: {
82
+ folder: {
83
+ type: 'string',
84
+ short: 'f',
85
+ default: '',
86
+ },
87
+
88
+ def: {
89
+ type: 'string',
90
+ short: 'd',
91
+ default: '',
92
+ },
93
+
94
+ watch: {
95
+ type: 'boolean',
96
+ short: 'w',
97
+ },
98
+
99
+ debug: {
100
+ type: 'string',
101
+ short: 'g',
102
+ default: 'info'
103
+ },
104
+
105
+ help: {
106
+ type: 'boolean',
107
+ short: 'h',
108
+ },
109
+
110
+ version: {
111
+ type: 'boolean',
112
+ short: 'v',
113
+ },
114
+
115
+ }
116
+ })
117
+
118
+ const options = {
119
+ name: args.positionals[0],
120
+ folder: '' === args.values.folder ? args.positionals[0] : args.values.folder,
121
+ def: args.values.def,
122
+ watch: !!args.values.watch,
123
+ debug: args.values.debug,
124
+ help: !!args.values.help,
125
+ version: !!args.values.version,
126
+ }
127
+
128
+ return options
129
+ }
130
+
131
+
132
+ function validateOptions(rawOptions) {
133
+ const optShape = Gubu({
134
+ name: Fault('The first argument should be the project name.', String),
135
+ folder: String,
136
+ def: '',
137
+ watch: Boolean,
138
+ debug: One(String,Boolean),
139
+ help: Boolean,
140
+ version: Boolean,
141
+ })
142
+
143
+ const err = []
144
+ const options = optShape(rawOptions,{err})
145
+
146
+ if(err[0]) {
147
+ throw new Error(err[0].text)
148
+ }
149
+
150
+ if('' !== options.def) {
151
+ options.def = Path.resolve(options.def)
152
+ const stat = statSync(options.def, {throwIfNoEntry:false})
153
+ if(null == stat) {
154
+ throw new Error('Definition file not found: '+options.def)
155
+ }
156
+ }
157
+
158
+ return options
159
+ }
160
+
161
+
162
+ function handleError(err) {
163
+ CONSOLE.log('Voxgig API Definition Error:')
164
+ CONSOLE.log(err)
165
+
166
+ exit(err)
167
+ }
168
+
169
+
170
+ function version() {
171
+ CONSOLE.log(Pkg.version)
172
+ }
173
+
174
+
175
+ function help() {
176
+ const s = 'TODO'
177
+ CONSOLE.log(s)
178
+ }
@@ -0,0 +1,330 @@
1
+ #!/usr/bin/env node
2
+
3
+ const Path = require('node:path')
4
+ const { statSync } = require('node:fs')
5
+ const { parseArgs } = require('node:util')
6
+
7
+ const { Gubu, Fault } = require('gubu')
8
+ const { Aontu, Context } = require('aontu')
9
+
10
+
11
+ const Pkg = require('../package.json')
12
+
13
+ const { CreateSdkGen } = require('../dist/create-sdkgen.js')
14
+
15
+ // const { Root } = require('../dist/standard/Root')
16
+ const rootpath = require.resolve('../dist/standard/Root')
17
+
18
+ let DEBUG = false
19
+ let CONSOLE = console
20
+
21
+
22
+ try {
23
+ let options = resolveOptions()
24
+
25
+ if(options.debug) {
26
+ DEBUG = true
27
+ }
28
+
29
+ if(options.version) {
30
+ version()
31
+ }
32
+
33
+ if(options.help) {
34
+ help()
35
+ }
36
+
37
+ if(options.version || options.help) {
38
+ exit()
39
+ }
40
+
41
+ options = validateOptions(options)
42
+
43
+ generate(options)
44
+
45
+ }
46
+ catch(err) {
47
+ handleError(err)
48
+ }
49
+
50
+
51
+
52
+ function exit(err) {
53
+ let code = 0
54
+ if(err) {
55
+ code = 1
56
+ }
57
+ process.exit(code)
58
+ }
59
+
60
+
61
+ function generate(options) {
62
+ const createSdkGen = CreateSdkGen({
63
+ folder: options.folder,
64
+ rootpath
65
+ })
66
+
67
+
68
+ const model = resolveModel(options)
69
+
70
+ const spec = {
71
+ // TODO: move to CreateSdkGen options
72
+ spec: options.spec,
73
+ model,
74
+ // root: Root,
75
+ }
76
+
77
+ if(options.watch) {
78
+ spec.watch = [
79
+ '../tm',
80
+ '../feature',
81
+ '../def',
82
+ '../dist',
83
+ ]
84
+ createSdkGen.watch(spec)
85
+ }
86
+ else {
87
+ createSdkGen.generate(spec)
88
+ }
89
+
90
+ }
91
+
92
+
93
+ function resolveModel(options) {
94
+
95
+ const typespec = `
96
+ name: string
97
+ year: number
98
+ def: filepath: string
99
+ feature: &: {
100
+ active: *false | boolean
101
+ }
102
+ `
103
+
104
+ const features = options.feature.map(name=>`feature:${name}:active:true`)
105
+
106
+ const src = [
107
+ typespec,
108
+ `name: ${options.name}`,
109
+ `year: ${new Date().getFullYear()}`,
110
+ `def: filepath: "${options.def || 'def.yml'}"`,
111
+ '' === options.model ? '' : `@"${options.model}"`,
112
+ ...(options.set||[]),
113
+ ...(features||[]),
114
+ ].join('\n')
115
+ const aopts = {}
116
+ const root = Aontu(src, aopts)
117
+ const hasErr = root.err && 0 < root.err.length
118
+
119
+ // TODO: collect all errors
120
+ if (hasErr) {
121
+ // console.log('ERROR ROOT', root.err)
122
+ throw root.err[0].msg
123
+ }
124
+
125
+ let genctx = new Context({ root })
126
+ const model = root.gen(genctx)
127
+
128
+ // TODO: collect all errors
129
+ if (genctx.err && 0 < genctx.err.length) {
130
+ // console.log(genctx.err)
131
+ // console.log('ERROR GEN', genctx.err)
132
+ throw new Error(JSON.stringify(genctx.err[0]))
133
+ }
134
+
135
+ model.def.filename = Path.basename(model.def.filepath)
136
+
137
+ // console.log('MODEL')
138
+ // console.dir(model,{depth:null})
139
+
140
+ return model
141
+ }
142
+
143
+
144
+ function resolveOptions() {
145
+
146
+ const args = parseArgs({
147
+ allowPositionals: true,
148
+ options: {
149
+ folder: {
150
+ type: 'string',
151
+ short: 'f',
152
+ default: '',
153
+ },
154
+
155
+ def: {
156
+ type: 'string',
157
+ short: 'd',
158
+ default: '',
159
+ },
160
+
161
+ set: {
162
+ type: 'string',
163
+ short: 's',
164
+ multiple: true,
165
+ },
166
+
167
+ model: {
168
+ type: 'string',
169
+ short: 'm',
170
+ default: '',
171
+ },
172
+
173
+ feature: {
174
+ type: 'string',
175
+ short: 't',
176
+ multiple: true,
177
+ },
178
+
179
+ watch: {
180
+ type: 'boolean',
181
+ short: 'w',
182
+ },
183
+
184
+ debug: {
185
+ type: 'boolean',
186
+ short: 'g',
187
+ },
188
+
189
+ help: {
190
+ type: 'boolean',
191
+ short: 'h',
192
+ },
193
+
194
+ version: {
195
+ type: 'boolean',
196
+ short: 'v',
197
+ },
198
+
199
+ }
200
+ })
201
+
202
+ const options = {
203
+ name: args.positionals[0],
204
+ folder: '' === args.values.folder ? args.positionals[0] : args.values.folder,
205
+ def: args.values.def,
206
+ model: args.values.model,
207
+ feature: args.values.feature,
208
+ set: args.values.set,
209
+ watch: !!args.values.watch,
210
+ debug: !!args.values.debug,
211
+ help: !!args.values.help,
212
+ version: !!args.values.version,
213
+ }
214
+
215
+ return options
216
+ }
217
+
218
+
219
+ function validateOptions(rawOptions) {
220
+ const optShape = Gubu({
221
+ name: Fault('The first argument should be the project name.', String),
222
+ folder: String,
223
+ def: '',
224
+ model: '',
225
+ feature: [String],
226
+ set: [String],
227
+ watch: Boolean,
228
+ debug: Boolean,
229
+ help: Boolean,
230
+ version: Boolean,
231
+ })
232
+
233
+ const err = []
234
+ const options = optShape(rawOptions,{err})
235
+
236
+ if(err[0]) {
237
+ throw new Error(err[0].text)
238
+ }
239
+
240
+ if('' !== options.def) {
241
+ options.def = Path.resolve(options.def)
242
+ const stat = statSync(options.def, {throwIfNoEntry:false})
243
+ if(null == stat) {
244
+ throw new Error('Definition file not found: '+options.def)
245
+ }
246
+ }
247
+
248
+ if('' !== options.model) {
249
+ options.model = Path.resolve(options.model)
250
+ const stat = statSync(options.model, {throwIfNoEntry:false})
251
+ if(null == stat) {
252
+ throw new Error('Model file not found: '+options.model)
253
+ }
254
+ }
255
+
256
+ return options
257
+ }
258
+
259
+
260
+ function handleError(err) {
261
+ CONSOLE.log('Voxgig SDK Generator Error:')
262
+
263
+ if(DEBUG) {
264
+ CONSOLE.log(err)
265
+ }
266
+ else {
267
+ CONSOLE.log(err.message)
268
+ }
269
+
270
+ exit(err)
271
+ }
272
+
273
+
274
+ function version() {
275
+ CONSOLE.log(Pkg.version)
276
+ }
277
+
278
+
279
+ function help() {
280
+ let s = `
281
+ Create a Voxgig SDK Generator project.
282
+
283
+ Usage: npm create @voxgig/sdkgen@latest <name> <args>
284
+
285
+ where <name> is the name of the SDK project. This is also used to
286
+ create a project folder if one is not explictly defined using the -f
287
+ argument. If run against an existing folder generated files will be
288
+ overwritten.
289
+
290
+ <args> are the command arguments:
291
+
292
+ --folder <folder> Specific the folder for the SDK project. Optional.
293
+ -f <folder> Default: <name> in current folder.
294
+
295
+ --def <file> Specify the API definition file (OpenAPI, Swagger, etc)
296
+ -d that defines the SDK. Optional.
297
+
298
+ --model <file> Specify the SDK model file to customize the SDK. Optional.
299
+ -m
300
+
301
+ --watch Run in watch mode. The SDK project will be updated if any of the
302
+ -w project inputs (such as the spec file) change.
303
+
304
+ --debug Print verbose logging.
305
+ -d
306
+
307
+ --help Print this help message.
308
+ -h
309
+
310
+ --version Print version number.
311
+ -v
312
+
313
+
314
+ Examples:
315
+
316
+ # Basic usage
317
+ > npm create @voxgig/sdkgen@latest foo
318
+ # Creates the SDK Project in the folder ./foo
319
+
320
+
321
+ # Custom project folder
322
+ > npm create @voxgig/sdkgen@latest foo -f ~/Projects/Foo
323
+ # Creates the SDK Project in the folder ~/Projects/Foo
324
+
325
+
326
+ See also: https://voxgig.com/sdkgen
327
+ `
328
+
329
+ CONSOLE.log(s)
330
+ }
package/dist/apidef.d.ts CHANGED
@@ -1,9 +1,18 @@
1
+ import Pino from 'pino';
1
2
  type ApiDefOptions = {
2
3
  fs?: any;
4
+ pino?: ReturnType<typeof Pino>;
5
+ debug?: boolean | string;
6
+ };
7
+ type ApiDefSpec = {
8
+ def: string;
9
+ model: string;
10
+ kind: string;
11
+ meta: Record<string, any>;
3
12
  };
4
13
  declare function ApiDef(opts?: ApiDefOptions): {
5
14
  watch: (spec: any) => Promise<void>;
6
- generate: (spec: any) => Promise<{
15
+ generate: (spec: ApiDefSpec) => Promise<{
7
16
  ok: boolean;
8
17
  model: {
9
18
  main: {