@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.
- package/dist/apidef.d.ts +5 -1
- package/dist/apidef.js +197 -112
- package/dist/apidef.js.map +1 -1
- package/dist/builder/entity/entity.d.ts +3 -0
- package/dist/builder/entity/{apiEntity.js → entity.js} +12 -9
- package/dist/builder/entity/entity.js.map +1 -0
- package/dist/builder/entity/info.d.ts +3 -0
- package/dist/builder/entity/info.js +22 -0
- package/dist/builder/entity/info.js.map +1 -0
- package/dist/builder/entity.js +7 -21
- package/dist/builder/entity.js.map +1 -1
- package/dist/builder/flow/flowHeuristic01.js +21 -11
- package/dist/builder/flow/flowHeuristic01.js.map +1 -1
- package/dist/builder/flow.d.ts +2 -1
- package/dist/builder/flow.js +29 -4
- package/dist/builder/flow.js.map +1 -1
- package/dist/def.d.ts +62 -0
- package/dist/def.js +4 -0
- package/dist/def.js.map +1 -0
- package/dist/desc.d.ts +89 -0
- package/dist/desc.js +4 -0
- package/dist/desc.js.map +1 -0
- package/dist/guide/guide.d.ts +2 -1
- package/dist/guide/guide.js +161 -30
- package/dist/guide/guide.js.map +1 -1
- package/dist/guide/heuristic01.d.ts +2 -1
- package/dist/guide/heuristic01.js +1120 -234
- package/dist/guide/heuristic01.js.map +1 -1
- package/dist/model.d.ts +55 -0
- package/dist/model.js +4 -0
- package/dist/model.js.map +1 -0
- package/dist/parse.d.ts +1 -2
- package/dist/parse.js +8 -47
- package/dist/parse.js.map +1 -1
- package/dist/transform/args.d.ts +3 -0
- package/dist/transform/args.js +58 -0
- package/dist/transform/args.js.map +1 -0
- package/dist/transform/clean.js +27 -3
- package/dist/transform/clean.js.map +1 -1
- package/dist/transform/entity.d.ts +11 -3
- package/dist/transform/entity.js +57 -41
- package/dist/transform/entity.js.map +1 -1
- package/dist/transform/field.d.ts +3 -3
- package/dist/transform/field.js +90 -65
- package/dist/transform/field.js.map +1 -1
- package/dist/transform/operation.d.ts +1 -1
- package/dist/transform/operation.js +94 -296
- package/dist/transform/operation.js.map +1 -1
- package/dist/transform/select.d.ts +3 -0
- package/dist/transform/select.js +44 -0
- package/dist/transform/select.js.map +1 -0
- package/dist/transform/top.d.ts +9 -0
- package/dist/transform/top.js +11 -2
- package/dist/transform/top.js.map +1 -1
- package/dist/transform.js +4 -0
- package/dist/transform.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +112 -19
- package/dist/types.js +4 -2
- package/dist/types.js.map +1 -1
- package/dist/utility.d.ts +30 -2
- package/dist/utility.js +381 -6
- package/dist/utility.js.map +1 -1
- package/model/apidef.jsonic +75 -1
- package/model/guide.jsonic +14 -44
- package/package.json +17 -14
- package/src/apidef.ts +264 -121
- package/src/builder/entity/{apiEntity.ts → entity.ts} +18 -11
- package/src/builder/entity/info.ts +53 -0
- package/src/builder/entity.ts +9 -35
- package/src/builder/flow/flowHeuristic01.ts +46 -12
- package/src/builder/flow.ts +39 -5
- package/src/def.ts +91 -0
- package/src/desc.ts +143 -0
- package/src/guide/guide.ts +207 -134
- package/src/guide/heuristic01.ts +1651 -272
- package/src/model.ts +98 -0
- package/src/parse.ts +5 -61
- package/src/schematron.ts.off +317 -0
- package/src/transform/args.ts +102 -0
- package/src/transform/clean.ts +43 -8
- package/src/transform/entity.ts +100 -51
- package/src/transform/field.ts +150 -71
- package/src/transform/operation.ts +118 -414
- package/src/transform/select.ts +90 -0
- package/src/transform/top.ts +76 -3
- package/src/transform.ts +4 -0
- package/src/types.ts +185 -5
- package/src/utility.ts +481 -9
- package/dist/builder/entity/apiEntity.d.ts +0 -3
- package/dist/builder/entity/apiEntity.js.map +0 -1
- package/dist/builder/entity/def.d.ts +0 -3
- package/dist/builder/entity/def.js +0 -19
- package/dist/builder/entity/def.js.map +0 -1
- package/src/builder/entity/def.ts +0 -44
- package/src/guide.ts.off +0 -136
package/src/utility.ts
CHANGED
|
@@ -1,15 +1,51 @@
|
|
|
1
1
|
|
|
2
2
|
import Path from 'node:path'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { snakify, camelify, kebabify, each } from 'jostraca'
|
|
5
|
+
|
|
6
|
+
import { decircular } from '@voxgig/util'
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
slice, merge, inject, clone, isnode, walk, transform, select
|
|
10
|
+
} from '@voxgig/struct'
|
|
5
11
|
|
|
6
12
|
|
|
7
13
|
import type {
|
|
8
14
|
FsUtil,
|
|
9
|
-
Log
|
|
15
|
+
Log,
|
|
16
|
+
Warner,
|
|
10
17
|
} from './types'
|
|
11
18
|
|
|
12
19
|
|
|
20
|
+
|
|
21
|
+
function makeWarner(spec: { point: string, log: Log }): Warner {
|
|
22
|
+
const { point, log } = spec
|
|
23
|
+
const history: ({ point: string, when: number } & Record<string, any>)[] = []
|
|
24
|
+
const warn = function warn(def: Record<string, any>) {
|
|
25
|
+
const warning = { point, when: Date.now(), ...def }
|
|
26
|
+
log.warn(warning)
|
|
27
|
+
history.push(warning)
|
|
28
|
+
}
|
|
29
|
+
warn.history = history
|
|
30
|
+
warn.point = point
|
|
31
|
+
return warn
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
function writeFileSyncWarn(warn: Warner, fs: any, path: string, text: string) {
|
|
36
|
+
try {
|
|
37
|
+
fs.writeFileSync(path, text)
|
|
38
|
+
}
|
|
39
|
+
catch (err: any) {
|
|
40
|
+
warn({
|
|
41
|
+
err,
|
|
42
|
+
note: 'Unable to save file: ' + path
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
13
49
|
function getdlog(
|
|
14
50
|
tagin?: string,
|
|
15
51
|
filepath?: string)
|
|
@@ -61,6 +97,7 @@ function depluralize(word: string): string {
|
|
|
61
97
|
|
|
62
98
|
// Common irregular plurals
|
|
63
99
|
const irregulars: Record<string, string> = {
|
|
100
|
+
'analytics': 'analytics',
|
|
64
101
|
'analyses': 'analysis',
|
|
65
102
|
'appendices': 'appendix',
|
|
66
103
|
'axes': 'axis',
|
|
@@ -68,7 +105,7 @@ function depluralize(word: string): string {
|
|
|
68
105
|
'courses': 'course',
|
|
69
106
|
'crises': 'crisis',
|
|
70
107
|
'criteria': 'criterion',
|
|
71
|
-
'data': 'datum',
|
|
108
|
+
// 'data': 'datum',
|
|
72
109
|
'diagnoses': 'diagnosis',
|
|
73
110
|
'feet': 'foot',
|
|
74
111
|
'furnace': 'furnaces',
|
|
@@ -82,6 +119,7 @@ function depluralize(word: string): string {
|
|
|
82
119
|
'mice': 'mouse',
|
|
83
120
|
'notice': 'notices',
|
|
84
121
|
'oases': 'oasis',
|
|
122
|
+
'releases': 'release',
|
|
85
123
|
'people': 'person',
|
|
86
124
|
'phenomena': 'phenomenon',
|
|
87
125
|
'practice': 'practices',
|
|
@@ -96,6 +134,12 @@ function depluralize(word: string): string {
|
|
|
96
134
|
return irregulars[word]
|
|
97
135
|
}
|
|
98
136
|
|
|
137
|
+
for (let ending in irregulars) {
|
|
138
|
+
if (word.endsWith(ending)) {
|
|
139
|
+
return word.replace(ending, irregulars[ending])
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
99
143
|
// Rules for regular plurals (applied in order)
|
|
100
144
|
|
|
101
145
|
if (word.endsWith('ies') && word.length > 3) {
|
|
@@ -136,7 +180,10 @@ function depluralize(word: string): string {
|
|
|
136
180
|
}
|
|
137
181
|
|
|
138
182
|
// -s -> remove -s (cats -> cat)
|
|
139
|
-
if (word.endsWith('s') &&
|
|
183
|
+
if (word.endsWith('s') &&
|
|
184
|
+
!word.endsWith('ss') &&
|
|
185
|
+
!word.endsWith('us')
|
|
186
|
+
) {
|
|
140
187
|
return word.slice(0, -1)
|
|
141
188
|
}
|
|
142
189
|
|
|
@@ -167,7 +214,8 @@ function capture(data: any, shape: any): Record<string, any> {
|
|
|
167
214
|
$APPEND,
|
|
168
215
|
$ANY,
|
|
169
216
|
$SELECT,
|
|
170
|
-
$LOWER,
|
|
217
|
+
$LOWER: $RECASE,
|
|
218
|
+
$UPPER: $RECASE,
|
|
171
219
|
},
|
|
172
220
|
errs,
|
|
173
221
|
meta
|
|
@@ -274,8 +322,6 @@ function $SELECT(inj: any, _val: any, _ref: any, store: any) {
|
|
|
274
322
|
.filter(n => isnode(n[1]))
|
|
275
323
|
.reduce((a, n) => (a[n[0]] = n[1], a), ({} as any))
|
|
276
324
|
|
|
277
|
-
// console.log('SELECT-FROM', dparents)
|
|
278
|
-
|
|
279
325
|
if (selector instanceof RegExp) {
|
|
280
326
|
selector = {
|
|
281
327
|
'$KEY': { '`$LIKE`': selector.toString() }
|
|
@@ -306,7 +352,7 @@ function $SELECT(inj: any, _val: any, _ref: any, store: any) {
|
|
|
306
352
|
}
|
|
307
353
|
|
|
308
354
|
|
|
309
|
-
function $
|
|
355
|
+
function $RECASE(inj: any, val: any, ref: any, store: any) {
|
|
310
356
|
if ('key:pre' === inj.mode) {
|
|
311
357
|
const dval = inj.parent[inj.key]
|
|
312
358
|
|
|
@@ -328,7 +374,7 @@ function $LOWER(inj: any, val: any, ref: any, store: any) {
|
|
|
328
374
|
let tval = ptval[gkey][dkey]
|
|
329
375
|
|
|
330
376
|
if ('string' === typeof tval) {
|
|
331
|
-
tval = tval.toLowerCase()
|
|
377
|
+
tval = '$UPPER' === ref ? tval.toUpperCase() : tval.toLowerCase()
|
|
332
378
|
}
|
|
333
379
|
|
|
334
380
|
inj.setval(tval, 2)
|
|
@@ -337,12 +383,438 @@ function $LOWER(inj: any, val: any, ref: any, store: any) {
|
|
|
337
383
|
|
|
338
384
|
|
|
339
385
|
|
|
386
|
+
type PathMatch = (string[] & { index: number, expr: string, path: string })
|
|
387
|
+
|
|
388
|
+
// A special-purpose regex-style matcher for url paths.
|
|
389
|
+
// t - text part
|
|
390
|
+
// p - param part
|
|
391
|
+
// / - part separator
|
|
392
|
+
// / at start - must match from start
|
|
393
|
+
// / at end - must match to end
|
|
394
|
+
// See utility.test.ts for examples
|
|
395
|
+
function pathMatch(path: string | string[], expr: string):
|
|
396
|
+
null | PathMatch {
|
|
397
|
+
|
|
398
|
+
if (null == path) {
|
|
399
|
+
return null
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const parts = (Array.isArray(path) ? path : path.split('/')).filter(p => '' !== p)
|
|
403
|
+
const res: any = []
|
|
404
|
+
res.index = -1
|
|
405
|
+
res.expr = expr
|
|
406
|
+
res.path = 'string' === typeof path ? path : '/' + path.join('/')
|
|
407
|
+
|
|
408
|
+
const plen = parts.length
|
|
409
|
+
const xlen = expr.length
|
|
410
|
+
|
|
411
|
+
let xI = 0, pI = 0, mI = -1
|
|
412
|
+
for (; pI <= parts.length; pI++) {
|
|
413
|
+
let p = parts[pI]
|
|
414
|
+
let x = expr[xI]
|
|
415
|
+
let isp = isParam(p)
|
|
416
|
+
|
|
417
|
+
if ('/' === x) {
|
|
418
|
+
if (0 === xI) {
|
|
419
|
+
if (0 === pI) {
|
|
420
|
+
mI = 0
|
|
421
|
+
pI--
|
|
422
|
+
xI++
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
break
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
else if (xI === xlen - 1) {
|
|
429
|
+
if (pI === plen) {
|
|
430
|
+
xI++
|
|
431
|
+
break
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
if (-1 < mI) {
|
|
435
|
+
// backtrack
|
|
436
|
+
pI = mI
|
|
437
|
+
mI = -1
|
|
438
|
+
}
|
|
439
|
+
xI = 0
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
else if (xI < xlen - 1) {
|
|
443
|
+
pI--
|
|
444
|
+
xI++
|
|
445
|
+
}
|
|
446
|
+
else {
|
|
447
|
+
xI = 0
|
|
448
|
+
break
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
else if ('t' === x && !isp) {
|
|
452
|
+
xI++
|
|
453
|
+
mI = mI < 0 ? pI : mI
|
|
454
|
+
}
|
|
455
|
+
else if ('p' === x && isp) {
|
|
456
|
+
xI++
|
|
457
|
+
mI = mI < 0 ? pI : mI
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
if (-1 < mI) {
|
|
461
|
+
// backtrack
|
|
462
|
+
pI = mI
|
|
463
|
+
mI = -1
|
|
464
|
+
}
|
|
465
|
+
xI = 0
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (xI === xlen) {
|
|
469
|
+
break
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (xI === xlen) {
|
|
474
|
+
res.index = mI
|
|
475
|
+
res.push(...parts.slice(mI, pI + 1))
|
|
476
|
+
return res
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return null
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
function isParam(partStr: string) {
|
|
484
|
+
return null != partStr && '{' === partStr[0] && '}' === partStr[partStr.length - 1]
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
function formatJSONIC(
|
|
489
|
+
val?: any,
|
|
490
|
+
opts?: {
|
|
491
|
+
hsepd?: number,
|
|
492
|
+
$?: boolean,
|
|
493
|
+
color?: boolean
|
|
494
|
+
maxlines?: number,
|
|
495
|
+
exclude?: string[],
|
|
496
|
+
}): string {
|
|
497
|
+
|
|
498
|
+
if (undefined === val) return ''
|
|
499
|
+
|
|
500
|
+
val = decircular(val)
|
|
501
|
+
|
|
502
|
+
const hsepd = opts?.hsepd ?? 1
|
|
503
|
+
const showd = !!opts?.$
|
|
504
|
+
const useColor = opts?.color ?? false
|
|
505
|
+
const maxlines = opts?.maxlines ?? Number.MAX_VALUE
|
|
506
|
+
const exclude = opts?.exclude ?? []
|
|
507
|
+
|
|
508
|
+
const space = ' '
|
|
509
|
+
const isBareKey = (k: string) => /^[A-Za-z_][_A-Za-z0-9]*$/.test(k)
|
|
510
|
+
const quoteKey = (k: string) => (isBareKey(k) ? k : JSON.stringify(k))
|
|
511
|
+
|
|
512
|
+
// ANSI color codes
|
|
513
|
+
const colors = {
|
|
514
|
+
reset: '\x1b[0m',
|
|
515
|
+
key: '\x1b[94m', // bright blue
|
|
516
|
+
string: '\x1b[92m', // bright green
|
|
517
|
+
number: '\x1b[93m', // bright yellow
|
|
518
|
+
boolean: '\x1b[96m', // bright cyan
|
|
519
|
+
null: '\x1b[90m', // bright gray
|
|
520
|
+
bracket: '\x1b[37m', // white
|
|
521
|
+
comment: '\x1b[90m', // bright gray
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const c = (color: keyof typeof colors, text: string) =>
|
|
525
|
+
useColor ? `${colors[color]}${text}${colors.reset}` : text
|
|
526
|
+
|
|
527
|
+
const renderPrimitive = (v: any): string => {
|
|
528
|
+
if (v === null) return c('null', 'null')
|
|
529
|
+
const t = typeof v
|
|
530
|
+
switch (t) {
|
|
531
|
+
case 'string': return c('string', !v.includes('\n') ? JSON.stringify(v) :
|
|
532
|
+
'`' + JSON.stringify(v)
|
|
533
|
+
.substring(1)
|
|
534
|
+
.replace(/\\n/g, '\n')
|
|
535
|
+
.replace(/\\"/g, ':')
|
|
536
|
+
.replace(/`/g, '\\`')
|
|
537
|
+
.replace(/"$/, '`'))
|
|
538
|
+
case 'number': return c('number', Number.isFinite(v) ? String(v) : 'null')
|
|
539
|
+
case 'boolean': return c('boolean', v ? 'true' : 'false')
|
|
540
|
+
case 'bigint': return c('string', JSON.stringify(v.toString()))
|
|
541
|
+
case 'symbol':
|
|
542
|
+
case 'function':
|
|
543
|
+
case 'undefined':
|
|
544
|
+
return c('null', 'null')
|
|
545
|
+
default: return JSON.stringify(v)
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const renderComment = (c: any): string | null => {
|
|
550
|
+
if (c == null) return null
|
|
551
|
+
if (Array.isArray(c) && c.every(x => typeof x === 'string')) return c.join('; ')
|
|
552
|
+
if (typeof c === 'string') return c
|
|
553
|
+
try { return JSON.stringify(c) } catch { return String(c) }
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
type ValueFrame = {
|
|
557
|
+
kind: 'value'
|
|
558
|
+
value: any
|
|
559
|
+
indentLevel: number
|
|
560
|
+
linePrefix: string
|
|
561
|
+
inlineComment?: string | null
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
type CloseFrame = {
|
|
565
|
+
kind: 'close'
|
|
566
|
+
token: '}' | ']'
|
|
567
|
+
indentLevel: number
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
let stack = new Array<ValueFrame | CloseFrame | undefined>(32)
|
|
571
|
+
let top = -1
|
|
572
|
+
|
|
573
|
+
// Seed root frame, capturing a possible top-level _COMMENT
|
|
574
|
+
let rootInline: string | null = null
|
|
575
|
+
if (val && typeof val === 'object') {
|
|
576
|
+
rootInline = renderComment((val as any)['_COMMENT'])
|
|
577
|
+
}
|
|
578
|
+
stack[++top] = {
|
|
579
|
+
kind: 'value', value: val, indentLevel: 0, linePrefix: '', inlineComment: rootInline
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const lines: string[] = []
|
|
583
|
+
|
|
584
|
+
while (top >= 0 && (lines.length < maxlines)) {
|
|
585
|
+
const frame = stack[top]!
|
|
586
|
+
|
|
587
|
+
stack[top] = undefined
|
|
588
|
+
top -= 1
|
|
589
|
+
|
|
590
|
+
if (frame.kind === 'close') {
|
|
591
|
+
const indent = space.repeat(frame.indentLevel)
|
|
592
|
+
const hsep = 0 < frame.indentLevel && frame.indentLevel <= hsepd
|
|
593
|
+
lines.push(`${indent}${c('bracket', frame.token)}${hsep ? '\n' : ''}`)
|
|
594
|
+
continue
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
let v = frame.value
|
|
598
|
+
while (v && typeof v === 'object' && typeof (v as any).toJSON === 'function') {
|
|
599
|
+
v = (v as any).toJSON()
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
const { indentLevel, linePrefix } = frame
|
|
603
|
+
const commentSuffix = frame.inlineComment ? ` ${c('comment', `# ${frame.inlineComment}`)}` : ''
|
|
604
|
+
|
|
605
|
+
if (v === null || typeof v !== 'object') {
|
|
606
|
+
lines.push(`${linePrefix}${renderPrimitive(v)}${commentSuffix}`)
|
|
607
|
+
continue
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if (Array.isArray(v)) {
|
|
611
|
+
const arr = v as any[]
|
|
612
|
+
if (arr.length === 0) {
|
|
613
|
+
lines.push(`${linePrefix}${c('bracket', '[')}${commentSuffix}`)
|
|
614
|
+
stack[++top] = { kind: 'close', token: ']', indentLevel }
|
|
615
|
+
continue
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// opening line
|
|
619
|
+
lines.push(`${linePrefix}${c('bracket', '[')}${commentSuffix}`)
|
|
620
|
+
stack[++top] = { kind: 'close', token: ']', indentLevel }
|
|
621
|
+
|
|
622
|
+
// children (reverse push)
|
|
623
|
+
const childPrefix = space.repeat(indentLevel + 1)
|
|
624
|
+
for (let i = arr.length - 1; i >= 0; i--) {
|
|
625
|
+
const idxComment = renderComment((v as any)[`${i}_COMMENT`])
|
|
626
|
+
stack[++top] = {
|
|
627
|
+
kind: 'value',
|
|
628
|
+
value: arr[i],
|
|
629
|
+
indentLevel: indentLevel + 1,
|
|
630
|
+
linePrefix: `${childPrefix}`,
|
|
631
|
+
inlineComment: idxComment
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
continue
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// Plain object
|
|
638
|
+
const obj = v as Record<string, any>
|
|
639
|
+
const keys = Object.keys(obj)
|
|
640
|
+
if (v instanceof Error) {
|
|
641
|
+
keys.unshift('name', 'message', 'stack')
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
const printableKeys = keys.filter(k => !k.endsWith('_COMMENT') &&
|
|
645
|
+
(showd || !k.endsWith('$')))
|
|
646
|
+
|
|
647
|
+
if (printableKeys.length === 0) {
|
|
648
|
+
lines.push(`${linePrefix}${c('bracket', '{')}${commentSuffix}`)
|
|
649
|
+
stack[++top] = { kind: 'close', token: '}', indentLevel }
|
|
650
|
+
continue
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// opening line
|
|
654
|
+
lines.push(`${linePrefix}${c('bracket', '{')}${commentSuffix}`)
|
|
655
|
+
stack[++top] = { kind: 'close', token: '}', indentLevel }
|
|
656
|
+
|
|
657
|
+
const nextIndentStr = space.repeat(indentLevel + 1)
|
|
658
|
+
for (let i = printableKeys.length - 1; i >= 0; i--) {
|
|
659
|
+
const k = printableKeys[i]
|
|
660
|
+
if (exclude.includes(k)) {
|
|
661
|
+
continue
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
const keyText = quoteKey(k)
|
|
665
|
+
const valForKey = obj[k]
|
|
666
|
+
const cmt = renderComment(obj[`${k}_COMMENT`])
|
|
667
|
+
|
|
668
|
+
stack[++top] = {
|
|
669
|
+
kind: 'value',
|
|
670
|
+
value: valForKey,
|
|
671
|
+
indentLevel: indentLevel + 1,
|
|
672
|
+
linePrefix: `${nextIndentStr}${c('key', keyText)}: `,
|
|
673
|
+
inlineComment: cmt
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
return lines.join('\n') + '\n'
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
const VALID_CANON: Record<string, string> = {
|
|
683
|
+
'string': '`$STRING`',
|
|
684
|
+
'number': '`$NUMBER`',
|
|
685
|
+
'integer': '`$INTEGER`',
|
|
686
|
+
'boolean': '`$BOOLEAN`',
|
|
687
|
+
'null': '`$NULL`',
|
|
688
|
+
'array': '`$ARRAY`',
|
|
689
|
+
'object': '`$OBJECT`',
|
|
690
|
+
'any': '`$ANY`',
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
function validator(torig: undefined | string | string[]): any {
|
|
695
|
+
if ('string' === typeof torig) {
|
|
696
|
+
const tstr = torig.toLowerCase().trim()
|
|
697
|
+
const canon = VALID_CANON[tstr] ?? 'Any'
|
|
698
|
+
return canon
|
|
699
|
+
}
|
|
700
|
+
else if (Array.isArray(torig)) {
|
|
701
|
+
return ['`$ONE`', torig.map((t: string) => validator(t))]
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
return '`$ANY`'
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
function canonize(s: string) {
|
|
709
|
+
return depluralize(snakify(s)).replace(/[^a-zA-Z_0-9]/g, '')
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
function warnOnError(where: string, warn: Warner, fn: Function, result?: any) {
|
|
714
|
+
try {
|
|
715
|
+
return fn()
|
|
716
|
+
}
|
|
717
|
+
catch (err: any) {
|
|
718
|
+
warn({
|
|
719
|
+
note: 'Error in ' + where + ': ' + err.message,
|
|
720
|
+
err
|
|
721
|
+
})
|
|
722
|
+
return result
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
function debugpath(pathStr: string, methodName: string | null | undefined, ...args: any[]): void {
|
|
729
|
+
const apipath = process.env.APIDEF_DEBUG_PATH
|
|
730
|
+
if (!apipath) return
|
|
731
|
+
|
|
732
|
+
const [targetPath, targetMethod] = apipath.split(':')
|
|
733
|
+
|
|
734
|
+
// Check if path matches
|
|
735
|
+
if (pathStr !== targetPath) return
|
|
736
|
+
|
|
737
|
+
// If a method is specified in apipath and we have a method name, check if it matches
|
|
738
|
+
if (targetMethod && methodName) {
|
|
739
|
+
if (methodName.toLowerCase() !== targetMethod.toLowerCase()) return
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
console.log(methodName || '', ...args)
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
function findPathsWithPrefix(
|
|
747
|
+
ctx: any,
|
|
748
|
+
pathStr: string,
|
|
749
|
+
opts?: { strict?: boolean, param?: boolean }
|
|
750
|
+
): number {
|
|
751
|
+
const strict = opts?.strict ?? false
|
|
752
|
+
const param = opts?.param ?? false
|
|
753
|
+
|
|
754
|
+
if (!param) {
|
|
755
|
+
pathStr = pathStr.replace(/\{[^}]+\}/g, '{}')
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
const matchingPaths = each(ctx.def.paths)
|
|
759
|
+
.filter((pathDef: any) => {
|
|
760
|
+
let path = pathDef.key$
|
|
761
|
+
if (!param) {
|
|
762
|
+
path = path.replace(/\{[^}]+\}/g, '{}')
|
|
763
|
+
}
|
|
764
|
+
if (strict) {
|
|
765
|
+
// Strict mode: path must start with prefix and have more segments
|
|
766
|
+
return path.startsWith(pathStr) && path.length > pathStr.length
|
|
767
|
+
} else {
|
|
768
|
+
// Non-strict mode: simple prefix match
|
|
769
|
+
return path.startsWith(pathStr)
|
|
770
|
+
}
|
|
771
|
+
})
|
|
772
|
+
|
|
773
|
+
return matchingPaths.length
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
// TODO: move to jostraca?
|
|
778
|
+
function allcapify(s?: string) {
|
|
779
|
+
return 'string' === typeof s ? snakify(s).toUpperCase() : ''
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
|
|
783
|
+
function nom(v: any, format: string): string {
|
|
784
|
+
let formatstr = 'string' == typeof format ? format : null
|
|
785
|
+
if (null == formatstr) {
|
|
786
|
+
return '__MISSING__'
|
|
787
|
+
}
|
|
788
|
+
const canon = canonize(formatstr)
|
|
789
|
+
let out = v?.[canon] ?? '__MISSING_' + formatstr + '__'
|
|
790
|
+
out =
|
|
791
|
+
/[A-Z][a-z]/.test(formatstr) ? camelify(out) :
|
|
792
|
+
/[A-Z][A-Z]/.test(formatstr) ? allcapify(out) :
|
|
793
|
+
/-/.test(formatstr) ? kebabify(out) : out
|
|
794
|
+
|
|
795
|
+
return out
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
export type {
|
|
799
|
+
PathMatch
|
|
800
|
+
}
|
|
340
801
|
|
|
341
802
|
export {
|
|
803
|
+
nom,
|
|
342
804
|
getdlog,
|
|
343
805
|
loadFile,
|
|
344
806
|
formatJsonSrc,
|
|
345
807
|
depluralize,
|
|
346
808
|
find,
|
|
347
809
|
capture,
|
|
810
|
+
pathMatch,
|
|
811
|
+
makeWarner,
|
|
812
|
+
formatJSONIC,
|
|
813
|
+
validator,
|
|
814
|
+
canonize,
|
|
815
|
+
debugpath,
|
|
816
|
+
findPathsWithPrefix,
|
|
817
|
+
writeFileSyncWarn,
|
|
818
|
+
warnOnError,
|
|
819
|
+
|
|
348
820
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"apiEntity.js","sourceRoot":"","sources":["../../../src/builder/entity/apiEntity.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/* Copyright (c) 2025 Voxgig, MIT License */
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.resolveDef = resolveDef;
|
|
5
|
-
const jostraca_1 = require("jostraca");
|
|
6
|
-
function resolveDef(apimodel, opts) {
|
|
7
|
-
const defFile = (null == opts.outprefix ? '' : opts.outprefix) + 'api-def.jsonic';
|
|
8
|
-
const modelDef = { main: { def: apimodel.main.def } };
|
|
9
|
-
let modelDefSrc = JSON.stringify(modelDef, null, 2);
|
|
10
|
-
modelDefSrc =
|
|
11
|
-
'# API Definition\n\n' +
|
|
12
|
-
modelDefSrc.substring(1, modelDefSrc.length - 1).replace(/\n /g, '\n');
|
|
13
|
-
return function defBuilder() {
|
|
14
|
-
(0, jostraca_1.Folder)({ name: 'api' }, () => {
|
|
15
|
-
(0, jostraca_1.File)({ name: defFile }, () => (0, jostraca_1.Content)(modelDefSrc));
|
|
16
|
-
});
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
//# sourceMappingURL=def.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"def.js","sourceRoot":"","sources":["../../../src/builder/entity/def.ts"],"names":[],"mappings":";AAAA,4CAA4C;;AA0C1C,gCAAU;AA/BZ,uCAIiB;AAGjB,SAAS,UAAU,CACjB,QAAkB,EAClB,IAAmB;IAEnB,MAAM,OAAO,GACX,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,gBAAgB,CAAA;IAEnE,MAAM,QAAQ,GAAG,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAA;IACrD,IAAI,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IAEnD,WAAW;QACT,sBAAsB;YACtB,WAAW,CAAC,SAAS,CAAC,CAAC,EAAE,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;IAEzE,OAAO,SAAS,UAAU;QACxB,IAAA,iBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE;YAC3B,IAAA,eAAI,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,CAAC,IAAA,kBAAO,EAAC,WAAW,CAAC,CAAC,CAAA;QACrD,CAAC,CAAC,CAAA;IACJ,CAAC,CAAA;AAEH,CAAC"}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/* Copyright (c) 2025 Voxgig, MIT License */
|
|
2
|
-
|
|
3
|
-
import Path from 'node:path'
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import type {
|
|
7
|
-
ApiDefOptions,
|
|
8
|
-
ApiModel,
|
|
9
|
-
} from '../../types'
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
File,
|
|
14
|
-
Folder,
|
|
15
|
-
Content
|
|
16
|
-
} from 'jostraca'
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
function resolveDef(
|
|
20
|
-
apimodel: ApiModel,
|
|
21
|
-
opts: ApiDefOptions,
|
|
22
|
-
) {
|
|
23
|
-
const defFile =
|
|
24
|
-
(null == opts.outprefix ? '' : opts.outprefix) + 'api-def.jsonic'
|
|
25
|
-
|
|
26
|
-
const modelDef = { main: { def: apimodel.main.def } }
|
|
27
|
-
let modelDefSrc = JSON.stringify(modelDef, null, 2)
|
|
28
|
-
|
|
29
|
-
modelDefSrc =
|
|
30
|
-
'# API Definition\n\n' +
|
|
31
|
-
modelDefSrc.substring(1, modelDefSrc.length - 1).replace(/\n /g, '\n')
|
|
32
|
-
|
|
33
|
-
return function defBuilder() {
|
|
34
|
-
Folder({ name: 'api' }, () => {
|
|
35
|
-
File({ name: defFile }, () => Content(modelDefSrc))
|
|
36
|
-
})
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
export {
|
|
43
|
-
resolveDef
|
|
44
|
-
}
|