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