@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
package/src/utility.ts CHANGED
@@ -1,15 +1,51 @@
1
1
 
2
2
  import Path from 'node:path'
3
3
 
4
- import { slice, merge, inject, clone, isnode, walk, transform, select } from '@voxgig/struct'
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: ' + relativizePath(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') && !word.endsWith('ss')) {
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 $LOWER(inj: any, val: any, ref: any, store: any) {
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,524 @@ 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
+ // Get value from object as string, and format as indicated.
784
+ // Example: nom({foo:'bar'},'Foo') -> 'Bar'
785
+ function nom(v: any, format: string): string {
786
+ let formatstr = 'string' == typeof format ? format : null
787
+ if (null == formatstr) {
788
+ return '__MISSING__'
789
+ }
790
+ const canon = canonize(formatstr)
791
+ let out = v?.[canon] ?? '__MISSING_' + formatstr + '__'
792
+ out =
793
+ /[A-Z][a-z]/.test(formatstr) ? camelify(out) :
794
+ /[A-Z][A-Z]/.test(formatstr) ? allcapify(out) :
795
+ /-/.test(formatstr) ? kebabify(out) : out
796
+
797
+ return out
798
+ }
799
+
800
+
801
+ function relativizePath(path: string): string {
802
+ const cwd = process.cwd()
803
+ if (path.startsWith(cwd)) {
804
+ return '.' + path.slice(cwd.length)
805
+ }
806
+ return path
807
+ }
808
+
809
+
810
+ function getModelPath(
811
+ model: any,
812
+ path: string,
813
+ flags?: { required?: boolean }
814
+ ): any {
815
+ const required = flags?.required ?? true
816
+
817
+ if (path === '') {
818
+ if (required) {
819
+ throw new Error('getModelPath: empty path provided')
820
+ }
821
+ return undefined
822
+ }
823
+
824
+ const parts = path.split('.')
825
+ const fullPath = path // Store the full path for error messages
826
+ let current = model
827
+ let validPath: string[] = []
828
+
829
+ for (let i = 0; i < parts.length; i++) {
830
+ const part = parts[i]
831
+
832
+ if (current == null) {
833
+ if (required) {
834
+ const validPathStr = validPath.length > 0 ? validPath.join('.') : '(root)'
835
+ throw new Error(
836
+ `getModelPath: path not found at '${fullPath}'.\n` +
837
+ `Valid path up to: '${validPathStr}'.\n` +
838
+ `Cannot access property '${part}' of ${current === null ? 'null' : 'undefined'}.`
839
+ )
840
+ }
841
+ return undefined
842
+ }
843
+
844
+ // Check if current is an object before using 'in' operator
845
+ if (typeof current !== 'object' || current === null) {
846
+ if (required) {
847
+ const validPathStr = validPath.length > 0 ? validPath.join('.') : '(root)'
848
+ throw new Error(
849
+ `getModelPath: path not found at '${fullPath}'.\n` +
850
+ `Valid path up to: '${validPathStr}'.\n` +
851
+ `Cannot access property '${part}' of ${typeof current}.`
852
+ )
853
+ }
854
+ return undefined
855
+ }
856
+
857
+ // Check if the key exists
858
+ if (!(part in current)) {
859
+ if (required) {
860
+ const validPathStr = validPath.length > 0 ? validPath.join('.') : '(root)'
861
+ const availableKeys = Array.isArray(current)
862
+ ? `array indices 0-${current.length - 1}`
863
+ : `[${Object.keys(current).join(', ')}]`
864
+
865
+ throw new Error(
866
+ `getModelPath: path not found at '${fullPath}'.\n` +
867
+ `Valid path up to: '${validPathStr}'.\n` +
868
+ `Property '${part}' does not exist.\n` +
869
+ `Available keys: ${availableKeys}`
870
+ )
871
+ }
872
+ return undefined
873
+ }
874
+
875
+ validPath.push(part)
876
+ current = current[part]
877
+ }
878
+
879
+ return current
880
+ }
881
+
882
+ export type {
883
+ PathMatch
884
+ }
340
885
 
341
886
  export {
887
+ nom,
342
888
  getdlog,
343
889
  loadFile,
344
890
  formatJsonSrc,
345
891
  depluralize,
346
892
  find,
347
893
  capture,
894
+ pathMatch,
895
+ makeWarner,
896
+ formatJSONIC,
897
+ validator,
898
+ canonize,
899
+ debugpath,
900
+ findPathsWithPrefix,
901
+ writeFileSyncWarn,
902
+ warnOnError,
903
+ relativizePath,
904
+ getModelPath,
905
+
348
906
  }
@@ -1,3 +0,0 @@
1
- import type { ApiDefOptions } from '../../types';
2
- declare function resolveApiEntity(apimodel: any, opts: ApiDefOptions): () => void;
3
- export { resolveApiEntity };
@@ -1 +0,0 @@
1
- {"version":3,"file":"apiEntity.js","sourceRoot":"","sources":["../../../src/builder/entity/apiEntity.ts"],"names":[],"mappings":"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"}
@@ -1,3 +0,0 @@
1
- import type { ApiDefOptions, ApiModel } from '../../types';
2
- declare function resolveDef(apimodel: ApiModel, opts: ApiDefOptions): () => void;
3
- export { resolveDef };
@@ -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"}