echoapi-converter 0.0.90 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ import { ApipostObj } from '@/types/index';
3
3
  declare class ApipostToSwagger {
4
4
  apiToSwaggerOrigin: Partial<ApipostObj>;
5
5
  apiToSwaggerResult: any;
6
- apiToSwaggerVersion: "2.0" | "3.0";
6
+ apiToSwaggerVersion: "2.0" | "3.0" | "3.1";
7
7
  constructor();
8
8
  private handleProject;
9
9
  private handleModels;
@@ -13,7 +13,12 @@ declare class ApipostToSwagger {
13
13
  private array2NamePathObj;
14
14
  private array2TreeExport;
15
15
  private handleApis;
16
- apiToswagger(apipostObj: ApipostObj, version: "2.0" | "3.0", language?: 'en' | 'zh-cn' | 'ja' | 'zh-hant' | 'id'): Promise<{
16
+ private sortApiNodes;
17
+ private sortByXSortPreserveStructure;
18
+ apiToswagger(apipostObj: ApipostObj, version: "2.0" | "3.0" | "3.1", language?: "en" | "zh-cn" | "ja" | "zh-hant" | "id", options?: {
19
+ includeExt?: boolean;
20
+ dirAsTags?: boolean;
21
+ }): Promise<{
17
22
  status: "error" | "success";
18
23
  message: string;
19
24
  data: any;
package/dist/index.js CHANGED
@@ -2170,7 +2170,7 @@ const t = (key, data) => {
2170
2170
  // @ts-nocheck
2171
2171
  const MAX_DEEP_COUNT = 10;
2172
2172
  const schemaPath = (name, version) => {
2173
- if (version === '2.0') {
2173
+ if (version === "2.0") {
2174
2174
  return `#/definitions/${name}`;
2175
2175
  }
2176
2176
  return `#/components/schemas/${name}`;
@@ -2179,18 +2179,19 @@ const deepModelRef = (originSchema, schemaMap, startSchema, count = 0) => {
2179
2179
  try {
2180
2180
  let deepCount = lodash.cloneDeep(count);
2181
2181
  const schema = lodash.cloneDeep(originSchema);
2182
- if (lodash.has(schema, 'x-schema-refs')) {
2183
- const refsValue = schema?.['x-schema-refs'];
2182
+ if (lodash.has(schema, "x-schema-refs")) {
2183
+ const refsValue = schema?.["x-schema-refs"];
2184
2184
  if (!lodash.isEmpty(refsValue) &&
2185
- Object.prototype.toString.call(schema?.properties) === '[object Object]') {
2186
- const properties = Object.prototype.toString.call(schema?.properties) === '[object Object]'
2185
+ Object.prototype.toString.call(schema?.properties) === "[object Object]") {
2186
+ const properties = Object.prototype.toString.call(schema?.properties) ===
2187
+ "[object Object]"
2187
2188
  ? schema?.properties
2188
2189
  : {};
2189
- schema['properties'] = lodash.values(refsValue)?.reduce((pre, item) => {
2190
- let refId = '';
2190
+ schema["properties"] = lodash.values(refsValue)?.reduce((pre, item) => {
2191
+ let refId = "";
2191
2192
  let refPath = item?.$ref;
2192
2193
  if (lodash.isString(refPath)) {
2193
- refId = refPath.split('/').pop() || '';
2194
+ refId = refPath.split("/").pop() || "";
2194
2195
  }
2195
2196
  if (refId &&
2196
2197
  lodash.has(schemaMap, refId) &&
@@ -2202,7 +2203,7 @@ const deepModelRef = (originSchema, schemaMap, startSchema, count = 0) => {
2202
2203
  // $ref: mapValue?.path,
2203
2204
  // };
2204
2205
  let mapSchema = mapValue?.schema;
2205
- if (lodash.has(mapValue?.schema, 'x-schema-refs')) {
2206
+ if (lodash.has(mapValue?.schema, "x-schema-refs")) {
2206
2207
  if (deepCount > MAX_DEEP_COUNT) {
2207
2208
  mapSchema = mapValue?.schema;
2208
2209
  }
@@ -2233,17 +2234,17 @@ const deepModelRef = (originSchema, schemaMap, startSchema, count = 0) => {
2233
2234
  }, properties);
2234
2235
  }
2235
2236
  }
2236
- if (lodash.has(schema, 'properties') &&
2237
- Object.prototype.toString.call(schema?.properties) === '[object Object]') {
2237
+ if (lodash.has(schema, "properties") &&
2238
+ Object.prototype.toString.call(schema?.properties) === "[object Object]") {
2238
2239
  let properties_keys = lodash.keys(schema?.properties);
2239
2240
  if (
2240
2241
  // 优化展示顺序
2241
- schema?.['x-schema-orders'] &&
2242
- lodash.isArray(schema?.['x-schema-orders']) &&
2243
- lodash.isEqual(lodash.sortBy(schema?.['x-schema-orders']), lodash.sortBy(lodash.keys(schema?.properties)))) {
2244
- properties_keys = schema?.['x-schema-orders'];
2242
+ schema?.["x-schema-orders"] &&
2243
+ lodash.isArray(schema?.["x-schema-orders"]) &&
2244
+ lodash.isEqual(lodash.sortBy(schema?.["x-schema-orders"]), lodash.sortBy(lodash.keys(schema?.properties)))) {
2245
+ properties_keys = schema?.["x-schema-orders"];
2245
2246
  }
2246
- schema['properties'] = properties_keys?.reduce((pre, key) => {
2247
+ schema["properties"] = properties_keys?.reduce((pre, key) => {
2247
2248
  if (key && !lodash.isEmpty(schema?.properties[key])) {
2248
2249
  if (deepCount > MAX_DEEP_COUNT) {
2249
2250
  pre[key] = schema?.properties[key];
@@ -2257,30 +2258,35 @@ const deepModelRef = (originSchema, schemaMap, startSchema, count = 0) => {
2257
2258
  }
2258
2259
  if (lodash.isString(schema?.$ref) && schema.$ref.length > 0) {
2259
2260
  let refPath = schema?.$ref;
2260
- const parts = refPath.split('/');
2261
+ const parts = refPath.split("/");
2261
2262
  if (parts.length > 0) {
2262
- let refId = parts[parts.length - 1] || '';
2263
+ let refId = parts[parts.length - 1] || "";
2263
2264
  if (lodash.has(schemaMap, refId)) {
2264
2265
  parts[parts.length - 1] = schemaMap[refId]?.name || refId; // 替换最后一部分
2265
- schema.$ref = parts.join('/');
2266
+ schema.$ref = parts.join("/");
2266
2267
  }
2267
2268
  }
2268
2269
  }
2269
2270
  // 特殊处理properties为数组的情况
2270
- if (lodash.has(schema, 'properties') && Object.prototype.toString.call(schema?.properties) !== '[object Object]') {
2271
+ if (lodash.has(schema, "properties") &&
2272
+ Object.prototype.toString.call(schema?.properties) !== "[object Object]") {
2271
2273
  schema.properties = {};
2272
2274
  }
2273
- if (schema?.type === 'array' && lodash.has(schema, 'items')) {
2274
- if (lodash.has(schema?.items, 'properties') &&
2275
- Object.prototype.toString.call(schema?.items?.properties) === '[object Object]') {
2275
+ if (schema?.type === "array" && lodash.has(schema, "items")) {
2276
+ if (lodash.has(schema?.items, "properties") &&
2277
+ Object.prototype.toString.call(schema?.items?.properties) ===
2278
+ "[object Object]") {
2276
2279
  if (deepCount <= MAX_DEEP_COUNT) {
2277
2280
  schema.items = deepModelRef(schema?.items, schemaMap, startSchema || originSchema, ++deepCount);
2278
2281
  }
2279
2282
  }
2283
+ else if (lodash.has(schema?.items, "$ref")) {
2284
+ schema.items = deepModelRef(schema?.items, schemaMap, startSchema || originSchema, deepCount);
2285
+ }
2280
2286
  }
2281
2287
  // 处理数组的引用
2282
- delete schema?.['x-schema-orders'];
2283
- delete schema?.['x-schema-refs'];
2288
+ delete schema?.["x-schema-orders"];
2289
+ delete schema?.["x-schema-refs"];
2284
2290
  return schema;
2285
2291
  }
2286
2292
  catch (err) {
@@ -2409,10 +2415,10 @@ const handlePathItemParameters = (type, arr, pathItem) => {
2409
2415
  example: String(item?.value || ""),
2410
2416
  schema: {
2411
2417
  type: item?.field_type?.toLowerCase(),
2412
- ...(item?.schema || {})
2418
+ ...(item?.schema || {}),
2413
2419
  },
2414
2420
  };
2415
- const oldFiledObj = OLD_FIELD_TYPES?.[param?.schema?.type || ''];
2421
+ const oldFiledObj = OLD_FIELD_TYPES?.[param?.schema?.type || ""];
2416
2422
  if (oldFiledObj) {
2417
2423
  param.field_type = oldFiledObj.type;
2418
2424
  param.schema.type = oldFiledObj.type;
@@ -2444,7 +2450,7 @@ const handlePathItemBodyParameters = (body, pathItem, modelsObj) => {
2444
2450
  name: "body",
2445
2451
  description: "",
2446
2452
  schema: deepModelRef(body?.raw_schema || {
2447
- type: 'object'
2453
+ type: "object",
2448
2454
  }, modelsObj),
2449
2455
  type: "string",
2450
2456
  example: body?.raw || "",
@@ -2477,22 +2483,22 @@ const handleGraphqlRequestBody = (query_list, query_schema, pathItem, modelsObj)
2477
2483
  if (!lodash.has(pathItem, "requestBody")) {
2478
2484
  pathItem.requestBody = {
2479
2485
  content: {},
2480
- 'x-schema': query_schema || {}
2486
+ "x-schema": query_schema || {},
2481
2487
  };
2482
2488
  }
2483
- query_list.forEach(m => {
2489
+ query_list.forEach((m) => {
2484
2490
  const mode = `x-${m?.param_id}`;
2485
2491
  pathItem.requestBody.content[mode] = {
2486
- 'x-query': m?.query || '',
2487
- 'x-variables': m?.variables || '',
2488
- 'x-summary': m?.name || '',
2489
- 'x-response': {
2490
- 'x-mode': m?.response?.mode || '',
2492
+ "x-query": m?.query || "",
2493
+ "x-variables": m?.variables || "",
2494
+ "x-summary": m?.name || "",
2495
+ "x-response": {
2496
+ "x-mode": m?.response?.mode || "",
2491
2497
  example: m?.response?.raw || "",
2492
2498
  schema: deepModelRef(m?.response?.raw_schema || {
2493
2499
  type: "object",
2494
2500
  }, modelsObj),
2495
- }
2501
+ },
2496
2502
  };
2497
2503
  });
2498
2504
  }
@@ -2500,16 +2506,22 @@ const handleGraphqlRequestBody = (query_list, query_schema, pathItem, modelsObj)
2500
2506
  const handleSocketMethodParameterDescription = (item, mode) => {
2501
2507
  let description = item?.description;
2502
2508
  let rules = item?.rules;
2503
- if (mode == 'fixed_message') {
2504
- return `${description}\n${t('tcpMethodMessage', {
2505
- direction: rules?.fill_type == 'left' ? t('left') : rules?.fill_type == 'right' ? t('right') : '',
2509
+ if (mode == "fixed_message") {
2510
+ return `${description}\n${t("tcpMethodMessage", {
2511
+ direction: rules?.fill_type == "left"
2512
+ ? t("left")
2513
+ : rules?.fill_type == "right"
2514
+ ? t("right")
2515
+ : "",
2506
2516
  size: rules?.length,
2507
- str: rules?.content_type == 'common' ? RULE_TYPES[rules?.common] : rules?.custom
2517
+ str: rules?.content_type == "common"
2518
+ ? RULE_TYPES[rules?.common]
2519
+ : rules?.custom,
2508
2520
  })}`;
2509
2521
  }
2510
- else if (mode == 'delimiter_message') {
2511
- return `${description}\n${t('tcpMethodDelimiterMessage', {
2512
- str: RULE_TYPES_ICON[rules?.delimiter]
2522
+ else if (mode == "delimiter_message") {
2523
+ return `${description}\n${t("tcpMethodDelimiterMessage", {
2524
+ str: RULE_TYPES_ICON[rules?.delimiter],
2513
2525
  })}`;
2514
2526
  }
2515
2527
  return description;
@@ -2523,9 +2535,9 @@ const handleSocketMethodRequestBody = (body, pathItem) => {
2523
2535
  }
2524
2536
  const mode = MODE_TYPE[body?.mode];
2525
2537
  const parameter = body?.parameter || [];
2526
- if (['delimiter_message', 'fixed_message'].includes(body?.mode)) {
2538
+ if (["delimiter_message", "fixed_message"].includes(body?.mode)) {
2527
2539
  pathItem.requestBody.content[body?.mode] = {
2528
- 'x-mode': body?.mode || '',
2540
+ "x-mode": body?.mode || "",
2529
2541
  schema: {
2530
2542
  type: "object",
2531
2543
  properties: parameter.reduce((lastData, item) => {
@@ -2547,10 +2559,12 @@ const handleSocketMethodRequestBody = (body, pathItem) => {
2547
2559
  else {
2548
2560
  if (mode) {
2549
2561
  pathItem.requestBody.content[mode] = {
2550
- 'x-mode': mode || '',
2551
- schema: lodash.isPlainObject(body?.raw_schema) && !lodash.isEmpty(body?.raw_schema) ? body?.raw_schema : {
2552
- type: "object",
2553
- },
2562
+ "x-mode": mode || "",
2563
+ schema: lodash.isPlainObject(body?.raw_schema) && !lodash.isEmpty(body?.raw_schema)
2564
+ ? body?.raw_schema
2565
+ : {
2566
+ type: "object",
2567
+ },
2554
2568
  example: body?.raw || "",
2555
2569
  };
2556
2570
  }
@@ -2564,22 +2578,22 @@ const handleMessagePathItemrequestBody = (message, pathItem, modelsObj) => {
2564
2578
  content: {},
2565
2579
  };
2566
2580
  }
2567
- message.forEach(m => {
2581
+ message.forEach((m) => {
2568
2582
  const mode = `x-${m?.param_id}`;
2569
2583
  pathItem.requestBody.content[mode] = {
2570
- 'x-mode': m?.request?.mode || '',
2571
- 'x-summary': m?.name || '',
2584
+ "x-mode": m?.request?.mode || "",
2585
+ "x-summary": m?.name || "",
2572
2586
  schema: deepModelRef(m?.request?.raw_schema || {
2573
2587
  type: "object",
2574
2588
  }, modelsObj),
2575
2589
  example: m?.request?.raw || "",
2576
- 'x-response': {
2577
- 'x-mode': m?.response?.mode || '',
2590
+ "x-response": {
2591
+ "x-mode": m?.response?.mode || "",
2578
2592
  example: m?.response?.raw || "",
2579
2593
  schema: deepModelRef(m?.response?.raw_schema || {
2580
2594
  type: "object",
2581
2595
  }, modelsObj),
2582
- }
2596
+ },
2583
2597
  };
2584
2598
  });
2585
2599
  }
@@ -2596,7 +2610,7 @@ const handlePathItemResponse = (response, modelsObj) => {
2596
2610
  content: {
2597
2611
  [MODE_TYPE?.[expect?.content_type] || "*/*"]: {
2598
2612
  schema: deepModelRef(expect?.schema || {}, modelsObj),
2599
- example: raw || '',
2613
+ example: raw || "",
2600
2614
  },
2601
2615
  },
2602
2616
  };
@@ -2639,20 +2653,21 @@ const handlePathItemRequestBody = (body, pathItem, modelsObj) => {
2639
2653
  item.key.length > 0 &&
2640
2654
  (!lodash.has(item, "is_checked") || item?.is_checked > 0)) {
2641
2655
  lastData[item.key] = {
2642
- type: item?.field_type?.toLowerCase() || 'string',
2656
+ type: item?.field_type?.toLowerCase() || "string",
2643
2657
  description: item?.description || "",
2644
2658
  example: item?.value || "",
2645
- ...(lodash.isPlainObject(item?.schema) ? item.schema : {})
2659
+ ...(lodash.isPlainObject(item?.schema) ? item.schema : {}),
2646
2660
  };
2647
2661
  }
2648
2662
  return lastData;
2649
2663
  }, {}),
2650
2664
  required: parameter.reduce((lastArr, item) => {
2651
- if (lodash.isEqual(item?.not_null, 1) && !lodash.includes(lastArr, item.key)) {
2665
+ if (lodash.isEqual(item?.not_null, 1) &&
2666
+ !lodash.includes(lastArr, item.key)) {
2652
2667
  lastArr.push(item.key);
2653
2668
  }
2654
2669
  return lastArr;
2655
- }, [])
2670
+ }, []),
2656
2671
  },
2657
2672
  };
2658
2673
  }
@@ -2670,39 +2685,40 @@ const getUserShowName = (data) => {
2670
2685
  if (lodash.isString(data?.nick_name) && data?.nick_name.length > 0) {
2671
2686
  return data?.nick_name;
2672
2687
  }
2673
- return '';
2688
+ return "";
2674
2689
  };
2675
2690
  const createPathItem = (item, sourceObj, pid, pidName, modelsObj, apiVersion, target_type) => {
2676
2691
  const path = recursiveFolderPath("", sourceObj, pid, pidName);
2677
2692
  const pathItem = {
2678
- 'x-target-id': item?.target_id || '',
2679
- 'x-protocol': target_type || '',
2693
+ "x-target-id": item?.target_id || "",
2694
+ "x-protocol": target_type || "",
2695
+ "x-sort": item?.sort || 0,
2680
2696
  summary: item?.name || "",
2681
2697
  description: item?.description || "",
2682
- tags: []
2698
+ tags: [],
2683
2699
  };
2684
- if (lodash.has(item, 'mock_server_enable')) {
2685
- pathItem['x-mock-server-enable'] = item?.mock_server_enable;
2700
+ if (lodash.has(item, "mock_server_enable")) {
2701
+ pathItem["x-mock-server-enable"] = item?.mock_server_enable;
2686
2702
  }
2687
2703
  if (lodash.isArray(item?.tags) && item?.tags.length > 0) {
2688
- pathItem['x-tags'] = item?.tags;
2704
+ pathItem["x-tags"] = item?.tags;
2689
2705
  }
2690
2706
  if (lodash.isString(item?.mark_id)) {
2691
- pathItem['x-mark-id'] = item?.mark_id;
2707
+ pathItem["x-mark-id"] = item?.mark_id;
2692
2708
  }
2693
2709
  if (lodash.isString(item?.updated_at)) {
2694
- pathItem['x-updated-at'] = item?.updated_at;
2710
+ pathItem["x-updated-at"] = item?.updated_at;
2695
2711
  }
2696
2712
  if (lodash.isString(item?.updated_user?.nick_name)) {
2697
- pathItem['x-updated-user-name'] = getUserShowName(item?.updated_user);
2713
+ pathItem["x-updated-user-name"] = getUserShowName(item?.updated_user);
2698
2714
  }
2699
2715
  if (lodash.isPlainObject(item?.attribute_info) && !lodash.isEmpty(item?.attribute_info)) {
2700
- pathItem['x-attribute-info'] = item?.attribute_info;
2716
+ pathItem["x-attribute-info"] = item?.attribute_info;
2701
2717
  }
2702
2718
  if (item?.is_exampled === 1) {
2703
- pathItem['x-exampled'] = item?.is_exampled;
2719
+ pathItem["x-exampled"] = item?.is_exampled;
2704
2720
  }
2705
- if (['http', 'sse'].includes(target_type)) {
2721
+ if (["http", "sse"].includes(target_type)) {
2706
2722
  // 处理 Query 和 Header 参数
2707
2723
  handlePathItemParameters("query", item?.request?.query?.parameter || [], pathItem);
2708
2724
  handlePathItemParameters("header", item?.request?.header?.parameter || [], pathItem);
@@ -2723,27 +2739,37 @@ const createPathItem = (item, sourceObj, pid, pidName, modelsObj, apiVersion, ta
2723
2739
  // 处理响应
2724
2740
  pathItem.responses = handlePathItemResponse(item?.response || {}, modelsObj);
2725
2741
  }
2726
- else if (['websocket2', 'socketio', 'graphql'].includes(target_type)) {
2742
+ else if (["websocket2", "socketio", "graphql"].includes(target_type)) {
2727
2743
  // 拼接description
2728
2744
  if (lodash.isPlainObject(item?.config)) {
2729
2745
  switch (target_type) {
2730
- case 'socketio':
2746
+ case "socketio":
2731
2747
  pathItem.description = `${pathItem.description}
2732
- ${item?.config?.shake_hands_path ? t('securityConnectionMessage', {
2733
- enable: item?.config?.certificate_verification == -1 ? t('notNeedEnable') : t('needEnable'),
2734
- version: item?.config?.socket_version || 'v4',
2735
- path: item?.config?.shake_hands_path
2736
- }) : t('securityConnectionMessageNoPath', {
2737
- enable: item?.config?.certificate_verification == -1 ? t('notNeedEnable') : t('needEnable'),
2738
- version: item?.config?.socket_version || 'v4',
2739
- })}
2748
+ ${item?.config?.shake_hands_path
2749
+ ? t("securityConnectionMessage", {
2750
+ enable: item?.config?.certificate_verification == -1
2751
+ ? t("notNeedEnable")
2752
+ : t("needEnable"),
2753
+ version: item?.config?.socket_version || "v4",
2754
+ path: item?.config?.shake_hands_path,
2755
+ })
2756
+ : t("securityConnectionMessageNoPath", {
2757
+ enable: item?.config?.certificate_verification == -1
2758
+ ? t("notNeedEnable")
2759
+ : t("needEnable"),
2760
+ version: item?.config?.socket_version || "v4",
2761
+ })}
2740
2762
  `;
2741
2763
  break;
2742
- case 'websocket2':
2764
+ case "websocket2":
2743
2765
  pathItem.description = `${pathItem.description}
2744
- ${t('wsConfigMessage', {
2745
- enable: item?.config?.certificate_verification == -1 ? t('notNeedEnable') : t('needEnable'),
2746
- content: !item?.config?.information_size || item?.config?.information_size == 0 ? t('unlimited') : `${item?.config?.information_size}M`
2766
+ ${t("wsConfigMessage", {
2767
+ enable: item?.config?.certificate_verification == -1
2768
+ ? t("notNeedEnable")
2769
+ : t("needEnable"),
2770
+ content: !item?.config?.information_size || item?.config?.information_size == 0
2771
+ ? t("unlimited")
2772
+ : `${item?.config?.information_size}M`,
2747
2773
  })}
2748
2774
  `;
2749
2775
  break;
@@ -2753,10 +2779,10 @@ ${t('wsConfigMessage', {
2753
2779
  handlePathItemParameters("header", item?.request?.header?.parameter || [], pathItem);
2754
2780
  handlePathItemParameters("cookie", item?.request?.cookie?.parameter || [], pathItem);
2755
2781
  handlePathItemParameters("path", item?.request?.restful?.parameter || [], pathItem);
2756
- if (target_type == 'socketio')
2782
+ if (target_type == "socketio")
2757
2783
  handlePathItemParameters("event", item?.request?.event?.parameter || [], pathItem);
2758
2784
  // 处理请体
2759
- if (target_type == 'graphql') {
2785
+ if (target_type == "graphql") {
2760
2786
  handleGraphqlRequestBody(item?.request?.body?.query_list || [], item?.request?.body?.query_schema || {}, pathItem, modelsObj);
2761
2787
  }
2762
2788
  else {
@@ -2767,22 +2793,24 @@ ${t('wsConfigMessage', {
2767
2793
  pathItem.tags = [...pathItem.tags, path];
2768
2794
  }
2769
2795
  }
2770
- else if (target_type == 'socket_method') {
2796
+ else if (target_type == "socket_method") {
2771
2797
  // 拼接description
2772
2798
  const configs = item?.request?.configs;
2773
2799
  if (lodash.isPlainObject(configs)) {
2774
- let requestString = `${t('requestEncodingFormat', { format: configs?.charset })} ${lodash.isArray(configs?.func?.request) && configs?.func?.request.length > 0 ?
2775
- `\n${t('needAddRequestDataHandleFunction')}\n${configs?.func?.request.map((r, index) => {
2800
+ let requestString = `${t("requestEncodingFormat", {
2801
+ format: configs?.charset,
2802
+ })} ${lodash.isArray(configs?.func?.request) && configs?.func?.request.length > 0
2803
+ ? `\n${t("needAddRequestDataHandleFunction")}\n${configs?.func?.request.map((r, index) => {
2776
2804
  let str = getTcpMethodFucString(r);
2777
2805
  return str;
2778
2806
  })}`
2779
- : ''}`;
2780
- let responseString = lodash.isArray(configs?.func?.response) && configs?.func?.response.length > 0 ?
2781
- `${t('needAddResponseDataHandleFunction')}\n${configs?.func?.response.map((r, index) => {
2807
+ : ""}`;
2808
+ let responseString = lodash.isArray(configs?.func?.response) && configs?.func?.response.length > 0
2809
+ ? `${t("needAddResponseDataHandleFunction")}\n${configs?.func?.response.map((r, index) => {
2782
2810
  let str = getTcpMethodFucString(r);
2783
2811
  return str;
2784
2812
  })}`
2785
- : '';
2813
+ : "";
2786
2814
  pathItem.description = `${pathItem.description}\n${requestString}\n${responseString}`;
2787
2815
  }
2788
2816
  handleSocketMethodRequestBody(item?.request?.body || {}, pathItem);
@@ -2793,7 +2821,7 @@ ${t('wsConfigMessage', {
2793
2821
  pathItem.tags = [...pathItem.tags, path];
2794
2822
  }
2795
2823
  }
2796
- else if (target_type == 'doc') {
2824
+ else if (target_type == "doc") {
2797
2825
  // 添加路径标签
2798
2826
  if (path?.length > 0 && !pathItem.tags.includes(path)) {
2799
2827
  pathItem.tags = [...pathItem.tags, path];
@@ -2803,33 +2831,47 @@ ${t('wsConfigMessage', {
2803
2831
  };
2804
2832
  const getTcpMethodFucString = (func) => {
2805
2833
  switch (func?.id) {
2806
- case 'addCharToPacketEnd':
2807
- return t('addNewline', { str: func?.option });
2808
- case 'calcLengthToPacketHeader':
2809
- return t('addContentLength', { size: func?.option });
2810
- case 'removePacketHeader':
2811
- return t('removeResponseHeader', { size: func?.option });
2812
- case 'removeWrapChar':
2813
- return t('trimTrailingNewline', { str: func?.option });
2814
- case 'parseXmlToJson':
2815
- return t('convertXmlToJson');
2816
- }
2817
- return '';
2834
+ case "addCharToPacketEnd":
2835
+ return t("addNewline", { str: func?.option });
2836
+ case "calcLengthToPacketHeader":
2837
+ return t("addContentLength", { size: func?.option });
2838
+ case "removePacketHeader":
2839
+ return t("removeResponseHeader", { size: func?.option });
2840
+ case "removeWrapChar":
2841
+ return t("trimTrailingNewline", { str: func?.option });
2842
+ case "parseXmlToJson":
2843
+ return t("convertXmlToJson");
2844
+ }
2845
+ return "";
2818
2846
  };
2819
2847
  const getSocketRequestStr = (end_func) => {
2820
2848
  switch (end_func?.name) {
2821
- case 'none':
2822
- return t('completeOnReceive');
2823
- case 'endedChar':
2824
- return t('specialCharText', { str: end_func?.option });
2825
- case 'endedHexChar':
2826
- return t('specialCharBinary', { str: end_func?.option });
2827
- case 'endedHeaderByteLength':
2828
- return t('headerBodyFormat', { size: end_func?.option });
2829
- case 'endedContentByteLength':
2830
- return t('fixedLength', { size: end_func?.option });
2831
- }
2832
- return '';
2849
+ case "none":
2850
+ return t("completeOnReceive");
2851
+ case "endedChar":
2852
+ return t("specialCharText", { str: end_func?.option });
2853
+ case "endedHexChar":
2854
+ return t("specialCharBinary", { str: end_func?.option });
2855
+ case "endedHeaderByteLength":
2856
+ return t("headerBodyFormat", { size: end_func?.option });
2857
+ case "endedContentByteLength":
2858
+ return t("fixedLength", { size: end_func?.option });
2859
+ }
2860
+ return "";
2861
+ };
2862
+ const stripExtensions = (obj) => {
2863
+ if (Array.isArray(obj))
2864
+ return obj.map(stripExtensions);
2865
+ if (obj && typeof obj === "object") {
2866
+ const result = {};
2867
+ for (const key of Object.keys(obj)) {
2868
+ if (!key.startsWith("x-")) {
2869
+ result[key] = stripExtensions(obj[key]);
2870
+ }
2871
+ }
2872
+ return result;
2873
+ }
2874
+ return obj;
2833
2875
  };
2834
2876
 
2835
2877
  // @ts-nocheck
@@ -2860,24 +2902,27 @@ class ApipostToSwagger {
2860
2902
  handleProject() {
2861
2903
  const { name, intro, marks, attributes, project_id } = this.apiToSwaggerOrigin.project || {};
2862
2904
  this.apiToSwaggerResult.info = {
2863
- title: name || t('createProj'),
2905
+ title: name || t("createProj"),
2864
2906
  description: intro || "",
2865
2907
  version: "1.0.0",
2866
2908
  };
2867
2909
  if (lodash.isString(project_id)) {
2868
- this.apiToSwaggerResult.info['x-project-id'] = project_id;
2910
+ this.apiToSwaggerResult.info["x-project-id"] = project_id;
2869
2911
  }
2870
2912
  if (this.apiToSwaggerVersion == "2.0") {
2871
2913
  this.apiToSwaggerResult.swagger = "2.0";
2872
2914
  }
2873
- else if (this.apiToSwaggerVersion == "3.0") {
2915
+ else if (this.apiToSwaggerVersion == "3.1") {
2916
+ this.apiToSwaggerResult.openapi = "3.1.0";
2917
+ }
2918
+ else {
2874
2919
  this.apiToSwaggerResult.openapi = "3.0.0";
2875
2920
  }
2876
2921
  if (lodash.isArray(marks) && marks.length > 0) {
2877
- this.apiToSwaggerResult.info['x-marks'] = marks;
2922
+ this.apiToSwaggerResult.info["x-marks"] = marks;
2878
2923
  }
2879
2924
  if (lodash.isArray(attributes) && attributes.length > 0) {
2880
- this.apiToSwaggerResult.info['x-attributes'] = attributes;
2925
+ this.apiToSwaggerResult.info["x-attributes"] = attributes;
2881
2926
  }
2882
2927
  }
2883
2928
  handleModels() {
@@ -2887,9 +2932,9 @@ class ApipostToSwagger {
2887
2932
  const schemaMap = childModels?.reduce((pre, item) => {
2888
2933
  if (item?.model_id && item?.name) {
2889
2934
  pre[item?.model_id] = {
2890
- schema: item?.schema || { type: 'object', properties: {} },
2935
+ schema: item?.schema || { type: "object", properties: {} },
2891
2936
  path: schemaPath(item?.name, version),
2892
- name: item?.name || '',
2937
+ name: item?.name || "",
2893
2938
  };
2894
2939
  }
2895
2940
  return pre;
@@ -2902,10 +2947,10 @@ class ApipostToSwagger {
2902
2947
  return pre;
2903
2948
  }, {});
2904
2949
  // 先不区分目录概念 把所有的schema都放在schemas目录下 后期加上schemas生成多目录
2905
- if (version === '2.0') {
2950
+ if (version === "2.0") {
2906
2951
  this.apiToSwaggerResult.definitions = schemas;
2907
2952
  }
2908
- if (lodash.isEqual(version, '3.0') || lodash.isEqual(version, '3.0.0')) {
2953
+ if (version === "3.1" || lodash.isEqual(version, "3.0") || lodash.isEqual(version, "3.0.0")) {
2909
2954
  this.apiToSwaggerResult.components = { schemas };
2910
2955
  }
2911
2956
  }
@@ -2918,9 +2963,9 @@ class ApipostToSwagger {
2918
2963
  for (const env of envs) {
2919
2964
  const servers = {
2920
2965
  url: env?.url || "",
2921
- description: env?.name || t('createEnv'),
2922
- "x-env-id": env?.env_id || '',
2923
- "x-selected": env?.is_selected
2966
+ description: env?.name || t("createEnv"),
2967
+ "x-env-id": env?.env_id || "",
2968
+ "x-selected": env?.is_selected,
2924
2969
  };
2925
2970
  if (lodash.isPlainObject(env?.env_var_list)) {
2926
2971
  servers.variables = {};
@@ -3001,17 +3046,18 @@ class ApipostToSwagger {
3001
3046
  if (!id || id == undefined) {
3002
3047
  continue;
3003
3048
  }
3004
- if (['sse', 'api'].includes(item.target_type)) {
3049
+ console.log(item.url, item.sort, "itemitem");
3050
+ if (["sse", "api"].includes(item.target_type)) {
3005
3051
  const temp_url = removeQuery(item?.url);
3006
3052
  const targetMethod = item?.method?.toLowerCase();
3007
- const pathItem = createPathItem(item, sourceObj, pid, pidName, modelsObj, this.apiToSwaggerVersion, item.target_type == 'api' ? 'http' : item.target_type);
3053
+ const pathItem = createPathItem(item, sourceObj, pid, pidName, modelsObj, this.apiToSwaggerVersion, item.target_type == "api" ? "http" : item.target_type);
3008
3054
  if (!paths[temp_url])
3009
3055
  paths[temp_url] = {};
3010
3056
  if (!paths[temp_url][targetMethod]) {
3011
3057
  paths[temp_url][targetMethod] = pathItem;
3012
3058
  }
3013
3059
  else if (pathItem.tags.length > 0) {
3014
- lodash.forEach(pathItem.tags, tag => {
3060
+ lodash.forEach(pathItem.tags, (tag) => {
3015
3061
  if (!paths[temp_url][targetMethod].tags.includes(tag)) {
3016
3062
  // 排除重复目录
3017
3063
  paths[temp_url][targetMethod].tags.push(tag);
@@ -3021,15 +3067,17 @@ class ApipostToSwagger {
3021
3067
  }
3022
3068
  else if (["folder", "socket"].includes(item.target_type)) {
3023
3069
  const path = recursiveFolderPath(`${item?.name}`, sourceObj, pid, pidName);
3070
+ console.log(path, "path-folder");
3024
3071
  item.path = path;
3025
- if (!lodash.has(folderObj, item?.name)) {
3072
+ if (!lodash.has(folderObj, item?.path)) {
3026
3073
  if (!this.apiToSwaggerResult.tags) {
3027
3074
  this.apiToSwaggerResult.tags = [];
3028
3075
  }
3029
3076
  // 收到数据即认为完成(不处理分包问题) none
3030
- if (item.target_type === 'socket' && lodash.isPlainObject(item?.request?.end_func)) {
3031
- if (!folderObj?.[item?.name]) {
3032
- item.description = `| ${t('serverAddress')} | ${t('pktCompletion')} |
3077
+ if (item.target_type === "socket" &&
3078
+ lodash.isPlainObject(item?.request?.end_func)) {
3079
+ if (!folderObj?.[item?.path]) {
3080
+ item.description = `| ${t("serverAddress")} | ${t("pktCompletion")} |
3033
3081
  | --- | --- |
3034
3082
  | ${item?.url} | ${getSocketRequestStr(item?.request?.end_func)}
3035
3083
  ${item.description}
@@ -3037,16 +3085,18 @@ class ApipostToSwagger {
3037
3085
  }
3038
3086
  }
3039
3087
  this.apiToSwaggerResult.tags.push({
3040
- name: item?.name || t('createDir'),
3088
+ name: item?.path || t("createDir"),
3041
3089
  description: item?.description || "",
3042
- 'x-type': item.target_type,
3043
- 'x-url': item?.url || '',
3044
- 'x-target-id': item?.target_id || ''
3090
+ "x-type": item.target_type,
3091
+ "x-url": item?.url || "",
3092
+ "x-target-id": item?.target_id || "",
3093
+ "x-sort": item?.sort || 0,
3094
+ "x-parent-id": item?.parent_id || "",
3045
3095
  });
3046
- folderObj[item?.name] = item;
3096
+ folderObj[item?.path] = item;
3047
3097
  }
3048
3098
  }
3049
- else if (['websocket2', 'socketio', 'graphql'].includes(item.target_type)) {
3099
+ else if (["websocket2", "socketio", "graphql"].includes(item.target_type)) {
3050
3100
  const temp_url = removeQuery(item?.url);
3051
3101
  const targetMethod = `x-${item?.target_id}`;
3052
3102
  const pathItem = createPathItem(item, sourceObj, pid, pidName, modelsObj, this.apiToSwaggerVersion, item.target_type);
@@ -3056,7 +3106,7 @@ class ApipostToSwagger {
3056
3106
  paths[temp_url][targetMethod] = pathItem;
3057
3107
  }
3058
3108
  else if (pathItem.tags.length > 0) {
3059
- lodash.forEach(pathItem.tags, tag => {
3109
+ lodash.forEach(pathItem.tags, (tag) => {
3060
3110
  if (!paths[temp_url][targetMethod].tags.includes(tag)) {
3061
3111
  // 排除重复目录
3062
3112
  paths[temp_url][targetMethod].tags.push(tag);
@@ -3064,8 +3114,10 @@ class ApipostToSwagger {
3064
3114
  });
3065
3115
  }
3066
3116
  }
3067
- else if (item.target_type === 'socket_method') {
3068
- const temp_url = sourceObj?.[pid] && sourceObj[pid]?.url ? sourceObj[pid]?.url : `${item?.name}`;
3117
+ else if (item.target_type === "socket_method") {
3118
+ const temp_url = sourceObj?.[pid] && sourceObj[pid]?.url
3119
+ ? sourceObj[pid]?.url
3120
+ : `${item?.name}`;
3069
3121
  const targetMethod = `x-${item.target_id}`;
3070
3122
  const pathItem = createPathItem(item, sourceObj, pid, pidName, modelsObj, this.apiToSwaggerVersion, item.target_type);
3071
3123
  if (!paths[temp_url])
@@ -3074,7 +3126,7 @@ class ApipostToSwagger {
3074
3126
  paths[temp_url][targetMethod] = pathItem;
3075
3127
  }
3076
3128
  else if (pathItem.tags.length > 0) {
3077
- lodash.forEach(pathItem.tags, tag => {
3129
+ lodash.forEach(pathItem.tags, (tag) => {
3078
3130
  if (!paths[temp_url][targetMethod].tags.includes(tag)) {
3079
3131
  // 排除重复目录
3080
3132
  paths[temp_url][targetMethod].tags.push(tag);
@@ -3082,7 +3134,7 @@ class ApipostToSwagger {
3082
3134
  });
3083
3135
  }
3084
3136
  }
3085
- else if (item.target_type === 'doc') {
3137
+ else if (item.target_type === "doc") {
3086
3138
  const temp_url = `x-${item.target_id}`;
3087
3139
  const targetMethod = `x-${item?.name}`;
3088
3140
  const pathItem = createPathItem(item, sourceObj, pid, pidName, modelsObj, this.apiToSwaggerVersion, item.target_type);
@@ -3092,7 +3144,7 @@ class ApipostToSwagger {
3092
3144
  paths[temp_url][targetMethod] = pathItem;
3093
3145
  }
3094
3146
  else if (pathItem.tags.length > 0) {
3095
- lodash.forEach(pathItem.tags, tag => {
3147
+ lodash.forEach(pathItem.tags, (tag) => {
3096
3148
  if (!paths[temp_url][targetMethod].tags.includes(tag)) {
3097
3149
  // 排除重复目录
3098
3150
  paths[temp_url][targetMethod].tags.push(tag);
@@ -3127,9 +3179,81 @@ class ApipostToSwagger {
3127
3179
  this.array2TreeExport("target_id", "parent_id", modelsObj) || [];
3128
3180
  }
3129
3181
  }
3130
- async apiToswagger(apipostObj, version, language) {
3182
+ sortApiNodes(nodes) {
3183
+ // 1. 构建 id -> node 的映射,并初始化 children
3184
+ const nodeMap = new Map();
3185
+ nodes.forEach((node) => {
3186
+ nodeMap.set(node["x-target-id"], { ...node, children: [] });
3187
+ });
3188
+ const roots = [];
3189
+ // 2. 建立父子关系
3190
+ nodes.forEach((node) => {
3191
+ const currentNode = nodeMap.get(node["x-target-id"]);
3192
+ const parentId = node["x-parent-id"];
3193
+ if (parentId === "0") {
3194
+ roots.push(currentNode);
3195
+ }
3196
+ else {
3197
+ const parent = nodeMap.get(parentId);
3198
+ if (parent) {
3199
+ parent.children.push(currentNode);
3200
+ }
3201
+ else {
3202
+ // 防御:若父节点不存在,当作根节点处理
3203
+ roots.push(currentNode);
3204
+ }
3205
+ }
3206
+ });
3207
+ // 辅助排序函数:按 x-sort 升序
3208
+ const sortBySort = (arr) => {
3209
+ arr.sort((a, b) => a["x-sort"] - b["x-sort"]);
3210
+ };
3211
+ // 3. 递归排序子节点
3212
+ const sortChildrenRecursively = (node) => {
3213
+ sortBySort(node.children);
3214
+ node.children.forEach((child) => sortChildrenRecursively(child));
3215
+ };
3216
+ sortBySort(roots); // 根节点排序
3217
+ roots.forEach((root) => sortChildrenRecursively(root));
3218
+ // 4. 深度优先遍历(先序)
3219
+ const result = [];
3220
+ const dfs = (node) => {
3221
+ result.push(node);
3222
+ node.children.forEach((child) => dfs(child));
3223
+ };
3224
+ roots.forEach((root) => dfs(root));
3225
+ // 5. 去除 children 字段,只保留原始属性
3226
+ return result.map(({ children, ...rest }) => rest);
3227
+ }
3228
+ sortByXSortPreserveStructure(data, ascending = true) {
3229
+ // 提取所有 (path, method, sort)
3230
+ const items = [];
3231
+ for (const [path, methods] of Object.entries(data)) {
3232
+ for (const [method, spec] of Object.entries(methods)) {
3233
+ if (spec && typeof spec === "object" && spec["x-sort"] !== undefined) {
3234
+ items.push({ path, method, sort: spec["x-sort"] });
3235
+ }
3236
+ }
3237
+ }
3238
+ // 排序
3239
+ items.sort((a, b) => (ascending ? a.sort - b.sort : b.sort - a.sort));
3240
+ // 重建对象
3241
+ const result = {};
3242
+ for (const { path, method } of items) {
3243
+ if (!result[path])
3244
+ result[path] = {};
3245
+ result[path][method] = data[path][method];
3246
+ }
3247
+ // 调试:打印排序后的 x-sort 列表
3248
+ console.log("排序后的顺序(path + method + x-sort):");
3249
+ items.forEach((item) => {
3250
+ console.log(` ${item.sort} - ${item.method} ${item.path}`);
3251
+ });
3252
+ return result;
3253
+ }
3254
+ async apiToswagger(apipostObj, version, language, options) {
3131
3255
  // 初始化语言包
3132
- await I18nInit(language || 'en');
3256
+ await I18nInit(language || "en");
3133
3257
  this.apiToSwaggerOrigin = apipostObj;
3134
3258
  this.apiToSwaggerVersion = version;
3135
3259
  this.apiToSwaggerResult = {};
@@ -3148,7 +3272,35 @@ class ApipostToSwagger {
3148
3272
  if (`${version}` === "metersphere") {
3149
3273
  this.formatData();
3150
3274
  }
3151
- return throwResult$2("success", "", this.apiToSwaggerResult);
3275
+ // 处理tags排序问题
3276
+ const tempTags = this.sortApiNodes(this.apiToSwaggerResult?.tags || []);
3277
+ // 处理paths排序
3278
+ console.log(this.apiToSwaggerResult.paths, "this.apiToSwaggerResult.paths");
3279
+ const tempPaths = this.sortByXSortPreserveStructure(this.apiToSwaggerResult?.paths);
3280
+ // this.apiToSwaggerResult.paths
3281
+ // sortBy(this.apiToSwaggerResult?.tags, ["x-sort", "x-parent-id"]);
3282
+ let result = { ...this.apiToSwaggerResult, tags: tempTags, paths: tempPaths };
3283
+ if (options) {
3284
+ if (options.includeExt === false) {
3285
+ result = stripExtensions(result);
3286
+ }
3287
+ if (options.dirAsTags === false) {
3288
+ result.tags = [];
3289
+ if (result.paths && typeof result.paths === "object") {
3290
+ for (const p of Object.keys(result.paths)) {
3291
+ const ms = result.paths[p];
3292
+ if (ms && typeof ms === "object") {
3293
+ for (const m of Object.keys(ms)) {
3294
+ if (ms[m] && Array.isArray(ms[m].tags)) {
3295
+ ms[m].tags = [];
3296
+ }
3297
+ }
3298
+ }
3299
+ }
3300
+ }
3301
+ }
3302
+ }
3303
+ return throwResult$2("success", "", result);
3152
3304
  }
3153
3305
  catch (error) {
3154
3306
  console.log(JSON.stringify(error.message), "error");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "echoapi-converter",
3
- "version": "0.0.90",
3
+ "version": "1.0.1",
4
4
  "description": "echoapi to swagger",
5
5
  "main": "dist/index.js",
6
6
  "files": [