zentao-api 0.3.2 → 0.4.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.
@@ -174,7 +174,6 @@ export const BUILTIN_MODULES = [
174
174
  pathParams: {
175
175
  userID: '用户ID',
176
176
  },
177
- render: 'action',
178
177
  }
179
178
  ],
180
179
  },
@@ -344,7 +343,6 @@ export const BUILTIN_MODULES = [
344
343
  pathParams: {
345
344
  programID: '项目集ID',
346
345
  },
347
- render: 'action',
348
346
  }
349
347
  ],
350
348
  },
@@ -562,7 +560,6 @@ export const BUILTIN_MODULES = [
562
560
  pathParams: {
563
561
  productID: '产品ID',
564
562
  },
565
- render: 'action',
566
563
  }
567
564
  ],
568
565
  },
@@ -757,7 +754,6 @@ export const BUILTIN_MODULES = [
757
754
  pathParams: {
758
755
  projectID: '项目ID',
759
756
  },
760
- render: 'action',
761
757
  }
762
758
  ],
763
759
  },
@@ -1006,7 +1002,6 @@ export const BUILTIN_MODULES = [
1006
1002
  pathParams: {
1007
1003
  executionID: '执行ID',
1008
1004
  },
1009
- render: 'action',
1010
1005
  }
1011
1006
  ],
1012
1007
  },
@@ -1021,13 +1016,12 @@ export const BUILTIN_MODULES = [
1021
1016
  display: '获取产品计划列表,支持获取产品下的产品计划',
1022
1017
  type: 'list',
1023
1018
  method: 'get',
1024
- path: '/{scope}/{scopeID}/productplans',
1019
+ path: '/products/{productID}/productplans',
1025
1020
  resultType: 'list',
1026
1021
  pagerGetter: 'pager',
1027
1022
  resultGetter: 'productplans',
1028
1023
  pathParams: {
1029
- scope: { description: '产品计划范围', options: [{ value: 'products', label: '产品' }] },
1030
- scopeID: '范围ID',
1024
+ productID: '所属产品ID',
1031
1025
  },
1032
1026
  params: [
1033
1027
  {
@@ -1193,7 +1187,6 @@ export const BUILTIN_MODULES = [
1193
1187
  pathParams: {
1194
1188
  productplanID: '产品计划ID',
1195
1189
  },
1196
- render: 'action',
1197
1190
  }
1198
1191
  ],
1199
1192
  },
@@ -1213,8 +1206,8 @@ export const BUILTIN_MODULES = [
1213
1206
  pagerGetter: 'pager',
1214
1207
  resultGetter: 'stories',
1215
1208
  pathParams: {
1216
- scope: { description: '需求范围', options: [{ value: 'products', label: '产品' }, { value: 'projects', label: '项目' }, { value: 'executions', label: '执行' }] },
1217
- scopeID: '范围ID',
1209
+ scope: { description: '需求所属范围', options: [{ value: 'products', label: '产品' }, { value: 'projects', label: '项目' }, { value: 'executions', label: '执行' }] },
1210
+ scopeID: '所属范围ID',
1218
1211
  },
1219
1212
  params: [
1220
1213
  {
@@ -1423,7 +1416,6 @@ export const BUILTIN_MODULES = [
1423
1416
  pathParams: {
1424
1417
  storyID: '需求ID',
1425
1418
  },
1426
- render: 'action',
1427
1419
  }, {
1428
1420
  name: 'activate',
1429
1421
  display: '激活需求',
@@ -1451,7 +1443,6 @@ export const BUILTIN_MODULES = [
1451
1443
  }
1452
1444
  },
1453
1445
  },
1454
- render: 'action',
1455
1446
  }, {
1456
1447
  name: 'change',
1457
1448
  display: '变更需求',
@@ -1493,7 +1484,6 @@ export const BUILTIN_MODULES = [
1493
1484
  ]
1494
1485
  },
1495
1486
  },
1496
- render: 'action',
1497
1487
  }, {
1498
1488
  name: 'close',
1499
1489
  display: '关闭需求',
@@ -1524,7 +1514,6 @@ export const BUILTIN_MODULES = [
1524
1514
  ]
1525
1515
  },
1526
1516
  },
1527
- render: 'action',
1528
1517
  }
1529
1518
  ],
1530
1519
  },
@@ -1539,13 +1528,12 @@ export const BUILTIN_MODULES = [
1539
1528
  display: '获取业务需求列表,支持获取产品下的业务需求',
1540
1529
  type: 'list',
1541
1530
  method: 'get',
1542
- path: '/{scope}/{scopeID}/epics',
1531
+ path: '/products/{productID}/epics',
1543
1532
  resultType: 'list',
1544
1533
  pagerGetter: 'pager',
1545
1534
  resultGetter: 'epics',
1546
1535
  pathParams: {
1547
- scope: { description: '业务需求范围', options: [{ value: 'products', label: '产品' }] },
1548
- scopeID: '范围ID',
1536
+ productID: '所属产品ID',
1549
1537
  },
1550
1538
  params: [
1551
1539
  {
@@ -1745,7 +1733,6 @@ export const BUILTIN_MODULES = [
1745
1733
  pathParams: {
1746
1734
  epicID: '业务需求ID',
1747
1735
  },
1748
- render: 'action',
1749
1736
  }, {
1750
1737
  name: 'activate',
1751
1738
  display: '激活业务需求',
@@ -1773,7 +1760,6 @@ export const BUILTIN_MODULES = [
1773
1760
  }
1774
1761
  },
1775
1762
  },
1776
- render: 'action',
1777
1763
  }, {
1778
1764
  name: 'change',
1779
1765
  display: '变更业务需求',
@@ -1815,7 +1801,6 @@ export const BUILTIN_MODULES = [
1815
1801
  ]
1816
1802
  },
1817
1803
  },
1818
- render: 'action',
1819
1804
  }, {
1820
1805
  name: 'close',
1821
1806
  display: '关闭业务需求',
@@ -1846,7 +1831,6 @@ export const BUILTIN_MODULES = [
1846
1831
  ]
1847
1832
  },
1848
1833
  },
1849
- render: 'action',
1850
1834
  }
1851
1835
  ],
1852
1836
  },
@@ -1861,13 +1845,12 @@ export const BUILTIN_MODULES = [
1861
1845
  display: '获取用户需求列表,支持获取产品下的用户需求',
1862
1846
  type: 'list',
1863
1847
  method: 'get',
1864
- path: '/{scope}/{scopeID}/requirements',
1848
+ path: '/products/{productID}/requirements',
1865
1849
  resultType: 'list',
1866
1850
  pagerGetter: 'pager',
1867
1851
  resultGetter: 'requirements',
1868
1852
  pathParams: {
1869
- scope: { description: '用户需求范围', options: [{ value: 'products', label: '产品' }] },
1870
- scopeID: '范围ID',
1853
+ productID: '所属产品ID',
1871
1854
  },
1872
1855
  params: [
1873
1856
  {
@@ -2066,7 +2049,6 @@ export const BUILTIN_MODULES = [
2066
2049
  pathParams: {
2067
2050
  requirementID: '用户需求ID',
2068
2051
  },
2069
- render: 'action',
2070
2052
  }, {
2071
2053
  name: 'activate',
2072
2054
  display: '激活用户需求',
@@ -2094,7 +2076,6 @@ export const BUILTIN_MODULES = [
2094
2076
  }
2095
2077
  },
2096
2078
  },
2097
- render: 'action',
2098
2079
  }, {
2099
2080
  name: 'change',
2100
2081
  display: '变更用户需求',
@@ -2126,7 +2107,6 @@ export const BUILTIN_MODULES = [
2126
2107
  }
2127
2108
  },
2128
2109
  },
2129
- render: 'action',
2130
2110
  }, {
2131
2111
  name: 'close',
2132
2112
  display: '关闭用户需求',
@@ -2157,7 +2137,6 @@ export const BUILTIN_MODULES = [
2157
2137
  ]
2158
2138
  },
2159
2139
  },
2160
- render: 'action',
2161
2140
  }
2162
2141
  ],
2163
2142
  },
@@ -2177,8 +2156,8 @@ export const BUILTIN_MODULES = [
2177
2156
  pagerGetter: 'pager',
2178
2157
  resultGetter: 'bugs',
2179
2158
  pathParams: {
2180
- scope: { description: 'Bug范围', options: [{ value: 'products', label: '产品' }, { value: 'projects', label: '项目' }, { value: 'executions', label: '执行' }] },
2181
- scopeID: '范围ID',
2159
+ scope: { description: 'Bug所属范围', options: [{ value: 'products', label: '产品' }, { value: 'projects', label: '项目' }, { value: 'executions', label: '执行' }] },
2160
+ scopeID: '所属范围ID',
2182
2161
  },
2183
2162
  params: [
2184
2163
  {
@@ -2376,7 +2355,6 @@ export const BUILTIN_MODULES = [
2376
2355
  pathParams: {
2377
2356
  bugID: 'Bug ID',
2378
2357
  },
2379
- render: 'action',
2380
2358
  }, {
2381
2359
  name: 'activate',
2382
2360
  display: '激活Bug',
@@ -2411,7 +2389,6 @@ export const BUILTIN_MODULES = [
2411
2389
  }
2412
2390
  },
2413
2391
  },
2414
- render: 'action',
2415
2392
  }, {
2416
2393
  name: 'close',
2417
2394
  display: '关闭Bug',
@@ -2435,7 +2412,6 @@ export const BUILTIN_MODULES = [
2435
2412
  }
2436
2413
  },
2437
2414
  },
2438
- render: 'action',
2439
2415
  }, {
2440
2416
  name: 'resolve',
2441
2417
  display: '解决Bug',
@@ -2478,7 +2454,6 @@ export const BUILTIN_MODULES = [
2478
2454
  ]
2479
2455
  },
2480
2456
  },
2481
- render: 'action',
2482
2457
  }
2483
2458
  ],
2484
2459
  },
@@ -2498,8 +2473,8 @@ export const BUILTIN_MODULES = [
2498
2473
  pagerGetter: 'pager',
2499
2474
  resultGetter: 'testcases',
2500
2475
  pathParams: {
2501
- scope: { description: '测试用例范围', options: [{ value: 'products', label: '产品' }, { value: 'projects', label: '项目' }, { value: 'executions', label: '执行' }] },
2502
- scopeID: '范围ID',
2476
+ scope: { description: '测试用例所属范围', options: [{ value: 'products', label: '产品' }, { value: 'projects', label: '项目' }, { value: 'executions', label: '执行' }] },
2477
+ scopeID: '所属范围ID',
2503
2478
  },
2504
2479
  params: [
2505
2480
  {
@@ -2715,7 +2690,6 @@ export const BUILTIN_MODULES = [
2715
2690
  pathParams: {
2716
2691
  testcasID: '测试用例ID',
2717
2692
  },
2718
- render: 'action',
2719
2693
  }
2720
2694
  ],
2721
2695
  },
@@ -2730,13 +2704,12 @@ export const BUILTIN_MODULES = [
2730
2704
  display: '获取任务列表,支持获取执行下的任务',
2731
2705
  type: 'list',
2732
2706
  method: 'get',
2733
- path: '/{scope}/{scopeID}/tasks',
2707
+ path: '/executions/{executionID}/tasks',
2734
2708
  resultType: 'list',
2735
2709
  pagerGetter: 'pager',
2736
2710
  resultGetter: 'tasks',
2737
2711
  pathParams: {
2738
- scope: { description: '任务范围', options: [{ value: 'executions', label: '执行' }] },
2739
- scopeID: '范围ID',
2712
+ executionID: '所属执行ID',
2740
2713
  },
2741
2714
  params: [
2742
2715
  {
@@ -2933,7 +2906,6 @@ export const BUILTIN_MODULES = [
2933
2906
  pathParams: {
2934
2907
  taskID: '任务ID',
2935
2908
  },
2936
- render: 'action',
2937
2909
  }, {
2938
2910
  name: 'activate',
2939
2911
  display: '激活任务',
@@ -2966,7 +2938,6 @@ export const BUILTIN_MODULES = [
2966
2938
  }
2967
2939
  },
2968
2940
  },
2969
- render: 'action',
2970
2941
  }, {
2971
2942
  name: 'close',
2972
2943
  display: '关闭任务',
@@ -2990,7 +2961,6 @@ export const BUILTIN_MODULES = [
2990
2961
  }
2991
2962
  },
2992
2963
  },
2993
- render: 'action',
2994
2964
  }, {
2995
2965
  name: 'finish',
2996
2966
  display: '完成任务',
@@ -3041,7 +3011,6 @@ export const BUILTIN_MODULES = [
3041
3011
  ]
3042
3012
  },
3043
3013
  },
3044
- render: 'action',
3045
3014
  }, {
3046
3015
  name: 'start',
3047
3016
  display: '启动任务',
@@ -3086,7 +3055,6 @@ export const BUILTIN_MODULES = [
3086
3055
  ]
3087
3056
  },
3088
3057
  },
3089
- render: 'action',
3090
3058
  }
3091
3059
  ],
3092
3060
  },
@@ -3101,13 +3069,12 @@ export const BUILTIN_MODULES = [
3101
3069
  display: '获取反馈列表,支持获取产品下的反馈',
3102
3070
  type: 'list',
3103
3071
  method: 'get',
3104
- path: '/{scope}/{scopeID}/feedbacks',
3072
+ path: '/products/{productID}/feedbacks',
3105
3073
  resultType: 'list',
3106
3074
  pagerGetter: 'pager',
3107
3075
  resultGetter: 'feedbacks',
3108
3076
  pathParams: {
3109
- scope: { description: '反馈范围', options: [{ value: 'products', label: '产品' }] },
3110
- scopeID: '范围ID',
3077
+ productID: '所属产品ID',
3111
3078
  },
3112
3079
  params: [
3113
3080
  {
@@ -3277,7 +3244,6 @@ export const BUILTIN_MODULES = [
3277
3244
  pathParams: {
3278
3245
  feedbackID: '反馈ID',
3279
3246
  },
3280
- render: 'action',
3281
3247
  }, {
3282
3248
  name: 'activate',
3283
3249
  display: '激活反馈',
@@ -3305,7 +3271,6 @@ export const BUILTIN_MODULES = [
3305
3271
  }
3306
3272
  },
3307
3273
  },
3308
- render: 'action',
3309
3274
  }, {
3310
3275
  name: 'close',
3311
3276
  display: '关闭反馈',
@@ -3336,7 +3301,6 @@ export const BUILTIN_MODULES = [
3336
3301
  ]
3337
3302
  },
3338
3303
  },
3339
- render: 'action',
3340
3304
  }
3341
3305
  ],
3342
3306
  },
@@ -3351,13 +3315,12 @@ export const BUILTIN_MODULES = [
3351
3315
  display: '获取工单列表,支持获取产品下的工单',
3352
3316
  type: 'list',
3353
3317
  method: 'get',
3354
- path: '/{scope}/{scopeID}/tickets',
3318
+ path: '/products/{productID}/tickets',
3355
3319
  resultType: 'list',
3356
3320
  pagerGetter: 'pager',
3357
3321
  resultGetter: 'tickets',
3358
3322
  pathParams: {
3359
- scope: { description: '工单范围', options: [{ value: 'products', label: '产品' }] },
3360
- scopeID: '范围ID',
3323
+ productID: '所属产品ID',
3361
3324
  },
3362
3325
  params: [
3363
3326
  {
@@ -3538,7 +3501,6 @@ export const BUILTIN_MODULES = [
3538
3501
  pathParams: {
3539
3502
  ticketID: '工单ID',
3540
3503
  },
3541
- render: 'action',
3542
3504
  }, {
3543
3505
  name: 'activate',
3544
3506
  display: '激活工单',
@@ -3566,7 +3528,6 @@ export const BUILTIN_MODULES = [
3566
3528
  }
3567
3529
  },
3568
3530
  },
3569
- render: 'action',
3570
3531
  }, {
3571
3532
  name: 'close',
3572
3533
  display: '关闭工单',
@@ -3598,7 +3559,6 @@ export const BUILTIN_MODULES = [
3598
3559
  ]
3599
3560
  },
3600
3561
  },
3601
- render: 'action',
3602
3562
  }
3603
3563
  ],
3604
3564
  },
@@ -3613,12 +3573,11 @@ export const BUILTIN_MODULES = [
3613
3573
  display: '获取应用列表,支持获取产品下的应用',
3614
3574
  type: 'list',
3615
3575
  method: 'get',
3616
- path: '/{scope}/{scopeID}/systems',
3576
+ path: '/products/{productID}/systems',
3617
3577
  resultType: 'list',
3618
3578
  pagerGetter: 'pager',
3619
3579
  pathParams: {
3620
- scope: { description: '应用范围', options: [{ value: 'products', label: '产品' }] },
3621
- scopeID: '范围ID',
3580
+ productID: '所属产品ID',
3622
3581
  },
3623
3582
  }, {
3624
3583
  name: 'create',
@@ -3724,8 +3683,8 @@ export const BUILTIN_MODULES = [
3724
3683
  pagerGetter: 'pager',
3725
3684
  resultGetter: 'builds',
3726
3685
  pathParams: {
3727
- scope: { description: '版本范围', options: [{ value: 'projects', label: '项目' }, { value: 'executions', label: '执行' }] },
3728
- scopeID: '范围ID',
3686
+ scope: { description: '版本所属范围', options: [{ value: 'projects', label: '项目' }, { value: 'executions', label: '执行' }] },
3687
+ scopeID: '所属范围ID',
3729
3688
  },
3730
3689
  }, {
3731
3690
  name: 'create',
@@ -3866,7 +3825,6 @@ export const BUILTIN_MODULES = [
3866
3825
  pathParams: {
3867
3826
  buildID: '版本ID',
3868
3827
  },
3869
- render: 'action',
3870
3828
  }
3871
3829
  ],
3872
3830
  },
@@ -3886,8 +3844,8 @@ export const BUILTIN_MODULES = [
3886
3844
  pagerGetter: 'pager',
3887
3845
  resultGetter: 'testtasks',
3888
3846
  pathParams: {
3889
- scope: { description: '测试单范围', options: [{ value: 'products', label: '产品' }, { value: 'projects', label: '项目' }, { value: 'executions', label: '执行' }] },
3890
- scopeID: '范围ID',
3847
+ scope: { description: '测试单所属范围', options: [{ value: 'products', label: '产品' }, { value: 'projects', label: '项目' }, { value: 'executions', label: '执行' }] },
3848
+ scopeID: '所属范围ID',
3891
3849
  },
3892
3850
  }, {
3893
3851
  name: 'create',
@@ -4034,7 +3992,6 @@ export const BUILTIN_MODULES = [
4034
3992
  pathParams: {
4035
3993
  testtaskID: '测试单ID',
4036
3994
  },
4037
- render: 'action',
4038
3995
  }
4039
3996
  ],
4040
3997
  },
@@ -4049,13 +4006,12 @@ export const BUILTIN_MODULES = [
4049
4006
  display: '获取发布列表,支持获取产品下的发布',
4050
4007
  type: 'list',
4051
4008
  method: 'get',
4052
- path: '/{scope}/{scopeID}/releases',
4009
+ path: '/products/{productID}/releases',
4053
4010
  resultType: 'list',
4054
4011
  pagerGetter: 'pager',
4055
4012
  resultGetter: 'releases',
4056
4013
  pathParams: {
4057
- scope: { description: '发布范围', options: [{ value: 'products', label: '产品' }] },
4058
- scopeID: '范围ID',
4014
+ productID: '所属产品ID',
4059
4015
  },
4060
4016
  }, {
4061
4017
  name: 'create',
@@ -4176,7 +4132,6 @@ export const BUILTIN_MODULES = [
4176
4132
  pathParams: {
4177
4133
  releasID: '发布ID',
4178
4134
  },
4179
- render: 'action',
4180
4135
  }
4181
4136
  ],
4182
4137
  },
@@ -4219,7 +4174,6 @@ export const BUILTIN_MODULES = [
4219
4174
  pathParams: {
4220
4175
  fileID: '附件ID',
4221
4176
  },
4222
- render: 'action',
4223
4177
  }
4224
4178
  ],
4225
4179
  }
@@ -1,4 +1,4 @@
1
- import type { ModuleAction, ModuleDefinition } from '../types/index.js';
1
+ import type { ModuleAction, ModuleActionParam, ModuleActionParamRole, ModuleDefinition } from '../types/index.js';
2
2
  /**
3
3
  * 获取模块定义。
4
4
  *
@@ -27,6 +27,18 @@ export declare function getModule(moduleName: string): ModuleDefinition;
27
27
  * @throws {ZentaoError} `E_INVALID_MODULE`(模块未注册)或 `E_INVALID_ACTION`(动作不存在)。
28
28
  */
29
29
  export declare function getModuleAction(moduleName: string, actionName: string): ModuleAction;
30
+ /**
31
+ * 获取指定模块下的某个动作的参数。
32
+ *
33
+ * @param moduleName - 模块名(大小写不敏感)。
34
+ * @param actionName - 动作名(大小写不敏感);支持 `ls` 作为 `list` 的别名。
35
+ * @param options - 选项。
36
+ * @param options.roles - 角色,可选 `path`、`query`、`body`。
37
+ * @returns 动作参数。
38
+ */
39
+ export declare function getModuleActionParams(moduleName: string, actionName: string, options?: {
40
+ roles?: ModuleActionParamRole[];
41
+ }): ModuleActionParam[];
30
42
  /**
31
43
  * 返回当前运行时注册表中的所有模块名。
32
44
  *
@@ -47,6 +47,65 @@ export function getModuleAction(moduleName, actionName) {
47
47
  }
48
48
  throw new ZentaoError('E_INVALID_ACTION', { module: moduleName, action: actionName });
49
49
  }
50
+ /**
51
+ * 获取指定模块下的某个动作的参数。
52
+ *
53
+ * @param moduleName - 模块名(大小写不敏感)。
54
+ * @param actionName - 动作名(大小写不敏感);支持 `ls` 作为 `list` 的别名。
55
+ * @param options - 选项。
56
+ * @param options.roles - 角色,可选 `path`、`query`、`body`。
57
+ * @returns 动作参数。
58
+ */
59
+ export function getModuleActionParams(moduleName, actionName, options) {
60
+ const { roles } = options ?? {};
61
+ const params = [];
62
+ const action = getModuleAction(moduleName, actionName);
63
+ if (!action) {
64
+ return [];
65
+ }
66
+ if (action.pathParams && (!roles || roles.includes('path'))) {
67
+ Object.entries(action.pathParams).forEach(([name, param]) => {
68
+ if (typeof param === 'string') {
69
+ param = {
70
+ description: param,
71
+ };
72
+ }
73
+ params.push({
74
+ name,
75
+ role: 'path',
76
+ required: true,
77
+ ...param,
78
+ });
79
+ });
80
+ }
81
+ if (action.params && (!roles || roles.includes('query'))) {
82
+ params.push(...action.params.map(x => ({ ...x, role: 'query' })));
83
+ }
84
+ const schema = action.requestBody?.schema;
85
+ if (schema && (!roles || roles.includes('body'))) {
86
+ if (schema.type === 'object') {
87
+ const requiredSet = new Set(schema.required ? schema.required.map(x => x.toLowerCase()) : []);
88
+ Object.entries(schema.properties).forEach(([name, property]) => {
89
+ params.push({
90
+ name,
91
+ role: 'body',
92
+ required: property.required ?? requiredSet.has(name.toLowerCase()),
93
+ type: (property.type === 'integer' ? 'number' : property.type) ?? 'string',
94
+ ...property,
95
+ });
96
+ });
97
+ }
98
+ else {
99
+ params.push({
100
+ name: 'data',
101
+ role: 'body',
102
+ required: true,
103
+ type: schema.type,
104
+ });
105
+ }
106
+ }
107
+ return params;
108
+ }
50
109
  /**
51
110
  * 返回当前运行时注册表中的所有模块名。
52
111
  *
@@ -10,6 +10,14 @@ export declare function deepClone<T>(value: T): T;
10
10
  export declare function deepMerge<T>(base: T, patch: unknown): T;
11
11
  export declare function cloneBuiltinModules(): ModuleDefinition[];
12
12
  export declare function deepFreeze<T>(value: T): T;
13
+ /**
14
+ * 按 {@link ModuleAction.type} 就地补齐省略的 `method` / `resultType`。
15
+ *
16
+ * 已显式声明的字段保持不变;当 `type` 无法推导出对应默认值时抛错,提示显式指定。
17
+ *
18
+ * @throws {ZentaoError} `E_INDETERMINATE_ACTION_METHOD` / `E_INDETERMINATE_ACTION_RESULT_TYPE`
19
+ */
20
+ export declare function normalizeAction(action: ModuleAction): ModuleAction;
13
21
  export declare function freezeAction(action: ModuleAction): ModuleAction;
14
22
  export declare function freezeModule(module: ModuleDefinition): ModuleDefinition;
15
23
  export declare function freezeModules(source: ModuleDefinition[]): ModuleDefinition[];
@@ -1,6 +1,23 @@
1
1
  import { ZentaoError } from '../misc/errors.js';
2
2
  import { isRecord } from '../utils/object.js';
3
3
  import { BUILTIN_MODULES } from './generated.js';
4
+ // 动作类型到 HTTP 方法 / 结果形态的默认推导表:当动作未显式声明 method / resultType 时按 type 补齐。
5
+ const METHOD_BY_TYPE = {
6
+ list: 'get',
7
+ get: 'get',
8
+ create: 'post',
9
+ update: 'put',
10
+ delete: 'delete',
11
+ action: 'post',
12
+ };
13
+ const RESULT_TYPE_BY_TYPE = {
14
+ list: 'list',
15
+ get: 'object',
16
+ create: 'object',
17
+ update: 'object',
18
+ delete: 'text',
19
+ action: 'text',
20
+ };
4
21
  // 运行时注册表存放「深克隆 + 深冻结」后的模块定义:
5
22
  // - 深克隆:避免用户后续修改自己的输入对象时污染注册表;
6
23
  // - 深冻结:让 getModule / getModuleAction 可以零拷贝返回引用,
@@ -57,8 +74,32 @@ export function deepFreeze(value) {
57
74
  }
58
75
  return Object.freeze(value);
59
76
  }
77
+ /**
78
+ * 按 {@link ModuleAction.type} 就地补齐省略的 `method` / `resultType`。
79
+ *
80
+ * 已显式声明的字段保持不变;当 `type` 无法推导出对应默认值时抛错,提示显式指定。
81
+ *
82
+ * @throws {ZentaoError} `E_INDETERMINATE_ACTION_METHOD` / `E_INDETERMINATE_ACTION_RESULT_TYPE`
83
+ */
84
+ export function normalizeAction(action) {
85
+ if (action.method === undefined) {
86
+ const method = METHOD_BY_TYPE[action.type];
87
+ if (method === undefined) {
88
+ throw new ZentaoError('E_INDETERMINATE_ACTION_METHOD', { action: String(action.name), type: String(action.type) });
89
+ }
90
+ action.method = method;
91
+ }
92
+ if (action.resultType === undefined) {
93
+ const resultType = RESULT_TYPE_BY_TYPE[action.type];
94
+ if (resultType === undefined) {
95
+ throw new ZentaoError('E_INDETERMINATE_ACTION_RESULT_TYPE', { action: String(action.name), type: String(action.type) });
96
+ }
97
+ action.resultType = resultType;
98
+ }
99
+ return action;
100
+ }
60
101
  export function freezeAction(action) {
61
- return deepFreeze(action);
102
+ return deepFreeze(normalizeAction(action));
62
103
  }
63
104
  export function freezeModule(module) {
64
105
  module.actions.forEach(freezeAction);
@@ -102,7 +143,14 @@ export function validateModule(module) {
102
143
  }
103
144
  }
104
145
  export function validateAction(action) {
105
- if (!action || typeof action.name !== 'string' || typeof action.path !== 'string' || typeof action.method !== 'string') {
146
+ if (!action || typeof action.name !== 'string' || typeof action.path !== 'string') {
147
+ throw new ZentaoError('E_INVALID_ACTION_DEFINITION');
148
+ }
149
+ // method / resultType 可省略(由 normalizeAction 按 type 推导),但显式给出时必须是字符串。
150
+ if (action.method !== undefined && typeof action.method !== 'string') {
151
+ throw new ZentaoError('E_INVALID_ACTION_DEFINITION');
152
+ }
153
+ if (action.resultType !== undefined && typeof action.resultType !== 'string') {
106
154
  throw new ZentaoError('E_INVALID_ACTION_DEFINITION');
107
155
  }
108
156
  }