wrekenfile-converter 2.0.1 → 2.0.4
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/README.md +58 -0
- package/dist/cli/cli-mini-wrekenfile-generator.d.ts +2 -0
- package/dist/cli/cli-mini-wrekenfile-generator.js +107 -0
- package/dist/cli/cli-openapi-to-wrekenfile.d.ts +2 -0
- package/dist/cli/cli-openapi-to-wrekenfile.js +115 -0
- package/dist/cli/cli-postman-to-wrekenfile.d.ts +2 -0
- package/dist/cli/cli-postman-to-wrekenfile.js +54 -0
- package/dist/openapi-to-wreken.js +103 -56
- package/dist/openapi-to-wrekenfile.d.ts +0 -0
- package/dist/openapi-to-wrekenfile.js +10 -0
- package/dist/openapi-v2-to-wrekenfile.js +86 -47
- package/dist/postman-to-wrekenfile.js +105 -31
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -96,6 +96,60 @@ const miniFiles: MiniWrekenfile[] = generateMiniWrekenfiles('./Wrekenfile.yaml')
|
|
|
96
96
|
// Each miniFile contains { content, metadata }
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
+
## CLI Tools
|
|
100
|
+
|
|
101
|
+
### Convert OpenAPI to Wrekenfile
|
|
102
|
+
|
|
103
|
+
Generate a Wrekenfile YAML from an OpenAPI (YAML or JSON) spec:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npx ts-node src/cli/cli-openapi-to-wrekenfile.ts --input <openapi.yaml|json> [--output <wrekenfile.yaml>] [--cwd <dir>]
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Options:**
|
|
110
|
+
- `--input` or `-i`: Path to your OpenAPI YAML or JSON file (required)
|
|
111
|
+
- `--output` or `-o`: Path to output Wrekenfile YAML (optional, defaults to `output_wrekenfile.yaml`)
|
|
112
|
+
- `--cwd`: Working directory for resolving $refs (optional, defaults to the input file's directory)
|
|
113
|
+
|
|
114
|
+
**Example:**
|
|
115
|
+
```bash
|
|
116
|
+
npx ts-node src/cli/cli-openapi-to-wrekenfile.ts --input examples/p3id_swagger.json --output wrekenfile.yaml --cwd .
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Convert Postman Collection to Wrekenfile
|
|
120
|
+
|
|
121
|
+
Convert a Postman collection JSON to a Wrekenfile YAML file:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
npx ts-node src/cli/cli-postman-to-wrekenfile.ts <postman_collection.json> <output_wrekenfile.yaml> [postman_environment.json]
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Example:**
|
|
128
|
+
```bash
|
|
129
|
+
npx ts-node src/cli/cli-postman-to-wrekenfile.ts examples/transact_bridge_postman.json wrekenfile.yaml
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Note:** The third argument (environment file) is optional.
|
|
133
|
+
|
|
134
|
+
### Generate Mini Wrekenfiles
|
|
135
|
+
|
|
136
|
+
Generate mini Wrekenfiles for each endpoint from a main Wrekenfile YAML:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
npx ts-node src/cli/cli-mini-wrekenfile-generator.ts --input <wrekenfile.yaml> [--output <dir>]
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Options:**
|
|
143
|
+
- `--input` or `-i`: Path to your main Wrekenfile YAML (required)
|
|
144
|
+
- `--output` or `-o`: Output directory for mini Wrekenfiles (optional, defaults to `./mini-wrekenfiles`)
|
|
145
|
+
|
|
146
|
+
**Example:**
|
|
147
|
+
```bash
|
|
148
|
+
npx ts-node src/cli/cli-mini-wrekenfile-generator.ts --input wrekenfile.yaml --output ./mini-wrekenfiles
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
This will generate one mini Wrekenfile per endpoint in the specified output directory.
|
|
152
|
+
|
|
99
153
|
## API Reference
|
|
100
154
|
|
|
101
155
|
### Core Functions
|
|
@@ -167,6 +221,10 @@ src/
|
|
|
167
221
|
├── postman-to-wrekenfile.ts # Postman converter
|
|
168
222
|
├── wrekenfile-validator.ts # Validation logic
|
|
169
223
|
├── mini-wrekenfile-generator.ts # Mini chunk generator
|
|
224
|
+
├── cli/ # CLI tools
|
|
225
|
+
│ ├── cli-openapi-to-wrekenfile.ts
|
|
226
|
+
│ ├── cli-postman-to-wrekenfile.ts
|
|
227
|
+
│ └── cli-mini-wrekenfile-generator.ts
|
|
170
228
|
└── example-usage.ts # Usage examples
|
|
171
229
|
|
|
172
230
|
dist/ # Compiled JavaScript + types
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
37
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
38
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
39
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
40
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
41
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
42
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const mini_wrekenfile_generator_1 = require("../mini-wrekenfile-generator");
|
|
49
|
+
function printUsage() {
|
|
50
|
+
console.log('Usage: npx ts-node src/cli/cli-mini-wrekenfile-generator.ts --input <wrekenfile.yaml> [--output <dir>]');
|
|
51
|
+
}
|
|
52
|
+
function parseArgs() {
|
|
53
|
+
const args = process.argv.slice(2);
|
|
54
|
+
const opts = {};
|
|
55
|
+
for (let i = 0; i < args.length; i++) {
|
|
56
|
+
if (args[i] === '--input' || args[i] === '-i') {
|
|
57
|
+
opts.input = args[++i];
|
|
58
|
+
}
|
|
59
|
+
else if (args[i] === '--output' || args[i] === '-o') {
|
|
60
|
+
opts.output = args[++i];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return opts;
|
|
64
|
+
}
|
|
65
|
+
function main() {
|
|
66
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
67
|
+
const opts = parseArgs();
|
|
68
|
+
if (!opts.input) {
|
|
69
|
+
printUsage();
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
const inputPath = path.resolve(opts.input);
|
|
73
|
+
const outputDir = opts.output ? path.resolve(opts.output) : path.resolve('./mini-wrekenfiles');
|
|
74
|
+
if (!fs.existsSync(inputPath)) {
|
|
75
|
+
console.error(`Input file not found: ${inputPath}`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
let wrekenfileContent;
|
|
79
|
+
try {
|
|
80
|
+
wrekenfileContent = fs.readFileSync(inputPath, 'utf8');
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
console.error('Failed to read input file:', err);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
let miniWrekenfiles;
|
|
87
|
+
try {
|
|
88
|
+
miniWrekenfiles = (0, mini_wrekenfile_generator_1.generateMiniWrekenfiles)(wrekenfileContent);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
console.error('Failed to generate mini Wrekenfiles:', err);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
(0, mini_wrekenfile_generator_1.saveMiniWrekenfiles)(miniWrekenfiles, outputDir);
|
|
96
|
+
console.log(`Generated ${miniWrekenfiles.length} mini Wrekenfiles in ${outputDir}`);
|
|
97
|
+
for (const mini of miniWrekenfiles) {
|
|
98
|
+
console.log(` - ${mini.metadata.filename}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
console.error('Failed to save mini Wrekenfiles:', err);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
main();
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
37
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
38
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
39
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
40
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
41
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
42
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const js_yaml_1 = require("js-yaml");
|
|
49
|
+
const openapi_to_wreken_1 = require("../openapi-to-wreken");
|
|
50
|
+
function printUsage() {
|
|
51
|
+
console.log(`Usage: npx ts-node src/cli/cli-openapi-to-wrekenfile.ts --input <openapi.yaml|json> [--output <wrekenfile.yaml>] [--cwd <dir>]`);
|
|
52
|
+
}
|
|
53
|
+
function parseArgs() {
|
|
54
|
+
const args = process.argv.slice(2);
|
|
55
|
+
const opts = {};
|
|
56
|
+
for (let i = 0; i < args.length; i++) {
|
|
57
|
+
if (args[i] === '--input' || args[i] === '-i') {
|
|
58
|
+
opts.input = args[++i];
|
|
59
|
+
}
|
|
60
|
+
else if (args[i] === '--output' || args[i] === '-o') {
|
|
61
|
+
opts.output = args[++i];
|
|
62
|
+
}
|
|
63
|
+
else if (args[i] === '--cwd') {
|
|
64
|
+
opts.cwd = args[++i];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return opts;
|
|
68
|
+
}
|
|
69
|
+
function main() {
|
|
70
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
71
|
+
const opts = parseArgs();
|
|
72
|
+
if (!opts.input) {
|
|
73
|
+
printUsage();
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
const inputPath = path.resolve(opts.input);
|
|
77
|
+
const outputPath = path.resolve(opts.output || 'output_wrekenfile.yaml');
|
|
78
|
+
const baseDir = opts.cwd ? path.resolve(opts.cwd) : path.dirname(inputPath);
|
|
79
|
+
if (!fs.existsSync(inputPath)) {
|
|
80
|
+
console.error(`Input file not found: ${inputPath}`);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
let openapiSpec;
|
|
84
|
+
try {
|
|
85
|
+
const raw = fs.readFileSync(inputPath, 'utf8');
|
|
86
|
+
if (inputPath.endsWith('.json')) {
|
|
87
|
+
openapiSpec = JSON.parse(raw);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
openapiSpec = (0, js_yaml_1.load)(raw);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
console.error('Failed to load OpenAPI file:', err);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
let wrekenfileYaml;
|
|
98
|
+
try {
|
|
99
|
+
wrekenfileYaml = (0, openapi_to_wreken_1.generateWrekenfile)(openapiSpec, baseDir);
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
console.error('Failed to generate Wrekenfile:', err);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
fs.writeFileSync(outputPath, wrekenfileYaml, 'utf8');
|
|
107
|
+
console.log(`Wrekenfile written to ${outputPath}`);
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
console.error('Failed to write output file:', err);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
main();
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
4
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
5
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
6
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
7
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
8
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
9
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
|
+
};
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
const fs_1 = __importDefault(require("fs"));
|
|
17
|
+
const postman_to_wrekenfile_1 = require("../postman-to-wrekenfile");
|
|
18
|
+
function printUsage() {
|
|
19
|
+
console.log('Usage: npx ts-node src/cli-postman-to-wrekenfile.ts <postman_collection.json> <output_wrekenfile.yaml> [postman_environment.json]');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
function main() {
|
|
23
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
+
const args = process.argv.slice(2);
|
|
25
|
+
if (args.length < 2) {
|
|
26
|
+
printUsage();
|
|
27
|
+
}
|
|
28
|
+
const [inputFile, outputFile, envFile] = args;
|
|
29
|
+
if (!fs_1.default.existsSync(inputFile)) {
|
|
30
|
+
console.error(`❌ Input file not found: ${inputFile}`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
let variables = {};
|
|
34
|
+
if (envFile) {
|
|
35
|
+
if (!fs_1.default.existsSync(envFile)) {
|
|
36
|
+
console.error(`❌ Environment file not found: ${envFile}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
variables = (0, postman_to_wrekenfile_1.loadEnvironmentFile)(envFile);
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const postmanContent = fs_1.default.readFileSync(inputFile, 'utf8');
|
|
43
|
+
const postmanCollection = JSON.parse(postmanContent);
|
|
44
|
+
const wrekenfileYaml = (0, postman_to_wrekenfile_1.generateWrekenfile)(postmanCollection, variables);
|
|
45
|
+
fs_1.default.writeFileSync(outputFile, wrekenfileYaml);
|
|
46
|
+
console.log(`✅ Wrekenfile generated: ${outputFile}`);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
console.error(`❌ Error generating Wrekenfile: ${error.message}`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
main();
|
|
@@ -38,6 +38,7 @@ exports.generateWrekenfile = generateWrekenfile;
|
|
|
38
38
|
const fs = __importStar(require("fs"));
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
40
|
const js_yaml_1 = require("js-yaml");
|
|
41
|
+
const js_yaml_2 = require("js-yaml");
|
|
41
42
|
const externalRefCache = {};
|
|
42
43
|
function mapType(type, format) {
|
|
43
44
|
if (format === 'uuid')
|
|
@@ -106,26 +107,27 @@ function resolveRef(ref, spec, baseDir) {
|
|
|
106
107
|
: externalRefCache[fullPath];
|
|
107
108
|
}
|
|
108
109
|
function getTypeFromSchema(schema, spec, baseDir) {
|
|
109
|
-
var _a
|
|
110
|
+
var _a;
|
|
111
|
+
if (!schema || typeof schema !== 'object') {
|
|
112
|
+
return 'ANY';
|
|
113
|
+
}
|
|
110
114
|
if (schema.$ref) {
|
|
111
115
|
const resolvedSchema = resolveRef(schema.$ref, spec, baseDir);
|
|
112
|
-
|
|
113
|
-
if (resolvedSchema.type && resolvedSchema.type !== 'object') {
|
|
116
|
+
if (resolvedSchema && resolvedSchema.type && resolvedSchema.type !== 'object') {
|
|
114
117
|
return mapType(resolvedSchema.type, resolvedSchema.format);
|
|
115
118
|
}
|
|
116
|
-
// It's a complex type, use STRUCT
|
|
117
119
|
return `STRUCT(${schema.$ref.split('/').pop()})`;
|
|
118
120
|
}
|
|
119
121
|
if (schema.type === 'array') {
|
|
120
|
-
if (
|
|
122
|
+
if (schema.items && schema.items.$ref) {
|
|
121
123
|
const resolvedItems = resolveRef(schema.items.$ref, spec, baseDir);
|
|
122
|
-
if (resolvedItems.type && resolvedItems.type !== 'object') {
|
|
124
|
+
if (resolvedItems && resolvedItems.type && resolvedItems.type !== 'object') {
|
|
123
125
|
return `[]${mapType(resolvedItems.type, resolvedItems.format)}`;
|
|
124
126
|
}
|
|
125
127
|
return `[]STRUCT(${schema.items.$ref.split('/').pop()})`;
|
|
126
128
|
}
|
|
127
129
|
else {
|
|
128
|
-
return `[]${mapType((
|
|
130
|
+
return `[]${mapType((_a = schema.items) === null || _a === void 0 ? void 0 : _a.type)}`;
|
|
129
131
|
}
|
|
130
132
|
}
|
|
131
133
|
if (schema.type && schema.type !== 'object') {
|
|
@@ -190,55 +192,55 @@ function extractStructs(spec, baseDir) {
|
|
|
190
192
|
const schemas = ((_a = spec.components) === null || _a === void 0 ? void 0 : _a.schemas) || {};
|
|
191
193
|
// Helper to recursively collect all referenced schemas, traversing all properties, arrays, and combiners
|
|
192
194
|
function collectAllReferencedSchemas(schema, name) {
|
|
193
|
-
if (!name || structs[name])
|
|
195
|
+
if (!schema || typeof schema !== 'object' || !name || structs[name])
|
|
194
196
|
return;
|
|
195
197
|
const resolved = schema.$ref ? resolveRef(schema.$ref, spec, baseDir) : schema;
|
|
196
198
|
const fields = parseSchema(name, resolved, spec, baseDir);
|
|
197
199
|
structs[name] = fields;
|
|
198
200
|
// Traverse all properties
|
|
199
|
-
if (resolved.type === 'object' && resolved.properties) {
|
|
201
|
+
if (resolved && resolved.type === 'object' && resolved.properties && typeof resolved.properties === 'object') {
|
|
200
202
|
for (const [propName, prop] of Object.entries(resolved.properties)) {
|
|
201
|
-
if (prop.$ref) {
|
|
203
|
+
if (prop && typeof prop === 'object' && prop.$ref) {
|
|
202
204
|
const refName = prop.$ref.split('/').pop();
|
|
203
205
|
if (refName)
|
|
204
206
|
collectAllReferencedSchemas(resolveRef(prop.$ref, spec, baseDir), refName);
|
|
205
207
|
}
|
|
206
|
-
else if (prop.type === 'array' && prop.items) {
|
|
207
|
-
if (prop.items.$ref) {
|
|
208
|
+
else if (prop && typeof prop === 'object' && prop.type === 'array' && prop.items) {
|
|
209
|
+
if (prop.items && typeof prop.items === 'object' && prop.items.$ref) {
|
|
208
210
|
const refName = prop.items.$ref.split('/').pop();
|
|
209
211
|
if (refName)
|
|
210
212
|
collectAllReferencedSchemas(resolveRef(prop.items.$ref, spec, baseDir), refName);
|
|
211
213
|
}
|
|
212
|
-
else if (prop.items.type === 'object' || prop.items.properties || prop.items.allOf || prop.items.oneOf || prop.items.anyOf) {
|
|
214
|
+
else if (prop.items && typeof prop.items === 'object' && (prop.items.type === 'object' || prop.items.properties || prop.items.allOf || prop.items.oneOf || prop.items.anyOf)) {
|
|
213
215
|
collectAllReferencedSchemas(prop.items, name + '_' + propName + '_Item');
|
|
214
216
|
}
|
|
215
217
|
}
|
|
216
|
-
else if (prop.type === 'object' || prop.properties || prop.allOf || prop.oneOf || prop.anyOf) {
|
|
218
|
+
else if (prop && typeof prop === 'object' && (prop.type === 'object' || prop.properties || prop.allOf || prop.oneOf || prop.anyOf)) {
|
|
217
219
|
collectAllReferencedSchemas(prop, name + '_' + propName);
|
|
218
220
|
}
|
|
219
221
|
}
|
|
220
222
|
}
|
|
221
223
|
// Traverse array items at root
|
|
222
|
-
if (resolved.type === 'array' && resolved.items) {
|
|
223
|
-
if (resolved.items.$ref) {
|
|
224
|
+
if (resolved && resolved.type === 'array' && resolved.items) {
|
|
225
|
+
if (resolved.items && typeof resolved.items === 'object' && resolved.items.$ref) {
|
|
224
226
|
const refName = resolved.items.$ref.split('/').pop();
|
|
225
227
|
if (refName)
|
|
226
228
|
collectAllReferencedSchemas(resolveRef(resolved.items.$ref, spec, baseDir), refName);
|
|
227
229
|
}
|
|
228
|
-
else if (resolved.items.type === 'object' || resolved.items.properties || resolved.items.allOf || resolved.items.oneOf || resolved.items.anyOf) {
|
|
230
|
+
else if (resolved.items && typeof resolved.items === 'object' && (resolved.items.type === 'object' || resolved.items.properties || resolved.items.allOf || resolved.items.oneOf || resolved.items.anyOf)) {
|
|
229
231
|
collectAllReferencedSchemas(resolved.items, name + '_Item');
|
|
230
232
|
}
|
|
231
233
|
}
|
|
232
234
|
// Traverse allOf/oneOf/anyOf
|
|
233
235
|
for (const combiner of ['allOf', 'oneOf', 'anyOf']) {
|
|
234
|
-
if (Array.isArray(resolved[combiner])) {
|
|
236
|
+
if (resolved && Array.isArray(resolved[combiner])) {
|
|
235
237
|
for (const subSchema of resolved[combiner]) {
|
|
236
|
-
if (subSchema.$ref) {
|
|
238
|
+
if (subSchema && typeof subSchema === 'object' && subSchema.$ref) {
|
|
237
239
|
const refName = subSchema.$ref.split('/').pop();
|
|
238
240
|
if (refName)
|
|
239
241
|
collectAllReferencedSchemas(resolveRef(subSchema.$ref, spec, baseDir), refName);
|
|
240
242
|
}
|
|
241
|
-
else {
|
|
243
|
+
else if (subSchema && typeof subSchema === 'object') {
|
|
242
244
|
collectAllReferencedSchemas(subSchema, name + '_' + combiner);
|
|
243
245
|
}
|
|
244
246
|
}
|
|
@@ -249,44 +251,46 @@ function extractStructs(spec, baseDir) {
|
|
|
249
251
|
for (const name in schemas) {
|
|
250
252
|
collectAllReferencedSchemas(schemas[name], name);
|
|
251
253
|
const schema = schemas[name];
|
|
252
|
-
if (schema.oneOf || schema.anyOf) {
|
|
254
|
+
if (schema && (schema.oneOf || schema.anyOf)) {
|
|
253
255
|
structs[`${name}_Union`] = [{ name: 'value', type: 'ANY', required: 'FALSE' }];
|
|
254
256
|
}
|
|
255
257
|
}
|
|
256
258
|
// Extract inline schemas from operations
|
|
257
|
-
|
|
258
|
-
for (const [
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
if (content.schema
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
259
|
+
if (spec.paths && typeof spec.paths === 'object') {
|
|
260
|
+
for (const [pathStr, methods] of Object.entries(spec.paths)) {
|
|
261
|
+
for (const [method, op] of Object.entries(methods)) {
|
|
262
|
+
const operationId = op.operationId || `${method}-${pathStr.replace(/[\/{}]/g, '-')}`;
|
|
263
|
+
// Extract request body schemas
|
|
264
|
+
if ((_b = op.requestBody) === null || _b === void 0 ? void 0 : _b.content) {
|
|
265
|
+
for (const [contentType, content] of Object.entries(op.requestBody.content)) {
|
|
266
|
+
if (content && content.schema) {
|
|
267
|
+
if (content.schema && content.schema.$ref) {
|
|
268
|
+
const refName = content.schema.$ref.split('/').pop();
|
|
269
|
+
if (refName)
|
|
270
|
+
collectAllReferencedSchemas(resolveRef(content.schema.$ref, spec, baseDir), refName);
|
|
271
|
+
}
|
|
272
|
+
else if (content.schema && typeof content.schema === 'object') {
|
|
273
|
+
const requestStructName = generateStructName(operationId, method, pathStr, 'Request');
|
|
274
|
+
collectAllReferencedSchemas(content.schema, requestStructName);
|
|
275
|
+
}
|
|
272
276
|
}
|
|
273
277
|
}
|
|
274
278
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
279
|
+
// Extract response schemas
|
|
280
|
+
if (op.responses) {
|
|
281
|
+
for (const [code, response] of Object.entries(op.responses)) {
|
|
282
|
+
if (response && response.content) {
|
|
283
|
+
for (const [contentType, content] of Object.entries(response.content)) {
|
|
284
|
+
if (content && content.schema) {
|
|
285
|
+
if (content.schema && content.schema.$ref) {
|
|
286
|
+
const refName = content.schema.$ref.split('/').pop();
|
|
287
|
+
if (refName)
|
|
288
|
+
collectAllReferencedSchemas(resolveRef(content.schema.$ref, spec, baseDir), refName);
|
|
289
|
+
}
|
|
290
|
+
else if (content.schema && typeof content.schema === 'object') {
|
|
291
|
+
const responseStructName = generateStructName(operationId, method, pathStr, `Response${code}`);
|
|
292
|
+
collectAllReferencedSchemas(content.schema, responseStructName);
|
|
293
|
+
}
|
|
290
294
|
}
|
|
291
295
|
}
|
|
292
296
|
}
|
|
@@ -416,13 +420,16 @@ function extractRequestBody(op, operationId, method, path, spec, baseDir) {
|
|
|
416
420
|
if (contentType === 'application/json' && ((_a = requestBody.content[contentType]) === null || _a === void 0 ? void 0 : _a.schema)) {
|
|
417
421
|
const bodySchema = requestBody.content[contentType].schema;
|
|
418
422
|
let type;
|
|
419
|
-
if (bodySchema.$ref) {
|
|
423
|
+
if (bodySchema && bodySchema.$ref) {
|
|
420
424
|
type = getTypeFromSchema(bodySchema, spec, baseDir);
|
|
421
425
|
}
|
|
422
|
-
else {
|
|
426
|
+
else if (bodySchema) {
|
|
423
427
|
const requestStructName = generateStructName(operationId, method, path, 'Request');
|
|
424
428
|
type = `STRUCT(${requestStructName})`;
|
|
425
429
|
}
|
|
430
|
+
else {
|
|
431
|
+
type = 'ANY';
|
|
432
|
+
}
|
|
426
433
|
inputParams.push({
|
|
427
434
|
name: 'body',
|
|
428
435
|
type,
|
|
@@ -431,9 +438,9 @@ function extractRequestBody(op, operationId, method, path, spec, baseDir) {
|
|
|
431
438
|
}
|
|
432
439
|
else if (contentType === 'multipart/form-data' && ((_b = requestBody.content[contentType]) === null || _b === void 0 ? void 0 : _b.schema)) {
|
|
433
440
|
const bodySchema = requestBody.content[contentType].schema;
|
|
434
|
-
if (bodySchema.properties) {
|
|
441
|
+
if (bodySchema && bodySchema.properties) {
|
|
435
442
|
for (const [key, prop] of Object.entries(bodySchema.properties)) {
|
|
436
|
-
const type = prop.format === 'binary' ? 'FILE' : getTypeFromSchema(prop, spec, baseDir);
|
|
443
|
+
const type = prop && prop.format === 'binary' ? 'FILE' : getTypeFromSchema(prop, spec, baseDir);
|
|
437
444
|
const required = (bodySchema.required || []).includes(key) ? 'TRUE' : 'FALSE';
|
|
438
445
|
inputParams.push({
|
|
439
446
|
name: key,
|
|
@@ -483,6 +490,11 @@ function extractInterfaces(spec, baseDir) {
|
|
|
483
490
|
const interfaces = {};
|
|
484
491
|
// Valid HTTP methods
|
|
485
492
|
const validMethods = ['get', 'post', 'put', 'delete', 'patch', 'head', 'options', 'trace'];
|
|
493
|
+
// Check if paths exists and is an object
|
|
494
|
+
if (!spec.paths || typeof spec.paths !== 'object') {
|
|
495
|
+
console.warn('Warning: No paths found in OpenAPI specification');
|
|
496
|
+
return interfaces;
|
|
497
|
+
}
|
|
486
498
|
for (const [pathStr, methods] of Object.entries(spec.paths)) {
|
|
487
499
|
for (const [method, op] of Object.entries(methods)) {
|
|
488
500
|
// Skip extension fields (x-*) and only process valid HTTP methods
|
|
@@ -558,6 +570,37 @@ function extractSecurityDefaults(spec) {
|
|
|
558
570
|
}
|
|
559
571
|
return defs;
|
|
560
572
|
}
|
|
573
|
+
function cleanYaml(yamlString) {
|
|
574
|
+
return yamlString
|
|
575
|
+
.replace(/\t/g, ' ')
|
|
576
|
+
.replace(/[\u00A0]/g, ' ')
|
|
577
|
+
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '')
|
|
578
|
+
.replace(/[ \t]+$/gm, '')
|
|
579
|
+
.replace(/\r\n/g, '\n');
|
|
580
|
+
}
|
|
581
|
+
function checkYamlForHiddenChars(yamlString) {
|
|
582
|
+
const lines = yamlString.split('\n');
|
|
583
|
+
for (let i = 0; i < lines.length; i++) {
|
|
584
|
+
const line = lines[i];
|
|
585
|
+
if (/\t/.test(line)) {
|
|
586
|
+
throw new Error(`YAML contains a TAB character at line ${i + 1}:\n${line}`);
|
|
587
|
+
}
|
|
588
|
+
if (/\u00A0/.test(line)) {
|
|
589
|
+
throw new Error(`YAML contains a non-breaking space (U+00A0) at line ${i + 1}:\n${line}`);
|
|
590
|
+
}
|
|
591
|
+
if (/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/.test(line)) {
|
|
592
|
+
throw new Error(`YAML contains a non-printable character at line ${i + 1}:\n${line}`);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
function validateYaml(yamlString) {
|
|
597
|
+
try {
|
|
598
|
+
(0, js_yaml_2.load)(yamlString);
|
|
599
|
+
}
|
|
600
|
+
catch (e) {
|
|
601
|
+
throw new Error('Generated YAML is invalid: ' + e.message);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
561
604
|
function generateWrekenfile(spec, baseDir) {
|
|
562
605
|
var _a, _b;
|
|
563
606
|
if (!spec || typeof spec !== 'object') {
|
|
@@ -566,7 +609,7 @@ function generateWrekenfile(spec, baseDir) {
|
|
|
566
609
|
if (!baseDir || typeof baseDir !== 'string') {
|
|
567
610
|
throw new Error("Argument 'baseDir' is required and must be a string");
|
|
568
611
|
}
|
|
569
|
-
|
|
612
|
+
let yamlString = (0, js_yaml_1.dump)({
|
|
570
613
|
VERSION: '1.2',
|
|
571
614
|
INIT: {
|
|
572
615
|
DEFAULTS: [
|
|
@@ -577,4 +620,8 @@ function generateWrekenfile(spec, baseDir) {
|
|
|
577
620
|
INTERFACES: extractInterfaces(spec, baseDir),
|
|
578
621
|
STRUCTS: extractStructs(spec, baseDir),
|
|
579
622
|
});
|
|
623
|
+
yamlString = cleanYaml(yamlString);
|
|
624
|
+
checkYamlForHiddenChars(yamlString);
|
|
625
|
+
validateYaml(yamlString);
|
|
626
|
+
return yamlString;
|
|
580
627
|
}
|
|
File without changes
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Defensive fixes for OpenAPI v3 schema traversal
|
|
3
|
+
// 1. Guard all property accesses ($ref, type, properties, items, allOf, oneOf, anyOf) with checks for existence and correct type
|
|
4
|
+
// 2. Guard all calls to getTypeFromSchema or similar with checks for defined/object
|
|
5
|
+
// 3. Apply these checks in all recursive struct extraction and interface extraction logic
|
|
6
|
+
// Example for a recursive schema traversal:
|
|
7
|
+
// if (schema && typeof schema === 'object' && schema.$ref) { ... }
|
|
8
|
+
// if (prop && typeof prop === 'object' && prop.type === 'array' && prop.items) { ... }
|
|
9
|
+
// ...
|
|
10
|
+
// Please apply these patterns throughout the file, especially in struct extraction and interface extraction functions.
|
|
@@ -38,6 +38,7 @@ exports.generateWrekenfile = generateWrekenfile;
|
|
|
38
38
|
const fs = __importStar(require("fs"));
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
40
|
const js_yaml_1 = require("js-yaml");
|
|
41
|
+
const js_yaml_2 = require("js-yaml");
|
|
41
42
|
const externalRefCache = {};
|
|
42
43
|
function mapType(type, format) {
|
|
43
44
|
if (format === 'uuid')
|
|
@@ -106,14 +107,16 @@ function resolveRef(ref, spec, baseDir) {
|
|
|
106
107
|
: externalRefCache[fullPath];
|
|
107
108
|
}
|
|
108
109
|
function parseSchema(name, schema, spec, baseDir, depth = 0) {
|
|
109
|
-
var _a, _b
|
|
110
|
+
var _a, _b;
|
|
110
111
|
if (depth > 3)
|
|
111
112
|
return [];
|
|
112
|
-
if (schema.$ref)
|
|
113
|
+
if (schema && typeof schema === 'object' && schema.$ref) {
|
|
113
114
|
return parseSchema(name, resolveRef(schema.$ref, spec, baseDir), spec, baseDir, depth + 1);
|
|
114
|
-
|
|
115
|
+
}
|
|
116
|
+
if (schema && typeof schema === 'object' && schema.allOf) {
|
|
115
117
|
return schema.allOf.flatMap((s) => parseSchema(name, s, spec, baseDir, depth + 1));
|
|
116
|
-
|
|
118
|
+
}
|
|
119
|
+
if (schema && typeof schema === 'object' && (schema.oneOf || schema.anyOf)) {
|
|
117
120
|
return [{
|
|
118
121
|
name: 'variant',
|
|
119
122
|
type: `STRUCT(${name}_Union)`,
|
|
@@ -121,32 +124,33 @@ function parseSchema(name, schema, spec, baseDir, depth = 0) {
|
|
|
121
124
|
}];
|
|
122
125
|
}
|
|
123
126
|
// Handle primitive types - return empty array (no struct needed)
|
|
124
|
-
if (schema.type && schema.type !== 'object' && schema.type !== 'array') {
|
|
127
|
+
if (schema && typeof schema === 'object' && schema.type && schema.type !== 'object' && schema.type !== 'array') {
|
|
125
128
|
return [];
|
|
126
129
|
}
|
|
127
130
|
// Handle empty objects (no properties) - return empty array
|
|
128
|
-
if (schema.type === 'object' && (!schema.properties || Object.keys(schema.properties).length === 0)) {
|
|
131
|
+
if (schema && typeof schema === 'object' && schema.type === 'object' && (!schema.properties || Object.keys(schema.properties).length === 0)) {
|
|
129
132
|
return [];
|
|
130
133
|
}
|
|
131
134
|
const fields = [];
|
|
132
|
-
if ((_a = schema.discriminator) === null || _a === void 0 ? void 0 : _a.propertyName) {
|
|
135
|
+
if (schema && typeof schema === 'object' && ((_a = schema.discriminator) === null || _a === void 0 ? void 0 : _a.propertyName)) {
|
|
133
136
|
fields.push({
|
|
134
137
|
name: schema.discriminator.propertyName,
|
|
135
138
|
type: 'STRING',
|
|
136
139
|
required: 'REQUIRED',
|
|
137
140
|
});
|
|
138
141
|
}
|
|
139
|
-
if (schema.type === 'object' && schema.properties) {
|
|
142
|
+
if (schema && typeof schema === 'object' && schema.type === 'object' && schema.properties) {
|
|
140
143
|
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
141
144
|
let type = 'ANY';
|
|
142
|
-
if (prop.$ref)
|
|
145
|
+
if (prop && typeof prop === 'object' && prop.$ref) {
|
|
143
146
|
type = `STRUCT(${prop.$ref.split('/').pop()})`;
|
|
144
|
-
|
|
145
|
-
|
|
147
|
+
}
|
|
148
|
+
else if (prop && typeof prop === 'object' && prop.type === 'array') {
|
|
149
|
+
if (prop && typeof prop === 'object' && prop.items && prop.items.$ref) {
|
|
146
150
|
type = `[]STRUCT(${prop.items.$ref.split('/').pop()})`;
|
|
147
151
|
}
|
|
148
|
-
else {
|
|
149
|
-
type = `[]${mapType((
|
|
152
|
+
else if (prop && typeof prop === 'object' && prop.items) {
|
|
153
|
+
type = `[]${mapType((_b = prop.items) === null || _b === void 0 ? void 0 : _b.type)}`;
|
|
150
154
|
}
|
|
151
155
|
}
|
|
152
156
|
else {
|
|
@@ -179,7 +183,7 @@ function extractStructs(spec, baseDir) {
|
|
|
179
183
|
const fields = parseSchema(name, definitions[name], spec, baseDir);
|
|
180
184
|
// Always add the struct, even if empty
|
|
181
185
|
structs[name] = fields;
|
|
182
|
-
if (definitions[name].oneOf || definitions[name].anyOf) {
|
|
186
|
+
if (definitions[name] && typeof definitions[name] === 'object' && (definitions[name].oneOf || definitions[name].anyOf)) {
|
|
183
187
|
structs[`${name}_Union`] = [{ name: 'value', type: 'ANY', required: 'OPTIONAL' }];
|
|
184
188
|
}
|
|
185
189
|
}
|
|
@@ -190,7 +194,7 @@ function extractStructs(spec, baseDir) {
|
|
|
190
194
|
// Extract request body schemas (OpenAPI v2 uses parameters with in: body)
|
|
191
195
|
if (op.parameters) {
|
|
192
196
|
for (const param of op.parameters) {
|
|
193
|
-
if (param.in === 'body' && param.schema && !param.schema.$ref) {
|
|
197
|
+
if (param && typeof param === 'object' && param.in === 'body' && param.schema && !param.schema.$ref) {
|
|
194
198
|
// Inline schema - create a struct for it only if it has fields
|
|
195
199
|
const requestStructName = generateStructName(operationId, method, pathStr, 'Request');
|
|
196
200
|
const fields = parseSchema(requestStructName, param.schema, spec, baseDir);
|
|
@@ -203,7 +207,7 @@ function extractStructs(spec, baseDir) {
|
|
|
203
207
|
// Extract response schemas (OpenAPI v2 has schema directly in response)
|
|
204
208
|
if (op.responses) {
|
|
205
209
|
for (const [code, response] of Object.entries(op.responses)) {
|
|
206
|
-
if (response.schema && !response.schema.$ref) {
|
|
210
|
+
if (response && typeof response === 'object' && response.schema && !response.schema.$ref) {
|
|
207
211
|
// Inline schema - create a struct for it only if it has fields
|
|
208
212
|
const responseStructName = generateStructName(operationId, method, pathStr, `Response${code}`);
|
|
209
213
|
const fields = parseSchema(responseStructName, response.schema, spec, baseDir);
|
|
@@ -220,7 +224,7 @@ function extractStructs(spec, baseDir) {
|
|
|
220
224
|
function getContentTypeAndBodyType(op) {
|
|
221
225
|
var _a;
|
|
222
226
|
// Check if there are formData parameters
|
|
223
|
-
const hasFormData = (_a = op.parameters) === null || _a === void 0 ? void 0 : _a.some((param) => param.in === 'formData');
|
|
227
|
+
const hasFormData = (_a = op.parameters) === null || _a === void 0 ? void 0 : _a.some((param) => param && typeof param === 'object' && param.in === 'formData');
|
|
224
228
|
if (hasFormData) {
|
|
225
229
|
return { contentType: 'multipart/form-data', bodyType: 'FORM' };
|
|
226
230
|
}
|
|
@@ -276,7 +280,7 @@ function getHeadersForOperation(op, spec) {
|
|
|
276
280
|
// Check if Authorization is used as a parameter but not defined in securityDefinitions
|
|
277
281
|
if (op.parameters) {
|
|
278
282
|
for (const param of op.parameters) {
|
|
279
|
-
if (param.in === 'header' && param.name === 'Authorization' && !headerMap.has('Authorization')) {
|
|
283
|
+
if (param && typeof param === 'object' && param.in === 'header' && param.name === 'Authorization' && !headerMap.has('Authorization')) {
|
|
280
284
|
headerMap.set('Authorization', 'bearer_token');
|
|
281
285
|
}
|
|
282
286
|
}
|
|
@@ -297,25 +301,25 @@ function extractParameters(op, spec) {
|
|
|
297
301
|
if (op.parameters) {
|
|
298
302
|
for (let param of op.parameters) {
|
|
299
303
|
// Resolve parameter references
|
|
300
|
-
if (param.$ref) {
|
|
304
|
+
if (param && typeof param === 'object' && param.$ref) {
|
|
301
305
|
if (!spec.swaggerFile) {
|
|
302
306
|
throw new Error("spec.swaggerFile is undefined. Please provide a valid baseDir when calling generateWrekenfile, or ensure all refs are internal.");
|
|
303
307
|
}
|
|
304
308
|
param = resolveRef(param.$ref, spec, path.dirname(spec.swaggerFile));
|
|
305
309
|
}
|
|
306
310
|
// Skip body and formData parameters, they are handled in extractRequestBody
|
|
307
|
-
if (param.in === 'body' || param.in === 'formData') {
|
|
311
|
+
if (param && typeof param === 'object' && (param.in === 'body' || param.in === 'formData')) {
|
|
308
312
|
continue;
|
|
309
313
|
}
|
|
310
|
-
const paramType = param.in || 'query';
|
|
311
|
-
const paramName = param.name;
|
|
312
|
-
const paramSchema = param.schema || {}; // Schema is sometimes at root of param
|
|
313
|
-
const paramRequired = param.required ? 'REQUIRED' : 'OPTIONAL';
|
|
314
|
+
const paramType = param && typeof param === 'object' ? param.in || 'query' : 'query';
|
|
315
|
+
const paramName = param && typeof param === 'object' ? param.name : '';
|
|
316
|
+
const paramSchema = param && typeof param === 'object' ? param.schema || {} : {}; // Schema is sometimes at root of param
|
|
317
|
+
const paramRequired = param && typeof param === 'object' ? param.required ? 'REQUIRED' : 'OPTIONAL' : 'OPTIONAL';
|
|
314
318
|
let type = 'STRING';
|
|
315
|
-
if (param.type) {
|
|
319
|
+
if (param && typeof param === 'object' && param.type) {
|
|
316
320
|
type = mapType(param.type, param.format);
|
|
317
321
|
}
|
|
318
|
-
else if (paramSchema.type) {
|
|
322
|
+
else if (paramSchema && typeof paramSchema === 'object' && paramSchema.type) {
|
|
319
323
|
type = mapType(paramSchema.type, paramSchema.format);
|
|
320
324
|
}
|
|
321
325
|
inputParams.push({
|
|
@@ -331,10 +335,10 @@ function extractParameters(op, spec) {
|
|
|
331
335
|
function extractRequestBody(op, operationId, method, path) {
|
|
332
336
|
var _a;
|
|
333
337
|
const inputParams = [];
|
|
334
|
-
const bodyParam = (op.parameters || []).find((p) => p.in === 'body');
|
|
338
|
+
const bodyParam = (op.parameters || []).find((p) => p && typeof p === 'object' && p.in === 'body');
|
|
335
339
|
if (bodyParam) {
|
|
336
340
|
let type;
|
|
337
|
-
if ((_a = bodyParam.schema) === null || _a === void 0 ? void 0 : _a.$ref) {
|
|
341
|
+
if (bodyParam && typeof bodyParam === 'object' && ((_a = bodyParam.schema) === null || _a === void 0 ? void 0 : _a.$ref)) {
|
|
338
342
|
type = `STRUCT(${bodyParam.schema.$ref.split('/').pop()})`;
|
|
339
343
|
}
|
|
340
344
|
else {
|
|
@@ -345,17 +349,17 @@ function extractRequestBody(op, operationId, method, path) {
|
|
|
345
349
|
inputParams.push({
|
|
346
350
|
name: 'body',
|
|
347
351
|
type,
|
|
348
|
-
required: bodyParam.required ? 'REQUIRED' : 'OPTIONAL',
|
|
352
|
+
required: bodyParam && typeof bodyParam === 'object' ? bodyParam.required ? 'REQUIRED' : 'OPTIONAL' : 'OPTIONAL',
|
|
349
353
|
});
|
|
350
354
|
}
|
|
351
355
|
// Handle formData for multipart/form-data
|
|
352
356
|
if (op.parameters) {
|
|
353
357
|
for (const param of op.parameters) {
|
|
354
|
-
if (param.in === 'formData') {
|
|
358
|
+
if (param && typeof param === 'object' && param.in === 'formData') {
|
|
355
359
|
inputParams.push({
|
|
356
|
-
name: param.name,
|
|
357
|
-
type: param.type === 'file' ? 'FILE' : mapType(param.type, param.format),
|
|
358
|
-
required: param.required ? 'REQUIRED' : 'OPTIONAL',
|
|
360
|
+
name: param && typeof param === 'object' ? param.name : '',
|
|
361
|
+
type: param && typeof param === 'object' ? param.type === 'file' ? 'FILE' : mapType(param.type, param.format) : 'STRING',
|
|
362
|
+
required: param && typeof param === 'object' ? param.required ? 'REQUIRED' : 'OPTIONAL' : 'OPTIONAL',
|
|
359
363
|
});
|
|
360
364
|
}
|
|
361
365
|
}
|
|
@@ -368,25 +372,25 @@ function extractResponses(op, operationId, method, path) {
|
|
|
368
372
|
// Handle all response codes (success and error)
|
|
369
373
|
for (const [code, response] of Object.entries(op.responses || {})) {
|
|
370
374
|
let returnType = 'ANY';
|
|
371
|
-
if (response.schema) {
|
|
372
|
-
if (response.schema.$ref) {
|
|
375
|
+
if (response && typeof response === 'object' && response.schema) {
|
|
376
|
+
if (response && typeof response === 'object' && response.schema.$ref) {
|
|
373
377
|
returnType = `STRUCT(${response.schema.$ref.split('/').pop()})`;
|
|
374
378
|
}
|
|
375
|
-
else if (response.schema.type === 'array') {
|
|
376
|
-
if ((_a = response.schema.items) === null || _a === void 0 ? void 0 : _a.$ref) {
|
|
379
|
+
else if (response && typeof response === 'object' && response.schema.type === 'array') {
|
|
380
|
+
if (response && typeof response === 'object' && ((_a = response.schema.items) === null || _a === void 0 ? void 0 : _a.$ref)) {
|
|
377
381
|
returnType = `[]STRUCT(${response.schema.items.$ref.split('/').pop()})`;
|
|
378
382
|
}
|
|
379
|
-
else {
|
|
383
|
+
else if (response && typeof response === 'object' && response.schema.items) {
|
|
380
384
|
returnType = `[]${mapType((_b = response.schema.items) === null || _b === void 0 ? void 0 : _b.type)}`;
|
|
381
385
|
}
|
|
382
386
|
}
|
|
383
|
-
else if (response.schema.type === 'object') {
|
|
387
|
+
else if (response && typeof response === 'object' && response.schema.type === 'object') {
|
|
384
388
|
// Inline schema - use generated struct name
|
|
385
389
|
const responseStructName = generateStructName(operationId, method, path, `Response${code}`);
|
|
386
390
|
returnType = `STRUCT(${responseStructName})`;
|
|
387
391
|
}
|
|
388
392
|
}
|
|
389
|
-
else if (code === '204' || response.description === 'No Content') {
|
|
393
|
+
else if (code === '204' || response && typeof response === 'object' && response.description === 'No Content') {
|
|
390
394
|
returnType = 'VOID';
|
|
391
395
|
}
|
|
392
396
|
returns.push({
|
|
@@ -413,7 +417,7 @@ function extractInterfaces(spec) {
|
|
|
413
417
|
const alias = operationId;
|
|
414
418
|
const endpoint = pathStr.includes('{') ? `\`${base}${pathStr}\`` : `${base}${pathStr}`;
|
|
415
419
|
// Check if operation is hidden from docs
|
|
416
|
-
const isPrivate = op['x-hidden-from-docs'] === true;
|
|
420
|
+
const isPrivate = op && typeof op === 'object' && op['x-hidden-from-docs'] === true;
|
|
417
421
|
const visibility = isPrivate ? 'PRIVATE' : 'PUBLIC';
|
|
418
422
|
const { bodyType } = getContentTypeAndBodyType(op);
|
|
419
423
|
const headers = getHeadersForOperation(op, spec);
|
|
@@ -443,23 +447,54 @@ function extractSecurityDefaults(spec) {
|
|
|
443
447
|
const defs = [];
|
|
444
448
|
const securityDefinitions = spec.securityDefinitions || {};
|
|
445
449
|
for (const [name, scheme] of Object.entries(securityDefinitions)) {
|
|
446
|
-
if (scheme.type === 'basic') {
|
|
450
|
+
if (scheme && typeof scheme === 'object' && scheme.type === 'basic') {
|
|
447
451
|
defs.push({ basic_auth: 'Basic <BASE64>' });
|
|
448
452
|
}
|
|
449
|
-
else if (scheme.type === 'apiKey') {
|
|
450
|
-
if (scheme.in === 'header') {
|
|
453
|
+
else if (scheme && typeof scheme === 'object' && scheme.type === 'apiKey') {
|
|
454
|
+
if (scheme && typeof scheme === 'object' && scheme.in === 'header') {
|
|
451
455
|
defs.push({ [scheme.name.toLowerCase()]: `<${scheme.name.toUpperCase()}>` });
|
|
452
456
|
}
|
|
453
|
-
else if (scheme.in === 'query') {
|
|
457
|
+
else if (scheme && typeof scheme === 'object' && scheme.in === 'query') {
|
|
454
458
|
defs.push({ [`query_${scheme.name.toLowerCase()}`]: `<${scheme.name.toUpperCase()}>` });
|
|
455
459
|
}
|
|
456
460
|
}
|
|
457
|
-
else if (scheme.type === 'oauth2') {
|
|
461
|
+
else if (scheme && typeof scheme === 'object' && scheme.type === 'oauth2') {
|
|
458
462
|
defs.push({ bearer_token: 'BEARER <ACCESS_TOKEN>' });
|
|
459
463
|
}
|
|
460
464
|
}
|
|
461
465
|
return defs;
|
|
462
466
|
}
|
|
467
|
+
function cleanYaml(yamlString) {
|
|
468
|
+
return yamlString
|
|
469
|
+
.replace(/\t/g, ' ')
|
|
470
|
+
.replace(/[\u00A0]/g, ' ')
|
|
471
|
+
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '')
|
|
472
|
+
.replace(/[ \t]+$/gm, '')
|
|
473
|
+
.replace(/\r\n/g, '\n');
|
|
474
|
+
}
|
|
475
|
+
function checkYamlForHiddenChars(yamlString) {
|
|
476
|
+
const lines = yamlString.split('\n');
|
|
477
|
+
for (let i = 0; i < lines.length; i++) {
|
|
478
|
+
const line = lines[i];
|
|
479
|
+
if (/\t/.test(line)) {
|
|
480
|
+
throw new Error(`YAML contains a TAB character at line ${i + 1}:\n${line}`);
|
|
481
|
+
}
|
|
482
|
+
if (/\u00A0/.test(line)) {
|
|
483
|
+
throw new Error(`YAML contains a non-breaking space (U+00A0) at line ${i + 1}:\n${line}`);
|
|
484
|
+
}
|
|
485
|
+
if (/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/.test(line)) {
|
|
486
|
+
throw new Error(`YAML contains a non-printable character at line ${i + 1}:\n${line}`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
function validateYaml(yamlString) {
|
|
491
|
+
try {
|
|
492
|
+
(0, js_yaml_2.load)(yamlString);
|
|
493
|
+
}
|
|
494
|
+
catch (e) {
|
|
495
|
+
throw new Error('Generated YAML is invalid: ' + e.message);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
463
498
|
function generateWrekenfile(spec, baseDir) {
|
|
464
499
|
var _a;
|
|
465
500
|
if (!spec || typeof spec !== 'object') {
|
|
@@ -470,7 +505,7 @@ function generateWrekenfile(spec, baseDir) {
|
|
|
470
505
|
}
|
|
471
506
|
// Add swaggerFile path to spec for ref resolution
|
|
472
507
|
spec.swaggerFile = baseDir;
|
|
473
|
-
|
|
508
|
+
let yamlString = (0, js_yaml_1.dump)({
|
|
474
509
|
VERSION: '1.2',
|
|
475
510
|
INIT: {
|
|
476
511
|
DEFAULTS: [
|
|
@@ -481,4 +516,8 @@ function generateWrekenfile(spec, baseDir) {
|
|
|
481
516
|
INTERFACES: extractInterfaces(spec),
|
|
482
517
|
STRUCTS: extractStructs(spec, baseDir),
|
|
483
518
|
}, { noArrayIndent: true });
|
|
519
|
+
yamlString = cleanYaml(yamlString);
|
|
520
|
+
checkYamlForHiddenChars(yamlString);
|
|
521
|
+
validateYaml(yamlString);
|
|
522
|
+
return yamlString;
|
|
484
523
|
}
|
|
@@ -43,6 +43,7 @@ exports.loadEnvironmentFile = loadEnvironmentFile;
|
|
|
43
43
|
exports.extractCollectionVariables = extractCollectionVariables;
|
|
44
44
|
exports.resolveVariables = resolveVariables;
|
|
45
45
|
const fs = __importStar(require("fs"));
|
|
46
|
+
const js_yaml_1 = require("js-yaml");
|
|
46
47
|
function mapType(value) {
|
|
47
48
|
if (typeof value === 'string') {
|
|
48
49
|
// Check for common patterns
|
|
@@ -103,14 +104,24 @@ function extractFieldsFromObject(obj, depth = 0, prefix = '') {
|
|
|
103
104
|
if (typeof obj !== 'object')
|
|
104
105
|
return [];
|
|
105
106
|
const fields = [];
|
|
107
|
+
const keyCount = {};
|
|
106
108
|
for (const [key, value] of Object.entries(obj)) {
|
|
107
109
|
let type = 'ANY';
|
|
108
110
|
let required = 'OPTIONAL';
|
|
111
|
+
// Handle duplicate keys
|
|
112
|
+
let fieldName = key;
|
|
113
|
+
if (keyCount[key] === undefined) {
|
|
114
|
+
keyCount[key] = 1;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
keyCount[key] += 1;
|
|
118
|
+
fieldName = `${key} ${keyCount[key]}`;
|
|
119
|
+
}
|
|
109
120
|
if (Array.isArray(value)) {
|
|
110
121
|
if (value.length > 0) {
|
|
111
122
|
const firstItem = value[0];
|
|
112
123
|
if (typeof firstItem === 'object' && firstItem !== null) {
|
|
113
|
-
type = `[]STRUCT(${prefix}${
|
|
124
|
+
type = `[]STRUCT(${prefix}${fieldName}Item)`;
|
|
114
125
|
}
|
|
115
126
|
else {
|
|
116
127
|
type = `[]${mapType(firstItem)}`;
|
|
@@ -121,13 +132,13 @@ function extractFieldsFromObject(obj, depth = 0, prefix = '') {
|
|
|
121
132
|
}
|
|
122
133
|
}
|
|
123
134
|
else if (typeof value === 'object' && value !== null) {
|
|
124
|
-
type = `STRUCT(${prefix}${
|
|
135
|
+
type = `STRUCT(${prefix}${fieldName})`;
|
|
125
136
|
}
|
|
126
137
|
else {
|
|
127
138
|
type = mapType(value);
|
|
128
139
|
}
|
|
129
140
|
fields.push({
|
|
130
|
-
name:
|
|
141
|
+
name: fieldName,
|
|
131
142
|
type,
|
|
132
143
|
required,
|
|
133
144
|
});
|
|
@@ -175,6 +186,17 @@ function resolveVariables(value, variables) {
|
|
|
175
186
|
}
|
|
176
187
|
function extractStructs(collection, variables) {
|
|
177
188
|
const structs = {};
|
|
189
|
+
const structNameCount = {};
|
|
190
|
+
function getUniqueStructName(name) {
|
|
191
|
+
if (structs[name] === undefined) {
|
|
192
|
+
structNameCount[name] = 1;
|
|
193
|
+
return name;
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
structNameCount[name] = (structNameCount[name] || 1) + 1;
|
|
197
|
+
return `${name} ${structNameCount[name]}`;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
178
200
|
function processItem(item) {
|
|
179
201
|
var _a;
|
|
180
202
|
if (item.request) {
|
|
@@ -186,47 +208,41 @@ function extractStructs(collection, variables) {
|
|
|
186
208
|
if (((_a = item.request.body) === null || _a === void 0 ? void 0 : _a.mode) === 'raw' && item.request.body.raw) {
|
|
187
209
|
const bodyData = parseJsonExample(item.request.body.raw);
|
|
188
210
|
if (bodyData) {
|
|
189
|
-
|
|
211
|
+
let requestStructName = generateStructName(itemName, method, path, 'Request');
|
|
212
|
+
requestStructName = getUniqueStructName(requestStructName);
|
|
190
213
|
const fields = extractFieldsFromObject(bodyData, 0, requestStructName);
|
|
191
|
-
// Always add the struct, even if empty
|
|
192
214
|
structs[requestStructName] = fields.length > 0 ? fields : [];
|
|
193
|
-
// Extract nested structs from request body
|
|
194
215
|
extractNestedStructs(bodyData, structs, requestStructName);
|
|
195
216
|
}
|
|
196
217
|
}
|
|
197
218
|
// Extract response structs from examples
|
|
198
219
|
if (item.response) {
|
|
199
220
|
for (const response of item.response) {
|
|
200
|
-
|
|
221
|
+
let responseStructName = generateStructName(itemName, method, path, `Response${response.code || '200'}`);
|
|
222
|
+
responseStructName = getUniqueStructName(responseStructName);
|
|
201
223
|
if (response.body) {
|
|
202
224
|
const responseData = parseJsonExample(response.body);
|
|
203
225
|
if (responseData) {
|
|
204
226
|
const fields = extractFieldsFromObject(responseData, 0, responseStructName);
|
|
205
|
-
// Always add the struct, even if empty
|
|
206
227
|
structs[responseStructName] = fields.length > 0 ? fields : [];
|
|
207
|
-
// Extract nested structs from response body
|
|
208
228
|
extractNestedStructs(responseData, structs, responseStructName);
|
|
209
229
|
}
|
|
210
230
|
else {
|
|
211
|
-
// If body is not valid JSON or empty, create an empty struct
|
|
212
231
|
structs[responseStructName] = [];
|
|
213
232
|
}
|
|
214
233
|
}
|
|
215
234
|
else {
|
|
216
|
-
// If no body at all, still create an empty struct
|
|
217
235
|
structs[responseStructName] = [];
|
|
218
236
|
}
|
|
219
237
|
}
|
|
220
238
|
}
|
|
221
239
|
}
|
|
222
|
-
// Recursively process nested items
|
|
223
240
|
if (item.item) {
|
|
224
241
|
for (const subItem of item.item) {
|
|
225
242
|
processItem(subItem);
|
|
226
243
|
}
|
|
227
244
|
}
|
|
228
245
|
}
|
|
229
|
-
// Process all items in the collection
|
|
230
246
|
for (const item of collection.item) {
|
|
231
247
|
processItem(item);
|
|
232
248
|
}
|
|
@@ -396,6 +412,17 @@ function extractResponses(item, itemName, method, path) {
|
|
|
396
412
|
}
|
|
397
413
|
function extractOperations(collection, variables) {
|
|
398
414
|
const operations = [];
|
|
415
|
+
const operationNameCount = {};
|
|
416
|
+
function getUniqueOperationName(name) {
|
|
417
|
+
if (operationNameCount[name] === undefined) {
|
|
418
|
+
operationNameCount[name] = 1;
|
|
419
|
+
return name;
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
operationNameCount[name] += 1;
|
|
423
|
+
return `${name} ${operationNameCount[name]}`;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
399
426
|
function processItem(item) {
|
|
400
427
|
if (item.request) {
|
|
401
428
|
const method = item.request.method || 'GET';
|
|
@@ -403,13 +430,13 @@ function extractOperations(collection, variables) {
|
|
|
403
430
|
const path = extractPathFromUrl(url, variables);
|
|
404
431
|
const itemName = item.name || 'unknown';
|
|
405
432
|
// Generate operation ID
|
|
406
|
-
|
|
433
|
+
let operationId = itemName.toLowerCase().replace(/[^a-z0-9]/g, '-');
|
|
434
|
+
operationId = getUniqueOperationName(operationId);
|
|
407
435
|
const { contentType, bodyType } = getContentTypeAndBodyType(item.request);
|
|
408
436
|
const headers = getHeadersForOperation(item.request, variables);
|
|
409
437
|
const inputs = extractParameters(item.request, variables);
|
|
410
438
|
const bodyInputs = extractRequestBody(item.request, itemName, method, path);
|
|
411
439
|
const returns = extractResponses(item, itemName, method, path);
|
|
412
|
-
// Combine all inputs (avoid duplicates)
|
|
413
440
|
const allInputs = [...inputs];
|
|
414
441
|
if (bodyInputs.length > 0) {
|
|
415
442
|
allInputs.push(...bodyInputs);
|
|
@@ -430,20 +457,51 @@ function extractOperations(collection, variables) {
|
|
|
430
457
|
RETURNS: returns,
|
|
431
458
|
});
|
|
432
459
|
}
|
|
433
|
-
// Recursively process nested items
|
|
434
460
|
if (item.item) {
|
|
435
461
|
for (const subItem of item.item) {
|
|
436
462
|
processItem(subItem);
|
|
437
463
|
}
|
|
438
464
|
}
|
|
439
465
|
}
|
|
440
|
-
// Process all items in the collection
|
|
441
466
|
for (const item of collection.item) {
|
|
442
467
|
processItem(item);
|
|
443
468
|
}
|
|
444
469
|
return operations;
|
|
445
470
|
}
|
|
471
|
+
function cleanYaml(yamlString) {
|
|
472
|
+
// Remove tabs, non-breaking spaces, and non-printable chars except standard whitespace and newlines
|
|
473
|
+
return yamlString
|
|
474
|
+
.replace(/\t/g, ' ')
|
|
475
|
+
.replace(/[\u00A0]/g, ' ')
|
|
476
|
+
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '')
|
|
477
|
+
.replace(/[ \t]+$/gm, '')
|
|
478
|
+
.replace(/\r\n/g, '\n');
|
|
479
|
+
}
|
|
480
|
+
function checkYamlForHiddenChars(yamlString) {
|
|
481
|
+
const lines = yamlString.split('\n');
|
|
482
|
+
for (let i = 0; i < lines.length; i++) {
|
|
483
|
+
const line = lines[i];
|
|
484
|
+
if (/\t/.test(line)) {
|
|
485
|
+
throw new Error(`YAML contains a TAB character at line ${i + 1}:\n${line}`);
|
|
486
|
+
}
|
|
487
|
+
if (/\u00A0/.test(line)) {
|
|
488
|
+
throw new Error(`YAML contains a non-breaking space (U+00A0) at line ${i + 1}:\n${line}`);
|
|
489
|
+
}
|
|
490
|
+
if (/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/.test(line)) {
|
|
491
|
+
throw new Error(`YAML contains a non-printable character at line ${i + 1}:\n${line}`);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
function validateYaml(yamlString) {
|
|
496
|
+
try {
|
|
497
|
+
(0, js_yaml_1.load)(yamlString);
|
|
498
|
+
}
|
|
499
|
+
catch (e) {
|
|
500
|
+
throw new Error('Generated YAML is invalid: ' + e.message);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
446
503
|
function generateWrekenfile(collection, variables) {
|
|
504
|
+
var _a;
|
|
447
505
|
if (!collection || typeof collection !== 'object') {
|
|
448
506
|
throw new Error("Argument 'collection' is required and must be an object");
|
|
449
507
|
}
|
|
@@ -453,7 +511,6 @@ function generateWrekenfile(collection, variables) {
|
|
|
453
511
|
const structs = extractStructs(collection, variables);
|
|
454
512
|
const operations = extractOperations(collection, variables);
|
|
455
513
|
let wrekenfile = `VERSION: '1.2'\n`;
|
|
456
|
-
// Add INIT section with defaults from environment variables
|
|
457
514
|
wrekenfile += `INIT:\n`;
|
|
458
515
|
wrekenfile += ` DEFAULTS:\n`;
|
|
459
516
|
if (Object.keys(variables).length === 0) {
|
|
@@ -461,7 +518,6 @@ function generateWrekenfile(collection, variables) {
|
|
|
461
518
|
}
|
|
462
519
|
else {
|
|
463
520
|
for (const [key, value] of Object.entries(variables)) {
|
|
464
|
-
// Skip sensitive values like API keys and signatures
|
|
465
521
|
const sensitiveKeys = ['api_key', 'api-key', 'x-api-key', 'signature', 'x-signature', 'authorization', 'token', 'password', 'secret'];
|
|
466
522
|
const isSensitive = sensitiveKeys.some(sensitiveKey => key.toLowerCase().includes(sensitiveKey));
|
|
467
523
|
if (isSensitive) {
|
|
@@ -472,13 +528,16 @@ function generateWrekenfile(collection, variables) {
|
|
|
472
528
|
}
|
|
473
529
|
}
|
|
474
530
|
}
|
|
475
|
-
// Add INTERFACES section
|
|
476
531
|
wrekenfile += `INTERFACES:\n`;
|
|
477
532
|
for (const operation of operations) {
|
|
478
533
|
wrekenfile += ` ${operation.name}:\n`;
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
534
|
+
// Quote SUMMARY and DESCRIPTION if they contain special characters
|
|
535
|
+
const summary = operation.SUMMARY.includes(':') || operation.SUMMARY.includes('"') ? `"${operation.SUMMARY.replace(/"/g, '\\"')}"` : operation.SUMMARY;
|
|
536
|
+
const description = operation.DESCRIPTION.includes(':') || operation.DESCRIPTION.includes('"') ? `"${operation.DESCRIPTION.replace(/"/g, '\\"')}"` : operation.DESCRIPTION;
|
|
537
|
+
const desc = operation.DESC.includes(':') || operation.DESC.includes('"') ? `"${operation.DESC.replace(/"/g, '\\"')}"` : operation.DESC;
|
|
538
|
+
wrekenfile += ` SUMMARY: ${summary}\n`;
|
|
539
|
+
wrekenfile += ` DESCRIPTION: ${description}\n`;
|
|
540
|
+
wrekenfile += ` DESC: ${desc}\n`;
|
|
482
541
|
wrekenfile += ` ENDPOINT: ${operation.ENDPOINT}\n`;
|
|
483
542
|
wrekenfile += ` VISIBILITY: ${operation.VISIBILITY}\n`;
|
|
484
543
|
wrekenfile += ` HTTP:\n`;
|
|
@@ -490,11 +549,12 @@ function generateWrekenfile(collection, variables) {
|
|
|
490
549
|
}
|
|
491
550
|
}
|
|
492
551
|
wrekenfile += ` BODYTYPE: ${operation.HTTP.BODYTYPE}\n`;
|
|
493
|
-
|
|
552
|
+
// INPUTS
|
|
494
553
|
if (operation.INPUTS.length === 0) {
|
|
495
|
-
wrekenfile += `
|
|
554
|
+
wrekenfile += ` INPUTS: []\n`;
|
|
496
555
|
}
|
|
497
556
|
else {
|
|
557
|
+
wrekenfile += ` INPUTS:\n`;
|
|
498
558
|
for (const input of operation.INPUTS) {
|
|
499
559
|
wrekenfile += ` - name: ${input.name}\n`;
|
|
500
560
|
wrekenfile += ` type: ${input.type}\n`;
|
|
@@ -504,6 +564,7 @@ function generateWrekenfile(collection, variables) {
|
|
|
504
564
|
}
|
|
505
565
|
}
|
|
506
566
|
}
|
|
567
|
+
// RETURNS
|
|
507
568
|
wrekenfile += ` RETURNS:\n`;
|
|
508
569
|
for (const ret of operation.RETURNS) {
|
|
509
570
|
wrekenfile += ` - RETURNTYPE: ${ret.RETURNTYPE}\n`;
|
|
@@ -511,20 +572,33 @@ function generateWrekenfile(collection, variables) {
|
|
|
511
572
|
wrekenfile += ` CODE: '${ret.CODE}'\n`;
|
|
512
573
|
}
|
|
513
574
|
}
|
|
514
|
-
// Add STRUCTS section
|
|
515
575
|
wrekenfile += `STRUCTS:\n`;
|
|
516
576
|
for (const [structName, fields] of Object.entries(structs)) {
|
|
517
|
-
wrekenfile += ` ${structName}:\n`;
|
|
518
577
|
if (fields.length === 0) {
|
|
519
|
-
wrekenfile += `
|
|
578
|
+
wrekenfile += ` ${structName}: []\n`;
|
|
520
579
|
}
|
|
521
580
|
else {
|
|
581
|
+
wrekenfile += ` ${structName}:\n`;
|
|
522
582
|
for (const field of fields) {
|
|
523
|
-
wrekenfile +=
|
|
524
|
-
|
|
525
|
-
|
|
583
|
+
wrekenfile += ' - name: ' + field.name + '\n';
|
|
584
|
+
// Quote type if it contains non-alphanumeric characters (except underscore)
|
|
585
|
+
let typeValue = field.type;
|
|
586
|
+
if (/[^a-zA-Z0-9_]/.test(typeValue)) {
|
|
587
|
+
typeValue = '"' + typeValue.replace(/"/g, '\"') + '"';
|
|
588
|
+
}
|
|
589
|
+
wrekenfile += ' type: ' + typeValue + '\n';
|
|
590
|
+
wrekenfile += " required: '" + field.required + "'\n";
|
|
526
591
|
}
|
|
527
592
|
}
|
|
528
593
|
}
|
|
594
|
+
// Debug print: show first 20 lines of STRUCTS block
|
|
595
|
+
const structsBlock = (_a = wrekenfile.split('STRUCTS:')[1]) === null || _a === void 0 ? void 0 : _a.split('\n').slice(0, 20).join('\n');
|
|
596
|
+
if (structsBlock) {
|
|
597
|
+
console.log('--- STRUCTS block preview ---');
|
|
598
|
+
console.log(structsBlock);
|
|
599
|
+
}
|
|
600
|
+
wrekenfile = cleanYaml(wrekenfile);
|
|
601
|
+
checkYamlForHiddenChars(wrekenfile);
|
|
602
|
+
validateYaml(wrekenfile);
|
|
529
603
|
return wrekenfile;
|
|
530
604
|
}
|
package/package.json
CHANGED