yapi-apifox-to-typescript 4.2.3 → 5.0.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.
@@ -1,588 +1,312 @@
1
- "use strict";
2
-
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
-
5
- exports.__esModule = true;
6
- exports.ApifoxToYApiData = void 0;
7
-
8
- var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
-
10
- var _createForOfIteratorHelperLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/createForOfIteratorHelperLoose"));
11
-
12
- var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
13
-
14
- var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
15
-
16
- var _axios = _interopRequireDefault(require("axios"));
17
-
18
- var _types = require("./types");
19
-
20
- var _utils = require("./utils");
21
-
22
- var ApifoxToYApiData = /*#__PURE__*/function () {
23
- function ApifoxToYApiData(options) {
24
- this.openApiDoc = null;
25
- this.options = void 0;
26
- this.project = {
27
- _id: 0,
28
- _url: '',
29
- name: 'ApifoxProject',
30
- desc: '',
31
- basepath: '',
32
- tag: [],
33
- env: [{
34
- name: '',
35
- domain: ''
36
- }]
37
- };
38
- this.schemas = {};
39
- this.interfaceIdCounter = 0;
40
- this.options = options;
41
- }
42
-
43
- var _proto = ApifoxToYApiData.prototype;
44
-
45
- _proto.resolveRef = function resolveRef(ref) {
46
- if (!ref.startsWith('#/components/schemas/')) {
47
- return null;
48
- }
49
-
50
- var schemaName = ref.replace('#/components/schemas/', '');
51
- return this.schemas[schemaName] || null;
52
- };
53
-
54
- _proto.resolveSchema = function resolveSchema(schema) {
55
- if (!schema) return undefined;
56
-
57
- if (schema.$ref) {
58
- return this.resolveRef(schema.$ref) || schema;
59
- }
60
-
61
- return schema;
62
- };
63
-
64
- _proto.getSchemaType = function getSchemaType(schema) {
65
- if (!schema) return 'string';
66
- var resolved = this.resolveSchema(schema);
67
- if (!resolved) return 'string';
68
- return resolved.type || 'string';
69
- };
70
-
71
- _proto.getRequestParamType = function getRequestParamType(schema) {
72
- var schemaType = this.getSchemaType(schema);
73
- if (schemaType === 'boolean') return _types.RequestParamType.boolean;
74
- if (schemaType === 'number') return _types.RequestParamType.number;
75
- return _types.RequestParamType.string;
76
- };
77
-
78
- _proto.getRequestQueryType = function getRequestQueryType(schema) {
79
- var schemaType = this.getSchemaType(schema);
80
- if (schemaType === 'boolean') return _types.RequestQueryType.boolean;
81
- if (schemaType === 'number') return _types.RequestQueryType.number;
82
- return _types.RequestQueryType.string;
83
- };
84
-
85
- _proto.convertParametersToReqParams = function convertParametersToReqParams(parameters, type) {
86
- var _this = this;
87
-
88
- if (parameters === void 0) {
89
- parameters = [];
90
- }
91
-
92
- if (type === 'path') {
93
- return parameters.filter(function (p) {
94
- return p.in === 'path';
95
- }).map(function (p) {
96
- return {
97
- name: p.name,
98
- desc: p.description || '',
99
- example: p.example || '',
100
- required: p.required ? _types.Required.true : _types.Required.false,
101
- type: _this.getRequestParamType(p.schema)
102
- };
103
- });
104
- }
105
-
106
- return parameters.filter(function (p) {
107
- return p.in === 'query';
108
- }).map(function (p) {
109
- return {
110
- name: p.name,
111
- desc: p.description || '',
112
- example: p.example || '',
113
- required: p.required ? _types.Required.true : _types.Required.false,
114
- type: _this.getRequestQueryType(p.schema)
115
- };
116
- });
117
- };
118
-
119
- _proto.convertRequestBody = function convertRequestBody(requestBody) {
120
- if (!(requestBody != null && requestBody.content)) {
121
- return {
122
- req_body_type: _types.RequestBodyType.none,
123
- req_body_is_json_schema: false,
124
- req_body_form: [],
125
- req_body_other: '',
126
- contentType: ''
127
- };
128
- }
129
-
130
- var contentTypes = Object.keys(requestBody.content);
131
- var jsonContentType = contentTypes.find(function (ct) {
132
- return ct.includes('application/json');
133
- });
134
- var formContentType = contentTypes.find(function (ct) {
135
- return ct.includes('application/x-www-form-urlencoded') || ct.includes('multipart/form-data');
136
- });
137
-
138
- if (jsonContentType) {
139
- var schema = requestBody.content[jsonContentType].schema;
140
- return {
141
- req_body_type: _types.RequestBodyType.json,
142
- req_body_is_json_schema: true,
143
- req_body_form: [],
144
- req_body_other: schema ? JSON.stringify(this.normalizeSchema(schema)) : '',
145
- contentType: jsonContentType
146
- };
147
- }
148
-
149
- if (formContentType) {
150
- var _schema = requestBody.content[formContentType].schema;
151
- var resolvedSchema = this.resolveSchema(_schema);
152
- var formItems = [];
153
-
154
- if (resolvedSchema != null && resolvedSchema.properties) {
155
- var requiredFields = resolvedSchema.required || [];
156
-
157
- for (var _i = 0, _Object$entries = Object.entries(resolvedSchema.properties); _i < _Object$entries.length; _i++) {
158
- var _Object$entries$_i = _Object$entries[_i],
159
- name = _Object$entries$_i[0],
160
- prop = _Object$entries$_i[1];
161
- var resolvedProp = this.resolveSchema(prop);
162
- formItems.push({
163
- name: name,
164
- type: (resolvedProp == null ? void 0 : resolvedProp.type) === 'file' || (resolvedProp == null ? void 0 : resolvedProp.format) === 'binary' ? _types.RequestFormItemType.file : _types.RequestFormItemType.text,
165
- desc: (resolvedProp == null ? void 0 : resolvedProp.description) || '',
166
- example: (resolvedProp == null ? void 0 : resolvedProp.example) || '',
167
- required: requiredFields.includes(name) ? _types.Required.true : _types.Required.false
168
- });
169
- }
170
- }
171
-
172
- return {
173
- req_body_type: _types.RequestBodyType.form,
174
- req_body_is_json_schema: false,
175
- req_body_form: formItems,
176
- req_body_other: '',
177
- contentType: formContentType
178
- };
179
- }
180
-
181
- return {
182
- req_body_type: _types.RequestBodyType.raw,
183
- req_body_is_json_schema: false,
184
- req_body_form: [],
185
- req_body_other: '',
186
- contentType: contentTypes[0] || ''
187
- };
188
- };
189
-
190
- _proto.normalizeSchema = function normalizeSchema(schema, visited) {
191
- if (visited === void 0) {
192
- visited = new Set();
193
- }
194
-
195
- var result = (0, _extends2.default)({}, schema);
196
-
197
- if (schema.$ref) {
198
- if (visited.has(schema.$ref)) {
199
- // 循环引用,直接返回空对象避免无限递归
200
- return {
201
- type: 'object'
202
- };
203
- }
204
-
205
- visited.add(schema.$ref);
206
- var resolved = this.resolveRef(schema.$ref);
207
-
208
- if (resolved) {
209
- return this.normalizeSchema(resolved, new Set(visited));
210
- }
211
- }
212
-
213
- if (schema.properties) {
214
- result.properties = {};
215
-
216
- for (var _i2 = 0, _Object$entries2 = Object.entries(schema.properties); _i2 < _Object$entries2.length; _i2++) {
217
- var _Object$entries2$_i = _Object$entries2[_i2],
218
- key = _Object$entries2$_i[0],
219
- _value = _Object$entries2$_i[1];
220
-
221
- if (_value) {
222
- result.properties[key] = this.normalizeSchema(_value, new Set(visited));
223
- }
224
- }
225
- }
226
-
227
- if (schema.items) {
228
- result.items = this.normalizeSchema(schema.items, new Set(visited));
229
- }
230
-
231
- delete result.$ref;
232
- return result;
233
- };
234
-
235
- _proto.convertResponse = function convertResponse(responses) {
236
- if (responses === void 0) {
237
- responses = {};
238
- }
239
-
240
- var successResponse = responses['200'] || responses['201'] || responses['204'];
241
-
242
- if (!(successResponse != null && successResponse.content)) {
243
- return {
244
- res_body_type: _types.ResponseBodyType.json,
245
- res_body_is_json_schema: false,
246
- res_body: ''
247
- };
248
- }
249
-
250
- var jsonContent = successResponse.content['application/json'];
251
-
252
- if (jsonContent != null && jsonContent.schema) {
253
- return {
254
- res_body_type: _types.ResponseBodyType.json,
255
- res_body_is_json_schema: true,
256
- res_body: JSON.stringify(this.normalizeSchema(jsonContent.schema))
257
- };
258
- }
259
-
260
- return {
261
- res_body_type: _types.ResponseBodyType.json,
262
- res_body_is_json_schema: false,
263
- res_body: ''
264
- };
265
- };
266
-
267
- _proto.generateInterfaceId = function generateInterfaceId() {
268
- this.interfaceIdCounter += 1;
269
- return this.interfaceIdCounter;
270
- }
271
- /**
272
- * 获取接口所属的目录名称
273
- * Apifox 优先使用 x-apifox-folder 字段,其次才用 tags[0]
274
- */
275
- ;
276
-
277
- _proto.getOperationFolder = function getOperationFolder(operation) {
278
- // 优先使用 x-apifox-folder
279
- if (operation['x-apifox-folder']) {
280
- return operation['x-apifox-folder'];
281
- } // 其次使用 tags[0]
282
-
283
-
284
- if (operation.tags && operation.tags.length > 0) {
285
- return operation.tags[0];
286
- }
287
-
288
- return 'default';
289
- };
290
-
291
- _proto.convertOperationToInterface = function convertOperationToInterface(path, method, operation, categoryName, categoryId) {
292
- var _operation$tags;
293
-
294
- var parameters = operation.parameters || [];
295
- var pathParams = this.convertParametersToReqParams(parameters, 'path');
296
- var queryParams = this.convertParametersToReqParams(parameters, 'query'); // const headerParams = parameters.filter(p => p.in === 'header')
297
-
298
- var reqHeaders = [];
299
- var requestBodyInfo = this.convertRequestBody(operation.requestBody);
300
-
301
- if (requestBodyInfo.contentType) {
302
- reqHeaders.push({
303
- name: 'Content-Type',
304
- value: requestBodyInfo.contentType,
305
- desc: '',
306
- example: '',
307
- required: _types.Required.true
308
- });
309
- }
310
-
311
- var responseInfo = this.convertResponse(operation.responses); // 获取接口所属目录:优先使用 x-apifox-folder,其次用 tags
312
-
313
- var folderName = this.getOperationFolder(operation);
314
- var interfaceTags = (_operation$tags = operation.tags) != null && _operation$tags.length ? operation.tags : folderName !== 'default' ? [folderName] : [];
315
- return {
316
- _id: this.generateInterfaceId(),
317
- _category: {
318
- _id: categoryId,
319
- _url: '',
320
- name: categoryName,
321
- desc: '',
322
- add_time: 0,
323
- up_time: 0
324
- },
325
- _project: (0, _extends2.default)({}, this.project),
326
- _url: '',
327
- title: operation.summary || path,
328
- status: operation.deprecated ? 'undone' : 'done',
329
- markdown: operation.description || '',
330
- path: path,
331
- method: method.toUpperCase(),
332
- project_id: this.options.projectId,
333
- catid: categoryId,
334
- tag: interfaceTags,
335
- req_headers: reqHeaders,
336
- req_params: pathParams,
337
- req_query: queryParams,
338
- req_body_type: requestBodyInfo.req_body_type,
339
- req_body_is_json_schema: requestBodyInfo.req_body_is_json_schema,
340
- req_body_form: requestBodyInfo.req_body_form,
341
- req_body_other: requestBodyInfo.req_body_other,
342
- res_body_type: responseInfo.res_body_type,
343
- res_body_is_json_schema: responseInfo.res_body_is_json_schema,
344
- res_body: responseInfo.res_body,
345
- add_time: Math.floor(Date.now() / 1000),
346
- up_time: Math.floor(Date.now() / 1000),
347
- uid: 0
348
- };
349
- };
350
-
351
- _proto.getProjectInfo = /*#__PURE__*/function () {
352
- var _getProjectInfo = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
353
- var _this$options$addFold, _this$options$oasVers, _this$options$exportF, _this$openApiDoc, _this$openApiDoc$comp, _this$openApiDoc2, _this$openApiDoc2$inf, _this$openApiDoc3, _this$openApiDoc3$inf;
354
-
355
- var baseUrl, response;
356
- return _regenerator.default.wrap(function _callee$(_context) {
357
- while (1) {
358
- switch (_context.prev = _context.next) {
359
- case 0:
360
- baseUrl = this.options.serverUrl.replace(/\/$/, '');
361
- _context.next = 3;
362
- return _axios.default.post(baseUrl + "/v1/projects/" + this.options.projectId + "/export-openapi?locale=zh-CN", {
363
- scope: {
364
- type: 'ALL',
365
- excludedByTags: []
366
- },
367
- options: {
368
- includeApifoxExtensionProperties: true,
369
- addFoldersToTags: (_this$options$addFold = this.options.addFoldersToTags) != null ? _this$options$addFold : true
370
- },
371
- oasVersion: (_this$options$oasVers = this.options.oasVersion) != null ? _this$options$oasVers : '3.0',
372
- exportFormat: (_this$options$exportF = this.options.exportFormat) != null ? _this$options$exportF : 'JSON'
373
- }, {
374
- headers: {
375
- 'Authorization': "Bearer " + this.options.token,
376
- 'X-Apifox-Api-Version': '2024-03-28',
377
- 'Content-Type': 'application/json'
378
- }
379
- });
380
-
381
- case 3:
382
- response = _context.sent;
383
- this.openApiDoc = response.data;
384
-
385
- if ((_this$openApiDoc = this.openApiDoc) != null && (_this$openApiDoc$comp = _this$openApiDoc.components) != null && _this$openApiDoc$comp.schemas) {
386
- this.schemas = this.openApiDoc.components.schemas;
387
- }
388
-
389
- this.project._id = this.options.projectId;
390
- this.project.name = ((_this$openApiDoc2 = this.openApiDoc) == null ? void 0 : (_this$openApiDoc2$inf = _this$openApiDoc2.info) == null ? void 0 : _this$openApiDoc2$inf.title) || 'ApifoxProject';
391
- this.project.desc = ((_this$openApiDoc3 = this.openApiDoc) == null ? void 0 : (_this$openApiDoc3$inf = _this$openApiDoc3.info) == null ? void 0 : _this$openApiDoc3$inf.description) || '';
392
- return _context.abrupt("return", (0, _extends2.default)({}, this.project, {
393
- cats: [],
394
- getMockUrl: function getMockUrl() {
395
- return '';
396
- },
397
- getDevUrl: function getDevUrl() {
398
- return '';
399
- },
400
- getProdUrl: function getProdUrl() {
401
- return '';
402
- }
403
- }));
404
-
405
- case 10:
406
- case "end":
407
- return _context.stop();
408
- }
409
- }
410
- }, _callee, this);
411
- }));
412
-
413
- function getProjectInfo() {
414
- return _getProjectInfo.apply(this, arguments);
415
- }
416
-
417
- return getProjectInfo;
418
- }();
419
-
420
- _proto.getTagId = function getTagId(tagName) {
421
- var hash = 0;
422
-
423
- for (var i = 0; i < tagName.length; i++) {
424
- var _char = tagName.charCodeAt(i);
425
-
426
- hash = (hash << 5) - hash + _char;
427
- hash = hash & hash;
428
- }
429
-
430
- return Math.abs(hash);
431
- }
432
- /**
433
- * 从 OpenAPI tags 中获取目录对应的 tag
434
- * 如果 x-apifox-folder 在 tags 中存在,则返回对应的 tag;否则返回 x-apifox-folder 本身
435
- */
436
- ;
437
-
438
- _proto.getMatchingTag = function getMatchingTag(xApifoxFolder) {
439
- var _this$openApiDoc4;
440
-
441
- if (!((_this$openApiDoc4 = this.openApiDoc) != null && _this$openApiDoc4.tags)) return null; // 先精确匹配
442
-
443
- var exactMatch = this.openApiDoc.tags.find(function (tag) {
444
- return tag.name === xApifoxFolder;
445
- });
446
- if (exactMatch) return exactMatch.name; // 如果 addFoldersToTags=true,可能目录名被添加到 tag 描述或其他地方
447
- // 或者 tag.name 本身就包含了目录路径
448
-
449
- return null;
450
- };
451
-
452
- _proto.getCats = function getCats(cats) {
453
- var _this2 = this;
454
-
455
- var categoryIds = (0, _utils.getFilteredCat)(cats, [], true);
456
-
457
- if (categoryIds.length === 0 || categoryIds.includes(0)) {
458
- var _this$openApiDoc5;
459
-
460
- if (!((_this$openApiDoc5 = this.openApiDoc) != null && _this$openApiDoc5.tags)) {
461
- return [];
462
- }
463
-
464
- var tagIds = this.openApiDoc.tags.map(function (tag) {
465
- return _this2.getTagId(tag.name);
466
- });
467
- return tagIds;
468
- }
469
-
470
- return categoryIds;
471
- };
472
-
473
- _proto.getInterfaceList = /*#__PURE__*/function () {
474
- var _getInterfaceList = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(syntheticalConfig) {
475
- var _this$openApiDoc6;
476
-
477
- var catId, interfaces, seenKeys, methods, _i3, _Object$entries3, _Object$entries3$_i, path, pathItem, _iterator, _step, method, operation, xApifoxFolder, tagId, key, categoryTag, interfaceInfo;
478
-
479
- return _regenerator.default.wrap(function _callee2$(_context2) {
480
- while (1) {
481
- switch (_context2.prev = _context2.next) {
482
- case 0:
483
- if ((_this$openApiDoc6 = this.openApiDoc) != null && _this$openApiDoc6.paths) {
484
- _context2.next = 2;
485
- break;
486
- }
487
-
488
- return _context2.abrupt("return", []);
489
-
490
- case 2:
491
- catId = syntheticalConfig.id;
492
- interfaces = []; // 用于去重:基于 path + method
493
-
494
- seenKeys = new Set(); // 调试:获取 tag 名称
495
- // let tagName = 'unknown'
496
- // if (this.openApiDoc?.tags) {
497
- // const tag = this.openApiDoc.tags.find(
498
- // t => this.getTagId(t.name) === catId,
499
- // )
500
- // tagName = tag?.name || `unknown(${catId})`
501
- // }
502
-
503
- methods = ['get', 'post', 'put', 'delete', 'patch', 'head', 'options'];
504
- _i3 = 0, _Object$entries3 = Object.entries(this.openApiDoc.paths);
505
-
506
- case 7:
507
- if (!(_i3 < _Object$entries3.length)) {
508
- _context2.next = 30;
509
- break;
510
- }
511
-
512
- _Object$entries3$_i = _Object$entries3[_i3], path = _Object$entries3$_i[0], pathItem = _Object$entries3$_i[1];
513
- _iterator = (0, _createForOfIteratorHelperLoose2.default)(methods);
514
-
515
- case 10:
516
- if ((_step = _iterator()).done) {
517
- _context2.next = 27;
518
- break;
519
- }
520
-
521
- method = _step.value;
522
- operation = pathItem[method];
523
-
524
- if (operation) {
525
- _context2.next = 15;
526
- break;
527
- }
528
-
529
- return _context2.abrupt("continue", 25);
530
-
531
- case 15:
532
- // 获取接口的目录信息:优先使用 x-apifox-folder
533
- xApifoxFolder = this.getOperationFolder(operation);
534
- tagId = this.getTagId(xApifoxFolder);
535
-
536
- if (!(tagId === catId)) {
537
- _context2.next = 25;
538
- break;
539
- }
540
-
541
- // 去重:基于 path + method
542
- key = method.toUpperCase() + ":" + path;
543
-
544
- if (!seenKeys.has(key)) {
545
- _context2.next = 21;
546
- break;
547
- }
548
-
549
- return _context2.abrupt("continue", 25);
550
-
551
- case 21:
552
- seenKeys.add(key); // 根据实际的目录名称来确定分类
553
-
554
- categoryTag = xApifoxFolder;
555
- interfaceInfo = this.convertOperationToInterface(path, method, operation, categoryTag, catId);
556
- interfaces.push(interfaceInfo);
557
-
558
- case 25:
559
- _context2.next = 10;
560
- break;
561
-
562
- case 27:
563
- _i3++;
564
- _context2.next = 7;
565
- break;
566
-
567
- case 30:
568
- return _context2.abrupt("return", interfaces);
569
-
570
- case 31:
571
- case "end":
572
- return _context2.stop();
573
- }
574
- }
575
- }, _callee2, this);
576
- }));
577
-
578
- function getInterfaceList(_x) {
579
- return _getInterfaceList.apply(this, arguments);
580
- }
581
-
582
- return getInterfaceList;
583
- }();
584
-
585
- return ApifoxToYApiData;
586
- }();
587
-
588
- exports.ApifoxToYApiData = ApifoxToYApiData;
1
+ const require_runtime = require("./_virtual/_rolldown/runtime.js");
2
+ const require_types = require("./types.js");
3
+ const require_utils = require("./utils.js");
4
+ let axios = require("axios");
5
+ axios = require_runtime.__toESM(axios);
6
+ //#region src/ApifoxToYApiData.ts
7
+ var ApifoxToYApiData = class {
8
+ constructor(options) {
9
+ this.openApiDoc = null;
10
+ this.project = {
11
+ _id: 0,
12
+ _url: "",
13
+ name: "ApifoxProject",
14
+ desc: "",
15
+ basepath: "",
16
+ tag: [],
17
+ env: [{
18
+ name: "",
19
+ domain: ""
20
+ }]
21
+ };
22
+ this.schemas = {};
23
+ this.interfaceIdCounter = 0;
24
+ this.options = options;
25
+ }
26
+ resolveRef(ref) {
27
+ if (!ref.startsWith("#/components/schemas/")) return null;
28
+ const schemaName = ref.replace("#/components/schemas/", "");
29
+ return this.schemas[schemaName] || null;
30
+ }
31
+ resolveSchema(schema) {
32
+ if (!schema) return void 0;
33
+ if (schema.$ref) return this.resolveRef(schema.$ref) || schema;
34
+ return schema;
35
+ }
36
+ getSchemaType(schema) {
37
+ if (!schema) return "string";
38
+ const resolved = this.resolveSchema(schema);
39
+ if (!resolved) return "string";
40
+ return resolved.type || "string";
41
+ }
42
+ getRequestParamType(schema) {
43
+ const schemaType = this.getSchemaType(schema);
44
+ if (schemaType === "boolean") return require_types.RequestParamType.boolean;
45
+ if (schemaType === "number") return require_types.RequestParamType.number;
46
+ return require_types.RequestParamType.string;
47
+ }
48
+ getRequestQueryType(schema) {
49
+ const schemaType = this.getSchemaType(schema);
50
+ if (schemaType === "boolean") return require_types.RequestQueryType.boolean;
51
+ if (schemaType === "number") return require_types.RequestQueryType.number;
52
+ return require_types.RequestQueryType.string;
53
+ }
54
+ convertParametersToReqParams(parameters = [], type) {
55
+ if (type === "path") return parameters.filter((p) => p.in === "path").map((p) => ({
56
+ name: p.name,
57
+ desc: p.description || "",
58
+ example: p.example || "",
59
+ required: p.required ? require_types.Required.true : require_types.Required.false,
60
+ type: this.getRequestParamType(p.schema)
61
+ }));
62
+ return parameters.filter((p) => p.in === "query").map((p) => ({
63
+ name: p.name,
64
+ desc: p.description || "",
65
+ example: p.example || "",
66
+ required: p.required ? require_types.Required.true : require_types.Required.false,
67
+ type: this.getRequestQueryType(p.schema)
68
+ }));
69
+ }
70
+ convertRequestBody(requestBody) {
71
+ if (!requestBody?.content) return {
72
+ req_body_type: require_types.RequestBodyType.none,
73
+ req_body_is_json_schema: false,
74
+ req_body_form: [],
75
+ req_body_other: "",
76
+ contentType: ""
77
+ };
78
+ const contentTypes = Object.keys(requestBody.content);
79
+ const jsonContentType = contentTypes.find((ct) => ct.includes("application/json"));
80
+ const formContentType = contentTypes.find((ct) => ct.includes("application/x-www-form-urlencoded") || ct.includes("multipart/form-data"));
81
+ if (jsonContentType) {
82
+ const schema = requestBody.content[jsonContentType].schema;
83
+ return {
84
+ req_body_type: require_types.RequestBodyType.json,
85
+ req_body_is_json_schema: true,
86
+ req_body_form: [],
87
+ req_body_other: schema ? JSON.stringify(this.normalizeSchema(schema)) : "",
88
+ contentType: jsonContentType
89
+ };
90
+ }
91
+ if (formContentType) {
92
+ const schema = requestBody.content[formContentType].schema;
93
+ const resolvedSchema = this.resolveSchema(schema);
94
+ const formItems = [];
95
+ if (resolvedSchema?.properties) {
96
+ const requiredFields = resolvedSchema.required || [];
97
+ for (const [name, prop] of Object.entries(resolvedSchema.properties)) {
98
+ const resolvedProp = this.resolveSchema(prop);
99
+ formItems.push({
100
+ name,
101
+ type: resolvedProp?.type === "file" || resolvedProp?.format === "binary" ? require_types.RequestFormItemType.file : require_types.RequestFormItemType.text,
102
+ desc: resolvedProp?.description || "",
103
+ example: resolvedProp?.example || "",
104
+ required: requiredFields.includes(name) ? require_types.Required.true : require_types.Required.false
105
+ });
106
+ }
107
+ }
108
+ return {
109
+ req_body_type: require_types.RequestBodyType.form,
110
+ req_body_is_json_schema: false,
111
+ req_body_form: formItems,
112
+ req_body_other: "",
113
+ contentType: formContentType
114
+ };
115
+ }
116
+ return {
117
+ req_body_type: require_types.RequestBodyType.raw,
118
+ req_body_is_json_schema: false,
119
+ req_body_form: [],
120
+ req_body_other: "",
121
+ contentType: contentTypes[0] || ""
122
+ };
123
+ }
124
+ normalizeSchema(schema, visited = /* @__PURE__ */ new Set()) {
125
+ const result = { ...schema };
126
+ if (schema.$ref) {
127
+ if (visited.has(schema.$ref)) return { type: "object" };
128
+ visited.add(schema.$ref);
129
+ const resolved = this.resolveRef(schema.$ref);
130
+ if (resolved) return this.normalizeSchema(resolved, new Set(visited));
131
+ }
132
+ if (schema.properties) {
133
+ result.properties = {};
134
+ for (const [key, value] of Object.entries(schema.properties)) if (value) result.properties[key] = this.normalizeSchema(value, new Set(visited));
135
+ }
136
+ if (schema.items) result.items = this.normalizeSchema(schema.items, new Set(visited));
137
+ delete result.$ref;
138
+ return result;
139
+ }
140
+ convertResponse(responses = {}) {
141
+ const successResponse = responses["200"] || responses["201"] || responses["204"];
142
+ if (!successResponse?.content) return {
143
+ res_body_type: require_types.ResponseBodyType.json,
144
+ res_body_is_json_schema: false,
145
+ res_body: ""
146
+ };
147
+ const jsonContent = successResponse.content["application/json"];
148
+ if (jsonContent?.schema) return {
149
+ res_body_type: require_types.ResponseBodyType.json,
150
+ res_body_is_json_schema: true,
151
+ res_body: JSON.stringify(this.normalizeSchema(jsonContent.schema))
152
+ };
153
+ return {
154
+ res_body_type: require_types.ResponseBodyType.json,
155
+ res_body_is_json_schema: false,
156
+ res_body: ""
157
+ };
158
+ }
159
+ generateInterfaceId() {
160
+ this.interfaceIdCounter += 1;
161
+ return this.interfaceIdCounter;
162
+ }
163
+ /**
164
+ * 获取接口所属的目录名称
165
+ * Apifox 优先使用 x-apifox-folder 字段,其次才用 tags[0]
166
+ */
167
+ getOperationFolder(operation) {
168
+ if (operation["x-apifox-folder"]) return operation["x-apifox-folder"];
169
+ if (operation.tags && operation.tags.length > 0) return operation.tags[0];
170
+ return "default";
171
+ }
172
+ convertOperationToInterface(path, method, operation, categoryName, categoryId) {
173
+ const parameters = operation.parameters || [];
174
+ const pathParams = this.convertParametersToReqParams(parameters, "path");
175
+ const queryParams = this.convertParametersToReqParams(parameters, "query");
176
+ const reqHeaders = [];
177
+ const requestBodyInfo = this.convertRequestBody(operation.requestBody);
178
+ if (requestBodyInfo.contentType) reqHeaders.push({
179
+ name: "Content-Type",
180
+ value: requestBodyInfo.contentType,
181
+ desc: "",
182
+ example: "",
183
+ required: require_types.Required.true
184
+ });
185
+ const responseInfo = this.convertResponse(operation.responses);
186
+ const folderName = this.getOperationFolder(operation);
187
+ const interfaceTags = operation.tags?.length ? operation.tags : folderName !== "default" ? [folderName] : [];
188
+ return {
189
+ _id: this.generateInterfaceId(),
190
+ _category: {
191
+ _id: categoryId,
192
+ _url: "",
193
+ name: categoryName,
194
+ desc: "",
195
+ add_time: 0,
196
+ up_time: 0
197
+ },
198
+ _project: { ...this.project },
199
+ _url: "",
200
+ title: operation.summary || path,
201
+ status: operation.deprecated ? "undone" : "done",
202
+ markdown: operation.description || "",
203
+ path,
204
+ method: method.toUpperCase(),
205
+ project_id: this.options.projectId,
206
+ catid: categoryId,
207
+ tag: interfaceTags,
208
+ req_headers: reqHeaders,
209
+ req_params: pathParams,
210
+ req_query: queryParams,
211
+ req_body_type: requestBodyInfo.req_body_type,
212
+ req_body_is_json_schema: requestBodyInfo.req_body_is_json_schema,
213
+ req_body_form: requestBodyInfo.req_body_form,
214
+ req_body_other: requestBodyInfo.req_body_other,
215
+ res_body_type: responseInfo.res_body_type,
216
+ res_body_is_json_schema: responseInfo.res_body_is_json_schema,
217
+ res_body: responseInfo.res_body,
218
+ add_time: Math.floor(Date.now() / 1e3),
219
+ up_time: Math.floor(Date.now() / 1e3),
220
+ uid: 0
221
+ };
222
+ }
223
+ async getProjectInfo() {
224
+ const baseUrl = this.options.serverUrl.replace(/\/$/, "");
225
+ const response = await axios.default.post(`${baseUrl}/v1/projects/${this.options.projectId}/export-openapi?locale=zh-CN`, {
226
+ scope: {
227
+ type: "ALL",
228
+ excludedByTags: []
229
+ },
230
+ options: {
231
+ includeApifoxExtensionProperties: true,
232
+ addFoldersToTags: this.options.addFoldersToTags ?? true
233
+ },
234
+ oasVersion: this.options.oasVersion ?? "3.0",
235
+ exportFormat: this.options.exportFormat ?? "JSON"
236
+ }, { headers: {
237
+ Authorization: `Bearer ${this.options.token}`,
238
+ "X-Apifox-Api-Version": "2024-03-28",
239
+ "Content-Type": "application/json"
240
+ } });
241
+ this.openApiDoc = response.data;
242
+ if (this.openApiDoc?.components?.schemas) this.schemas = this.openApiDoc.components.schemas;
243
+ this.project._id = this.options.projectId;
244
+ this.project.name = this.openApiDoc?.info?.title || "ApifoxProject";
245
+ this.project.desc = this.openApiDoc?.info?.description || "";
246
+ return {
247
+ ...this.project,
248
+ cats: [],
249
+ getMockUrl: () => "",
250
+ getDevUrl: () => "",
251
+ getProdUrl: () => ""
252
+ };
253
+ }
254
+ getTagId(tagName) {
255
+ let hash = 0;
256
+ for (let i = 0; i < tagName.length; i++) {
257
+ const char = tagName.charCodeAt(i);
258
+ hash = (hash << 5) - hash + char;
259
+ hash = hash & hash;
260
+ }
261
+ return Math.abs(hash);
262
+ }
263
+ /**
264
+ * 从 OpenAPI tags 中获取目录对应的 tag
265
+ * 如果 x-apifox-folder 在 tags 中存在,则返回对应的 tag;否则返回 x-apifox-folder 本身
266
+ */
267
+ getMatchingTag(xApifoxFolder) {
268
+ if (!this.openApiDoc?.tags) return null;
269
+ const exactMatch = this.openApiDoc.tags.find((tag) => tag.name === xApifoxFolder);
270
+ if (exactMatch) return exactMatch.name;
271
+ return null;
272
+ }
273
+ getCats(cats) {
274
+ const categoryIds = require_utils.getFilteredCat(cats, [], true);
275
+ if (categoryIds.length === 0 || categoryIds.includes(0)) {
276
+ if (!this.openApiDoc?.tags) return [];
277
+ return this.openApiDoc.tags.map((tag) => this.getTagId(tag.name));
278
+ }
279
+ return categoryIds;
280
+ }
281
+ async getInterfaceList(syntheticalConfig) {
282
+ if (!this.openApiDoc?.paths) return [];
283
+ const catId = syntheticalConfig.id;
284
+ const interfaces = [];
285
+ const seenKeys = /* @__PURE__ */ new Set();
286
+ const methods = [
287
+ "get",
288
+ "post",
289
+ "put",
290
+ "delete",
291
+ "patch",
292
+ "head",
293
+ "options"
294
+ ];
295
+ for (const [path, pathItem] of Object.entries(this.openApiDoc.paths)) for (const method of methods) {
296
+ const operation = pathItem[method];
297
+ if (!operation) continue;
298
+ const xApifoxFolder = this.getOperationFolder(operation);
299
+ if (this.getTagId(xApifoxFolder) === catId) {
300
+ const key = `${method.toUpperCase()}:${path}`;
301
+ if (seenKeys.has(key)) continue;
302
+ seenKeys.add(key);
303
+ const categoryTag = xApifoxFolder;
304
+ const interfaceInfo = this.convertOperationToInterface(path, method, operation, categoryTag, catId);
305
+ interfaces.push(interfaceInfo);
306
+ }
307
+ }
308
+ return interfaces;
309
+ }
310
+ };
311
+ //#endregion
312
+ exports.ApifoxToYApiData = ApifoxToYApiData;