@voxgig/apidef 2.4.1 → 3.0.2

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.
Files changed (96) hide show
  1. package/dist/apidef.d.ts +5 -1
  2. package/dist/apidef.js +197 -112
  3. package/dist/apidef.js.map +1 -1
  4. package/dist/builder/entity/entity.d.ts +3 -0
  5. package/dist/builder/entity/{apiEntity.js → entity.js} +12 -9
  6. package/dist/builder/entity/entity.js.map +1 -0
  7. package/dist/builder/entity/info.d.ts +3 -0
  8. package/dist/builder/entity/info.js +22 -0
  9. package/dist/builder/entity/info.js.map +1 -0
  10. package/dist/builder/entity.js +7 -21
  11. package/dist/builder/entity.js.map +1 -1
  12. package/dist/builder/flow/flowHeuristic01.js +21 -11
  13. package/dist/builder/flow/flowHeuristic01.js.map +1 -1
  14. package/dist/builder/flow.d.ts +2 -1
  15. package/dist/builder/flow.js +29 -4
  16. package/dist/builder/flow.js.map +1 -1
  17. package/dist/def.d.ts +62 -0
  18. package/dist/def.js +4 -0
  19. package/dist/def.js.map +1 -0
  20. package/dist/desc.d.ts +89 -0
  21. package/dist/desc.js +4 -0
  22. package/dist/desc.js.map +1 -0
  23. package/dist/guide/guide.d.ts +2 -1
  24. package/dist/guide/guide.js +161 -30
  25. package/dist/guide/guide.js.map +1 -1
  26. package/dist/guide/heuristic01.d.ts +2 -1
  27. package/dist/guide/heuristic01.js +1120 -234
  28. package/dist/guide/heuristic01.js.map +1 -1
  29. package/dist/model.d.ts +55 -0
  30. package/dist/model.js +4 -0
  31. package/dist/model.js.map +1 -0
  32. package/dist/parse.d.ts +1 -2
  33. package/dist/parse.js +8 -47
  34. package/dist/parse.js.map +1 -1
  35. package/dist/transform/args.d.ts +3 -0
  36. package/dist/transform/args.js +58 -0
  37. package/dist/transform/args.js.map +1 -0
  38. package/dist/transform/clean.js +27 -3
  39. package/dist/transform/clean.js.map +1 -1
  40. package/dist/transform/entity.d.ts +11 -3
  41. package/dist/transform/entity.js +57 -41
  42. package/dist/transform/entity.js.map +1 -1
  43. package/dist/transform/field.d.ts +3 -3
  44. package/dist/transform/field.js +90 -65
  45. package/dist/transform/field.js.map +1 -1
  46. package/dist/transform/operation.d.ts +1 -1
  47. package/dist/transform/operation.js +94 -296
  48. package/dist/transform/operation.js.map +1 -1
  49. package/dist/transform/select.d.ts +3 -0
  50. package/dist/transform/select.js +44 -0
  51. package/dist/transform/select.js.map +1 -0
  52. package/dist/transform/top.d.ts +9 -0
  53. package/dist/transform/top.js +11 -2
  54. package/dist/transform/top.js.map +1 -1
  55. package/dist/transform.js +4 -0
  56. package/dist/transform.js.map +1 -1
  57. package/dist/tsconfig.tsbuildinfo +1 -1
  58. package/dist/types.d.ts +112 -19
  59. package/dist/types.js +4 -2
  60. package/dist/types.js.map +1 -1
  61. package/dist/utility.d.ts +30 -2
  62. package/dist/utility.js +381 -6
  63. package/dist/utility.js.map +1 -1
  64. package/model/apidef.jsonic +75 -1
  65. package/model/guide.jsonic +14 -44
  66. package/package.json +17 -14
  67. package/src/apidef.ts +264 -121
  68. package/src/builder/entity/{apiEntity.ts → entity.ts} +18 -11
  69. package/src/builder/entity/info.ts +53 -0
  70. package/src/builder/entity.ts +9 -35
  71. package/src/builder/flow/flowHeuristic01.ts +46 -12
  72. package/src/builder/flow.ts +39 -5
  73. package/src/def.ts +91 -0
  74. package/src/desc.ts +143 -0
  75. package/src/guide/guide.ts +207 -134
  76. package/src/guide/heuristic01.ts +1651 -272
  77. package/src/model.ts +98 -0
  78. package/src/parse.ts +5 -61
  79. package/src/schematron.ts.off +317 -0
  80. package/src/transform/args.ts +102 -0
  81. package/src/transform/clean.ts +43 -8
  82. package/src/transform/entity.ts +100 -51
  83. package/src/transform/field.ts +150 -71
  84. package/src/transform/operation.ts +118 -414
  85. package/src/transform/select.ts +90 -0
  86. package/src/transform/top.ts +76 -3
  87. package/src/transform.ts +4 -0
  88. package/src/types.ts +185 -5
  89. package/src/utility.ts +481 -9
  90. package/dist/builder/entity/apiEntity.d.ts +0 -3
  91. package/dist/builder/entity/apiEntity.js.map +0 -1
  92. package/dist/builder/entity/def.d.ts +0 -3
  93. package/dist/builder/entity/def.js +0 -19
  94. package/dist/builder/entity/def.js.map +0 -1
  95. package/src/builder/entity/def.ts +0 -44
  96. package/src/guide.ts.off +0 -136
@@ -1,31 +1,51 @@
1
1
 
2
- import Path from 'node:path'
3
2
 
4
- import { Jostraca, Project, names, File, Content, each } from 'jostraca'
3
+ import Path from 'node:path'
5
4
 
6
- import { Aontu, Val, Nil, Context } from 'aontu'
5
+ import { Jostraca, Project, File, Content, each } from 'jostraca'
7
6
 
7
+ import { Aontu } from 'aontu'
8
8
 
9
- import { items } from '@voxgig/struct'
9
+ import { items, isempty } from '@voxgig/struct'
10
10
 
11
11
 
12
12
  import { heuristic01 } from './heuristic01'
13
13
 
14
+
15
+ import {
16
+ ApiDefContext,
17
+
18
+ Guide,
19
+ GuideMetrics,
20
+ GuideEntity,
21
+ GuidePath,
22
+ GuidePathAction,
23
+ GuideRenameParam,
24
+ GuidePathOp,
25
+ } from '../types'
26
+
27
+
28
+
29
+
14
30
  import {
15
31
  getdlog,
32
+ debugpath,
33
+ formatJSONIC,
16
34
  } from '../utility'
17
35
 
18
36
 
19
37
  // Log non-fatal wierdness.
20
38
  const dlog = getdlog('apidef', __filename)
21
39
 
40
+ const aontu = new Aontu()
41
+
42
+
22
43
 
23
- async function buildGuide(ctx: any): Promise<any> {
44
+ async function buildGuide(ctx: ApiDefContext): Promise<any> {
45
+ const log = ctx.log
24
46
  const errs: any[] = []
25
47
 
26
- // console.log(ctx)
27
48
  const folder = Path.resolve(ctx.opts.folder)
28
- // console.log('GUIDE folder', folder)
29
49
 
30
50
  try {
31
51
  const basejres = await buildBaseGuide(ctx)
@@ -37,11 +57,18 @@ async function buildGuide(ctx: any): Promise<any> {
37
57
  handleErrors(ctx, errs)
38
58
 
39
59
  let src = ''
40
- let guidePath = Path.join(folder, 'guide',
60
+ let guidepath = Path.join(folder, 'guide',
41
61
  (null == ctx.opts.outprefix ? '' : ctx.opts.outprefix) + 'guide.jsonic')
42
62
 
63
+ log.info({
64
+ point: 'generate-guide',
65
+ note: guidepath.replace(process.cwd(), '.'),
66
+ guidepath,
67
+ })
68
+
69
+
43
70
  try {
44
- src = ctx.fs.readFileSync(guidePath, 'utf8')
71
+ src = ctx.fs.readFileSync(guidepath, 'utf8')
45
72
  }
46
73
  catch (err: any) {
47
74
  errs.push(err)
@@ -50,41 +77,59 @@ async function buildGuide(ctx: any): Promise<any> {
50
77
  handleErrors(ctx, errs)
51
78
 
52
79
 
53
- const opts = {
54
- path: guidePath
55
- }
56
-
57
- const guideRoot = Aontu(src, opts)
58
- errs.push(...guideRoot.err)
80
+ if (0 === errs.length) {
59
81
 
60
- handleErrors(ctx, errs)
82
+ const opts = {
83
+ path: guidepath,
84
+ fs: ctx.fs,
85
+ errs,
86
+ }
61
87
 
88
+ const guideModel = aontu.generate(src, opts)
89
+ // console.log('GUIDE-MODEL', guideModel, errs)
62
90
 
63
- let genctx = new Context({ root: guideRoot })
64
- const guideModel = guideRoot.gen(genctx)
91
+ // console.dir(guideModel, { depth: null })
65
92
 
66
- errs.push(...genctx.err)
93
+ handleErrors(ctx, errs)
67
94
 
68
- handleErrors(ctx, errs)
95
+ return guideModel
69
96
 
70
- return guideModel
97
+ }
71
98
  }
72
99
 
73
100
 
74
101
  function handleErrors(ctx: any, errs: any[]) {
75
102
  if (0 < errs.length) {
76
- let topmsg: string[] = []
103
+ const topmsg: string[] = []
104
+ const stacks: string[] = []
77
105
  for (let err of errs) {
78
- topmsg.push((err?.message?.split('\n')[0]) || '')
79
- ctx.log.error({ err })
106
+ err = err instanceof Error ? err :
107
+ err.err instanceof Error ? err.err :
108
+ Array.isArray(err.err) && null != err.err[0] ? err.err[0] :
109
+ err
110
+
111
+ const msg =
112
+ 'string' === typeof err?.message ? err.message :
113
+ err instanceof Error ? err.message : '' + err
114
+
115
+ topmsg.push(msg)
116
+
117
+ stacks.push('' + err.stack)
80
118
  }
81
- throw new Error('SUMMARY: ' + topmsg.join('; '))
119
+ const summary: any = new Error(`SUMMARY (${errs.length} errors): ` + topmsg.join(' | '))
120
+ summary.stack = stacks.join('\n')
121
+ ctx.log.error(summary)
122
+ summary.errs = () => errs
123
+ throw summary
82
124
  }
83
125
  }
84
126
 
85
127
 
86
- async function buildBaseGuide(ctx: any) {
87
- let baseguide: Record<string, any> = {}
128
+
129
+
130
+
131
+ async function buildBaseGuide(ctx: ApiDefContext) {
132
+ let baseguide: Guide
88
133
 
89
134
  if ('heuristic01' === ctx.opts.strategy) {
90
135
  baseguide = await heuristic01(ctx)
@@ -93,28 +138,90 @@ async function buildBaseGuide(ctx: any) {
93
138
  throw new Error('Unknown guide strategy: ' + ctx.opts.strategy)
94
139
  }
95
140
 
141
+ // console.dir(baseguide, { depth: null })
142
+
143
+
96
144
  const guideBlocks = [
97
145
  '# Guide',
98
146
  '',
99
147
  'guide: {',
100
148
  ]
101
149
 
150
+ const metrics = baseguide.metrics
151
+
152
+ // TODO: these should influence the IS_ENTCMP_METHOD_RATE etc. values
153
+ const epr =
154
+ 0 < metrics.count.path ? (metrics.count.entity / metrics.count.path).toFixed(3) : -1
155
+ const emr =
156
+ 0 < metrics.count.method ? (metrics.count.entity / metrics.count.method).toFixed(3) : -1
157
+
158
+ ctx.log.info({
159
+ point: 'metrics',
160
+ metrics,
161
+ note: `epr=${epr} emr=${emr} ` +
162
+ `(entity=${metrics.count.entity} ` +
163
+ `paths=${metrics.count.path} methods=${metrics.count.method})`
164
+ })
165
+
166
+ validateBaseBuide(ctx, baseguide)
167
+
168
+ const sw = (s: string) => ctx.opts.why?.show ? s : ''
169
+ const qs = (v: any) => JSON.stringify(v)
170
+ const qt = (v: any) => '(' + qs(v) + ')'
171
+
172
+ guideBlocks.push(` metrics: count: entity: ${metrics.count.entity}
173
+ metrics: count: path: ${metrics.count.path}
174
+ metrics: count: method: ${metrics.count.method}`)
175
+
176
+ // NOTE: items(...) sorts the iteration elements, so the generated model code
177
+ // is deterministic.
178
+
179
+ items(baseguide.entity).map(([entname, entity]: [string, GuideEntity]) => {
102
180
 
103
- items(baseguide.entity).map(([entityname, entity]: any[]) => {
104
181
  guideBlocks.push(`
105
- entity: ${entityname}: {` +
106
- (0 < entity.why_name.length ? ' # name:' + entity.why_name.join(';') : ''))
182
+ entity: ${entname}: {`
183
+ // sw(0 < entity.why_name.length ? ' # name:' + entity.why_name.join(';') : '')
184
+ )
185
+
186
+ // NOTE: items(...) sorts the paths
187
+ items(entity.path).map(([pathstr, path]: [string, GuidePath]) => {
188
+ debugpath(pathstr, null, 'BASE-GUIDE', entname, pathstr,
189
+ formatJSONIC(path, { hsepd: 0, $: true, color: true }))
190
+
191
+ guideBlocks.push(` path: ${qs(pathstr)}: {` +
192
+ sw(0 < path.why_path.length ?
193
+ ' # ent=' + entname + ';' +
194
+ (entity.orig !== entname && null != entity.orig ? 'orig=' + entity.orig + ';' : '') +
195
+ path.why_path.join(';') : ''))
196
+
197
+ if (!isempty(path.action)) {
198
+ items(path.action).map(([actname, actdesc]: [string, GuidePathAction]) => {
199
+ guideBlocks.push(` action: ${qs(actname)}: {}` +
200
+ sw(0 < actdesc.why_action.length ?
201
+ ' # ' + actdesc.why_action.join(';') : ''))
202
+ })
203
+ }
107
204
 
108
- items(entity.path).map(([pathname, path]: any[]) => {
109
- guideBlocks.push(` path: '${pathname}': op: {` +
110
- (0 < path.why_ent.length ? ' # ent:' + path.why_ent.join(';') : ''))
205
+ if (!isempty(path.rename?.param)) {
206
+ items(path.rename.param).map(([psrc, rp]: [string, GuideRenameParam]) => {
207
+ guideBlocks.push(` rename: param: ${qs(psrc)}: *${qs(rp.target)}` +
208
+ sw(0 < rp.why_rename.length ?
209
+ ' # ' + rp.why_rename.join(';') : ''))
210
+ })
211
+ }
111
212
 
112
- items(path.op).map(([opname, op]: any[]) => {
113
- guideBlocks.push(` '${opname}': method: ${op.method}` +
114
- (0 < op.why_op.length ? ' # ' + op.why_op : ''))
115
- if (op.transform?.reqform) {
213
+ items(path.op).map(([opname, op]: [string, GuidePathOp]) => {
214
+ guideBlocks.push(` op: ${opname}: method: *${op.method}` +
215
+ sw(0 < op.why_op.length ? ' # ' + op.why_op : ''))
216
+ if (null != op.transform.req) {
116
217
  guideBlocks.push(
117
- ` '${opname}': transform: reqform: ${JSON.stringify(op.transform.reqform)}`)
218
+ // ` op: ${opname}: transform: res: *${qt(op.transform.res)}|top`)
219
+ ` op: ${opname}: transform: res: *${qt(op.transform.res)}|top`)
220
+ }
221
+ if (null != op.transform.res) {
222
+ guideBlocks.push(
223
+ // ` op: ${opname}: transform: res: *${qt(op.transform.res)}|top`)
224
+ ` op: ${opname}: transform: res: *${qt(op.transform.res)}|top`)
118
225
  }
119
226
  })
120
227
 
@@ -128,6 +235,7 @@ async function buildBaseGuide(ctx: any) {
128
235
 
129
236
  const guideSrc = guideBlocks.join('\n')
130
237
 
238
+
131
239
  ctx.note.guide = { base: guideSrc }
132
240
 
133
241
  const baseGuideFileName =
@@ -152,121 +260,86 @@ async function buildBaseGuide(ctx: any) {
152
260
  }
153
261
 
154
262
 
155
- /*
156
- async function resolveGuide(ctx: any) {
157
- let baseguide: Record<string, any> = {}
158
- let override: Record<string, any> = ctx.model.main.api.guide
159
-
160
- if ('heuristic01' === ctx.opts.strategy) {
161
- baseguide = await heuristic01(ctx)
162
- }
163
- else {
164
- throw new Error('Unknown guide strategy: ' + ctx.opts.strategy)
165
- }
166
263
 
167
- // Override generated base guide with custom hints
168
- let guide = merge([{}, baseguide, override])
169
264
 
170
- // TODO: this is a hack!!!
171
- // Instead, update @voxgig/model, so that builders can request a reload of the entire
172
- // model. This allows builders to modify the model for later buidlers
173
- // during a single generation pass.
265
+ function validateBaseBuide(ctx: ApiDefContext, baseguide: any) {
266
+ const srcm: any = {}
174
267
 
268
+ // Each orig path.
269
+ each(ctx.def.paths, (pdef: any) => {
270
+ const pathStr = pdef.key$
175
271
 
176
- guide = cleanGuide(guide)
272
+ // Each orig method.
273
+ each(pdef, (mdef: any) => {
274
+ if (mdef.key$.match(/^get|post|put|patch|delete|head|options$/i)) {
275
+ let key = pathStr + ' ' + mdef.key$.toUpperCase()
276
+ let desc = (srcm[key] = (srcm[key] || { c: 0 }))
277
+ desc.c++
278
+ }
279
+ })
280
+ })
177
281
 
282
+ const genm: any = {}
178
283
 
179
- // TODO: FIX: sdk.jsonic should have final version of guide
180
- if (ctx.model.main?.api) {
181
- ctx.model.main.api.guide = guide
182
- }
183
- else {
184
- dlog('missing', 'ctx.model.main.api')
185
- }
284
+ // Each entity.
285
+ each(baseguide.entity, (entm: GuideEntity) => {
186
286
 
187
- const guideFile =
188
- Path.join(ctx.opts.folder,
189
- (null == ctx.opts.outprefix ? '' : ctx.opts.outprefix) + 'base-guide.jsonic')
287
+ if (isempty(entm.path)) {
288
+ ctx.warn({
289
+ note: `No paths defined for entity=${entm.name}`,
290
+ entm,
291
+ })
292
+ }
190
293
 
191
- const guideBlocks = [
192
- '# Guide',
193
- '',
194
- 'main: api: guide: {',
195
- ]
294
+ // Each path.
295
+ each(entm.path, (pathm: GuidePath, pathStr) => {
196
296
 
297
+ if (isempty(pathm.op)) {
298
+ ctx.warn({
299
+ note: `No operations defined for entity=${entm.name} path=${pathStr}`,
300
+ path: pathStr,
301
+ entm,
302
+ pathm,
303
+ })
304
+ }
197
305
 
198
- guideBlocks.push(...each(baseguide.entity, (entity, entityname) => {
199
- guideBlocks.push(`
200
- entity: ${entityname}: {` +
201
- (0 < entity.why_name.length ? ' # name:' + entity.why_name.join(';') : ''))
202
-
203
- items(entity.path).map((pathn) => {
204
- const [pathname, path] = pathn
205
- guideBlocks.push(` path: '${pathname}': op: {` +
206
- (0 < path.why_ent.length ? ' # ent:' + path.why_ent.join(';') : ''))
207
-
208
- items(path.op).map((opn) => {
209
- const [opname, op] = opn
210
- guideBlocks.push(` '${opname}': method: ${op.method}` +
211
- (0 < op.why_op.length ? ' # ' + op.why_op : ''))
212
- if (op.transform?.reqform) {
213
- guideBlocks.push(
214
- ` '${opname}': transform: reqform: ${JSON.stringify(op.transform.reqform)}`)
215
- }
306
+ // Each op.
307
+ each(pathm.op, (odef) => {
308
+ let key = pathStr + ' ' + odef.method
309
+ let desc = (genm[key] = (genm[key] || { c: 0 }))
310
+ desc.c++
216
311
  })
217
-
218
- guideBlocks.push(` }`)
219
312
  })
313
+ })
220
314
 
221
- guideBlocks.push(`}`)
222
- }))
223
-
224
- guideBlocks.push('}')
225
-
226
- const guideSrc = guideBlocks.join('\n')
227
-
228
- ctx.note.guide = { base: guideSrc }
229
-
230
- return () => {
231
- // Save base guide for reference
232
- File({ name: '../def/' + Path.basename(guideFile) }, () => Content(guideSrc))
233
- }
234
- }
235
-
315
+ const srcp = Object.keys(srcm).sort()
316
+ .reduce((a, k) => (a.push(k + ':c=' + srcm[k].c), a), [] as string[])
236
317
 
237
- function cleanGuide(guide: Record<string, any>): Record<string, any> {
238
- const clean: Record<string, any> = {
239
- control: guide.control,
240
- entity: {}
241
- }
318
+ const genp = Object.keys(genm).sort()
319
+ .reduce((a, k) => (a.push(k + ':c=' + genm[k].c), a), [] as string[])
242
320
 
243
- const exclude_entity = guide.exclude?.entity?.split(',') || []
244
- const include_entity = guide.include?.entity?.split(',') || []
321
+ // Check that all paths have been assigned to entities.
322
+ if (srcp.join(';') !== genp.join(';')) {
323
+ console.log(' ', 'SRC-PATH'.padEnd(60, ' '), 'GEN-PATH')
324
+ for (let i = 0, j = 0; i < srcp.length || j < genp.length; i++, j++) {
325
+ let srcps = srcp[i]
326
+ let genps = genp[j]
327
+ let prefix = ' '
328
+ if (srcps !== genps) {
329
+ prefix = ' *** '
245
330
 
246
- each(guide.entity, (entity: any, name: string) => {
247
- if (exclude_entity.includes(name)) {
248
- return
249
- }
250
- if (exclude_entity.includes('*')) {
251
- if (!include_entity.includes(name)) {
252
- return
331
+ if (srcps === genp[j + 1]) {
332
+ j++
333
+ }
334
+ else if (genps === srcp[i + 1]) {
335
+ i++
336
+ }
253
337
  }
338
+ console.log(prefix, srcps.padEnd(60, ' '), genps)
254
339
  }
255
-
256
- let ent: any = clean.entity[name] = clean.entity[name] = {
257
- name,
258
- why_name: entity.why_name || [],
259
- path: {}
260
- }
261
-
262
- each(entity.path, (path: any, pathname: string) => {
263
- ent.path[pathname] = path
264
- })
265
- })
266
-
267
- return clean
340
+ throw new Error('PATH MISMATCH')
341
+ }
268
342
  }
269
- */
270
343
 
271
344
 
272
345
  export {