@voxgig/apidef 1.9.0 → 2.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 +3 -29
- package/dist/apidef.js +65 -186
- package/dist/apidef.js.map +1 -1
- package/dist/builder/entity/apiEntity.d.ts +3 -0
- package/dist/builder/entity/apiEntity.js +51 -0
- package/dist/builder/entity/apiEntity.js.map +1 -0
- package/dist/builder/entity/def.d.ts +3 -0
- package/dist/builder/entity/def.js +19 -0
- package/dist/builder/entity/def.js.map +1 -0
- package/dist/builder/entity.d.ts +2 -0
- package/dist/builder/entity.js +30 -0
- package/dist/builder/entity.js.map +1 -0
- package/dist/builder/flow/flowHeuristic01.d.ts +2 -0
- package/dist/builder/flow/flowHeuristic01.js +153 -0
- package/dist/builder/flow/flowHeuristic01.js.map +1 -0
- package/dist/builder/flow.d.ts +2 -0
- package/dist/builder/flow.js +41 -0
- package/dist/builder/flow.js.map +1 -0
- package/dist/guide/heuristic01.d.ts +2 -0
- package/dist/guide/heuristic01.js +119 -0
- package/dist/guide/heuristic01.js.map +1 -0
- package/dist/guide.d.ts +2 -0
- package/dist/guide.js +60 -0
- package/dist/guide.js.map +1 -0
- package/dist/parse.d.ts +1 -1
- package/dist/parse.js +5 -4
- package/dist/parse.js.map +1 -1
- package/dist/resolver.d.ts +2 -0
- package/dist/resolver.js +62 -0
- package/dist/resolver.js.map +1 -0
- package/dist/transform/entity.js +25 -4
- package/dist/transform/entity.js.map +1 -1
- package/dist/transform/field.js +4 -84
- package/dist/transform/field.js.map +1 -1
- package/dist/transform/operation.d.ts +2 -2
- package/dist/transform/operation.js +60 -30
- package/dist/transform/operation.js.map +1 -1
- package/dist/transform/top.d.ts +2 -2
- package/dist/transform/top.js +5 -4
- package/dist/transform/top.js.map +1 -1
- package/dist/transform.d.ts +1 -1
- package/dist/transform.js +20 -10
- package/dist/transform.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +691 -0
- package/dist/types.js +39 -0
- package/dist/types.js.map +1 -0
- package/dist/utility.d.ts +5 -0
- package/dist/utility.js +84 -0
- package/dist/utility.js.map +1 -0
- package/model/apidef.jsonic +28 -24
- package/package.json +9 -7
- package/src/apidef.ts +94 -271
- package/src/builder/entity/apiEntity.ts +88 -0
- package/src/builder/entity/def.ts +44 -0
- package/src/builder/entity.ts +54 -0
- package/src/builder/flow/flowHeuristic01.ts +200 -0
- package/src/builder/flow.ts +61 -0
- package/src/guide/heuristic01.ts +178 -0
- package/src/guide.ts +87 -0
- package/src/parse.ts +6 -4
- package/src/resolver.ts +91 -0
- package/src/transform/entity.ts +36 -10
- package/src/transform/field.ts +9 -92
- package/src/transform/operation.ts +112 -46
- package/src/transform/top.ts +11 -9
- package/src/transform.ts +22 -11
- package/src/types.ts +89 -0
- package/src/utility.ts +161 -0
- package/dist/transform/manual.d.ts +0 -3
- package/dist/transform/manual.js +0 -12
- package/dist/transform/manual.js.map +0 -1
- package/src/transform/manual.ts +0 -29
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA,4CAA4C;;;AAK5C,+BAAsC;AAmBtC,MAAM,UAAU,GAAG,IAAA,WAAI,EAAC;IACtB,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,MAAM;IACX,IAAI,EAAE;QACJ,GAAG,EAAE,EAAE;QACP,GAAG,EAAE,EAAE;QACP,GAAG,EAAE;YACH,KAAK,EAAE,EAAE;YACT,MAAM,EAAE,EAAE;SACX;KACF;CACF,CAAC,CAAA;AACF,MAAM,cAAc,GAAG,IAAA,WAAI,EAAC,IAAA,WAAI,EAAC,UAAU,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;AAuC9D,wCAAc;AAlChB,MAAM,UAAU,GAAG,IAAA,WAAI,EAAC;IACtB,IAAI,EAAE;QACJ,IAAI,EAAE,EAAE;QACR,IAAI,EAAE,EAAE;QACR,KAAK,EAAE,EAAE;QACT,GAAG,EAAE,EAAE;QACP,GAAG,EAAE,EAAE;QACP,OAAO,EAAE,EAAE;QACX,GAAG,EAAE,EAAE;QACP,EAAE,EAAE,IAAA,UAAG,GAAE;QACT,KAAK,EAAE;YACL,GAAG,EAAE,IAAI;YACT,GAAG,EAAE,IAAI;YACT,GAAG,EAAE,IAAI;SACV;KACF;CACF,CAAC,CAAA;AACF,MAAM,cAAc,GAAG,IAAA,WAAI,EAAC,IAAA,WAAI,EAAC,UAAU,CAAC,CAAC,CAAA;AAkB3C,wCAAc"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { FsUtil, Log } from './types';
|
|
2
|
+
declare function loadFile(path: string, what: string, fs: FsUtil, log: Log): string;
|
|
3
|
+
declare function formatJsonSrc(jsonsrc: string): string;
|
|
4
|
+
declare function depluralize(word: string): string;
|
|
5
|
+
export { loadFile, formatJsonSrc, depluralize, };
|
package/dist/utility.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadFile = loadFile;
|
|
4
|
+
exports.formatJsonSrc = formatJsonSrc;
|
|
5
|
+
exports.depluralize = depluralize;
|
|
6
|
+
function loadFile(path, what, fs, log) {
|
|
7
|
+
try {
|
|
8
|
+
const source = fs.readFileSync(path, 'utf8');
|
|
9
|
+
return source;
|
|
10
|
+
}
|
|
11
|
+
catch (err) {
|
|
12
|
+
log.error({ load: 'fail', what, path, err });
|
|
13
|
+
throw err;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function formatJsonSrc(jsonsrc) {
|
|
17
|
+
return jsonsrc
|
|
18
|
+
.replace(/"([a-zA-Z_][a-zA-Z_0-9]*)": /g, '$1: ')
|
|
19
|
+
.replace(/},/g, '}\n')
|
|
20
|
+
// .replace(/([a-zA-Z_][a-zA-Z_0-9]*)_COMMENT:/g, '# $1')
|
|
21
|
+
.replace(/\n(\s*)([a-zA-Z_][a-zA-Z_0-9]*)_COMMENT:\s*"(.*)",/g, '\n\n$1# $2 $3');
|
|
22
|
+
}
|
|
23
|
+
function depluralize(word) {
|
|
24
|
+
if (!word || word.length === 0) {
|
|
25
|
+
return word;
|
|
26
|
+
}
|
|
27
|
+
// Common irregular plurals
|
|
28
|
+
const irregulars = {
|
|
29
|
+
'children': 'child',
|
|
30
|
+
'men': 'man',
|
|
31
|
+
'women': 'woman',
|
|
32
|
+
'teeth': 'tooth',
|
|
33
|
+
'feet': 'foot',
|
|
34
|
+
'geese': 'goose',
|
|
35
|
+
'mice': 'mouse',
|
|
36
|
+
'people': 'person',
|
|
37
|
+
'data': 'datum',
|
|
38
|
+
'criteria': 'criterion',
|
|
39
|
+
'phenomena': 'phenomenon',
|
|
40
|
+
'indices': 'index',
|
|
41
|
+
'matrices': 'matrix',
|
|
42
|
+
'vertices': 'vertex',
|
|
43
|
+
'analyses': 'analysis',
|
|
44
|
+
'axes': 'axis',
|
|
45
|
+
'crises': 'crisis',
|
|
46
|
+
'diagnoses': 'diagnosis',
|
|
47
|
+
'oases': 'oasis',
|
|
48
|
+
'theses': 'thesis',
|
|
49
|
+
'appendices': 'appendix'
|
|
50
|
+
};
|
|
51
|
+
if (irregulars[word]) {
|
|
52
|
+
return irregulars[word];
|
|
53
|
+
}
|
|
54
|
+
// Rules for regular plurals (applied in order)
|
|
55
|
+
// -ies -> -y (cities -> city)
|
|
56
|
+
if (word.endsWith('ies') && word.length > 3) {
|
|
57
|
+
return word.slice(0, -3) + 'y';
|
|
58
|
+
}
|
|
59
|
+
// -ves -> -f or -fe (wolves -> wolf, knives -> knife)
|
|
60
|
+
if (word.endsWith('ves')) {
|
|
61
|
+
const stem = word.slice(0, -3);
|
|
62
|
+
// Check if it should be -fe (like knife, wife, life)
|
|
63
|
+
if (['kni', 'wi', 'li'].includes(stem)) {
|
|
64
|
+
return stem + 'fe';
|
|
65
|
+
}
|
|
66
|
+
return stem + 'f';
|
|
67
|
+
}
|
|
68
|
+
// -oes -> -o (potatoes -> potato)
|
|
69
|
+
if (word.endsWith('oes')) {
|
|
70
|
+
return word.slice(0, -2);
|
|
71
|
+
}
|
|
72
|
+
// -ses, -xes, -zes, -shes, -ches -> remove -es (boxes -> box)
|
|
73
|
+
if (word.endsWith('ses') || word.endsWith('xes') || word.endsWith('zes') ||
|
|
74
|
+
word.endsWith('shes') || word.endsWith('ches')) {
|
|
75
|
+
return word.slice(0, -2);
|
|
76
|
+
}
|
|
77
|
+
// -s -> remove -s (cats -> cat)
|
|
78
|
+
if (word.endsWith('s') && !word.endsWith('ss')) {
|
|
79
|
+
return word.slice(0, -1);
|
|
80
|
+
}
|
|
81
|
+
// If none of the rules apply, return as is
|
|
82
|
+
return word;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=utility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utility.js","sourceRoot":"","sources":["../src/utility.ts"],"names":[],"mappings":"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"}
|
package/model/apidef.jsonic
CHANGED
|
@@ -1,33 +1,36 @@
|
|
|
1
1
|
|
|
2
|
-
main: api: guide: control:
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
main: api: guide: control: {
|
|
3
|
+
|
|
4
|
+
transform: openapi: {
|
|
5
|
+
order: *`
|
|
6
|
+
top,
|
|
7
|
+
entity,
|
|
8
|
+
operation,
|
|
9
|
+
field,
|
|
10
|
+
` | string,
|
|
11
|
+
|
|
12
|
+
element: {
|
|
13
|
+
top: {}
|
|
14
|
+
entity: {}
|
|
15
|
+
operation: {}
|
|
16
|
+
field: {}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
8
19
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
` | string
|
|
16
|
-
}
|
|
20
|
+
builder: standard: {
|
|
21
|
+
order: *`
|
|
22
|
+
entity,
|
|
23
|
+
flow,
|
|
24
|
+
` | string,
|
|
17
25
|
|
|
26
|
+
element: {
|
|
27
|
+
entity: {}
|
|
28
|
+
flow: {}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
18
31
|
|
|
19
|
-
main: api: guide: transform: &: {
|
|
20
|
-
name: .$KEY
|
|
21
|
-
load: string
|
|
22
32
|
}
|
|
23
33
|
|
|
24
|
-
main: api: guide: transform: {
|
|
25
|
-
top: {}
|
|
26
|
-
entity: {}
|
|
27
|
-
operation: {}
|
|
28
|
-
field: {}
|
|
29
|
-
manual: {}
|
|
30
|
-
}
|
|
31
34
|
|
|
32
35
|
|
|
33
36
|
main: api: guide: entity: &: {
|
|
@@ -46,3 +49,4 @@ main: api: guide: entity: &: {
|
|
|
46
49
|
|
|
47
50
|
|
|
48
51
|
|
|
52
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voxgig/apidef",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"main": "dist/apidef.js",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"types": "dist/apidef.d.ts",
|
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
"voxgig-apidef": "bin/voxgig-apidef"
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|
|
22
|
-
"test": "node --enable-source-maps --test dist-test",
|
|
23
|
-
"
|
|
24
|
-
"test-some": "node --enable-source-maps --test-name-pattern=\"$npm_config_pattern\" --test dist-test",
|
|
22
|
+
"test": "node --enable-source-maps --test \"dist-test/**/*.test.js\"",
|
|
23
|
+
"test-some": "node --enable-source-maps --test-name-pattern=\"$npm_config_pattern\" --test \"dist-test/**/*.test.js\"",
|
|
25
24
|
"watch": "tsc --build src test -w",
|
|
26
25
|
"build": "tsc --build src test",
|
|
27
26
|
"clean": "rm -rf dist dist-test node_modules yarn.lock package-lock.json",
|
|
28
27
|
"reset": "npm run clean && npm i && npm run build && npm test",
|
|
28
|
+
"postinstall": "patch-package",
|
|
29
29
|
"repo-tag": "REPO_VERSION=`node -e \"console.log(require('./package').version)\"` && echo TAG: v$REPO_VERSION && git commit -a -m v$REPO_VERSION && git push && git tag v$REPO_VERSION && git push --tags;",
|
|
30
30
|
"repo-publish": "npm run clean && npm i && npm run repo-publish-quick",
|
|
31
31
|
"repo-publish-quick": "npm run build && npm run test && npm run repo-tag && npm publish --registry https://registry.npmjs.org --access=public"
|
|
@@ -41,19 +41,21 @@
|
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@hapi/code": "^9.0.3",
|
|
43
43
|
"@types/js-yaml": "^4.0.9",
|
|
44
|
-
"@types/node": "
|
|
44
|
+
"@types/node": "24.0.12",
|
|
45
45
|
"aontu": "^0.28.0",
|
|
46
|
-
"esbuild": "^0.25.
|
|
46
|
+
"esbuild": "^0.25.6",
|
|
47
47
|
"json-schema-to-ts": "^3.1.1",
|
|
48
48
|
"memfs": "^4.17.2",
|
|
49
|
+
"patch-package": "^8.0.0",
|
|
49
50
|
"typescript": "^5.8.3"
|
|
50
51
|
},
|
|
51
52
|
"dependencies": {
|
|
52
53
|
"@redocly/openapi-core": "^1.34.3",
|
|
54
|
+
"@voxgig/struct": "^0.0.4",
|
|
53
55
|
"@voxgig/util": "^0.0.10",
|
|
54
56
|
"chokidar": "^4.0.3",
|
|
55
57
|
"gubu": "^9.0.0",
|
|
56
|
-
"jostraca": "^0.
|
|
58
|
+
"jostraca": "^0.20.0",
|
|
57
59
|
"pino": "^9.7.0",
|
|
58
60
|
"pino-pretty": "^13.0.0",
|
|
59
61
|
"sonic-boom": "^4.2.0"
|
package/src/apidef.ts
CHANGED
|
@@ -1,85 +1,70 @@
|
|
|
1
|
-
/* Copyright (c) 2024 Voxgig, MIT License */
|
|
1
|
+
/* Copyright (c) 2024-2025 Voxgig, MIT License */
|
|
2
2
|
|
|
3
3
|
import * as Fs from 'node:fs'
|
|
4
4
|
import Path from 'node:path'
|
|
5
5
|
import { inspect } from 'node:util'
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
|
|
7
|
+
import { Jostraca, Project, names } from 'jostraca'
|
|
8
|
+
|
|
9
|
+
import { prettyPino } from '@voxgig/util'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
import type {
|
|
13
|
+
ApiDefOptions,
|
|
14
|
+
Model,
|
|
15
|
+
Build,
|
|
16
|
+
ApiModel,
|
|
17
|
+
} from './types'
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
OpenModelShape,
|
|
21
|
+
OpenBuildShape,
|
|
22
|
+
} from './types'
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
resolveGuide,
|
|
27
|
+
} from './guide'
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
import {
|
|
31
|
+
parse
|
|
32
|
+
} from './parse'
|
|
11
33
|
|
|
12
34
|
|
|
13
35
|
import {
|
|
14
|
-
resolveTransforms,
|
|
15
|
-
processTransforms,
|
|
16
36
|
fixName,
|
|
17
37
|
} from './transform'
|
|
18
38
|
|
|
19
39
|
|
|
20
|
-
type ApiDefOptions = {
|
|
21
|
-
def?: string
|
|
22
|
-
fs?: any
|
|
23
|
-
pino?: ReturnType<typeof Pino>
|
|
24
|
-
debug?: boolean | string
|
|
25
|
-
folder?: string
|
|
26
|
-
meta?: Record<string, any>
|
|
27
|
-
outprefix?: string
|
|
28
|
-
}
|
|
29
|
-
|
|
30
40
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
entity: Record<string, any>
|
|
35
|
-
}
|
|
36
|
-
def: Record<string, any>
|
|
37
|
-
}
|
|
38
|
-
}
|
|
41
|
+
import {
|
|
42
|
+
resolveElements
|
|
43
|
+
} from './resolver'
|
|
39
44
|
|
|
45
|
+
import {
|
|
46
|
+
loadFile,
|
|
47
|
+
} from './utility'
|
|
40
48
|
|
|
41
|
-
const ModelShape = Gubu({
|
|
42
|
-
def: String,
|
|
43
|
-
main: {
|
|
44
|
-
sdk: {},
|
|
45
|
-
def: {},
|
|
46
|
-
api: {},
|
|
47
|
-
}
|
|
48
|
-
})
|
|
49
|
-
const OpenModelShape = Gubu(Open(ModelShape))
|
|
50
|
-
|
|
51
|
-
type Model = ReturnType<typeof ModelShape>
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const BuildShape = Gubu({
|
|
55
|
-
spec: {
|
|
56
|
-
base: '',
|
|
57
|
-
path: '',
|
|
58
|
-
debug: '',
|
|
59
|
-
use: {},
|
|
60
|
-
res: [],
|
|
61
|
-
require: '',
|
|
62
|
-
log: {},
|
|
63
|
-
fs: Any(),
|
|
64
|
-
watch: {
|
|
65
|
-
mod: true,
|
|
66
|
-
add: true,
|
|
67
|
-
rem: true,
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
})
|
|
71
|
-
const OpenBuildShape = Gubu(Open(BuildShape))
|
|
72
49
|
|
|
73
|
-
|
|
50
|
+
import { topTransform } from './transform/top'
|
|
51
|
+
import { entityTransform } from './transform/entity'
|
|
52
|
+
import { operationTransform } from './transform/operation'
|
|
53
|
+
import { fieldTransform } from './transform/field'
|
|
74
54
|
|
|
55
|
+
import { makeEntityBuilder } from './builder/entity'
|
|
56
|
+
import { makeFlowBuilder } from './builder/flow'
|
|
75
57
|
|
|
76
58
|
|
|
77
59
|
function ApiDef(opts: ApiDefOptions) {
|
|
60
|
+
|
|
61
|
+
// TODO: gubu opts!
|
|
78
62
|
const fs = opts.fs || Fs
|
|
79
63
|
const pino = prettyPino('apidef', opts)
|
|
80
|
-
|
|
81
64
|
const log = pino.child({ cmp: 'apidef' })
|
|
82
65
|
|
|
66
|
+
opts.strategy = opts.strategy || 'heuristic01'
|
|
67
|
+
|
|
83
68
|
|
|
84
69
|
async function generate(spec: any) {
|
|
85
70
|
const start = Date.now()
|
|
@@ -87,6 +72,17 @@ function ApiDef(opts: ApiDefOptions) {
|
|
|
87
72
|
const model: Model = OpenModelShape(spec.model)
|
|
88
73
|
const build: Build = OpenBuildShape(spec.build)
|
|
89
74
|
|
|
75
|
+
names(model, model.name)
|
|
76
|
+
|
|
77
|
+
const apimodel: ApiModel = {
|
|
78
|
+
main: {
|
|
79
|
+
api: {
|
|
80
|
+
entity: {}
|
|
81
|
+
},
|
|
82
|
+
def: {},
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
|
|
90
86
|
const buildspec = build.spec
|
|
91
87
|
|
|
92
88
|
let defpath = model.def
|
|
@@ -101,76 +97,63 @@ function ApiDef(opts: ApiDefOptions) {
|
|
|
101
97
|
|
|
102
98
|
// TODO: Validate spec
|
|
103
99
|
const ctx = {
|
|
100
|
+
fs,
|
|
104
101
|
log,
|
|
105
102
|
spec,
|
|
106
103
|
opts,
|
|
107
104
|
util: { fixName },
|
|
108
105
|
defpath: Path.dirname(defpath),
|
|
109
106
|
model,
|
|
107
|
+
apimodel,
|
|
108
|
+
def: undefined
|
|
110
109
|
}
|
|
111
110
|
|
|
112
|
-
const
|
|
111
|
+
const defsrc = loadFile(defpath, 'def', fs, log)
|
|
113
112
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
note: log.levelVal <= 20 ? inspect(transformSpec) : ''
|
|
117
|
-
})
|
|
113
|
+
const def = await parse('OpenAPI', defsrc, { file: defpath })
|
|
114
|
+
ctx.def = def
|
|
118
115
|
|
|
116
|
+
const guideBuilder = await resolveGuide(ctx)
|
|
119
117
|
|
|
120
|
-
let source
|
|
121
|
-
try {
|
|
122
|
-
source = fs.readFileSync(defpath, 'utf8')
|
|
123
|
-
}
|
|
124
|
-
catch (err: any) {
|
|
125
|
-
log.error({ read: 'fail', what: 'def', file: defpath, err })
|
|
126
|
-
throw err
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const config = await createConfig({})
|
|
131
|
-
let bundle
|
|
132
|
-
|
|
133
|
-
try {
|
|
134
|
-
bundle = await bundleFromString({
|
|
135
|
-
source,
|
|
136
|
-
config,
|
|
137
|
-
dereference: true,
|
|
138
|
-
})
|
|
139
|
-
}
|
|
140
|
-
catch (err: any) {
|
|
141
|
-
log.error({ parse: 'fail', what: 'openapi', file: defpath, err })
|
|
142
|
-
throw err
|
|
143
|
-
}
|
|
144
118
|
|
|
119
|
+
// const transformSpec = await resolveTransforms(ctx)
|
|
120
|
+
const transforms = await resolveElements(ctx, 'transform', 'openapi', {
|
|
121
|
+
top: topTransform,
|
|
122
|
+
entity: entityTransform,
|
|
123
|
+
operation: operationTransform,
|
|
124
|
+
field: fieldTransform,
|
|
125
|
+
})
|
|
145
126
|
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
},
|
|
151
|
-
def: {},
|
|
152
|
-
},
|
|
153
|
-
}
|
|
127
|
+
const builders = await resolveElements(ctx, 'builder', 'standard', {
|
|
128
|
+
entity: makeEntityBuilder,
|
|
129
|
+
flow: makeFlowBuilder,
|
|
130
|
+
})
|
|
154
131
|
|
|
155
|
-
const def = bundle.bundle.parsed
|
|
156
|
-
const processResult = await processTransforms(ctx, transformSpec, apimodel, def)
|
|
157
132
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
})
|
|
133
|
+
const jostraca = Jostraca({
|
|
134
|
+
now: spec.now,
|
|
135
|
+
fs: () => fs,
|
|
136
|
+
log,
|
|
137
|
+
})
|
|
164
138
|
|
|
165
|
-
|
|
166
|
-
}
|
|
139
|
+
const jmodel = {}
|
|
167
140
|
|
|
141
|
+
const root = () => Project({ folder: '.' }, async () => {
|
|
142
|
+
guideBuilder()
|
|
143
|
+
// entityBuilder()
|
|
144
|
+
// flowBuilder()
|
|
168
145
|
|
|
169
|
-
|
|
146
|
+
for (let builder of builders) {
|
|
147
|
+
builder()
|
|
148
|
+
}
|
|
149
|
+
})
|
|
170
150
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
151
|
+
const jres = await jostraca.generate({
|
|
152
|
+
// folder: Path.dirname(opts.folder as string),
|
|
153
|
+
folder: opts.folder,
|
|
154
|
+
model: jmodel,
|
|
155
|
+
existing: { txt: { merge: true } }
|
|
156
|
+
}, root)
|
|
174
157
|
|
|
175
158
|
log.info({ point: 'generate-end', note: 'success', break: true })
|
|
176
159
|
|
|
@@ -181,154 +164,20 @@ function ApiDef(opts: ApiDefOptions) {
|
|
|
181
164
|
}
|
|
182
165
|
}
|
|
183
166
|
|
|
184
|
-
|
|
185
|
-
function buildModel_api(apimodel: ApiModel, modelPath: string) {
|
|
186
|
-
const modelapi = { main: { api: apimodel.main.api } }
|
|
187
|
-
let modelSrc = JSON.stringify(modelapi, null, 2)
|
|
188
|
-
|
|
189
|
-
modelSrc =
|
|
190
|
-
'# GENERATED FILE - DO NOT EDIT\n\n' +
|
|
191
|
-
modelSrc.substring(1, modelSrc.length - 1).replace(/\n /g, '\n')
|
|
192
|
-
|
|
193
|
-
writeChanged('api-model', modelPath, modelSrc)
|
|
194
|
-
return modelPath
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
function buildModel_def(apimodel: ApiModel, modelPath: string) {
|
|
199
|
-
const modelBasePath = Path.dirname(modelPath)
|
|
200
|
-
const defFilePath = Path.join(modelBasePath,
|
|
201
|
-
(null == opts.outprefix ? '' : opts.outprefix) + 'def-generated.jsonic')
|
|
202
|
-
|
|
203
|
-
const modelDef = { main: { def: apimodel.main.def } }
|
|
204
|
-
let modelDefSrc = JSON.stringify(modelDef, null, 2)
|
|
205
|
-
|
|
206
|
-
modelDefSrc =
|
|
207
|
-
'# GENERATED FILE - DO NOT EDIT\n\n' +
|
|
208
|
-
modelDefSrc.substring(1, modelDefSrc.length - 1).replace(/\n /g, '\n')
|
|
209
|
-
|
|
210
|
-
writeChanged('def-model', defFilePath, modelDefSrc)
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
function buildModel_entity(apimodel: ApiModel, modelPath: string) {
|
|
215
|
-
const modelBasePath = Path.dirname(modelPath)
|
|
216
|
-
|
|
217
|
-
const entityIncludes: string[] = []
|
|
218
|
-
|
|
219
|
-
each(apimodel.main.api.entity, ((entity: any) => {
|
|
220
|
-
entityIncludes.push(entity.name)
|
|
221
|
-
|
|
222
|
-
// HEURISTIC: id may be name_id or nameId
|
|
223
|
-
const fieldAliases =
|
|
224
|
-
each(entity.op, (op: any) =>
|
|
225
|
-
each(op.param))
|
|
226
|
-
.flat()
|
|
227
|
-
.reduce((a: any, p: any) =>
|
|
228
|
-
|
|
229
|
-
(entity.field[p.keys] ? null :
|
|
230
|
-
(p.key$.toLowerCase().includes(entity.name) ?
|
|
231
|
-
(a[p.key$] = 'id', a.id = p.key$) :
|
|
232
|
-
null)
|
|
233
|
-
|
|
234
|
-
, a), {})
|
|
235
|
-
|
|
236
|
-
const fieldAliasesSrc =
|
|
237
|
-
JSON.stringify(fieldAliases, null, 2)
|
|
238
|
-
.replace(/\n/g, '\n ')
|
|
239
|
-
|
|
240
|
-
const entityFileSrc = `
|
|
241
|
-
# Entity ${entity.name}
|
|
242
|
-
|
|
243
|
-
main: sdk: entity: ${entity.name}: {
|
|
244
|
-
alias: field: ${fieldAliasesSrc}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
`
|
|
248
|
-
const entityFilePath = Path.join(modelBasePath, 'entity',
|
|
249
|
-
(null == opts.outprefix ? '' : opts.outprefix) + entity.name + '.jsonic')
|
|
250
|
-
|
|
251
|
-
fs.mkdirSync(Path.dirname(entityFilePath), { recursive: true })
|
|
252
|
-
|
|
253
|
-
// TODO: diff merge
|
|
254
|
-
writeChanged('entity-model', entityFilePath, entityFileSrc, { update: false })
|
|
255
|
-
}))
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
modifyModel(
|
|
259
|
-
fs,
|
|
260
|
-
Path.join(
|
|
261
|
-
modelBasePath,
|
|
262
|
-
(null == opts.outprefix ? '' : opts.outprefix) + 'sdk.jsonic'),
|
|
263
|
-
entityIncludes
|
|
264
|
-
)
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
function writeChanged(
|
|
269
|
-
point: string, path: string, content: string,
|
|
270
|
-
flags?: { update?: boolean }
|
|
271
|
-
) {
|
|
272
|
-
let exists = false
|
|
273
|
-
let changed = false
|
|
274
|
-
|
|
275
|
-
flags = flags || {}
|
|
276
|
-
flags.update = null == flags.update ? true : !!flags.update
|
|
277
|
-
|
|
278
|
-
let action = ''
|
|
279
|
-
try {
|
|
280
|
-
let existingContent: string = ''
|
|
281
|
-
path = Path.normalize(path)
|
|
282
|
-
|
|
283
|
-
exists = fs.existsSync(path)
|
|
284
|
-
|
|
285
|
-
if (exists) {
|
|
286
|
-
action = 'read'
|
|
287
|
-
existingContent = fs.readFileSync(path, 'utf8')
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
changed = existingContent !== content
|
|
291
|
-
|
|
292
|
-
action = flags.update ? 'write' : 'skip'
|
|
293
|
-
|
|
294
|
-
log.info({
|
|
295
|
-
point: 'write-' + point,
|
|
296
|
-
note: (changed ? '' : 'not-') + 'changed ' + path,
|
|
297
|
-
write: 'file', skip: !changed, exists, changed,
|
|
298
|
-
contentLength: content.length, file: path
|
|
299
|
-
})
|
|
300
|
-
|
|
301
|
-
if (!exists || (changed && flags.update)) {
|
|
302
|
-
fs.writeFileSync(path, content)
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
catch (err: any) {
|
|
306
|
-
log.error({
|
|
307
|
-
fail: action, point, file: path, exists, changed,
|
|
308
|
-
contentLength: content.length, err
|
|
309
|
-
})
|
|
310
|
-
err.__logged__ = true
|
|
311
|
-
throw err
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
167
|
return {
|
|
316
168
|
generate,
|
|
317
169
|
}
|
|
318
170
|
}
|
|
319
171
|
|
|
320
172
|
|
|
321
|
-
|
|
322
173
|
ApiDef.makeBuild = async function(opts: ApiDefOptions) {
|
|
323
174
|
let apidef: any = undefined
|
|
324
175
|
|
|
325
|
-
const outprefix = null == opts.outprefix ? '' : opts.outprefix
|
|
176
|
+
// const outprefix = null == opts.outprefix ? '' : opts.outprefix
|
|
326
177
|
|
|
327
178
|
const config = {
|
|
328
179
|
def: opts.def || 'no-def',
|
|
329
180
|
kind: 'openapi3',
|
|
330
|
-
model: opts.folder ?
|
|
331
|
-
(opts.folder + '/' + outprefix + 'api-generated.jsonic') : 'no-model',
|
|
332
181
|
meta: opts.meta || {},
|
|
333
182
|
}
|
|
334
183
|
|
|
@@ -351,41 +200,15 @@ ApiDef.makeBuild = async function(opts: ApiDefOptions) {
|
|
|
351
200
|
|
|
352
201
|
|
|
353
202
|
|
|
354
|
-
async function modifyModel(fs: any, path: string, entityIncludes: string[]) {
|
|
355
|
-
// TODO: This is a kludge.
|
|
356
|
-
// Aontu should provide option for as-is AST so that can be used
|
|
357
|
-
// to find injection point more reliably
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
let src = fs.existsSync(path) ? fs.readFileSync(path, 'utf8') :
|
|
361
|
-
'main: sdk: entity: {}\n'
|
|
362
|
-
|
|
363
|
-
let newsrc = '' + src
|
|
364
|
-
|
|
365
|
-
// Inject target file references into model
|
|
366
|
-
entityIncludes.sort().map((entname: string) => {
|
|
367
|
-
const lineRE =
|
|
368
|
-
new RegExp(`@"entity/${entname}.jsonic"`)
|
|
369
|
-
|
|
370
|
-
if (!src.match(lineRE)) {
|
|
371
|
-
newsrc = newsrc.replace(/(main:\s+sdk:\s+entity:\s+\{\s*\}\n)/, '$1' +
|
|
372
|
-
`@"entity/${entname}.jsonic"\n`)
|
|
373
|
-
}
|
|
374
|
-
})
|
|
375
|
-
|
|
376
|
-
if (newsrc.length !== src.length) {
|
|
377
|
-
fs.writeFileSync(path, newsrc)
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
203
|
|
|
381
204
|
|
|
382
205
|
|
|
383
206
|
export type {
|
|
384
207
|
ApiDefOptions,
|
|
385
|
-
// ApiDefSpec,
|
|
386
208
|
}
|
|
387
209
|
|
|
388
210
|
|
|
389
211
|
export {
|
|
390
212
|
ApiDef,
|
|
213
|
+
parse,
|
|
391
214
|
}
|